// /*************************************************************************** // The Disc Image Chef // ---------------------------------------------------------------------------- // // Filename : Modes.cs // Author(s) : Natalia Portillo // // Component : Device structures decoders. // // --[ Description ] ---------------------------------------------------------- // // Decodes and encodines SCSI modes in MODE SENSE/SELECT (6) format. // // --[ 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-2018 Natalia Portillo // ****************************************************************************/ using System; using System.Collections.Generic; using System.Linq; namespace DiscImageChef.Decoders.SCSI { public static partial class Modes { public static ModeHeader? DecodeModeHeader6(byte[] modeResponse, PeripheralDeviceTypes deviceType) { if(modeResponse == null || modeResponse.Length < 4 || modeResponse.Length < modeResponse[0] + 1) return null; ModeHeader header = new ModeHeader(); header.MediumType = (MediumTypes)modeResponse[1]; if(modeResponse[3] > 0) { header.BlockDescriptors = new BlockDescriptor[modeResponse[3] / 8]; for(int i = 0; i < header.BlockDescriptors.Length; i++) { header.BlockDescriptors[i].Density = (DensityType)modeResponse[0 + i * 8 + 4]; header.BlockDescriptors[i].Blocks += (ulong)(modeResponse[1 + i * 8 + 4] << 16); header.BlockDescriptors[i].Blocks += (ulong)(modeResponse[2 + i * 8 + 4] << 8); header.BlockDescriptors[i].Blocks += modeResponse[3 + i * 8 + 4]; header.BlockDescriptors[i].BlockLength += (uint)(modeResponse[5 + i * 8 + 4] << 16); header.BlockDescriptors[i].BlockLength += (uint)(modeResponse[6 + i * 8 + 4] << 8); header.BlockDescriptors[i].BlockLength += modeResponse[7 + i * 8 + 4]; } } switch(deviceType) { case PeripheralDeviceTypes.DirectAccess: case PeripheralDeviceTypes.MultiMediaDevice: header.WriteProtected = (modeResponse[2] & 0x80) == 0x80; header.DPOFUA = (modeResponse[2] & 0x10) == 0x10; break; case PeripheralDeviceTypes.SequentialAccess: header.WriteProtected = (modeResponse[2] & 0x80) == 0x80; header.Speed = (byte)(modeResponse[2] & 0x0F); header.BufferedMode = (byte)((modeResponse[2] & 0x70) >> 4); break; case PeripheralDeviceTypes.PrinterDevice: header.BufferedMode = (byte)((modeResponse[2] & 0x70) >> 4); break; case PeripheralDeviceTypes.OpticalDevice: header.WriteProtected = (modeResponse[2] & 0x80) == 0x80; header.EBC = (modeResponse[2] & 0x01) == 0x01; header.DPOFUA = (modeResponse[2] & 0x10) == 0x10; break; } return header; } public static string PrettifyModeHeader6(byte[] modeResponse, PeripheralDeviceTypes deviceType) { return PrettifyModeHeader(DecodeModeHeader6(modeResponse, deviceType), deviceType); } public static DecodedMode? DecodeMode6(byte[] modeResponse, PeripheralDeviceTypes deviceType) { ModeHeader? hdr = DecodeModeHeader6(modeResponse, deviceType); if(!hdr.HasValue) return null; DecodedMode decoded = new DecodedMode(); decoded.Header = hdr.Value; int blkDrLength = 0; if(decoded.Header.BlockDescriptors != null) blkDrLength = decoded.Header.BlockDescriptors.Length; int offset = 4 + blkDrLength * 8; int length = modeResponse[0] + 1; if(length != modeResponse.Length) return decoded; List listpages = new List(); while(offset < modeResponse.Length) { bool isSubpage = (modeResponse[offset] & 0x40) == 0x40; ModePage pg = new ModePage(); byte pageNo = (byte)(modeResponse[offset] & 0x3F); if(pageNo == 0) { pg.PageResponse = new byte[modeResponse.Length - offset]; Array.Copy(modeResponse, offset, pg.PageResponse, 0, pg.PageResponse.Length); pg.Page = 0; pg.Subpage = 0; offset += pg.PageResponse.Length; } else { if(isSubpage) { if(offset + 3 >= modeResponse.Length) break; pg.PageResponse = new byte[(modeResponse[offset + 2] << 8) + modeResponse[offset + 3] + 4]; if(pg.PageResponse.Length + offset > modeResponse.Length) return decoded; Array.Copy(modeResponse, offset, pg.PageResponse, 0, pg.PageResponse.Length); pg.Page = (byte)(modeResponse[offset] & 0x3F); pg.Subpage = modeResponse[offset + 1]; offset += pg.PageResponse.Length; } else { if(offset + 1 >= modeResponse.Length) break; pg.PageResponse = new byte[modeResponse[offset + 1] + 2]; if(pg.PageResponse.Length + offset > modeResponse.Length) return decoded; Array.Copy(modeResponse, offset, pg.PageResponse, 0, pg.PageResponse.Length); pg.Page = (byte)(modeResponse[offset] & 0x3F); pg.Subpage = 0; offset += pg.PageResponse.Length; } } listpages.Add(pg); } decoded.Pages = listpages.ToArray(); return decoded; } public static byte[] EncodeModeHeader6(ModeHeader header, PeripheralDeviceTypes deviceType) { byte[] hdr; if(header.BlockDescriptors != null) hdr = new byte[4 + header.BlockDescriptors.Length * 8]; else hdr = new byte[4]; hdr[1] = (byte)header.MediumType; switch(deviceType) { case PeripheralDeviceTypes.DirectAccess: case PeripheralDeviceTypes.MultiMediaDevice: if(header.WriteProtected) hdr[2] += 0x80; if(header.DPOFUA) hdr[2] += 0x10; break; case PeripheralDeviceTypes.SequentialAccess: if(header.WriteProtected) hdr[2] += 0x80; hdr[2] += (byte)(header.Speed & 0x0F); hdr[2] += (byte)((header.BufferedMode << 4) & 0x70); break; case PeripheralDeviceTypes.PrinterDevice: hdr[2] += (byte)((header.BufferedMode << 4) & 0x70); break; case PeripheralDeviceTypes.OpticalDevice: if(header.WriteProtected) hdr[2] += 0x80; if(header.EBC) hdr[2] += 0x01; if(header.DPOFUA) hdr[2] += 0x10; break; } if(header.BlockDescriptors == null) return hdr; hdr[3] = (byte)(header.BlockDescriptors.Length * 8); for(int i = 0; i < header.BlockDescriptors.Length; i++) { hdr[0 + i * 8 + 4] = (byte)header.BlockDescriptors[i].Density; hdr[1 + i * 8 + 4] = (byte)((header.BlockDescriptors[i].Blocks & 0xFF0000) >> 16); hdr[2 + i * 8 + 4] = (byte)((header.BlockDescriptors[i].Blocks & 0xFF00) >> 8); hdr[3 + i * 8 + 4] = (byte)(header.BlockDescriptors[i].Blocks & 0xFF); hdr[5 + i * 8 + 4] = (byte)((header.BlockDescriptors[i].BlockLength & 0xFF0000) >> 16); hdr[6 + i * 8 + 4] = (byte)((header.BlockDescriptors[i].BlockLength & 0xFF00) >> 8); hdr[7 + i * 8 + 4] = (byte)(header.BlockDescriptors[i].BlockLength & 0xFF); } return hdr; } public static byte[] EncodeMode6(DecodedMode mode, PeripheralDeviceTypes deviceType) { int modeSize = 0; if(mode.Pages != null) modeSize += mode.Pages.Sum(page => page.PageResponse.Length); byte[] hdr = EncodeModeHeader6(mode.Header, deviceType); modeSize += hdr.Length; byte[] md = new byte[modeSize]; Array.Copy(hdr, 0, md, 0, hdr.Length); if(mode.Pages == null) return md; { int offset = hdr.Length; foreach(ModePage page in mode.Pages) { Array.Copy(page.PageResponse, 0, md, offset, page.PageResponse.Length); offset += page.PageResponse.Length; } } return md; } } }