diff --git a/DiscImageChef.Decoders/ChangeLog b/DiscImageChef.Decoders/ChangeLog index de8462e49..92da95ba1 100644 --- a/DiscImageChef.Decoders/ChangeLog +++ b/DiscImageChef.Decoders/ChangeLog @@ -1,3 +1,9 @@ +2016-08-21 Natalia Portillo + + * LisaTag.cs: + * DiscImageChef.Decoders.csproj: Separate Lisa tag decoding + from Lisa filesystem and added Priam tags. + 2016-08-18 Natalia Portillo * ATIP.cs: Make ATIP manufacturer resolver a public method. diff --git a/DiscImageChef.Decoders/DiscImageChef.Decoders.csproj b/DiscImageChef.Decoders/DiscImageChef.Decoders.csproj index f5b2d5b10..63f28ca6d 100644 --- a/DiscImageChef.Decoders/DiscImageChef.Decoders.csproj +++ b/DiscImageChef.Decoders/DiscImageChef.Decoders.csproj @@ -91,6 +91,7 @@ + diff --git a/DiscImageChef.Decoders/LisaTag.cs b/DiscImageChef.Decoders/LisaTag.cs new file mode 100644 index 000000000..190f4b633 --- /dev/null +++ b/DiscImageChef.Decoders/LisaTag.cs @@ -0,0 +1,360 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : LisaTag.cs +// Author(s) : Natalia Portillo +// +// Component : Component +// +// --[ Description ] ---------------------------------------------------------- +// +// Description +// +// --[ License ] -------------------------------------------------------------- +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2016 Natalia Portillo +// ****************************************************************************/ +using System; +namespace DiscImageChef.Decoders +{ + public static class LisaTag + { + /// + /// LisaOS tag as stored on Apple Profile and FileWare disks (20 bytes) + /// + public struct ProfileTag + { + /// 0x00, Lisa OS version number + public ushort version; + /// 0x02 bits 7 to 6, kind of info in this block + public byte kind; + /// 0x02 bits 5 to 0, reserved + public byte reserved; + /// 0x03, disk volume number + public byte volume; + /// 0x04, file ID + public short fileID; + /// + /// 0x06 bit 7, checksum valid? + /// + public bool validChk; + /// + /// 0x06 bits 6 to 0, used bytes in block + /// + public ushort usedBytes; + /// + /// 0x08, 3 bytes, absolute page number + /// + public uint absPage; + /// + /// 0x0B, checksum of data + /// + public byte checksum; + /// + /// 0x0C, relative page number + /// + public ushort relPage; + /// + /// 0x0E, 3 bytes, next block, 0xFFFFFF if it's last block + /// + public uint nextBlock; + /// + /// 0x11, 3 bytes, previous block, 0xFFFFFF if it's first block + /// + public uint prevBlock; + + /// On-memory value for easy first block search. + public bool isFirst; + /// On-memory value for easy last block search. + public bool isLast; +} + + /// + /// LisaOS tag as stored on Priam DataTower disks (24 bytes) + /// + public struct PriamTag + { + /// 0x00, Lisa OS version number + public ushort version; + /// 0x02 bits 7 to 6, kind of info in this block + public byte kind; + /// 0x02 bits 5 to 0, reserved + public byte reserved; + /// 0x03, disk volume number + public byte volume; + /// 0x04, file ID + public short fileID; + /// + /// 0x06 bit 7, checksum valid? + /// + public bool validChk; + /// + /// 0x06 bits 6 to 0, used bytes in block + /// + public ushort usedBytes; + /// + /// 0x08, 3 bytes, absolute page number + /// + public uint absPage; + /// + /// 0x0B, checksum of data + /// + public byte checksum; + /// + /// 0x0C, relative page number + /// + public ushort relPage; + /// + /// 0x0E, 3 bytes, next block, 0xFFFFFF if it's last block + /// + public uint nextBlock; + /// + /// 0x11, 3 bytes, previous block, 0xFFFFFF if it's first block + /// + public uint prevBlock; + /// + /// 0x14, disk size + /// + public uint diskSize; + + /// On-memory value for easy first block search. + public bool isFirst; + /// On-memory value for easy last block search. + public bool isLast; + } + + /// + /// LisaOS tag as stored on Apple Sony disks (12 bytes) + /// + public struct SonyTag + { + /// 0x00, Lisa OS version number + public ushort version; + /// 0x02 bits 7 to 6, kind of info in this block + public byte kind; + /// 0x02 bits 5 to 0, reserved + public byte reserved; + /// 0x03, disk volume number + public byte volume; + /// 0x04, file ID + public short fileID; + /// + /// 0x06, relative page number + /// + public ushort relPage; + /// + /// 0x08, 3 bytes, next block, 0x7FF if it's last block, 0x8000 set if block is valid + /// + public ushort nextBlock; + /// + /// 0x0A, 3 bytes, previous block, 0x7FF if it's first block + /// + public ushort prevBlock; + + /// On-memory value for easy first block search. + public bool isFirst; + /// On-memory value for easy last block search. + public bool isLast; + } + + public static SonyTag? DecodeSonyTag(byte[] tag) + { + if(tag == null || tag.Length != 12) + return null; + + SonyTag snTag = new SonyTag(); + + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + + byte[] tmp = new byte[4]; + + snTag.version = BigEndianBitConverter.ToUInt16(tag, 0); + snTag.kind = (byte)((tag[2] & 0xC0) >> 6); + snTag.reserved = (byte)(tag[2] & 0x3F); + snTag.volume = tag[3]; + snTag.fileID = BigEndianBitConverter.ToInt16(tag, 4); + snTag.relPage = BigEndianBitConverter.ToUInt16(tag, 6); + snTag.nextBlock = (ushort)(BigEndianBitConverter.ToUInt16(tag, 8) & 0x7FF); + snTag.prevBlock = (ushort)(BigEndianBitConverter.ToUInt16(tag, 10) & 0x7FF); + + snTag.isLast = snTag.nextBlock == 0x7FF; + snTag.isFirst = snTag.prevBlock == 0x7FF; + + return snTag; + } + + public static ProfileTag? DecodeProfileTag(byte[] tag) + { + if(tag == null || tag.Length != 20) + return null; + + ProfileTag phTag = new ProfileTag(); + + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + + byte[] tmp = new byte[4]; + + phTag.version = BigEndianBitConverter.ToUInt16(tag, 0); + phTag.kind = (byte)((tag[2] & 0xC0) >> 6); + phTag.reserved = (byte)(tag[2] & 0x3F); + phTag.volume = tag[3]; + phTag.fileID = BigEndianBitConverter.ToInt16(tag, 4); + phTag.validChk |= (tag[6] & 0x80) == 0x80; + phTag.usedBytes = (ushort)(BigEndianBitConverter.ToUInt16(tag, 6) & 0x7FFF); + + tmp[0] = 0x00; + tmp[1] = tag[8]; + tmp[2] = tag[9]; + tmp[3] = tag[10]; + phTag.absPage = BigEndianBitConverter.ToUInt32(tmp, 0); + + phTag.checksum = tag[11]; + phTag.relPage = BigEndianBitConverter.ToUInt16(tag, 12); + + tmp[0] = 0x00; + tmp[1] = tag[14]; + tmp[2] = tag[15]; + tmp[3] = tag[16]; + phTag.nextBlock = BigEndianBitConverter.ToUInt32(tmp, 0); + + tmp[0] = 0x00; + tmp[1] = tag[17]; + tmp[2] = tag[18]; + tmp[3] = tag[19]; + phTag.prevBlock = BigEndianBitConverter.ToUInt32(tmp, 0); + + phTag.isLast = phTag.nextBlock == 0xFFFFFF; + phTag.isFirst = phTag.prevBlock == 0xFFFFFF; + + return phTag; + } + + public static PriamTag? DecodePriamTag(byte[] tag) + { + if(tag == null || tag.Length != 24) + return null; + + PriamTag pmTag = new PriamTag(); + + BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian; + + byte[] tmp = new byte[4]; + + pmTag.version = BigEndianBitConverter.ToUInt16(tag, 0); + pmTag.kind = (byte)((tag[2] & 0xC0) >> 6); + pmTag.reserved = (byte)(tag[2] & 0x3F); + pmTag.volume = tag[3]; + pmTag.fileID = BigEndianBitConverter.ToInt16(tag, 4); + pmTag.validChk |= (tag[6] & 0x80) == 0x80; + pmTag.usedBytes = (ushort)(BigEndianBitConverter.ToUInt16(tag, 6) & 0x7FFF); + + tmp[0] = 0x00; + tmp[1] = tag[8]; + tmp[2] = tag[9]; + tmp[3] = tag[10]; + pmTag.absPage = BigEndianBitConverter.ToUInt32(tmp, 0); + + pmTag.checksum = tag[11]; + pmTag.relPage = BigEndianBitConverter.ToUInt16(tag, 12); + + tmp[0] = 0x00; + tmp[1] = tag[14]; + tmp[2] = tag[15]; + tmp[3] = tag[16]; + pmTag.nextBlock = BigEndianBitConverter.ToUInt32(tmp, 0); + + tmp[0] = 0x00; + tmp[1] = tag[17]; + tmp[2] = tag[18]; + tmp[3] = tag[19]; + pmTag.prevBlock = BigEndianBitConverter.ToUInt32(tmp, 0); + + pmTag.diskSize = BigEndianBitConverter.ToUInt32(tag, 20); + + pmTag.isLast = pmTag.nextBlock == 0xFFFFFF; + pmTag.isFirst = pmTag.prevBlock == 0xFFFFFF; + + return pmTag; + } + + public static PriamTag? DecodeTag(byte[] tag) + { + if(tag == null) + return null; + + PriamTag pmTag = new PriamTag(); + + switch(tag.Length) + { + case 12: + SonyTag? snTag = DecodeSonyTag(tag); + + if(snTag == null) + return null; + + pmTag = new PriamTag(); + pmTag.absPage = 0; + pmTag.checksum = 0; + pmTag.diskSize = 0; + pmTag.fileID = snTag.Value.fileID; + pmTag.kind = snTag.Value.kind; + pmTag.nextBlock = snTag.Value.nextBlock; + pmTag.prevBlock = snTag.Value.prevBlock; + pmTag.relPage = snTag.Value.relPage; + pmTag.reserved = snTag.Value.reserved; + pmTag.usedBytes = 0; + pmTag.validChk = false; + pmTag.version = snTag.Value.version; + pmTag.volume = snTag.Value.volume; + pmTag.isFirst = snTag.Value.isFirst; + pmTag.isLast = snTag.Value.isLast; + + return pmTag; + case 20: + ProfileTag? phTag = DecodeProfileTag(tag); + + if(phTag == null) + return null; + + pmTag = new PriamTag(); + pmTag.absPage = phTag.Value.absPage; + pmTag.checksum = phTag.Value.checksum; + pmTag.diskSize = 0; + pmTag.fileID = phTag.Value.fileID; + pmTag.kind = phTag.Value.kind; + pmTag.nextBlock = phTag.Value.nextBlock; + pmTag.prevBlock = phTag.Value.prevBlock; + pmTag.relPage = phTag.Value.relPage; + pmTag.reserved = phTag.Value.reserved; + pmTag.usedBytes = phTag.Value.usedBytes; + pmTag.validChk = phTag.Value.validChk; + pmTag.version = phTag.Value.version; + pmTag.volume = phTag.Value.volume; + pmTag.isFirst = phTag.Value.isFirst; + pmTag.isLast = phTag.Value.isLast; + + return pmTag; + case 24: + return DecodePriamTag(tag); + default: + return null; + } + } + } +} + diff --git a/DiscImageChef.Filesystems/ChangeLog b/DiscImageChef.Filesystems/ChangeLog index 5173a338b..b0d7481b7 100644 --- a/DiscImageChef.Filesystems/ChangeLog +++ b/DiscImageChef.Filesystems/ChangeLog @@ -1,3 +1,15 @@ +2016-08-21 Natalia Portillo + + * Dir.cs: + * File.cs: + * Info.cs: + * Super.cs: + * Xattr.cs: + * Extent.cs: + * Structs.cs: + * DiscImageChef.Filesystems.csproj: Separate Lisa tag decoding + from Lisa filesystem and added Priam tags. + 2016-08-18 Natalia Portillo * exFAT.cs: diff --git a/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj b/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj index 6c5523155..54b850e9e 100644 --- a/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj +++ b/DiscImageChef.Filesystems/DiscImageChef.Filesystems.csproj @@ -118,6 +118,10 @@ {9F213318-5CB8-4066-A757-074489C9F818} DiscImageChef.Metadata + + {0BEB3088-B634-4289-AE17-CDF2D25D00D5} + DiscImageChef.Decoders + diff --git a/DiscImageChef.Filesystems/LisaFS/Dir.cs b/DiscImageChef.Filesystems/LisaFS/Dir.cs index 1c2edf052..c0a60a221 100644 --- a/DiscImageChef.Filesystems/LisaFS/Dir.cs +++ b/DiscImageChef.Filesystems/LisaFS/Dir.cs @@ -33,6 +33,7 @@ using System; using System.Collections.Generic; using DiscImageChef.ImagePlugins; +using DiscImageChef.Decoders; namespace DiscImageChef.Filesystems.LisaFS { @@ -174,10 +175,10 @@ namespace DiscImageChef.Filesystems.LisaFS // If root catalog is not pointed in MDDF (unchecked) maybe it's always following S-Records File? for(ulong i = 0; i < device.GetSectors(); i++) { - Tag catTag; + LisaTag.PriamTag catTag; DecodeTag(device.ReadSectorTag(i, SectorTagType.AppleSectorTag), out catTag); - if(catTag.fileID == FILEID_CATALOG && catTag.relBlock == 0) + if(catTag.fileID == FILEID_CATALOG && catTag.relPage == 0) { firstCatalogBlock = device.ReadSectors(i, 4); break; @@ -194,7 +195,7 @@ namespace DiscImageChef.Filesystems.LisaFS // Traverse double-linked list until first catalog block while(prevCatalogPointer != 0xFFFFFFFF) { - Tag prevTag; + LisaTag.PriamTag prevTag; DecodeTag(device.ReadSectorTag(prevCatalogPointer + mddf.mddf_block + volumePrefix, SectorTagType.AppleSectorTag), out prevTag); if(prevTag.fileID != FILEID_CATALOG) @@ -213,7 +214,7 @@ namespace DiscImageChef.Filesystems.LisaFS // Traverse double-linked list to read full catalog while(nextCatalogPointer != 0xFFFFFFFF) { - Tag nextTag; + LisaTag.PriamTag nextTag; DecodeTag(device.ReadSectorTag(nextCatalogPointer + mddf.mddf_block + volumePrefix, SectorTagType.AppleSectorTag), out nextTag); if(nextTag.fileID != FILEID_CATALOG) diff --git a/DiscImageChef.Filesystems/LisaFS/Extent.cs b/DiscImageChef.Filesystems/LisaFS/Extent.cs index 1b4250e88..385206258 100644 --- a/DiscImageChef.Filesystems/LisaFS/Extent.cs +++ b/DiscImageChef.Filesystems/LisaFS/Extent.cs @@ -33,6 +33,7 @@ using System; using DiscImageChef.Console; using DiscImageChef.ImagePlugins; +using DiscImageChef.Decoders; namespace DiscImageChef.Filesystems.LisaFS { @@ -76,7 +77,7 @@ namespace DiscImageChef.Filesystems.LisaFS // Pointers are relative to MDDF ptr += mddf.mddf_block + volumePrefix; - Tag extTag; + LisaTag.PriamTag extTag; // This happens on some disks. // This is a filesystem corruption that makes LisaOS crash on scavenge. diff --git a/DiscImageChef.Filesystems/LisaFS/File.cs b/DiscImageChef.Filesystems/LisaFS/File.cs index eee84abcf..5fb6fefdd 100644 --- a/DiscImageChef.Filesystems/LisaFS/File.cs +++ b/DiscImageChef.Filesystems/LisaFS/File.cs @@ -33,6 +33,7 @@ using System; using DiscImageChef.ImagePlugins; using DiscImageChef.Console; +using DiscImageChef.Decoders; namespace DiscImageChef.Filesystems.LisaFS { @@ -212,7 +213,7 @@ namespace DiscImageChef.Filesystems.LisaFS } } - Tag sysTag; + LisaTag.PriamTag sysTag; // Should be enough to check 100 sectors? for(ulong i = 0; i < 100; i++) @@ -247,9 +248,9 @@ namespace DiscImageChef.Filesystems.LisaFS // Relative block for $Loader starts at $Boot block if(sysTag.fileID == FILEID_LOADER_SIGNED) - sysTag.relBlock--; + sysTag.relPage--; - Array.Copy(sector, 0, buf, sector.Length * sysTag.relBlock, sector.Length); + Array.Copy(sector, 0, buf, sector.Length * sysTag.relPage, sector.Length); } } diff --git a/DiscImageChef.Filesystems/LisaFS/Info.cs b/DiscImageChef.Filesystems/LisaFS/Info.cs index af1266423..7942e34d4 100644 --- a/DiscImageChef.Filesystems/LisaFS/Info.cs +++ b/DiscImageChef.Filesystems/LisaFS/Info.cs @@ -33,6 +33,7 @@ using System; using System.Text; using DiscImageChef.Console; +using DiscImageChef.Decoders; using DiscImageChef.ImagePlugins; namespace DiscImageChef.Filesystems.LisaFS @@ -61,7 +62,7 @@ namespace DiscImageChef.Filesystems.LisaFS // LisaOS searches sectors until tag tells MDDF resides there, so we'll search 100 sectors for(int i = 0; i < 100; i++) { - Tag searchTag; + LisaTag.PriamTag searchTag; DecodeTag(imagePlugin.ReadSectorTag((ulong)i, SectorTagType.AppleSectorTag), out searchTag); DicConsole.DebugWriteLine("LisaFS plugin", "Sector {0}, file ID 0x{1:X4}", i, searchTag.fileID); @@ -150,7 +151,7 @@ namespace DiscImageChef.Filesystems.LisaFS // LisaOS searches sectors until tag tells MDDF resides there, so we'll search 100 sectors for(int i = 0; i < 100; i++) { - Tag searchTag; + LisaTag.PriamTag searchTag; DecodeTag(imagePlugin.ReadSectorTag((ulong)i, SectorTagType.AppleSectorTag), out searchTag); DicConsole.DebugWriteLine("LisaFS plugin", "Sector {0}, file ID 0x{1:X4}", i, searchTag.fileID); diff --git a/DiscImageChef.Filesystems/LisaFS/Structs.cs b/DiscImageChef.Filesystems/LisaFS/Structs.cs index e980147e6..743290664 100644 --- a/DiscImageChef.Filesystems/LisaFS/Structs.cs +++ b/DiscImageChef.Filesystems/LisaFS/Structs.cs @@ -207,48 +207,6 @@ namespace DiscImageChef.Filesystems.LisaFS public byte scavenge_flag; } - /// - /// The sector tag. Before the sector is encoded to GCR the tag is attached to the data. - /// Its size and format varies depending on device. - /// Lisa OS relies on tags for scavenging a floppy, but ignores most of them for normal usage, - /// except on hard disks where the checksum byte is absolutely enforced (a sector with an invalid - /// one gives an OS error). - /// - struct Tag - { - /// 0x00 version - public ushort version; - /// 0x02 unknown - public ushort unknown; - /// 0x04 File ID. Negative numbers are extents for the file with same absolute value number - public short fileID; - /// Only in 20 bytes tag at 0x06, mask 0x8000 if valid tag - public ushort usedBytes; - /// Only in 20 bytes tag at 0x08, 3 bytes - public uint absoluteBlock; - /// Only in 20 bytes tag at 0x0B, checksum byte - public byte checksum; - /// 0x06 in 12 bytes tag, 0x0C in 20 bytes tag, relative block - public ushort relBlock; - /// - /// Next block for this file. - /// In 12 bytes tag at 0x08, 2 bytes, 0x8000 bit seems always set, 0x07FF means this is last block. - /// In 20 bytes tag at 0x0E, 3 bytes, 0xFFFFFF means this is last block. - /// - public uint nextBlock; - /// - /// Previous block for this file. - /// In 12 bytes tag at 0x0A, 2 bytes, 0x07FF means this is first block. - /// In 20 bytes tag at 0x11, 3 bytes, 0xFFFFFF means this is first block. - /// - public uint prevBlock; - - /// On-memory value for easy first block search. - public bool isFirst; - /// On-memory value for easy last block search. - public bool isLast; - } - /// /// 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. diff --git a/DiscImageChef.Filesystems/LisaFS/Super.cs b/DiscImageChef.Filesystems/LisaFS/Super.cs index e9ad87ee8..33bbf102e 100644 --- a/DiscImageChef.Filesystems/LisaFS/Super.cs +++ b/DiscImageChef.Filesystems/LisaFS/Super.cs @@ -34,6 +34,7 @@ using System; using System.Collections.Generic; using DiscImageChef.Console; using DiscImageChef.ImagePlugins; +using DiscImageChef.Decoders; namespace DiscImageChef.Filesystems.LisaFS { @@ -80,7 +81,7 @@ namespace DiscImageChef.Filesystems.LisaFS // LisaOS searches sectors until tag tells MDDF resides there, so we'll search 100 sectors for(ulong i = 0; i < 100; i++) { - Tag searchTag; + LisaTag.PriamTag searchTag; DecodeTag(device.ReadSectorTag(i, SectorTagType.AppleSectorTag), out searchTag); DicConsole.DebugWriteLine("LisaFS plugin", "Sector {0}, file ID 0x{1:X4}", i, searchTag.fileID); diff --git a/DiscImageChef.Filesystems/LisaFS/Xattr.cs b/DiscImageChef.Filesystems/LisaFS/Xattr.cs index ea973d05a..5eafe0526 100644 --- a/DiscImageChef.Filesystems/LisaFS/Xattr.cs +++ b/DiscImageChef.Filesystems/LisaFS/Xattr.cs @@ -34,6 +34,7 @@ using System; using System.Collections.Generic; using System.Text; +using DiscImageChef.Decoders; namespace DiscImageChef.Filesystems.LisaFS { @@ -214,47 +215,15 @@ namespace DiscImageChef.Filesystems.LisaFS /// Error number. /// Sector tag. /// Decoded sector tag. - Errno DecodeTag(byte[] tag, out Tag decoded) + Errno DecodeTag(byte[] tag, out LisaTag.PriamTag decoded) { - decoded = new Tag(); + decoded = new LisaTag.PriamTag(); + LisaTag.PriamTag? pmTag = LisaTag.DecodeTag(tag); - if(tag.Length == 12) - { - decoded.version = BigEndianBitConverter.ToUInt16(tag, 0x00); - decoded.unknown = BigEndianBitConverter.ToUInt16(tag, 0x02); - decoded.fileID = BigEndianBitConverter.ToInt16(tag, 0x04); - decoded.relBlock = BigEndianBitConverter.ToUInt16(tag, 0x06); - decoded.nextBlock = BigEndianBitConverter.ToUInt16(tag, 0x08); - decoded.nextBlock &= 0x7FF; - decoded.prevBlock = BigEndianBitConverter.ToUInt16(tag, 0x0A); - decoded.prevBlock &= 0x7FF; - - if(decoded.nextBlock == 0x7FF) - decoded.isLast = true; - if(decoded.prevBlock == 0x7FF) - decoded.isFirst = true; - } - else - { - decoded.version = BigEndianBitConverter.ToUInt16(tag, 0x00); - decoded.unknown = BigEndianBitConverter.ToUInt16(tag, 0x02); - decoded.fileID = BigEndianBitConverter.ToInt16(tag, 0x04); - decoded.usedBytes = BigEndianBitConverter.ToUInt16(tag, 0x06); - decoded.absoluteBlock = BigEndianBitConverter.ToUInt32(tag, 0x07); - decoded.absoluteBlock &= 0xFFFFFF; - decoded.checksum = tag[0x0B]; - decoded.relBlock = BigEndianBitConverter.ToUInt16(tag, 0x0C); - decoded.nextBlock = BigEndianBitConverter.ToUInt32(tag, 0x0D); - decoded.nextBlock &= 0xFFFFFF; - decoded.prevBlock = BigEndianBitConverter.ToUInt32(tag, 0x10); - decoded.prevBlock &= 0xFFFFFF; - - if(decoded.nextBlock == 0xFFFFFF) - decoded.isLast = true; - if(decoded.prevBlock == 0xFFFFFF) - decoded.isFirst = true; - } + if(!pmTag.HasValue) + return Errno.InvalidArgument; + decoded = pmTag.Value; return Errno.NoError; } }