Exemple #1
0
        /// <summary>
        ///     Creates a metadata sidecar for a block media (e.g. floppy, hard disk, flash card, usb stick)
        /// </summary>
        /// <param name="image">Image</param>
        /// <param name="filterId">Filter uuid</param>
        /// <param name="imagePath">Image path</param>
        /// <param name="fi">Image file information</param>
        /// <param name="plugins">Image plugins</param>
        /// <param name="imgChecksums">List of image checksums</param>
        /// <param name="sidecar">Metadata sidecar</param>
        static void BlockMedia(IMediaImage image, Guid filterId, string imagePath,
                               FileInfo fi, PluginBase plugins,
                               List <ChecksumType> imgChecksums, ref CICMMetadataType sidecar, Encoding encoding)
        {
            sidecar.BlockMedia = new[]
            {
                new BlockMediaType
                {
                    Checksums = imgChecksums.ToArray(),
                    Image     = new ImageType
                    {
                        format          = image.Format,
                        offset          = 0,
                        offsetSpecified = true,
                        Value           = Path.GetFileName(imagePath)
                    },
                    Size     = fi.Length,
                    Sequence = new SequenceType {
                        MediaTitle = image.Info.MediaTitle
                    }
                }
            };

            if (image.Info.MediaSequence != 0 && image.Info.LastMediaSequence != 0)
            {
                sidecar.BlockMedia[0].Sequence.MediaSequence = image.Info.MediaSequence;
                sidecar.BlockMedia[0].Sequence.TotalMedia    = image.Info.LastMediaSequence;
            }
            else
            {
                sidecar.BlockMedia[0].Sequence.MediaSequence = 1;
                sidecar.BlockMedia[0].Sequence.TotalMedia    = 1;
            }

            foreach (MediaTagType tagType in image.Info.ReadableMediaTags)
            {
                switch (tagType)
                {
                case MediaTagType.ATAPI_IDENTIFY:
                    sidecar.BlockMedia[0].ATA = new ATAType
                    {
                        Identify = new DumpType
                        {
                            Checksums =
                                Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.ATAPI_IDENTIFY)).ToArray(),
                            Size = image.ReadDiskTag(MediaTagType.ATAPI_IDENTIFY).Length
                        }
                    };
                    break;

                case MediaTagType.ATA_IDENTIFY:
                    sidecar.BlockMedia[0].ATA = new ATAType
                    {
                        Identify = new DumpType
                        {
                            Checksums =
                                Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.ATA_IDENTIFY)).ToArray(),
                            Size = image.ReadDiskTag(MediaTagType.ATA_IDENTIFY).Length
                        }
                    };
                    break;

                case MediaTagType.PCMCIA_CIS:
                    byte[] cis = image.ReadDiskTag(MediaTagType.PCMCIA_CIS);
                    sidecar.BlockMedia[0].PCMCIA = new PCMCIAType
                    {
                        CIS = new DumpType {
                            Checksums = Checksum.GetChecksums(cis).ToArray(), Size = 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:
                    sidecar.BlockMedia[0].SCSI = new SCSIType
                    {
                        Inquiry = new DumpType
                        {
                            Checksums =
                                Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SCSI_INQUIRY)).ToArray(),
                            Size = image.ReadDiskTag(MediaTagType.SCSI_INQUIRY).Length
                        }
                    };
                    break;

                case MediaTagType.SD_CID:
                    if (sidecar.BlockMedia[0].SecureDigital == null)
                    {
                        sidecar.BlockMedia[0].SecureDigital = new SecureDigitalType();
                    }
                    sidecar.BlockMedia[0].SecureDigital.CID = new DumpType
                    {
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SD_CID)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.SD_CID).Length
                    };
                    break;

                case MediaTagType.SD_CSD:
                    if (sidecar.BlockMedia[0].SecureDigital == null)
                    {
                        sidecar.BlockMedia[0].SecureDigital = new SecureDigitalType();
                    }
                    sidecar.BlockMedia[0].SecureDigital.CSD = new DumpType
                    {
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SD_CSD)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.SD_CSD).Length
                    };
                    break;

                case MediaTagType.SD_SCR:
                    if (sidecar.BlockMedia[0].SecureDigital == null)
                    {
                        sidecar.BlockMedia[0].SecureDigital = new SecureDigitalType();
                    }
                    sidecar.BlockMedia[0].SecureDigital.SCR = new DumpType
                    {
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SD_SCR)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.SD_SCR).Length
                    };
                    break;

                case MediaTagType.SD_OCR:
                    if (sidecar.BlockMedia[0].SecureDigital == null)
                    {
                        sidecar.BlockMedia[0].SecureDigital = new SecureDigitalType();
                    }
                    sidecar.BlockMedia[0].SecureDigital.OCR = new DumpType
                    {
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SD_OCR)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.SD_OCR).Length
                    };
                    break;

                case MediaTagType.MMC_CID:
                    if (sidecar.BlockMedia[0].MultiMediaCard == null)
                    {
                        sidecar.BlockMedia[0].MultiMediaCard = new MultiMediaCardType();
                    }
                    sidecar.BlockMedia[0].MultiMediaCard.CID = new DumpType
                    {
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SD_CID)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.SD_CID).Length
                    };
                    break;

                case MediaTagType.MMC_CSD:
                    if (sidecar.BlockMedia[0].MultiMediaCard == null)
                    {
                        sidecar.BlockMedia[0].MultiMediaCard = new MultiMediaCardType();
                    }
                    sidecar.BlockMedia[0].MultiMediaCard.CSD = new DumpType
                    {
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SD_CSD)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.SD_CSD).Length
                    };
                    break;

                case MediaTagType.MMC_OCR:
                    if (sidecar.BlockMedia[0].MultiMediaCard == null)
                    {
                        sidecar.BlockMedia[0].MultiMediaCard = new MultiMediaCardType();
                    }
                    sidecar.BlockMedia[0].MultiMediaCard.OCR = new DumpType
                    {
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SD_OCR)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.SD_OCR).Length
                    };
                    break;

                case MediaTagType.MMC_ExtendedCSD:
                    if (sidecar.BlockMedia[0].MultiMediaCard == null)
                    {
                        sidecar.BlockMedia[0].MultiMediaCard = new MultiMediaCardType();
                    }
                    sidecar.BlockMedia[0].MultiMediaCard.ExtendedCSD = new DumpType
                    {
                        Checksums =
                            Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.MMC_ExtendedCSD)).ToArray(),
                        Size = image.ReadDiskTag(MediaTagType.MMC_ExtendedCSD).Length
                    };
                    break;

                case MediaTagType.USB_Descriptors:
                    if (sidecar.BlockMedia[0].USB == null)
                    {
                        sidecar.BlockMedia[0].USB = new USBType();
                    }
                    sidecar.BlockMedia[0].USB.Descriptors = new DumpType
                    {
                        Checksums =
                            Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.USB_Descriptors)).ToArray(),
                        Size = image.ReadDiskTag(MediaTagType.USB_Descriptors).Length
                    };
                    break;

                case MediaTagType.SCSI_MODESENSE_6:
                    if (sidecar.BlockMedia[0].SCSI == null)
                    {
                        sidecar.BlockMedia[0].SCSI = new SCSIType();
                    }
                    sidecar.BlockMedia[0].SCSI.ModeSense = new DumpType
                    {
                        Checksums =
                            Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SCSI_MODESENSE_6)).ToArray(),
                        Size = image.ReadDiskTag(MediaTagType.SCSI_MODESENSE_6).Length
                    };
                    break;

                case MediaTagType.SCSI_MODESENSE_10:
                    if (sidecar.BlockMedia[0].SCSI == null)
                    {
                        sidecar.BlockMedia[0].SCSI = new SCSIType();
                    }
                    sidecar.BlockMedia[0].SCSI.ModeSense10 = new DumpType
                    {
                        Checksums =
                            Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.SCSI_MODESENSE_10)).ToArray(),
                        Size = image.ReadDiskTag(MediaTagType.SCSI_MODESENSE_10).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
            {
                Checksum contentChkWorker = new Checksum();

                // For fast debugging, skip checksum
                //goto skipImageChecksum;

                uint  sectorsToRead = 512;
                ulong sectors       = image.Info.Sectors;
                ulong doneSectors   = 0;

                InitProgress2();
                while (doneSectors < sectors)
                {
                    byte[] sector;

                    if (sectors - doneSectors >= sectorsToRead)
                    {
                        sector = image.ReadSectors(doneSectors, sectorsToRead);
                        UpdateProgress2("Hashings sector {0} of {1}", (long)doneSectors, (long)sectors);
                        doneSectors += sectorsToRead;
                    }
                    else
                    {
                        sector = image.ReadSectors(doneSectors, (uint)(sectors - doneSectors));
                        UpdateProgress2("Hashings sector {0} of {1}", (long)doneSectors, (long)sectors);
                        doneSectors += sectors - doneSectors;
                    }

                    contentChkWorker.Update(sector);
                }

                // For fast debugging, skip checksum
                //skipImageChecksum:

                List <ChecksumType> cntChecksums = contentChkWorker.End();

                sidecar.BlockMedia[0].ContentChecksums = cntChecksums.ToArray();

                EndProgress2();
            }

            MediaType.MediaTypeToString(image.Info.MediaType, out string dskType, out string dskSubType);
            sidecar.BlockMedia[0].DiskType    = dskType;
            sidecar.BlockMedia[0].DiskSubType = dskSubType;
            Statistics.AddMedia(image.Info.MediaType, false);

            sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(image.Info.MediaType);

            sidecar.BlockMedia[0].LogicalBlocks    = (long)image.Info.Sectors;
            sidecar.BlockMedia[0].LogicalBlockSize = (int)image.Info.SectorSize;
            // TODO: Detect it
            sidecar.BlockMedia[0].PhysicalBlockSize = (int)image.Info.SectorSize;

            UpdateStatus("Checking filesystems...");

            List <Partition> partitions = Partitions.GetAll(image);

            Partitions.AddSchemesToStats(partitions);

            sidecar.BlockMedia[0].FileSystemInformation = new PartitionType[1];
            if (partitions.Count > 0)
            {
                sidecar.BlockMedia[0].FileSystemInformation = new PartitionType[partitions.Count];
                for (int i = 0; i < partitions.Count; i++)
                {
                    sidecar.BlockMedia[0].FileSystemInformation[i] = new PartitionType
                    {
                        Description = partitions[i].Description,
                        EndSector   = (int)partitions[i].End,
                        Name        = partitions[i].Name,
                        Sequence    = (int)partitions[i].Sequence,
                        StartSector = (int)partitions[i].Start,
                        Type        = partitions[i].Type
                    };
                    List <FileSystemType> lstFs = new List <FileSystemType>();

                    foreach (IFilesystem plugin in plugins.PluginsList.Values)
                    {
                        try
                        {
                            if (!plugin.Identify(image, partitions[i]))
                            {
                                continue;
                            }

                            plugin.GetInformation(image, partitions[i], out _, encoding);
                            lstFs.Add(plugin.XmlFsType);
                            Statistics.AddFilesystem(plugin.XmlFsType.Type);
                        }
                    }
        /// <summary>
        ///     Creates a metadata sidecar for an optical disc (e.g. CD, DVD, GD, BD, XGD, GOD)
        /// </summary>
        /// <param name="image">Image</param>
        /// <param name="filterId">Filter uuid</param>
        /// <param name="imagePath">Image path</param>
        /// <param name="fi">Image file information</param>
        /// <param name="plugins">Image plugins</param>
        /// <param name="imgChecksums">List of image checksums</param>
        /// <param name="sidecar">Metadata sidecar</param>
        static void OpticalDisc(IMediaImage image, Guid filterId, string imagePath,
                                FileInfo fi, PluginBase plugins,
                                List <ChecksumType> imgChecksums, ref CICMMetadataType sidecar, Encoding encoding)
        {
            sidecar.OpticalDisc = new[]
            {
                new OpticalDiscType
                {
                    Checksums = imgChecksums.ToArray(),
                    Image     = new ImageType
                    {
                        format          = image.Format,
                        offset          = 0,
                        offsetSpecified = true,
                        Value           = Path.GetFileName(imagePath)
                    },
                    Size     = fi.Length,
                    Sequence = new SequenceType {
                        MediaTitle = image.Info.MediaTitle
                    }
                }
            };

            if (image.Info.MediaSequence != 0 && image.Info.LastMediaSequence != 0)
            {
                sidecar.OpticalDisc[0].Sequence.MediaSequence = image.Info.MediaSequence;
                sidecar.OpticalDisc[0].Sequence.TotalMedia    = image.Info.LastMediaSequence;
            }
            else
            {
                sidecar.OpticalDisc[0].Sequence.MediaSequence = 1;
                sidecar.OpticalDisc[0].Sequence.TotalMedia    = 1;
            }

            MediaType dskType = image.Info.MediaType;

            foreach (MediaTagType tagType in image.Info.ReadableMediaTags)
            {
                switch (tagType)
                {
                case MediaTagType.CD_ATIP:
                    sidecar.OpticalDisc[0].ATIP = new DumpType
                    {
                        Image     = Path.GetFileName(imagePath),
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_ATIP)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.CD_ATIP).Length
                    };
                    ATIP.CDATIP?atip = ATIP.Decode(image.ReadDiskTag(MediaTagType.CD_ATIP));
                    if (atip.HasValue)
                    {
                        if (atip.Value.DDCD)
                        {
                            dskType = atip.Value.DiscType ? MediaType.DDCDRW : MediaType.DDCDR;
                        }
                        else
                        {
                            dskType = atip.Value.DiscType ? MediaType.CDRW : MediaType.CDR;
                        }
                    }
                    break;

                case MediaTagType.DVD_BCA:
                    sidecar.OpticalDisc[0].BCA = new DumpType
                    {
                        Image     = Path.GetFileName(imagePath),
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.DVD_BCA)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.DVD_BCA).Length
                    };
                    break;

                case MediaTagType.BD_BCA:
                    sidecar.OpticalDisc[0].BCA = new DumpType
                    {
                        Image     = Path.GetFileName(imagePath),
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.BD_BCA)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.BD_BCA).Length
                    };
                    break;

                case MediaTagType.DVD_CMI:
                    sidecar.OpticalDisc[0].CMI = new DumpType
                    {
                        Image     = Path.GetFileName(imagePath),
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.DVD_CMI)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.DVD_CMI).Length
                    };
                    CSS_CPRM.LeadInCopyright?cmi =
                        CSS_CPRM.DecodeLeadInCopyright(image.ReadDiskTag(MediaTagType.DVD_CMI));
                    if (cmi.HasValue)
                    {
                        switch (cmi.Value.CopyrightType)
                        {
                        case CopyrightType.AACS:
                            sidecar.OpticalDisc[0].CopyProtection = "AACS";
                            break;

                        case CopyrightType.CSS:
                            sidecar.OpticalDisc[0].CopyProtection = "CSS";
                            break;

                        case CopyrightType.CPRM:
                            sidecar.OpticalDisc[0].CopyProtection = "CPRM";
                            break;
                        }
                    }

                    break;

                case MediaTagType.DVD_DMI:
                    sidecar.OpticalDisc[0].DMI = new DumpType
                    {
                        Image     = Path.GetFileName(imagePath),
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.DVD_DMI)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.DVD_DMI).Length
                    };
                    if (DMI.IsXbox(image.ReadDiskTag(MediaTagType.DVD_DMI)))
                    {
                        dskType = MediaType.XGD;
                        sidecar.OpticalDisc[0].Dimensions = new DimensionsType {
                            Diameter = 120, Thickness = 1.2
                        };
                    }
                    else if (DMI.IsXbox360(image.ReadDiskTag(MediaTagType.DVD_DMI)))
                    {
                        dskType = MediaType.XGD2;
                        sidecar.OpticalDisc[0].Dimensions = new DimensionsType {
                            Diameter = 120, Thickness = 1.2
                        };
                    }

                    break;

                case MediaTagType.DVD_PFI:
                    sidecar.OpticalDisc[0].PFI = new DumpType
                    {
                        Image     = Path.GetFileName(imagePath),
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.DVD_PFI)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.DVD_PFI).Length
                    };
                    PFI.PhysicalFormatInformation?pfi = PFI.Decode(image.ReadDiskTag(MediaTagType.DVD_PFI));
                    if (pfi.HasValue)
                    {
                        if (dskType != MediaType.XGD && dskType != MediaType.XGD2 && dskType != MediaType.XGD3)
                        {
                            switch (pfi.Value.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 = MediaType.DVDR;
                                break;

                            case DiskCategory.DVDRAM:
                                dskType = MediaType.DVDRAM;
                                break;

                            case DiskCategory.DVDROM:
                                dskType = MediaType.DVDROM;
                                break;

                            case DiskCategory.DVDRW:
                                dskType = 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 = 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 == 3)
                            {
                                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;

                case MediaTagType.CD_PMA:
                    sidecar.OpticalDisc[0].PMA = new DumpType
                    {
                        Image     = Path.GetFileName(imagePath),
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_PMA)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.CD_PMA).Length
                    };
                    break;

                case MediaTagType.CD_FullTOC:
                    sidecar.OpticalDisc[0].TOC = new DumpType
                    {
                        Image     = Path.GetFileName(imagePath),
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_FullTOC)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.CD_FullTOC).Length
                    };
                    break;

                case MediaTagType.CD_LeadIn:
                    sidecar.OpticalDisc[0].LeadIn = new[]
                    {
                        new BorderType
                        {
                            Image     = Path.GetFileName(imagePath),
                            Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_LeadIn)).ToArray(),
                            Size      = image.ReadDiskTag(MediaTagType.CD_LeadIn).Length
                        }
                    };
                    break;

                case MediaTagType.Xbox_SecuritySector:
                    if (sidecar.OpticalDisc[0].Xbox == null)
                    {
                        sidecar.OpticalDisc[0].Xbox = new XboxType();
                    }

                    sidecar.OpticalDisc[0].Xbox.SecuritySectors = new[]
                    {
                        new XboxSecuritySectorsType
                        {
                            RequestNumber   = 0,
                            RequestVersion  = 1,
                            SecuritySectors = new DumpType
                            {
                                Image     = Path.GetFileName(imagePath),
                                Checksums =
                                    Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.Xbox_SecuritySector))
                                    .ToArray(),
                                Size = image.ReadDiskTag(MediaTagType.Xbox_SecuritySector).Length
                            }
                        }
                    };

                    break;

                case MediaTagType.Xbox_PFI:
                    if (sidecar.OpticalDisc[0].Xbox == null)
                    {
                        sidecar.OpticalDisc[0].Xbox = new XboxType();
                    }

                    sidecar.OpticalDisc[0].Xbox.PFI = new DumpType
                    {
                        Image     = Path.GetFileName(imagePath),
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.Xbox_PFI)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.Xbox_PFI).Length
                    };
                    break;

                case MediaTagType.Xbox_DMI:
                    if (sidecar.OpticalDisc[0].Xbox == null)
                    {
                        sidecar.OpticalDisc[0].Xbox = new XboxType();
                    }

                    sidecar.OpticalDisc[0].Xbox.DMI = new DumpType
                    {
                        Image     = Path.GetFileName(imagePath),
                        Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.Xbox_DMI)).ToArray(),
                        Size      = image.ReadDiskTag(MediaTagType.Xbox_DMI).Length
                    };
                    break;
                }
            }

            try
            {
                List <Session> sessions = image.Sessions;
                sidecar.OpticalDisc[0].Sessions = sessions?.Count ?? 1;
            }
            catch { sidecar.OpticalDisc[0].Sessions = 1; }

            List <Track>     tracks  = image.Tracks;
            List <TrackType> trksLst = null;

            if (tracks != null)
            {
                sidecar.OpticalDisc[0].Tracks    = new int[1];
                sidecar.OpticalDisc[0].Tracks[0] = tracks.Count;
                trksLst = new List <TrackType>();
            }

            if (sidecar.OpticalDisc[0].Dimensions == null && image.Info.MediaType != MediaType.Unknown)
            {
                sidecar.OpticalDisc[0].Dimensions = Dimensions.DimensionsFromMediaType(image.Info.MediaType);
            }

            InitProgress();

            UpdateStatus("Checking filesystems");
            List <Partition> partitions = Partitions.GetAll(image);

            Partitions.AddSchemesToStats(partitions);

            foreach (Track trk in tracks)
            {
                TrackType xmlTrk = new TrackType();
                switch (trk.TrackType)
                {
                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.TrackSession, TrackNumber = (int)trk.TrackSequence
                };
                xmlTrk.StartSector = (long)trk.TrackStartSector;
                xmlTrk.EndSector   = (long)trk.TrackEndSector;

                if (trk.Indexes != null && trk.Indexes.ContainsKey(0))
                {
                    if (trk.Indexes.TryGetValue(0, out ulong idx0))
                    {
                        xmlTrk.StartSector = (long)idx0;
                    }
                }

                switch (sidecar.OpticalDisc[0].DiscType)
                {
                case "CD":
                case "GD":
                    xmlTrk.StartMSF = LbaToMsf(xmlTrk.StartSector);
                    xmlTrk.EndMSF   = LbaToMsf(xmlTrk.EndSector);
                    break;

                case "DDCD":
                    xmlTrk.StartMSF = DdcdLbaToMsf(xmlTrk.StartSector);
                    xmlTrk.EndMSF   = DdcdLbaToMsf(xmlTrk.EndSector);
                    break;
                }

                xmlTrk.Image = new ImageType {
                    Value = Path.GetFileName(trk.TrackFile), format = trk.TrackFileType
                };

                if (trk.TrackFileOffset > 0)
                {
                    xmlTrk.Image.offset          = (long)trk.TrackFileOffset;
                    xmlTrk.Image.offsetSpecified = true;
                }

                xmlTrk.Size           = (xmlTrk.EndSector - xmlTrk.StartSector + 1) * trk.TrackRawBytesPerSector;
                xmlTrk.BytesPerSector = trk.TrackBytesPerSector;

                uint  sectorsToRead = 512;
                ulong sectors       = (ulong)(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.TrackSequence, tracks.Count);

                    // For fast debugging, skip checksum
                    //goto skipChecksum;

                    Checksum trkChkWorker = new Checksum();

                    InitProgress2();
                    while (doneSectors < sectors)
                    {
                        byte[] sector;

                        if (sectors - doneSectors >= sectorsToRead)
                        {
                            sector = image.ReadSectorsLong(doneSectors, sectorsToRead,
                                                           (uint)xmlTrk.Sequence.TrackNumber);
                            UpdateProgress2("Hashings sector {0} of {1}", (long)doneSectors,
                                            (long)(trk.TrackEndSector - trk.TrackStartSector + 1));
                            doneSectors += sectorsToRead;
                        }
                        else
                        {
                            sector = image.ReadSectorsLong(doneSectors, (uint)(sectors - doneSectors),
                                                           (uint)xmlTrk.Sequence.TrackNumber);
                            UpdateProgress2("Hashings sector {0} of {1}", (long)doneSectors,
                                            (long)(trk.TrackEndSector - trk.TrackStartSector + 1));
                            doneSectors += sectors - doneSectors;
                        }

                        trkChkWorker.Update(sector);
                    }

                    List <ChecksumType> trkChecksums = trkChkWorker.End();

                    xmlTrk.Checksums = trkChecksums.ToArray();

                    EndProgress2();
                }

                if (trk.TrackSubchannelType != TrackSubchannelType.None)
                {
                    xmlTrk.SubChannel = new SubChannelType
                    {
                        Image = new ImageType {
                            Value = trk.TrackSubchannelFile
                        },
                        // TODO: Packed subchannel has different size?
                        Size = (xmlTrk.EndSector - xmlTrk.StartSector + 1) * 96
                    };

                    switch (trk.TrackSubchannelType)
                    {
                    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.TrackFileOffset > 0)
                    {
                        xmlTrk.SubChannel.Image.offset          = (long)trk.TrackSubchannelOffset;
                        xmlTrk.SubChannel.Image.offsetSpecified = true;
                    }

                    Checksum subChkWorker = new Checksum();

                    sectors     = (ulong)(xmlTrk.EndSector - xmlTrk.StartSector + 1);
                    doneSectors = 0;

                    InitProgress2();
                    while (doneSectors < sectors)
                    {
                        byte[] sector;

                        if (sectors - doneSectors >= sectorsToRead)
                        {
                            sector = image.ReadSectorsTag(doneSectors, sectorsToRead, (uint)xmlTrk.Sequence.TrackNumber,
                                                          SectorTagType.CdSectorSubchannel);
                            UpdateProgress2("Hashings subchannel sector {0} of {1}", (long)doneSectors,
                                            (long)(trk.TrackEndSector - trk.TrackStartSector + 1));
                            doneSectors += sectorsToRead;
                        }
                        else
                        {
                            sector = image.ReadSectorsTag(doneSectors, (uint)(sectors - doneSectors),
                                                          (uint)xmlTrk.Sequence.TrackNumber,
                                                          SectorTagType.CdSectorSubchannel);
                            UpdateProgress2("Hashings subchannel sector {0} of {1}", (long)doneSectors,
                                            (long)(trk.TrackEndSector - trk.TrackStartSector + 1));
                            doneSectors += sectors - doneSectors;
                        }

                        subChkWorker.Update(sector);
                    }

                    List <ChecksumType> subChecksums = subChkWorker.End();

                    xmlTrk.SubChannel.Checksums = subChecksums.ToArray();

                    EndProgress2();
                }

                // For fast debugging, skip checksum
                //skipChecksum:

                List <Partition> trkPartitions = partitions
                                                 .Where(p => p.Start >= trk.TrackStartSector &&
                                                        p.End <= trk.TrackEndSector).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[i] = new PartitionType
                        {
                            Description = trkPartitions[i].Description,
                            EndSector   = (int)trkPartitions[i].End,
                            Name        = trkPartitions[i].Name,
                            Sequence    = (int)trkPartitions[i].Sequence,
                            StartSector = (int)trkPartitions[i].Start,
                            Type        = trkPartitions[i].Type
                        };
                        List <FileSystemType> lstFs = new List <FileSystemType>();

                        foreach (IFilesystem plugin in plugins.PluginsList.Values)
                        {
                            try
                            {
                                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;
                                }
                            }
                        }
Exemple #3
0
        /// <summary>Creates a metadata sidecar for an optical disc (e.g. CD, DVD, GD, BD, XGD, GOD)</summary>
        /// <param name="image">Image</param>
        /// <param name="filterId">Filter uuid</param>
        /// <param name="imagePath">Image path</param>
        /// <param name="fi">Image file information</param>
        /// <param name="plugins">Image plugins</param>
        /// <param name="imgChecksums">List of image checksums</param>
        /// <param name="sidecar">Metadata sidecar</param>
        void OpticalDisc(IOpticalMediaImage image, Guid filterId, string imagePath, FileInfo fi, PluginBase plugins,
            List<ChecksumType> 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;

            UpdateStatus("Hashing media tags...");

            foreach(MediaTagType tagType in image.Info.ReadableMediaTags)
            {
                if(aborted)
                    return;

                switch(tagType)
                {
                    case MediaTagType.CD_ATIP:
                        sidecar.OpticalDisc[0].ATIP = new DumpType
                        {
                            Image     = Path.GetFileName(imagePath),
                            Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_ATIP)).ToArray(),
                            Size      = (ulong)image.ReadDiskTag(MediaTagType.CD_ATIP).Length
                        };

                        ATIP.CDATIP? atip = ATIP.Decode(image.ReadDiskTag(MediaTagType.CD_ATIP));

                        if(atip.HasValue)
                            if(atip.Value.DDCD)
                                dskType = atip.Value.DiscType ? MediaType.DDCDRW : MediaType.DDCDR;
                            else
                                dskType = atip.Value.DiscType ? MediaType.CDRW : MediaType.CDR;

                        break;
                    case MediaTagType.DVD_BCA:
                        sidecar.OpticalDisc[0].BCA = new DumpType
                        {
                            Image     = Path.GetFileName(imagePath),
                            Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.DVD_BCA)).ToArray(),
                            Size      = (ulong)image.ReadDiskTag(MediaTagType.DVD_BCA).Length
                        };

                        break;
                    case MediaTagType.BD_BCA:
                        sidecar.OpticalDisc[0].BCA = new DumpType
                        {
                            Image     = Path.GetFileName(imagePath),
                            Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.BD_BCA)).ToArray(),
                            Size      = (ulong)image.ReadDiskTag(MediaTagType.BD_BCA).Length
                        };

                        break;
                    case MediaTagType.DVD_CMI:
                        sidecar.OpticalDisc[0].CMI = new DumpType
                        {
                            Image     = Path.GetFileName(imagePath),
                            Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.DVD_CMI)).ToArray(),
                            Size      = (ulong)image.ReadDiskTag(MediaTagType.DVD_CMI).Length
                        };

                        CSS_CPRM.LeadInCopyright? cmi =
                            CSS_CPRM.DecodeLeadInCopyright(image.ReadDiskTag(MediaTagType.DVD_CMI));

                        if(cmi.HasValue)
                            switch(cmi.Value.CopyrightType)
                            {
                                case CopyrightType.AACS:
                                    sidecar.OpticalDisc[0].CopyProtection = "AACS";

                                    break;
                                case CopyrightType.CSS:
                                    sidecar.OpticalDisc[0].CopyProtection = "CSS";

                                    break;
                                case CopyrightType.CPRM:
                                    sidecar.OpticalDisc[0].CopyProtection = "CPRM";

                                    break;
                            }

                        break;
                    case MediaTagType.DVD_DMI:
                        sidecar.OpticalDisc[0].DMI = new DumpType
                        {
                            Image     = Path.GetFileName(imagePath),
                            Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.DVD_DMI)).ToArray(),
                            Size      = (ulong)image.ReadDiskTag(MediaTagType.DVD_DMI).Length
                        };

                        if(DMI.IsXbox(image.ReadDiskTag(MediaTagType.DVD_DMI)))
                        {
                            dskType = MediaType.XGD;

                            sidecar.OpticalDisc[0].Dimensions = new DimensionsType
                            {
                                Diameter = 120, Thickness = 1.2
                            };
                        }
                        else if(DMI.IsXbox360(image.ReadDiskTag(MediaTagType.DVD_DMI)))
                        {
                            dskType = MediaType.XGD2;

                            sidecar.OpticalDisc[0].Dimensions = new DimensionsType
                            {
                                Diameter = 120, Thickness = 1.2
                            };
                        }

                        break;
                    case MediaTagType.DVD_PFI:
                        sidecar.OpticalDisc[0].PFI = new DumpType
                        {
                            Image     = Path.GetFileName(imagePath),
                            Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.DVD_PFI)).ToArray(),
                            Size      = (ulong)image.ReadDiskTag(MediaTagType.DVD_PFI).Length
                        };

                        PFI.PhysicalFormatInformation? pfi = PFI.Decode(image.ReadDiskTag(MediaTagType.DVD_PFI));

                        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.DVDPRDL:
                                        dskType = MediaType.DVDPRDL;

                                        break;
                                    case DiskCategory.DVDPRW:
                                        dskType = MediaType.DVDPRW;

                                        break;
                                    case DiskCategory.DVDPRWDL:
                                        dskType = MediaType.DVDPRWDL;

                                        break;
                                    case DiskCategory.DVDR:
                                        dskType = MediaType.DVDR;

                                        break;
                                    case DiskCategory.DVDRAM:
                                        dskType = MediaType.DVDRAM;

                                        break;
                                    case DiskCategory.DVDROM:
                                        dskType = MediaType.DVDROM;

                                        break;
                                    case DiskCategory.DVDRW:
                                        dskType = 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 = 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 == 3)
                                    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;
                    case MediaTagType.CD_PMA:
                        sidecar.OpticalDisc[0].PMA = new DumpType
                        {
                            Image     = Path.GetFileName(imagePath),
                            Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_PMA)).ToArray(),
                            Size      = (ulong)image.ReadDiskTag(MediaTagType.CD_PMA).Length
                        };

                        break;
                    case MediaTagType.CD_FullTOC:
                        sidecar.OpticalDisc[0].TOC = new DumpType
                        {
                            Image     = Path.GetFileName(imagePath),
                            Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_FullTOC)).ToArray(),
                            Size      = (ulong)image.ReadDiskTag(MediaTagType.CD_FullTOC).Length
                        };

                        break;
                    case MediaTagType.CD_FirstTrackPregap:
                        sidecar.OpticalDisc[0].FirstTrackPregrap = new[]
                        {
                            new BorderType
                            {
                                Image = Path.GetFileName(imagePath),
                                Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_FirstTrackPregap)).
                                                     ToArray(),
                                Size = (ulong)image.ReadDiskTag(MediaTagType.CD_FirstTrackPregap).Length
                            }
                        };

                        break;
                    case MediaTagType.CD_LeadIn:
                        sidecar.OpticalDisc[0].LeadIn = new[]
                        {
                            new BorderType
                            {
                                Image     = Path.GetFileName(imagePath),
                                Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.CD_LeadIn)).ToArray(),
                                Size      = (ulong)image.ReadDiskTag(MediaTagType.CD_LeadIn).Length
                            }
                        };

                        break;
                    case MediaTagType.Xbox_SecuritySector:
                        if(sidecar.OpticalDisc[0].Xbox == null)
                            sidecar.OpticalDisc[0].Xbox = new XboxType();

                        sidecar.OpticalDisc[0].Xbox.SecuritySectors = new[]
                        {
                            new XboxSecuritySectorsType
                            {
                                RequestNumber = 0, RequestVersion = 1, SecuritySectors = new DumpType
                                {
                                    Image = Path.GetFileName(imagePath),
                                    Checksums = Checksum.
                                                GetChecksums(image.ReadDiskTag(MediaTagType.Xbox_SecuritySector)).
                                                ToArray(),
                                    Size = (ulong)image.ReadDiskTag(MediaTagType.Xbox_SecuritySector).Length
                                }
                            }
                        };

                        break;
                    case MediaTagType.Xbox_PFI:
                        if(sidecar.OpticalDisc[0].Xbox == null)
                            sidecar.OpticalDisc[0].Xbox = new XboxType();

                        sidecar.OpticalDisc[0].Xbox.PFI = new DumpType
                        {
                            Image     = Path.GetFileName(imagePath),
                            Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.Xbox_PFI)).ToArray(),
                            Size      = (ulong)image.ReadDiskTag(MediaTagType.Xbox_PFI).Length
                        };

                        break;
                    case MediaTagType.Xbox_DMI:
                        if(sidecar.OpticalDisc[0].Xbox == null)
                            sidecar.OpticalDisc[0].Xbox = new XboxType();

                        sidecar.OpticalDisc[0].Xbox.DMI = new DumpType
                        {
                            Image     = Path.GetFileName(imagePath),
                            Checksums = Checksum.GetChecksums(image.ReadDiskTag(MediaTagType.Xbox_DMI)).ToArray(),
                            Size      = (ulong)image.ReadDiskTag(MediaTagType.Xbox_DMI).Length
                        };

                        break;
                }
            }

            try
            {
                List<Session> sessions = image.Sessions;
                sidecar.OpticalDisc[0].Sessions = (uint)(sessions?.Count ?? 1);
            }
            catch
            {
                sidecar.OpticalDisc[0].Sessions = 1;
            }

            List<Track>     tracks  = image.Tracks;
            List<TrackType> trksLst = null;

            if(tracks != null)
            {
                sidecar.OpticalDisc[0].Tracks    = new uint[1];
                sidecar.OpticalDisc[0].Tracks[0] = (uint)tracks.Count;
                trksLst                          = new List<TrackType>();
            }

            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<Partition> 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.TrackType)
                {
                    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.TrackSession, TrackNumber = trk.TrackSequence
                };

                xmlTrk.StartSector = trk.TrackStartSector;
                xmlTrk.EndSector   = trk.TrackEndSector;

                if(trk.Indexes != null &&
                   trk.Indexes.ContainsKey(0))
                    if(trk.Indexes.TryGetValue(0, out ulong idx0))
                        xmlTrk.StartSector = 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.TrackFile), format = trk.TrackFileType
                };

                if(trk.TrackFileOffset > 0)
                {
                    xmlTrk.Image.offset          = trk.TrackFileOffset;
                    xmlTrk.Image.offsetSpecified = true;
                }

                xmlTrk.Size           = (xmlTrk.EndSector - xmlTrk.StartSector + 1) * (ulong)trk.TrackRawBytesPerSector;
                xmlTrk.BytesPerSector = (uint)trk.TrackBytesPerSector;

                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.TrackSequence, 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)
                        {
                            sector = image.ReadSectorsLong(doneSectors, sectorsToRead, xmlTrk.Sequence.TrackNumber);

                            UpdateProgress2("Hashings sector {0} of {1}", (long)doneSectors,
                                            (long)(trk.TrackEndSector - trk.TrackStartSector + 1));

                            doneSectors += sectorsToRead;
                        }
                        else
                        {
                            sector = image.ReadSectorsLong(doneSectors, (uint)(sectors - doneSectors),
                                                           xmlTrk.Sequence.TrackNumber);

                            UpdateProgress2("Hashings sector {0} of {1}", (long)doneSectors,
                                            (long)(trk.TrackEndSector - trk.TrackStartSector + 1));

                            doneSectors += sectors - doneSectors;
                        }

                        trkChkWorker.Update(sector);
                    }

                    List<ChecksumType> trkChecksums = trkChkWorker.End();

                    xmlTrk.Checksums = trkChecksums.ToArray();

                    EndProgress2();
                }

                if(trk.TrackSubchannelType != TrackSubchannelType.None)
                {
                    xmlTrk.SubChannel = new SubChannelType
                    {
                        Image = new ImageType
                        {
                            Value = trk.TrackSubchannelFile
                        },

                        // TODO: Packed subchannel has different size?
                        Size = (xmlTrk.EndSector - xmlTrk.StartSector + 1) * 96
                    };

                    switch(trk.TrackSubchannelType)
                    {
                        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.TrackFileOffset > 0)
                    {
                        xmlTrk.SubChannel.Image.offset          = trk.TrackSubchannelOffset;
                        xmlTrk.SubChannel.Image.offsetSpecified = true;
                    }

                    var subChkWorker = new Checksum();

                    sectors     = xmlTrk.EndSector - xmlTrk.StartSector + 1;
                    doneSectors = 0;

                    InitProgress2();

                    while(doneSectors < sectors)
                    {
                        if(aborted)
                        {
                            EndProgress();
                            EndProgress2();

                            return;
                        }

                        byte[] sector;

                        if(sectors - doneSectors >= sectorsToRead)
                        {
                            sector = image.ReadSectorsTag(doneSectors, sectorsToRead, xmlTrk.Sequence.TrackNumber,
                                                          SectorTagType.CdSectorSubchannel);

                            UpdateProgress2("Hashings subchannel sector {0} of {1}", (long)doneSectors,
                                            (long)(trk.TrackEndSector - trk.TrackStartSector + 1));

                            doneSectors += sectorsToRead;
                        }
                        else
                        {
                            sector = image.ReadSectorsTag(doneSectors, (uint)(sectors - doneSectors),
                                                          xmlTrk.Sequence.TrackNumber,
                                                          SectorTagType.CdSectorSubchannel);

                            UpdateProgress2("Hashings subchannel sector {0} of {1}", (long)doneSectors,
                                            (long)(trk.TrackEndSector - trk.TrackStartSector + 1));

                            doneSectors += sectors - doneSectors;
                        }

                        subChkWorker.Update(sector);
                    }

                    List<ChecksumType> subChecksums = subChkWorker.End();

                    xmlTrk.SubChannel.Checksums = subChecksums.ToArray();

                    EndProgress2();
                }

                // For fast debugging, skip checksum
                //skipChecksum:

                List<Partition> trkPartitions = partitions.
                                                Where(p => p.Start >= trk.TrackStartSector &&
                                                           p.End   <= trk.TrackEndSector).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[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<FileSystemType> lstFs = new List<FileSystemType>();

                        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
                            {
                                //DicConsole.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
                    };

                    List<FileSystemType> lstFs = new List<FileSystemType>();

                    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
                        {
                            if(aborted)
                            {
                                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)
                            {
                                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
                        {
                            //DicConsole.DebugWriteLine("Create-sidecar command", "Plugin {0} crashed", _plugin.Name);
                        }

                    if(lstFs.Count > 0)
                        xmlTrk.FileSystemInformation[0].FileSystems = lstFs.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
                            }
                    }
                };
        }