2017-07-19 16:31:08 +01:00
|
|
|
|
// /***************************************************************************
|
2016-07-28 18:13:49 +01:00
|
|
|
|
// The Disc Image Chef
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
|
//
|
2017-10-08 20:41:54 +01:00
|
|
|
|
// Filename : Info.cs
|
2016-07-28 18:13:49 +01:00
|
|
|
|
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
|
|
|
|
|
//
|
2017-12-19 03:50:57 +00:00
|
|
|
|
// Component : ISO9660 filesystem plugin.
|
2016-07-28 18:13:49 +01:00
|
|
|
|
//
|
|
|
|
|
|
// --[ Description ] ----------------------------------------------------------
|
|
|
|
|
|
//
|
2017-12-19 03:50:57 +00:00
|
|
|
|
// Identifies the ISO9660 filesystem and shows information.
|
2016-07-28 18:13:49 +01:00
|
|
|
|
//
|
|
|
|
|
|
// --[ 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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
//
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
2017-12-19 03:50:57 +00:00
|
|
|
|
// Copyright © 2011-2018 Natalia Portillo
|
2016-07-28 18:13:49 +01:00
|
|
|
|
// ****************************************************************************/
|
2017-12-19 19:33:46 +00:00
|
|
|
|
|
2011-03-03 18:34:33 +00:00
|
|
|
|
using System;
|
2017-10-09 11:25:47 +01:00
|
|
|
|
using System.Collections.Generic;
|
2017-10-08 20:28:56 +01:00
|
|
|
|
using System.Runtime.InteropServices;
|
2014-04-14 02:29:13 +00:00
|
|
|
|
using System.Text;
|
2017-12-21 14:30:38 +00:00
|
|
|
|
using DiscImageChef.Checksums;
|
2017-07-19 16:31:08 +01:00
|
|
|
|
using DiscImageChef.CommonTypes;
|
2015-10-18 22:04:03 +01:00
|
|
|
|
using DiscImageChef.Console;
|
2017-12-21 14:30:38 +00:00
|
|
|
|
using DiscImageChef.Decoders.Sega;
|
|
|
|
|
|
using DiscImageChef.DiscImages;
|
|
|
|
|
|
using Schemas;
|
2015-10-18 22:04:03 +01:00
|
|
|
|
|
2017-10-08 20:41:54 +01:00
|
|
|
|
namespace DiscImageChef.Filesystems.ISO9660
|
2011-03-03 18:34:33 +00:00
|
|
|
|
{
|
2017-12-21 16:27:09 +00:00
|
|
|
|
public partial class ISO9660
|
2011-03-03 18:34:33 +00:00
|
|
|
|
{
|
2017-12-21 14:30:38 +00:00
|
|
|
|
public override bool Identify(ImagePlugin imagePlugin, Partition partition)
|
2011-03-03 18:34:33 +00:00
|
|
|
|
{
|
2014-04-14 02:29:13 +00:00
|
|
|
|
byte VDType;
|
2012-08-03 05:43:58 +00:00
|
|
|
|
|
2014-04-14 01:14:20 +00:00
|
|
|
|
// ISO9660 is designed for 2048 bytes/sector devices
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(imagePlugin.GetSectorSize() < 2048) return false;
|
2014-04-14 01:14:20 +00:00
|
|
|
|
|
|
|
|
|
|
// ISO9660 Primary Volume Descriptor starts at sector 16, so that's minimal size.
|
2017-12-20 17:26:28 +00:00
|
|
|
|
if(partition.End <= 16 + partition.Start) return false;
|
2011-03-03 18:34:33 +00:00
|
|
|
|
|
2014-04-14 01:14:20 +00:00
|
|
|
|
// Read to Volume Descriptor
|
2017-07-19 16:37:11 +01:00
|
|
|
|
byte[] vd_sector = imagePlugin.ReadSector(16 + partition.Start);
|
2014-04-14 01:14:20 +00:00
|
|
|
|
|
2015-12-06 07:51:46 +00:00
|
|
|
|
int xa_off = 0;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(vd_sector.Length == 2336) xa_off = 8;
|
2015-12-06 07:51:46 +00:00
|
|
|
|
|
|
|
|
|
|
VDType = vd_sector[0 + xa_off];
|
2014-04-14 02:29:13 +00:00
|
|
|
|
byte[] VDMagic = new byte[5];
|
2017-10-08 22:47:09 +01:00
|
|
|
|
byte[] HSMagic = new byte[5];
|
2011-03-03 18:34:33 +00:00
|
|
|
|
|
2017-10-08 22:47:09 +01:00
|
|
|
|
// This indicates the end of a volume descriptor. HighSierra here would have 16 so no problem
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(VDType == 255) return false;
|
2011-03-03 18:34:33 +00:00
|
|
|
|
|
2015-12-06 07:51:46 +00:00
|
|
|
|
Array.Copy(vd_sector, 0x001 + xa_off, VDMagic, 0, 5);
|
2017-10-08 22:47:09 +01:00
|
|
|
|
Array.Copy(vd_sector, 0x009 + xa_off, HSMagic, 0, 5);
|
2015-12-06 07:51:46 +00:00
|
|
|
|
|
2017-06-06 21:23:20 +01:00
|
|
|
|
DicConsole.DebugWriteLine("ISO9660 plugin", "VDMagic = {0}", CurrentEncoding.GetString(VDMagic));
|
2017-10-08 22:47:09 +01:00
|
|
|
|
DicConsole.DebugWriteLine("ISO9660 plugin", "HSMagic = {0}", CurrentEncoding.GetString(HSMagic));
|
2011-03-03 18:34:33 +00:00
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
|
return CurrentEncoding.GetString(VDMagic) == IsoMagic ||
|
|
|
|
|
|
CurrentEncoding.GetString(HSMagic) == HighSierraMagic ||
|
|
|
|
|
|
CurrentEncoding.GetString(VDMagic) == CdiMagic;
|
2014-04-14 02:29:13 +00:00
|
|
|
|
}
|
2016-04-19 02:11:47 +01:00
|
|
|
|
|
2017-12-21 14:30:38 +00:00
|
|
|
|
public override void GetInformation(ImagePlugin imagePlugin, Partition partition,
|
2017-12-19 20:33:03 +00:00
|
|
|
|
out string information)
|
2016-04-19 02:11:47 +01:00
|
|
|
|
{
|
2011-03-03 18:34:33 +00:00
|
|
|
|
information = "";
|
|
|
|
|
|
StringBuilder ISOMetadata = new StringBuilder();
|
2017-12-19 20:33:03 +00:00
|
|
|
|
byte VDType; // Volume Descriptor Type, should be 1 or 2.
|
|
|
|
|
|
byte[] VDMagic = new byte[5]; // Volume Descriptor magic "CD001"
|
|
|
|
|
|
byte[] HSMagic = new byte[5]; // Volume Descriptor magic "CDROM"
|
2017-10-08 20:28:56 +01:00
|
|
|
|
|
2011-03-03 18:34:33 +00:00
|
|
|
|
string BootSpec = "";
|
|
|
|
|
|
|
|
|
|
|
|
byte[] VDPathTableStart = new byte[4];
|
|
|
|
|
|
byte[] RootDirectoryLocation = new byte[4];
|
|
|
|
|
|
|
2017-10-08 20:28:56 +01:00
|
|
|
|
PrimaryVolumeDescriptor? pvd = null;
|
|
|
|
|
|
PrimaryVolumeDescriptor? jolietvd = null;
|
|
|
|
|
|
BootRecord? bvd = null;
|
2017-10-08 22:47:09 +01:00
|
|
|
|
HighSierraPrimaryVolumeDescriptor? hsvd = null;
|
2017-10-13 21:50:10 +01:00
|
|
|
|
FileStructureVolumeDescriptor? fsvd = null;
|
2017-10-09 00:32:17 +01:00
|
|
|
|
ElToritoBootRecord? torito = null;
|
2017-10-08 20:28:56 +01:00
|
|
|
|
|
2014-04-14 01:14:20 +00:00
|
|
|
|
// ISO9660 is designed for 2048 bytes/sector devices
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(imagePlugin.GetSectorSize() < 2048) return;
|
2011-03-03 18:34:33 +00:00
|
|
|
|
|
2014-04-14 01:14:20 +00:00
|
|
|
|
// ISO9660 Primary Volume Descriptor starts at sector 16, so that's minimal size.
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(partition.End < 16) return;
|
2011-03-03 18:34:33 +00:00
|
|
|
|
|
2014-04-14 01:14:20 +00:00
|
|
|
|
ulong counter = 0;
|
2011-03-03 18:34:33 +00:00
|
|
|
|
|
2017-10-08 22:47:09 +01:00
|
|
|
|
byte[] vd_sector = imagePlugin.ReadSector(16 + counter + partition.Start);
|
2017-10-13 22:13:29 +01:00
|
|
|
|
int xa_off = vd_sector.Length == 2336 ? 8 : 0;
|
2017-10-08 22:47:09 +01:00
|
|
|
|
Array.Copy(vd_sector, 0x009 + xa_off, HSMagic, 0, 5);
|
|
|
|
|
|
bool HighSierra = CurrentEncoding.GetString(HSMagic) == HighSierraMagic;
|
|
|
|
|
|
int hs_off = 0;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(HighSierra) hs_off = 8;
|
2017-10-13 21:50:10 +01:00
|
|
|
|
bool CDi = false;
|
2017-10-08 22:47:09 +01:00
|
|
|
|
|
2016-04-19 02:11:47 +01:00
|
|
|
|
while(true)
|
2011-03-03 18:34:33 +00:00
|
|
|
|
{
|
2015-10-18 22:04:03 +01:00
|
|
|
|
DicConsole.DebugWriteLine("ISO9660 plugin", "Processing VD loop no. {0}", counter);
|
2011-03-03 18:34:33 +00:00
|
|
|
|
// Seek to Volume Descriptor
|
2017-07-19 16:37:11 +01:00
|
|
|
|
DicConsole.DebugWriteLine("ISO9660 plugin", "Reading sector {0}", 16 + counter + partition.Start);
|
|
|
|
|
|
byte[] vd_sector_tmp = imagePlugin.ReadSector(16 + counter + partition.Start);
|
2017-10-08 22:47:09 +01:00
|
|
|
|
vd_sector = new byte[vd_sector_tmp.Length - xa_off];
|
|
|
|
|
|
Array.Copy(vd_sector_tmp, xa_off, vd_sector, 0, vd_sector.Length);
|
2011-03-03 18:34:33 +00:00
|
|
|
|
|
2017-10-08 22:47:09 +01:00
|
|
|
|
VDType = vd_sector[0 + hs_off];
|
2015-10-18 22:04:03 +01:00
|
|
|
|
DicConsole.DebugWriteLine("ISO9660 plugin", "VDType = {0}", VDType);
|
2011-03-03 18:34:33 +00:00
|
|
|
|
|
2016-04-19 02:11:47 +01:00
|
|
|
|
if(VDType == 255) // Supposedly we are in the PVD.
|
2011-03-03 18:34:33 +00:00
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(counter == 0) return;
|
|
|
|
|
|
|
2011-03-03 18:34:33 +00:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-04-14 01:14:20 +00:00
|
|
|
|
Array.Copy(vd_sector, 0x001, VDMagic, 0, 5);
|
2017-10-08 22:47:09 +01:00
|
|
|
|
Array.Copy(vd_sector, 0x009, HSMagic, 0, 5);
|
2011-03-03 18:34:33 +00:00
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(CurrentEncoding.GetString(VDMagic) != IsoMagic &&
|
|
|
|
|
|
CurrentEncoding.GetString(HSMagic) != HighSierraMagic &&
|
|
|
|
|
|
CurrentEncoding.GetString(VDMagic) != CdiMagic
|
|
|
|
|
|
) // Recognized, it is an ISO9660, now check for rest of data.
|
2011-03-03 18:34:33 +00:00
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(counter == 0) return;
|
|
|
|
|
|
|
2011-03-03 18:34:33 +00:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-10-13 21:50:10 +01:00
|
|
|
|
CDi |= CurrentEncoding.GetString(VDMagic) == CdiMagic;
|
|
|
|
|
|
|
2011-03-03 18:34:33 +00:00
|
|
|
|
switch(VDType)
|
|
|
|
|
|
{
|
2017-10-09 00:32:17 +01:00
|
|
|
|
case 0:
|
2017-12-19 20:33:03 +00:00
|
|
|
|
{
|
|
|
|
|
|
IntPtr ptr = Marshal.AllocHGlobal(2048);
|
|
|
|
|
|
Marshal.Copy(vd_sector, hs_off, ptr, 2048 - hs_off);
|
|
|
|
|
|
bvd = (BootRecord)Marshal.PtrToStructure(ptr, typeof(BootRecord));
|
|
|
|
|
|
Marshal.FreeHGlobal(ptr);
|
|
|
|
|
|
|
|
|
|
|
|
BootSpec = "Unknown";
|
|
|
|
|
|
|
|
|
|
|
|
if(CurrentEncoding.GetString(bvd.Value.system_id).Substring(0, 23) == "EL TORITO SPECIFICATION")
|
2011-03-03 18:34:33 +00:00
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
|
BootSpec = "El Torito";
|
|
|
|
|
|
ptr = Marshal.AllocHGlobal(2048);
|
2017-10-08 22:47:09 +01:00
|
|
|
|
Marshal.Copy(vd_sector, hs_off, ptr, 2048 - hs_off);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
torito = (ElToritoBootRecord)Marshal.PtrToStructure(ptr, typeof(ElToritoBootRecord));
|
2017-10-08 20:28:56 +01:00
|
|
|
|
Marshal.FreeHGlobal(ptr);
|
2011-03-03 18:34:33 +00:00
|
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2011-03-03 18:34:33 +00:00
|
|
|
|
case 1:
|
2017-12-19 20:33:03 +00:00
|
|
|
|
{
|
|
|
|
|
|
if(HighSierra)
|
2011-03-03 18:34:33 +00:00
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
|
IntPtr ptr = Marshal.AllocHGlobal(2048);
|
|
|
|
|
|
Marshal.Copy(vd_sector, 0, ptr, 2048);
|
|
|
|
|
|
hsvd =
|
|
|
|
|
|
(HighSierraPrimaryVolumeDescriptor)
|
|
|
|
|
|
Marshal.PtrToStructure(ptr, typeof(HighSierraPrimaryVolumeDescriptor));
|
|
|
|
|
|
Marshal.FreeHGlobal(ptr);
|
2011-03-03 18:34:33 +00:00
|
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
|
else if(CDi)
|
|
|
|
|
|
fsvd =
|
|
|
|
|
|
BigEndianMarshal
|
|
|
|
|
|
.ByteArrayToStructureBigEndian<FileStructureVolumeDescriptor>(vd_sector);
|
|
|
|
|
|
else
|
2011-03-03 18:34:33 +00:00
|
|
|
|
{
|
2017-10-08 20:28:56 +01:00
|
|
|
|
IntPtr ptr = Marshal.AllocHGlobal(2048);
|
|
|
|
|
|
Marshal.Copy(vd_sector, 0, ptr, 2048);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
pvd = (PrimaryVolumeDescriptor)Marshal.PtrToStructure(ptr, typeof(PrimaryVolumeDescriptor));
|
2017-10-08 20:28:56 +01:00
|
|
|
|
Marshal.FreeHGlobal(ptr);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
case 2:
|
|
|
|
|
|
{
|
2017-12-21 16:07:20 +00:00
|
|
|
|
PrimaryVolumeDescriptor svd;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
IntPtr ptr = Marshal.AllocHGlobal(2048);
|
|
|
|
|
|
Marshal.Copy(vd_sector, 0, ptr, 2048);
|
|
|
|
|
|
svd = (PrimaryVolumeDescriptor)Marshal.PtrToStructure(ptr, typeof(PrimaryVolumeDescriptor));
|
|
|
|
|
|
Marshal.FreeHGlobal(ptr);
|
2017-10-08 20:28:56 +01:00
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
|
// Check if this is Joliet
|
|
|
|
|
|
if(svd.escape_sequences[0] == '%' && svd.escape_sequences[1] == '/')
|
|
|
|
|
|
if(svd.escape_sequences[2] == '@' || svd.escape_sequences[2] == 'C' ||
|
2017-12-20 23:07:46 +00:00
|
|
|
|
svd.escape_sequences[2] == 'E') jolietvd = svd;
|
|
|
|
|
|
else break;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
else DicConsole.WriteLine("ISO9660 plugin", "Found unknown supplementary volume descriptor");
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2011-03-03 18:34:33 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
counter++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-04-19 02:11:47 +01:00
|
|
|
|
DecodedVolumeDescriptor decodedVD = new DecodedVolumeDescriptor();
|
|
|
|
|
|
DecodedVolumeDescriptor decodedJolietVD = new DecodedVolumeDescriptor();
|
2012-08-03 05:43:58 +00:00
|
|
|
|
|
2017-12-21 14:30:38 +00:00
|
|
|
|
xmlFSType = new FileSystemType();
|
2017-10-08 20:28:56 +01:00
|
|
|
|
|
2017-10-13 21:50:10 +01:00
|
|
|
|
if(pvd == null && hsvd == null && fsvd == null)
|
2017-10-08 20:28:56 +01:00
|
|
|
|
{
|
|
|
|
|
|
information = "ERROR: Could not find primary volume descriptor";
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(HighSierra) decodedVD = DecodeVolumeDescriptor(hsvd.Value);
|
|
|
|
|
|
else if(CDi) decodedVD = DecodeVolumeDescriptor(fsvd.Value);
|
|
|
|
|
|
else decodedVD = DecodeVolumeDescriptor(pvd.Value);
|
|
|
|
|
|
|
|
|
|
|
|
if(jolietvd != null) decodedJolietVD = DecodeJolietDescriptor(jolietvd.Value);
|
2012-08-03 05:43:58 +00:00
|
|
|
|
|
2017-10-13 22:15:44 +01:00
|
|
|
|
uint rootLocation = 0;
|
2017-10-09 09:21:30 +01:00
|
|
|
|
uint rootSize = 0;
|
|
|
|
|
|
|
2017-10-13 22:15:44 +01:00
|
|
|
|
// No need to read root on CD-i, as extensions are not supported...
|
|
|
|
|
|
if(!CDi)
|
2017-10-09 09:21:30 +01:00
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
|
rootLocation = HighSierra
|
|
|
|
|
|
? hsvd.Value.root_directory_record.extent
|
|
|
|
|
|
: pvd.Value.root_directory_record.extent;
|
2017-10-13 22:15:44 +01:00
|
|
|
|
|
|
|
|
|
|
if(HighSierra)
|
|
|
|
|
|
{
|
|
|
|
|
|
rootSize = hsvd.Value.root_directory_record.size / hsvd.Value.logical_block_size;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(hsvd.Value.root_directory_record.size % hsvd.Value.logical_block_size > 0) rootSize++;
|
2017-10-13 22:15:44 +01:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
rootSize = pvd.Value.root_directory_record.size / pvd.Value.logical_block_size;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(pvd.Value.root_directory_record.size % pvd.Value.logical_block_size > 0) rootSize++;
|
2017-10-13 22:15:44 +01:00
|
|
|
|
}
|
2017-10-09 09:21:30 +01:00
|
|
|
|
}
|
2017-10-09 02:26:45 +01:00
|
|
|
|
|
|
|
|
|
|
byte[] root_dir = imagePlugin.ReadSectors(rootLocation + partition.Start, rootSize);
|
|
|
|
|
|
int rootOff = 0;
|
|
|
|
|
|
bool XA = false;
|
2017-10-09 09:48:28 +01:00
|
|
|
|
bool Apple = false;
|
2017-10-09 11:25:47 +01:00
|
|
|
|
bool SUSP = false;
|
2017-10-09 12:07:48 +01:00
|
|
|
|
bool RRIP = false;
|
2017-10-09 12:21:38 +01:00
|
|
|
|
bool ziso = false;
|
2017-10-09 12:31:39 +01:00
|
|
|
|
bool Amiga = false;
|
2017-10-09 12:54:46 +01:00
|
|
|
|
bool AAIP = false;
|
2017-10-09 11:25:47 +01:00
|
|
|
|
List<ContinuationArea> contareas = new List<ContinuationArea>();
|
|
|
|
|
|
List<byte[]> refareas = new List<byte[]>();
|
|
|
|
|
|
StringBuilder suspInformation = new StringBuilder();
|
2017-10-09 09:48:28 +01:00
|
|
|
|
|
|
|
|
|
|
BigEndianBitConverter.IsLittleEndian = BitConverter.IsLittleEndian;
|
2017-10-09 02:26:45 +01:00
|
|
|
|
|
|
|
|
|
|
// Walk thru root directory to see system area extensions in use
|
2017-10-13 21:50:10 +01:00
|
|
|
|
while(rootOff + Marshal.SizeOf(typeof(DirectoryRecord)) < root_dir.Length && !CDi)
|
2017-10-09 02:26:45 +01:00
|
|
|
|
{
|
|
|
|
|
|
DirectoryRecord record = new DirectoryRecord();
|
|
|
|
|
|
IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(record));
|
|
|
|
|
|
Marshal.Copy(root_dir, rootOff, ptr, Marshal.SizeOf(record));
|
|
|
|
|
|
record = (DirectoryRecord)Marshal.PtrToStructure(ptr, typeof(DirectoryRecord));
|
|
|
|
|
|
Marshal.FreeHGlobal(ptr);
|
|
|
|
|
|
|
|
|
|
|
|
int sa_off = Marshal.SizeOf(record) + record.name_len;
|
2017-10-09 09:21:30 +01:00
|
|
|
|
sa_off += sa_off % 2;
|
2017-10-09 02:26:45 +01:00
|
|
|
|
int sa_len = record.length - sa_off;
|
|
|
|
|
|
|
|
|
|
|
|
if(sa_len > 0 && rootOff + sa_off + sa_len <= root_dir.Length)
|
|
|
|
|
|
{
|
|
|
|
|
|
byte[] sa = new byte[sa_len];
|
|
|
|
|
|
Array.Copy(root_dir, rootOff + sa_off, sa, 0, sa_len);
|
2017-10-09 09:21:30 +01:00
|
|
|
|
sa_off = 0;
|
2017-10-09 02:26:45 +01:00
|
|
|
|
|
2017-10-09 09:48:28 +01:00
|
|
|
|
while(sa_off < sa_len)
|
2017-10-09 02:26:45 +01:00
|
|
|
|
{
|
2017-10-09 09:48:28 +01:00
|
|
|
|
bool noneFound = true;
|
|
|
|
|
|
|
|
|
|
|
|
if(Marshal.SizeOf(typeof(CdromXa)) + sa_off <= sa_len)
|
|
|
|
|
|
{
|
|
|
|
|
|
CdromXa xa = BigEndianMarshal.ByteArrayToStructureBigEndian<CdromXa>(sa);
|
|
|
|
|
|
if(xa.signature == XaMagic)
|
|
|
|
|
|
{
|
|
|
|
|
|
XA = true;
|
|
|
|
|
|
sa_off += Marshal.SizeOf(typeof(CdromXa));
|
|
|
|
|
|
noneFound = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(sa_off + 2 >= sa_len) break;
|
2017-10-09 09:48:28 +01:00
|
|
|
|
|
|
|
|
|
|
ushort nextSignature = BigEndianBitConverter.ToUInt16(sa, sa_off);
|
|
|
|
|
|
|
2017-12-21 04:43:29 +00:00
|
|
|
|
switch(nextSignature) {
|
|
|
|
|
|
// Easy, contains size field
|
|
|
|
|
|
case AppleMagic:
|
|
|
|
|
|
Apple = true;
|
|
|
|
|
|
sa_off += sa[sa_off + 2];
|
|
|
|
|
|
noneFound = false;
|
|
|
|
|
|
break;
|
|
|
|
|
|
// Not easy, contains size field
|
|
|
|
|
|
case AppleMagicOld:
|
|
|
|
|
|
Apple = true;
|
|
|
|
|
|
AppleOldId apple_id = (AppleOldId)sa[sa_off + 2];
|
|
|
|
|
|
noneFound = false;
|
2017-10-09 11:25:47 +01:00
|
|
|
|
|
2017-12-21 04:43:29 +00:00
|
|
|
|
switch(apple_id)
|
2017-10-09 11:25:47 +01:00
|
|
|
|
{
|
2017-12-21 04:43:29 +00:00
|
|
|
|
case AppleOldId.ProDOS:
|
|
|
|
|
|
sa_off += Marshal.SizeOf(typeof(AppleProDOSOldSystemUse));
|
|
|
|
|
|
break;
|
|
|
|
|
|
case AppleOldId.TypeCreator:
|
|
|
|
|
|
case AppleOldId.TypeCreatorBundle:
|
|
|
|
|
|
sa_off += Marshal.SizeOf(typeof(AppleHFSTypeCreatorSystemUse));
|
|
|
|
|
|
break;
|
|
|
|
|
|
case AppleOldId.TypeCreatorIcon:
|
|
|
|
|
|
case AppleOldId.TypeCreatorIconBundle:
|
|
|
|
|
|
sa_off += Marshal.SizeOf(typeof(AppleHFSIconSystemUse));
|
|
|
|
|
|
break;
|
|
|
|
|
|
case AppleOldId.HFS:
|
|
|
|
|
|
sa_off += Marshal.SizeOf(typeof(AppleHFSOldSystemUse));
|
|
|
|
|
|
break;
|
2017-10-09 11:25:47 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-21 04:43:29 +00:00
|
|
|
|
break;
|
|
|
|
|
|
// IEEE-P1281 aka SUSP 1.12
|
|
|
|
|
|
case SUSP_Indicator:
|
|
|
|
|
|
SUSP = true;
|
|
|
|
|
|
sa_off += sa[sa_off + 2];
|
|
|
|
|
|
noneFound = false;
|
|
|
|
|
|
|
|
|
|
|
|
while(sa_off + 2 < sa_len)
|
2017-10-09 11:25:47 +01:00
|
|
|
|
{
|
2017-12-21 04:43:29 +00:00
|
|
|
|
nextSignature = BigEndianBitConverter.ToUInt16(sa, sa_off);
|
|
|
|
|
|
|
|
|
|
|
|
switch(nextSignature) {
|
|
|
|
|
|
case AppleMagic:
|
|
|
|
|
|
if(sa[sa_off + 3] == 1 && sa[sa_off + 2] == 7) Apple = true;
|
|
|
|
|
|
else Apple |= sa[sa_off + 3] != 1;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case SUSP_Continuation when sa_off + sa[sa_off + 2] <= sa_len:
|
|
|
|
|
|
byte[] ce = new byte[sa[sa_off + 2]];
|
|
|
|
|
|
Array.Copy(sa, sa_off, ce, 0, ce.Length);
|
|
|
|
|
|
ContinuationArea ca =
|
|
|
|
|
|
BigEndianMarshal.ByteArrayToStructureBigEndian<ContinuationArea>(ce);
|
|
|
|
|
|
contareas.Add(ca);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case SUSP_Reference when sa_off + sa[sa_off + 2] <= sa_len:
|
|
|
|
|
|
byte[] er = new byte[sa[sa_off + 2]];
|
|
|
|
|
|
Array.Copy(sa, sa_off, er, 0, er.Length);
|
|
|
|
|
|
refareas.Add(er);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
RRIP |= nextSignature == RRIP_Magic || nextSignature == RRIP_PosixAttributes ||
|
|
|
|
|
|
nextSignature == RRIP_PosixDevNo || nextSignature == RRIP_Symlink ||
|
|
|
|
|
|
nextSignature == RRIP_Name || nextSignature == RRIP_Childlink ||
|
|
|
|
|
|
nextSignature == RRIP_Parentlink || nextSignature == RRIP_RelocatedDir ||
|
|
|
|
|
|
nextSignature == RRIP_Timestamps || nextSignature == RRIP_Sparse;
|
|
|
|
|
|
|
|
|
|
|
|
ziso |= nextSignature == ziso_Magic;
|
|
|
|
|
|
Amiga |= nextSignature == Amiga_Magic;
|
|
|
|
|
|
AAIP |= nextSignature == AAIP_Magic ||
|
|
|
|
|
|
nextSignature == AAIP_OldMagic && sa[sa_off + 3] == 1 && sa[sa_off + 2] >= 9;
|
|
|
|
|
|
|
|
|
|
|
|
sa_off += sa[sa_off + 2];
|
|
|
|
|
|
|
|
|
|
|
|
if(nextSignature == SUSP_Terminator) break;
|
2017-10-09 11:25:47 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-21 04:43:29 +00:00
|
|
|
|
break;
|
2017-10-09 11:25:47 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(noneFound) break;
|
2017-10-09 02:26:45 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
rootOff += record.length;
|
|
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(record.length == 0) break;
|
2017-10-09 02:26:45 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-10-09 11:25:47 +01:00
|
|
|
|
foreach(ContinuationArea ca in contareas)
|
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
|
uint ca_len = (ca.ca_length_be + ca.offset_be) /
|
|
|
|
|
|
(HighSierra ? hsvd.Value.logical_block_size : pvd.Value.logical_block_size);
|
|
|
|
|
|
if((ca.ca_length_be + ca.offset_be) %
|
|
|
|
|
|
(HighSierra ? hsvd.Value.logical_block_size : pvd.Value.logical_block_size) > 0) ca_len++;
|
2017-10-09 11:25:47 +01:00
|
|
|
|
|
|
|
|
|
|
byte[] ca_sectors = imagePlugin.ReadSectors(ca.block_be, ca_len);
|
|
|
|
|
|
byte[] ca_data = new byte[ca.ca_length_be];
|
|
|
|
|
|
Array.Copy(ca_sectors, ca.offset_be, ca_data, 0, ca.ca_length_be);
|
|
|
|
|
|
int ca_off = 0;
|
|
|
|
|
|
|
|
|
|
|
|
while(ca_off < ca.ca_length_be)
|
|
|
|
|
|
{
|
|
|
|
|
|
ushort nextSignature = BigEndianBitConverter.ToUInt16(ca_data, ca_off);
|
|
|
|
|
|
|
2017-12-21 04:43:29 +00:00
|
|
|
|
switch(nextSignature) {
|
|
|
|
|
|
// Apple never said to include its extensions inside a continuation area, but just in case
|
|
|
|
|
|
case AppleMagic:
|
|
|
|
|
|
if(ca_data[ca_off + 3] == 1 && ca_data[ca_off + 2] == 7) Apple = true;
|
|
|
|
|
|
else Apple |= ca_data[ca_off + 3] != 1;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case SUSP_Reference when ca_off + ca_data[ca_off + 2] <= ca.ca_length_be:
|
|
|
|
|
|
byte[] er = new byte[ca_data[ca_off + 2]];
|
|
|
|
|
|
Array.Copy(ca_data, ca_off, er, 0, er.Length);
|
|
|
|
|
|
refareas.Add(er);
|
|
|
|
|
|
break;
|
2017-10-09 11:25:47 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-10-09 12:07:48 +01:00
|
|
|
|
RRIP |= nextSignature == RRIP_Magic || nextSignature == RRIP_PosixAttributes ||
|
2017-12-19 20:33:03 +00:00
|
|
|
|
nextSignature == RRIP_PosixDevNo || nextSignature == RRIP_Symlink ||
|
|
|
|
|
|
nextSignature == RRIP_Name || nextSignature == RRIP_Childlink ||
|
|
|
|
|
|
nextSignature == RRIP_Parentlink || nextSignature == RRIP_RelocatedDir ||
|
|
|
|
|
|
nextSignature == RRIP_Timestamps || nextSignature == RRIP_Sparse;
|
2017-10-09 12:07:48 +01:00
|
|
|
|
|
2017-10-09 12:21:38 +01:00
|
|
|
|
ziso |= nextSignature == ziso_Magic;
|
2017-10-09 12:31:39 +01:00
|
|
|
|
Amiga |= nextSignature == Amiga_Magic;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
AAIP |= nextSignature == AAIP_Magic ||
|
2017-12-20 17:26:28 +00:00
|
|
|
|
nextSignature == AAIP_OldMagic && ca_data[ca_off + 3] == 1 && ca_data[ca_off + 2] >= 9;
|
2017-10-09 12:21:38 +01:00
|
|
|
|
|
2017-10-09 11:25:47 +01:00
|
|
|
|
ca_off += ca_data[ca_off + 2];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if(refareas.Count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
suspInformation.AppendLine("----------------------------------------");
|
|
|
|
|
|
suspInformation.AppendLine("SYSTEM USE SHARING PROTOCOL INFORMATION:");
|
|
|
|
|
|
suspInformation.AppendLine("----------------------------------------");
|
|
|
|
|
|
|
|
|
|
|
|
counter = 1;
|
|
|
|
|
|
foreach(byte[] erb in refareas)
|
|
|
|
|
|
{
|
|
|
|
|
|
ReferenceArea er = BigEndianMarshal.ByteArrayToStructureBigEndian<ReferenceArea>(erb);
|
|
|
|
|
|
string ext_id = CurrentEncoding.GetString(erb, Marshal.SizeOf(er), er.id_len);
|
|
|
|
|
|
string ext_des = CurrentEncoding.GetString(erb, Marshal.SizeOf(er) + er.id_len, er.des_len);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
string ext_src =
|
|
|
|
|
|
CurrentEncoding.GetString(erb, Marshal.SizeOf(er) + er.id_len + er.des_len, er.src_len);
|
2017-10-09 11:25:47 +01:00
|
|
|
|
suspInformation.AppendFormat("Extension: {0}", counter).AppendLine();
|
|
|
|
|
|
suspInformation.AppendFormat("\tID: {0}, version {1}", ext_id, er.ext_ver).AppendLine();
|
|
|
|
|
|
suspInformation.AppendFormat("\tDescription: {0}", ext_des).AppendLine();
|
|
|
|
|
|
suspInformation.AppendFormat("\tSource: {0}", ext_src).AppendLine();
|
|
|
|
|
|
counter++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2011-03-03 18:34:33 +00:00
|
|
|
|
|
2017-07-19 16:37:11 +01:00
|
|
|
|
byte[] ipbin_sector = imagePlugin.ReadSector(0 + partition.Start);
|
2017-12-21 14:30:38 +00:00
|
|
|
|
CD.IPBin? SegaCD = CD.DecodeIPBin(ipbin_sector);
|
|
|
|
|
|
Saturn.IPBin? Saturn = Decoders.Sega.Saturn.DecodeIPBin(ipbin_sector);
|
|
|
|
|
|
Dreamcast.IPBin? Dreamcast = Decoders.Sega.Dreamcast.DecodeIPBin(ipbin_sector);
|
2011-03-03 18:34:33 +00:00
|
|
|
|
|
2017-10-13 21:50:10 +01:00
|
|
|
|
string fsFormat;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(HighSierra) fsFormat = "High Sierra Format";
|
|
|
|
|
|
else if(CDi) fsFormat = "CD-i";
|
|
|
|
|
|
else fsFormat = "ISO9660";
|
|
|
|
|
|
|
2017-10-13 21:50:10 +01:00
|
|
|
|
ISOMetadata.AppendFormat("{0} file system", fsFormat).AppendLine();
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(XA) ISOMetadata.AppendLine("CD-ROM XA 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.");
|
2017-10-08 20:28:56 +01:00
|
|
|
|
if(bvd != null)
|
2011-03-03 18:34:33 +00:00
|
|
|
|
ISOMetadata.AppendFormat("Disc bootable following {0} specifications.", BootSpec).AppendLine();
|
2017-10-08 17:50:29 +01:00
|
|
|
|
if(SegaCD != null)
|
2011-03-03 18:34:33 +00:00
|
|
|
|
{
|
|
|
|
|
|
ISOMetadata.AppendLine("This is a SegaCD / MegaCD disc.");
|
2017-12-21 14:30:38 +00:00
|
|
|
|
ISOMetadata.AppendLine(CD.Prettify(SegaCD));
|
2011-03-03 18:34:33 +00:00
|
|
|
|
}
|
2017-10-08 17:50:29 +01:00
|
|
|
|
if(Saturn != null)
|
2011-03-03 18:34:33 +00:00
|
|
|
|
{
|
|
|
|
|
|
ISOMetadata.AppendLine("This is a Sega Saturn disc.");
|
2017-10-08 17:50:29 +01:00
|
|
|
|
ISOMetadata.AppendLine(Decoders.Sega.Saturn.Prettify(Saturn));
|
2011-03-03 18:34:33 +00:00
|
|
|
|
}
|
2017-10-08 17:50:29 +01:00
|
|
|
|
if(Dreamcast != null)
|
2011-03-03 18:34:33 +00:00
|
|
|
|
{
|
|
|
|
|
|
ISOMetadata.AppendLine("This is a Sega Dreamcast disc.");
|
2017-10-08 17:50:29 +01:00
|
|
|
|
ISOMetadata.AppendLine(Decoders.Sega.Dreamcast.Prettify(Dreamcast));
|
2011-03-03 18:34:33 +00:00
|
|
|
|
}
|
2017-10-13 22:19:04 +01:00
|
|
|
|
ISOMetadata.AppendFormat("{0}------------------------------", CDi ? "---------------" : "").AppendLine();
|
|
|
|
|
|
ISOMetadata.AppendFormat("{0}VOLUME DESCRIPTOR INFORMATION:", CDi ? "FILE STRUCTURE " : "").AppendLine();
|
|
|
|
|
|
ISOMetadata.AppendFormat("{0}------------------------------", CDi ? "---------------" : "").AppendLine();
|
2011-03-03 18:34:33 +00:00
|
|
|
|
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();
|
2014-04-14 02:29:13 +00:00
|
|
|
|
ISOMetadata.AppendFormat("Volume creation date: {0}", decodedVD.CreationTime).AppendLine();
|
2016-04-19 02:11:47 +01:00
|
|
|
|
if(decodedVD.HasModificationTime)
|
2014-04-14 02:29:13 +00:00
|
|
|
|
ISOMetadata.AppendFormat("Volume modification date: {0}", decodedVD.ModificationTime).AppendLine();
|
2017-12-19 20:33:03 +00:00
|
|
|
|
else ISOMetadata.AppendFormat("Volume has not been modified.").AppendLine();
|
2016-04-19 02:11:47 +01:00
|
|
|
|
if(decodedVD.HasExpirationTime)
|
2014-04-14 02:29:13 +00:00
|
|
|
|
ISOMetadata.AppendFormat("Volume expiration date: {0}", decodedVD.ExpirationTime).AppendLine();
|
2017-12-19 20:33:03 +00:00
|
|
|
|
else ISOMetadata.AppendFormat("Volume does not expire.").AppendLine();
|
2016-04-19 02:11:47 +01:00
|
|
|
|
if(decodedVD.HasEffectiveTime)
|
2014-04-14 02:29:13 +00:00
|
|
|
|
ISOMetadata.AppendFormat("Volume effective date: {0}", decodedVD.EffectiveTime).AppendLine();
|
2017-12-19 20:33:03 +00:00
|
|
|
|
else ISOMetadata.AppendFormat("Volume has always been effective.").AppendLine();
|
|
|
|
|
|
ISOMetadata.AppendFormat("Volume has {0} blocks of {1} bytes each", decodedVD.Blocks, decodedVD.BlockSize)
|
|
|
|
|
|
.AppendLine();
|
2011-03-03 18:34:33 +00:00
|
|
|
|
|
2017-10-08 20:28:56 +01:00
|
|
|
|
if(jolietvd != null)
|
2016-04-19 02:11:47 +01:00
|
|
|
|
{
|
2017-10-09 00:32:17 +01:00
|
|
|
|
ISOMetadata.AppendLine("-------------------------------------");
|
2016-04-19 02:11:47 +01:00
|
|
|
|
ISOMetadata.AppendLine("JOLIET VOLUME DESCRIPTOR INFORMATION:");
|
2017-10-09 00:32:17 +01:00
|
|
|
|
ISOMetadata.AppendLine("-------------------------------------");
|
2016-04-19 02:11:47 +01:00
|
|
|
|
ISOMetadata.AppendFormat("System identifier: {0}", decodedJolietVD.SystemIdentifier).AppendLine();
|
|
|
|
|
|
ISOMetadata.AppendFormat("Volume identifier: {0}", decodedJolietVD.VolumeIdentifier).AppendLine();
|
2017-12-19 20:33:03 +00:00
|
|
|
|
ISOMetadata.AppendFormat("Volume set identifier: {0}", decodedJolietVD.VolumeSetIdentifier)
|
|
|
|
|
|
.AppendLine();
|
2016-04-19 02:11:47 +01:00
|
|
|
|
ISOMetadata.AppendFormat("Publisher identifier: {0}", decodedJolietVD.PublisherIdentifier).AppendLine();
|
2017-12-19 20:33:03 +00:00
|
|
|
|
ISOMetadata.AppendFormat("Data preparer identifier: {0}", decodedJolietVD.DataPreparerIdentifier)
|
|
|
|
|
|
.AppendLine();
|
|
|
|
|
|
ISOMetadata.AppendFormat("Application identifier: {0}", decodedJolietVD.ApplicationIdentifier)
|
|
|
|
|
|
.AppendLine();
|
2016-04-19 02:11:47 +01:00
|
|
|
|
ISOMetadata.AppendFormat("Volume creation date: {0}", decodedJolietVD.CreationTime).AppendLine();
|
|
|
|
|
|
if(decodedJolietVD.HasModificationTime)
|
2017-12-19 20:33:03 +00:00
|
|
|
|
ISOMetadata.AppendFormat("Volume modification date: {0}", decodedJolietVD.ModificationTime)
|
|
|
|
|
|
.AppendLine();
|
|
|
|
|
|
else ISOMetadata.AppendFormat("Volume has not been modified.").AppendLine();
|
2016-04-19 02:11:47 +01:00
|
|
|
|
if(decodedJolietVD.HasExpirationTime)
|
2017-12-19 20:33:03 +00:00
|
|
|
|
ISOMetadata.AppendFormat("Volume expiration date: {0}", decodedJolietVD.ExpirationTime)
|
|
|
|
|
|
.AppendLine();
|
|
|
|
|
|
else ISOMetadata.AppendFormat("Volume does not expire.").AppendLine();
|
2016-04-19 02:11:47 +01:00
|
|
|
|
if(decodedJolietVD.HasEffectiveTime)
|
2014-04-14 02:29:13 +00:00
|
|
|
|
ISOMetadata.AppendFormat("Volume effective date: {0}", decodedJolietVD.EffectiveTime).AppendLine();
|
2017-12-19 20:33:03 +00:00
|
|
|
|
else ISOMetadata.AppendFormat("Volume has always been effective.").AppendLine();
|
2016-04-19 02:11:47 +01:00
|
|
|
|
}
|
2012-08-03 05:43:58 +00:00
|
|
|
|
|
2017-10-09 00:32:17 +01:00
|
|
|
|
if(torito != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
vd_sector = imagePlugin.ReadSector(torito.Value.catalog_sector + partition.Start);
|
2017-12-21 14:30:38 +00:00
|
|
|
|
Sha1Context sha1Ctx = new Sha1Context();
|
2017-10-09 00:32:17 +01:00
|
|
|
|
sha1Ctx.Init();
|
|
|
|
|
|
byte[] boot_image;
|
|
|
|
|
|
|
|
|
|
|
|
int torito_off = 0;
|
|
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(vd_sector[torito_off] != 1) goto exit_torito;
|
2017-10-09 00:32:17 +01:00
|
|
|
|
|
2017-12-21 16:07:20 +00:00
|
|
|
|
ElToritoValidationEntry valentry;
|
2017-10-09 00:32:17 +01:00
|
|
|
|
IntPtr ptr = Marshal.AllocHGlobal(ElToritoEntrySize);
|
|
|
|
|
|
Marshal.Copy(vd_sector, torito_off, ptr, ElToritoEntrySize);
|
|
|
|
|
|
valentry = (ElToritoValidationEntry)Marshal.PtrToStructure(ptr, typeof(ElToritoValidationEntry));
|
|
|
|
|
|
Marshal.FreeHGlobal(ptr);
|
|
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(valentry.signature != ElToritoMagic) goto exit_torito;
|
2017-10-09 00:32:17 +01:00
|
|
|
|
|
|
|
|
|
|
torito_off += ElToritoEntrySize;
|
|
|
|
|
|
|
2017-12-21 16:07:20 +00:00
|
|
|
|
ElToritoInitialEntry initial_entry;
|
2017-10-09 00:32:17 +01:00
|
|
|
|
ptr = Marshal.AllocHGlobal(ElToritoEntrySize);
|
|
|
|
|
|
Marshal.Copy(vd_sector, torito_off, ptr, ElToritoEntrySize);
|
|
|
|
|
|
initial_entry = (ElToritoInitialEntry)Marshal.PtrToStructure(ptr, typeof(ElToritoInitialEntry));
|
|
|
|
|
|
Marshal.FreeHGlobal(ptr);
|
|
|
|
|
|
initial_entry.boot_type = (ElToritoEmulation)((byte)initial_entry.boot_type & 0xF);
|
|
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
|
boot_image =
|
|
|
|
|
|
imagePlugin.ReadSectors(initial_entry.load_rba + partition.Start, initial_entry.sector_count);
|
2017-10-09 00:32:17 +01:00
|
|
|
|
|
|
|
|
|
|
ISOMetadata.AppendLine("----------------------");
|
|
|
|
|
|
ISOMetadata.AppendLine("EL TORITO INFORMATION:");
|
|
|
|
|
|
ISOMetadata.AppendLine("----------------------");
|
|
|
|
|
|
|
|
|
|
|
|
ISOMetadata.AppendLine("Initial entry:");
|
2017-12-19 20:33:03 +00:00
|
|
|
|
ISOMetadata.AppendFormat("\tDeveloper ID: {0}", CurrentEncoding.GetString(valentry.developer_id))
|
|
|
|
|
|
.AppendLine();
|
2017-10-09 00:32:17 +01:00
|
|
|
|
if(initial_entry.bootable == ElToritoIndicator.Bootable)
|
|
|
|
|
|
{
|
|
|
|
|
|
ISOMetadata.AppendFormat("\tBootable on {0}", valentry.platform_id).AppendLine();
|
2017-12-19 20:33:03 +00:00
|
|
|
|
ISOMetadata.AppendFormat("\tBootable image starts at sector {0} and runs for {1} sectors",
|
|
|
|
|
|
initial_entry.load_rba, initial_entry.sector_count).AppendLine();
|
2017-10-09 00:32:17 +01:00
|
|
|
|
if(valentry.platform_id == ElToritoPlatform.x86)
|
2017-12-19 20:33:03 +00:00
|
|
|
|
ISOMetadata.AppendFormat("\tBootable image will be loaded at segment {0:X4}h",
|
|
|
|
|
|
initial_entry.load_seg == 0 ? 0x7C0 : initial_entry.load_seg)
|
|
|
|
|
|
.AppendLine();
|
2017-10-09 00:32:17 +01:00
|
|
|
|
else
|
2017-12-19 20:33:03 +00:00
|
|
|
|
ISOMetadata.AppendFormat("\tBootable image will be loaded at 0x{0:X8}",
|
|
|
|
|
|
(uint)initial_entry.load_seg * 10).AppendLine();
|
2017-10-09 00:32:17 +01:00
|
|
|
|
switch(initial_entry.boot_type)
|
|
|
|
|
|
{
|
|
|
|
|
|
case ElToritoEmulation.None:
|
|
|
|
|
|
ISOMetadata.AppendLine("\tImage uses no emulation");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ElToritoEmulation.Md2hd:
|
|
|
|
|
|
ISOMetadata.AppendLine("\tImage emulates a 5.25\" high-density (MD2HD, 1.2Mb) floppy");
|
|
|
|
|
|
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:
|
2017-12-19 20:33:03 +00:00
|
|
|
|
ISOMetadata.AppendFormat("\tImage uses unknown emulation type {0}",
|
|
|
|
|
|
(byte)initial_entry.boot_type).AppendLine();
|
2017-10-09 00:32:17 +01:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
|
|
2017-10-09 00:32:17 +01:00
|
|
|
|
ISOMetadata.AppendFormat("\tSystem type: 0x{0:X2}", initial_entry.system_type).AppendLine();
|
2017-12-19 20:33:03 +00:00
|
|
|
|
ISOMetadata.AppendFormat("\tBootable image's SHA1: {0}", sha1Ctx.Data(boot_image, out byte[] hash))
|
|
|
|
|
|
.AppendLine();
|
2017-10-09 00:32:17 +01:00
|
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
|
else ISOMetadata.AppendLine("\tNot bootable");
|
2017-10-09 00:32:17 +01:00
|
|
|
|
|
|
|
|
|
|
torito_off += ElToritoEntrySize;
|
|
|
|
|
|
|
|
|
|
|
|
int section_counter = 2;
|
|
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
|
while(torito_off < vd_sector.Length && (vd_sector[torito_off] == (byte)ElToritoIndicator.Header ||
|
|
|
|
|
|
vd_sector[torito_off] == (byte)ElToritoIndicator.LastHeader))
|
2017-10-09 00:32:17 +01:00
|
|
|
|
{
|
2017-12-21 16:07:20 +00:00
|
|
|
|
ElToritoSectionHeaderEntry section_header;
|
2017-10-09 00:32:17 +01:00
|
|
|
|
ptr = Marshal.AllocHGlobal(ElToritoEntrySize);
|
|
|
|
|
|
Marshal.Copy(vd_sector, torito_off, ptr, ElToritoEntrySize);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
section_header =
|
|
|
|
|
|
(ElToritoSectionHeaderEntry)Marshal.PtrToStructure(ptr, typeof(ElToritoSectionHeaderEntry));
|
2017-10-09 00:32:17 +01:00
|
|
|
|
Marshal.FreeHGlobal(ptr);
|
|
|
|
|
|
torito_off += ElToritoEntrySize;
|
|
|
|
|
|
|
|
|
|
|
|
ISOMetadata.AppendFormat("Boot section {0}:", section_counter);
|
2017-12-19 20:33:03 +00:00
|
|
|
|
ISOMetadata.AppendFormat("\tSection ID: {0}", CurrentEncoding.GetString(section_header.identifier))
|
|
|
|
|
|
.AppendLine();
|
2017-10-09 00:32:17 +01:00
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
|
for(int entry_counter = 1; entry_counter <= section_header.entries && torito_off < vd_sector.Length;
|
|
|
|
|
|
entry_counter++)
|
2017-10-09 00:32:17 +01:00
|
|
|
|
{
|
2017-12-21 16:07:20 +00:00
|
|
|
|
ElToritoSectionEntry section_entry;
|
2017-10-09 00:32:17 +01:00
|
|
|
|
ptr = Marshal.AllocHGlobal(ElToritoEntrySize);
|
|
|
|
|
|
Marshal.Copy(vd_sector, torito_off, ptr, ElToritoEntrySize);
|
|
|
|
|
|
section_entry = (ElToritoSectionEntry)Marshal.PtrToStructure(ptr, typeof(ElToritoSectionEntry));
|
|
|
|
|
|
Marshal.FreeHGlobal(ptr);
|
|
|
|
|
|
torito_off += ElToritoEntrySize;
|
|
|
|
|
|
|
|
|
|
|
|
ISOMetadata.AppendFormat("\tEntry {0}:", entry_counter);
|
|
|
|
|
|
if(section_entry.bootable == ElToritoIndicator.Bootable)
|
|
|
|
|
|
{
|
2017-12-19 20:33:03 +00:00
|
|
|
|
boot_image =
|
|
|
|
|
|
imagePlugin.ReadSectors(section_entry.load_rba + partition.Start,
|
|
|
|
|
|
section_entry.sector_count);
|
2017-10-09 00:32:17 +01:00
|
|
|
|
ISOMetadata.AppendFormat("\t\tBootable on {0}", section_header.platform_id).AppendLine();
|
2017-12-19 20:33:03 +00:00
|
|
|
|
ISOMetadata.AppendFormat("\t\tBootable image starts at sector {0} and runs for {1} sectors",
|
|
|
|
|
|
section_entry.load_rba, section_entry.sector_count).AppendLine();
|
2017-10-09 00:32:17 +01:00
|
|
|
|
if(valentry.platform_id == ElToritoPlatform.x86)
|
2017-12-19 20:33:03 +00:00
|
|
|
|
ISOMetadata.AppendFormat("\t\tBootable image will be loaded at segment {0:X4}h",
|
|
|
|
|
|
section_entry.load_seg == 0 ? 0x7C0 : section_entry.load_seg)
|
|
|
|
|
|
.AppendLine();
|
2017-10-09 00:32:17 +01:00
|
|
|
|
else
|
2017-12-19 20:33:03 +00:00
|
|
|
|
ISOMetadata.AppendFormat("\t\tBootable image will be loaded at 0x{0:X8}",
|
|
|
|
|
|
(uint)section_entry.load_seg * 10).AppendLine();
|
2017-10-09 00:32:17 +01:00
|
|
|
|
switch((ElToritoEmulation)((byte)section_entry.boot_type & 0xF))
|
|
|
|
|
|
{
|
|
|
|
|
|
case ElToritoEmulation.None:
|
|
|
|
|
|
ISOMetadata.AppendLine("\t\tImage uses no emulation");
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ElToritoEmulation.Md2hd:
|
2017-12-19 20:33:03 +00:00
|
|
|
|
ISOMetadata
|
|
|
|
|
|
.AppendLine("\t\tImage emulates a 5.25\" high-density (MD2HD, 1.2Mb) floppy");
|
2017-10-09 00:32:17 +01:00
|
|
|
|
break;
|
|
|
|
|
|
case ElToritoEmulation.Mf2hd:
|
2017-12-19 20:33:03 +00:00
|
|
|
|
ISOMetadata
|
|
|
|
|
|
.AppendLine("\t\tImage emulates a 3.5\" high-density (MF2HD, 1.44Mb) floppy");
|
2017-10-09 00:32:17 +01:00
|
|
|
|
break;
|
|
|
|
|
|
case ElToritoEmulation.Mf2ed:
|
2017-12-19 20:33:03 +00:00
|
|
|
|
ISOMetadata
|
|
|
|
|
|
.AppendLine("\t\tImage emulates a 3.5\" extra-density (MF2ED, 2.88Mb) floppy");
|
2017-10-09 00:32:17 +01:00
|
|
|
|
break;
|
|
|
|
|
|
default:
|
2017-12-19 20:33:03 +00:00
|
|
|
|
ISOMetadata.AppendFormat("\t\tImage uses unknown emulation type {0}",
|
|
|
|
|
|
(byte)initial_entry.boot_type).AppendLine();
|
2017-10-09 00:32:17 +01:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
|
ISOMetadata.AppendFormat("\t\tSelection criteria type: {0}",
|
|
|
|
|
|
section_entry.selection_criteria_type).AppendLine();
|
|
|
|
|
|
ISOMetadata.AppendFormat("\t\tSystem type: 0x{0:X2}", section_entry.system_type)
|
|
|
|
|
|
.AppendLine();
|
|
|
|
|
|
ISOMetadata.AppendFormat("\t\tBootable image's SHA1: {0}",
|
|
|
|
|
|
sha1Ctx.Data(boot_image, out byte[] hash)).AppendLine();
|
2017-10-09 00:32:17 +01:00
|
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
|
else ISOMetadata.AppendLine("\t\tNot bootable");
|
2017-10-09 00:32:17 +01:00
|
|
|
|
|
|
|
|
|
|
ElToritoFlags flags = (ElToritoFlags)((byte)section_entry.boot_type & 0xF0);
|
|
|
|
|
|
if(flags.HasFlag(ElToritoFlags.ATAPI))
|
|
|
|
|
|
ISOMetadata.AppendLine("\t\tImage contains ATAPI drivers");
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(flags.HasFlag(ElToritoFlags.SCSI)) ISOMetadata.AppendLine("\t\tImage contains SCSI drivers");
|
2017-10-09 00:32:17 +01:00
|
|
|
|
|
2017-12-21 06:06:19 +00:00
|
|
|
|
if(!flags.HasFlag(ElToritoFlags.Continued)) continue;
|
|
|
|
|
|
|
|
|
|
|
|
while(true && torito_off < vd_sector.Length)
|
|
|
|
|
|
{
|
2017-12-21 16:07:20 +00:00
|
|
|
|
ElToritoSectionEntryExtension section_extension;
|
2017-12-21 06:06:19 +00:00
|
|
|
|
ptr = Marshal.AllocHGlobal(ElToritoEntrySize);
|
|
|
|
|
|
Marshal.Copy(vd_sector, torito_off, ptr, ElToritoEntrySize);
|
|
|
|
|
|
section_extension =
|
|
|
|
|
|
(ElToritoSectionEntryExtension)
|
|
|
|
|
|
Marshal.PtrToStructure(ptr, typeof(ElToritoSectionEntryExtension));
|
|
|
|
|
|
Marshal.FreeHGlobal(ptr);
|
|
|
|
|
|
torito_off += ElToritoEntrySize;
|
|
|
|
|
|
|
|
|
|
|
|
if(!section_extension.extension_flags.HasFlag(ElToritoFlags.Continued)) break;
|
|
|
|
|
|
}
|
2017-10-09 00:32:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(section_header.header_id == ElToritoIndicator.LastHeader) break;
|
2017-10-09 00:32:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
|
exit_torito:
|
2017-12-21 14:30:38 +00:00
|
|
|
|
if(refareas.Count > 0) ISOMetadata.Append(suspInformation);
|
2017-10-09 11:25:47 +01:00
|
|
|
|
|
2017-10-13 21:50:10 +01:00
|
|
|
|
xmlFSType.Type = fsFormat;
|
2015-12-23 23:46:31 +00:00
|
|
|
|
|
2017-10-08 20:28:56 +01:00
|
|
|
|
if(jolietvd != null)
|
2015-12-05 17:10:27 +00:00
|
|
|
|
{
|
2015-12-06 05:09:31 +00:00
|
|
|
|
xmlFSType.VolumeName = decodedJolietVD.VolumeIdentifier;
|
2015-12-23 23:46:31 +00:00
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(decodedJolietVD.SystemIdentifier == null ||
|
|
|
|
|
|
decodedVD.SystemIdentifier.Length > decodedJolietVD.SystemIdentifier.Length)
|
2015-12-23 23:46:31 +00:00
|
|
|
|
xmlFSType.SystemIdentifier = decodedVD.SystemIdentifier;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
else xmlFSType.SystemIdentifier = decodedJolietVD.SystemIdentifier;
|
2016-04-19 02:11:47 +01:00
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(decodedJolietVD.VolumeSetIdentifier == null || decodedVD.VolumeSetIdentifier.Length >
|
|
|
|
|
|
decodedJolietVD.VolumeSetIdentifier.Length)
|
2015-12-23 23:46:31 +00:00
|
|
|
|
xmlFSType.VolumeSetIdentifier = decodedVD.VolumeSetIdentifier;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
else xmlFSType.VolumeSetIdentifier = decodedJolietVD.VolumeSetIdentifier;
|
2016-04-19 02:11:47 +01:00
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(decodedJolietVD.PublisherIdentifier == null || decodedVD.PublisherIdentifier.Length >
|
|
|
|
|
|
decodedJolietVD.PublisherIdentifier.Length)
|
2015-12-23 23:46:31 +00:00
|
|
|
|
xmlFSType.PublisherIdentifier = decodedVD.PublisherIdentifier;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
else xmlFSType.PublisherIdentifier = decodedJolietVD.PublisherIdentifier;
|
2016-04-19 02:11:47 +01:00
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(decodedJolietVD.DataPreparerIdentifier == null || decodedVD.DataPreparerIdentifier.Length >
|
|
|
|
|
|
decodedJolietVD.DataPreparerIdentifier.Length)
|
2015-12-23 23:46:31 +00:00
|
|
|
|
xmlFSType.DataPreparerIdentifier = decodedVD.DataPreparerIdentifier;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
else xmlFSType.DataPreparerIdentifier = decodedJolietVD.SystemIdentifier;
|
2016-04-19 02:11:47 +01:00
|
|
|
|
|
2017-12-19 20:33:03 +00:00
|
|
|
|
if(decodedJolietVD.ApplicationIdentifier == null || decodedVD.ApplicationIdentifier.Length >
|
|
|
|
|
|
decodedJolietVD.ApplicationIdentifier.Length)
|
2015-12-23 23:46:31 +00:00
|
|
|
|
xmlFSType.ApplicationIdentifier = decodedVD.ApplicationIdentifier;
|
2017-12-19 20:33:03 +00:00
|
|
|
|
else xmlFSType.ApplicationIdentifier = decodedJolietVD.SystemIdentifier;
|
2015-12-23 23:46:31 +00:00
|
|
|
|
|
2015-12-06 05:09:31 +00:00
|
|
|
|
xmlFSType.CreationDate = decodedJolietVD.CreationTime;
|
|
|
|
|
|
xmlFSType.CreationDateSpecified = true;
|
2016-04-19 02:11:47 +01:00
|
|
|
|
if(decodedJolietVD.HasModificationTime)
|
2015-12-06 05:09:31 +00:00
|
|
|
|
{
|
|
|
|
|
|
xmlFSType.ModificationDate = decodedJolietVD.ModificationTime;
|
|
|
|
|
|
xmlFSType.ModificationDateSpecified = true;
|
|
|
|
|
|
}
|
2016-04-19 02:11:47 +01:00
|
|
|
|
if(decodedJolietVD.HasExpirationTime)
|
2015-12-06 05:09:31 +00:00
|
|
|
|
{
|
|
|
|
|
|
xmlFSType.ExpirationDate = decodedJolietVD.ExpirationTime;
|
|
|
|
|
|
xmlFSType.ExpirationDateSpecified = true;
|
|
|
|
|
|
}
|
2016-04-19 02:11:47 +01:00
|
|
|
|
if(decodedJolietVD.HasEffectiveTime)
|
2015-12-06 05:09:31 +00:00
|
|
|
|
{
|
|
|
|
|
|
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;
|
2016-04-19 02:11:47 +01:00
|
|
|
|
if(decodedVD.HasModificationTime)
|
2015-12-06 05:09:31 +00:00
|
|
|
|
{
|
|
|
|
|
|
xmlFSType.ModificationDate = decodedVD.ModificationTime;
|
|
|
|
|
|
xmlFSType.ModificationDateSpecified = true;
|
|
|
|
|
|
}
|
2016-04-19 02:11:47 +01:00
|
|
|
|
if(decodedVD.HasExpirationTime)
|
2015-12-06 05:09:31 +00:00
|
|
|
|
{
|
|
|
|
|
|
xmlFSType.ExpirationDate = decodedVD.ExpirationTime;
|
|
|
|
|
|
xmlFSType.ExpirationDateSpecified = true;
|
|
|
|
|
|
}
|
2016-04-19 02:11:47 +01:00
|
|
|
|
if(decodedVD.HasEffectiveTime)
|
2015-12-06 05:09:31 +00:00
|
|
|
|
{
|
|
|
|
|
|
xmlFSType.EffectiveDate = decodedVD.EffectiveTime;
|
|
|
|
|
|
xmlFSType.EffectiveDateSpecified = true;
|
|
|
|
|
|
}
|
2015-12-05 17:10:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2017-10-08 20:28:56 +01:00
|
|
|
|
xmlFSType.Bootable |= bvd != null || SegaCD != null || Saturn != null || Dreamcast != null;
|
2017-10-08 21:54:56 +01:00
|
|
|
|
xmlFSType.Clusters = decodedVD.Blocks;
|
|
|
|
|
|
xmlFSType.ClusterSize = decodedVD.BlockSize;
|
2015-12-06 05:09:31 +00:00
|
|
|
|
|
2011-03-03 18:34:33 +00:00
|
|
|
|
information = ISOMetadata.ToString();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2017-12-19 20:33:03 +00:00
|
|
|
|
}
|