diff --git a/.gitmodules b/.gitmodules index b0e82270..1cf09b6d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "CICMMetadata"] path = CICMMetadata url = https://github.com/claunia/CICMMetadata +[submodule "cuetoolsnet"] + path = cuetoolsnet + url = https://github.com/claunia/cuetoolsnet.git diff --git a/.idea/.idea.DiscImageChef/.idea/contentModel.xml b/.idea/.idea.DiscImageChef/.idea/contentModel.xml index a21793c4..c06e9f14 100644 --- a/.idea/.idea.DiscImageChef/.idea/contentModel.xml +++ b/.idea/.idea.DiscImageChef/.idea/contentModel.xml @@ -86,6 +86,12 @@ + + + + + + @@ -423,6 +429,7 @@ + @@ -968,6 +975,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.idea/.idea.DiscImageChef/.idea/vcs.xml b/.idea/.idea.DiscImageChef/.idea/vcs.xml index f80ba8eb..a5979d41 100644 --- a/.idea/.idea.DiscImageChef/.idea/vcs.xml +++ b/.idea/.idea.DiscImageChef/.idea/vcs.xml @@ -3,5 +3,6 @@ + \ No newline at end of file diff --git a/DiscImageChef.CommonTypes/MediaType.cs b/DiscImageChef.CommonTypes/MediaType.cs index a7d8df5d..bc7c56bf 100644 --- a/DiscImageChef.CommonTypes/MediaType.cs +++ b/DiscImageChef.CommonTypes/MediaType.cs @@ -48,975 +48,973 @@ namespace DiscImageChef.CommonTypes /// /// Contains an enumeration of all known types of media. /// - public enum MediaType + public enum MediaType : uint { + #region Generics, types 0 to 9 /// Unknown disk type - Unknown, + Unknown = 0, + /// Unknown magneto-optical + UnknownMO = 1, + /// Generic hard disk + GENERIC_HDD = 2, + /// Microdrive type hard disk + Microdrive = 3, + /// Zoned hard disk + Zone_HDD = 4, + /// USB flash drives + FlashDrive = 5, + #endregion Generics, types 0 to 9 - #region Somewhat standard Compact Disc formats - /// CD Digital Audio (Red Book) - CDDA, - /// CD+G (Red Book) - CDG, - /// CD+EG (Red Book) - CDEG, - /// CD-i (Green Book) - CDI, - /// CD-ROM (Yellow Book) - CDROM, - /// CD-ROM XA (Yellow Book) - CDROMXA, - /// CD+ (Blue Book) - CDPLUS, - /// CD-MO (Orange Book) - CDMO, - /// CD-Recordable (Orange Book) - CDR, - /// CD-ReWritable (Orange Book) - CDRW, - /// Mount-Rainier CD-RW - CDMRW, - /// Video CD (White Book) - VCD, - /// Super Video CD (White Book) - SVCD, - /// Photo CD (Beige Book) - PCD, - /// Super Audio CD (Scarlet Book) - SACD, - /// Double-Density CD-ROM (Purple Book) - DDCD, - /// DD CD-R (Purple Book) - DDCDR, - /// DD CD-RW (Purple Book) - DDCDRW, - /// DTS audio CD (non-standard) - DTSCD, - /// CD-MIDI (Red Book) - CDMIDI, - /// CD-Video (ISO/IEC 61104) - CDV, + #region Somewhat standard Compact Disc formats, types 10 to 39 /// Any unknown or standard violating CD - CD, - #endregion Somewhat standard Compact Disc formats + CD = 10, + /// CD Digital Audio (Red Book) + CDDA = 11, + /// CD+G (Red Book) + CDG = 12, + /// CD+EG (Red Book) + CDEG = 13, + /// CD-i (Green Book) + CDI = 14, + /// CD-ROM (Yellow Book) + CDROM = 15, + /// CD-ROM XA (Yellow Book) + CDROMXA = 16, + /// CD+ (Blue Book) + CDPLUS = 17, + /// CD-MO (Orange Book) + CDMO = 18, + /// CD-Recordable (Orange Book) + CDR = 19, + /// CD-ReWritable (Orange Book) + CDRW = 20, + /// Mount-Rainier CD-RW + CDMRW = 21, + /// Video CD (White Book) + VCD = 22, + /// Super Video CD (White Book) + SVCD = 23, + /// Photo CD (Beige Book) + PCD = 24, + /// Super Audio CD (Scarlet Book) + SACD = 25, + /// Double-Density CD-ROM (Purple Book) + DDCD = 26, + /// DD CD-R (Purple Book) + DDCDR = 27, + /// DD CD-RW (Purple Book) + DDCDRW = 28, + /// DTS audio CD (non-standard) + DTSCD = 29, + /// CD-MIDI (Red Book) + CDMIDI = 30, + /// CD-Video (ISO/IEC 61104) + CDV = 31, + /// 120mm, Phase-Change, 1298496 sectors, 512 bytes/sector, PD650, ECMA-240, ISO 15485 + PD650 = 32, + /// 120mm, Write-Once, 1281856 sectors, 512 bytes/sector, PD650, ECMA-240, ISO 15485 + PD650_WORM = 33, + #endregion Somewhat standard Compact Disc formats, types 10 to 39 - #region Standard DVD formats + #region Standard DVD formats, types 40 to 50 /// DVD-ROM (applies to DVD Video and DVD Audio) - DVDROM, + DVDROM = 40, /// DVD-R - DVDR, + DVDR = 41, /// DVD-RW - DVDRW, + DVDRW = 42, /// DVD+R - DVDPR, + DVDPR = 43, /// DVD+RW - DVDPRW, + DVDPRW = 44, /// DVD+RW DL - DVDPRWDL, + DVDPRWDL = 45, /// DVD-R DL - DVDRDL, + DVDRDL = 46, /// DVD+R DL - DVDPRDL, + DVDPRDL = 47, /// DVD-RAM - DVDRAM, + DVDRAM = 48, /// DVD-RW DL - DVDRWDL, + DVDRWDL = 49, /// DVD-Download - DVDDownload, - #endregion Standard DVD formats + DVDDownload = 50, + #endregion Standard DVD formats, types 40 to 50 - #region Standard HD-DVD formats + #region Standard HD-DVD formats, types 51 to 59 /// HD DVD-ROM (applies to HD DVD Video) - HDDVDROM, + HDDVDROM = 51, /// HD DVD-RAM - HDDVDRAM, + HDDVDRAM = 52, /// HD DVD-R - HDDVDR, + HDDVDR = 53, /// HD DVD-RW - HDDVDRW, + HDDVDRW = 54, /// HD DVD-R DL - HDDVDRDL, + HDDVDRDL = 55, /// HD DVD-RW DL - HDDVDRWDL, - #endregion Standard HD-DVD formats + HDDVDRWDL = 56, + #endregion Standard HD-DVD formats, types 51 to 59 - #region Standard Blu-ray formats + #region Standard Blu-ray formats, types 60 to 69 /// BD-ROM (and BD Video) - BDROM, + BDROM = 60, /// BD-R - BDR, + BDR = 61, /// BD-RE - BDRE, + BDRE = 62, /// BD-R XL - BDRXL, + BDRXL = 63, /// BD-RE XL - BDREXL, - #endregion Standard Blu-ray formats + BDREXL = 64, + #endregion Standard Blu-ray formats, types 60 to 69 - #region Rare or uncommon optical standards + #region Rare or uncommon optical standards, types 70 to 79 /// Enhanced Versatile Disc - EVD, + EVD = 70, /// Forward Versatile Disc - FVD, + FVD = 71, /// Holographic Versatile Disc - HVD, + HVD = 72, /// China Blue High Definition - CBHD, + CBHD = 73, /// High Definition Versatile Multilayer Disc - HDVMD, + HDVMD = 74, /// Versatile Compact Disc High Density - VCDHD, + VCDHD = 75, /// Stacked Volumetric Optical Disc - SVOD, + SVOD = 76, /// Five Dimensional disc - FDDVD, - #endregion Rare or uncommon optical standards + FDDVD = 77, + #endregion Rare or uncommon optical standards, types 70 to 79 - #region LaserDisc based + #region LaserDisc based, types 80 to 89 /// Pioneer LaserDisc - LD, + LD = 80, /// Pioneer LaserDisc data - LDROM, - LDROM2, - LVROM, - MegaLD, - #endregion LaserDisc based + LDROM = 81, + LDROM2 = 82, + LVROM = 83, + MegaLD = 84, + #endregion LaserDisc based, types 80 to 89 - #region MiniDisc based + #region MiniDisc based, types 90 to 99 /// Sony Hi-MD - HiMD, + HiMD = 90, /// Sony MiniDisc - MD, - MDData, - MDData2, - #endregion MiniDisc based + MD = 91, + MDData = 92, + MDData2 = 93, + #endregion MiniDisc based, types 90 to 99 - #region Plasmon UDO + #region Plasmon UDO, types 100 to 109 /// 5.25", Phase-Change, 1834348 sectors, 8192 bytes/sector, Ultra Density Optical, ECMA-350, ISO 17345 - UDO, + UDO = 100, /// 5.25", Phase-Change, 3669724 sectors, 8192 bytes/sector, Ultra Density Optical 2, ECMA-380, ISO 11976 - UDO2, + UDO2 = 101, /// 5.25", Write-Once, 3668759 sectors, 8192 bytes/sector, Ultra Density Optical 2, ECMA-380, ISO 11976 - UDO2_WORM, - #endregion Plasmon UDO + UDO2_WORM = 102, + #endregion Plasmon UDO, types 100 to 109 - #region Sony game media - PlayStationMemoryCard, - PlayStationMemoryCard2, + #region Sony game media, types 110 to 129 + PlayStationMemoryCard = 110, + PlayStationMemoryCard2 = 111, /// Sony PlayStation game CD - PS1CD, + PS1CD = 112, /// Sony PlayStation 2 game CD - PS2CD, + PS2CD = 113, /// Sony PlayStation 2 game DVD - PS2DVD, + PS2DVD = 114, /// Sony PlayStation 3 game DVD - PS3DVD, + PS3DVD = 115, /// Sony PlayStation 3 game Blu-ray - PS3BD, + PS3BD = 116, /// Sony PlayStation 4 game Blu-ray - PS4BD, + PS4BD = 117, /// Sony PlayStation Portable Universal Media Disc (ECMA-365) - UMD, - #endregion Sony game media + UMD = 118, + #endregion Sony game media, types 110 to 129 - #region Microsoft game media + #region Microsoft game media, types 130 to 149 /// Microsoft X-box Game Disc - XGD, + XGD = 130, /// Microsoft X-box 360 Game Disc - XGD2, + XGD2 = 131, /// Microsoft X-box 360 Game Disc - XGD3, + XGD3 = 132, /// Microsoft X-box One Game Disc - XGD4, - #endregion Microsoft game media + XGD4 = 133, + #endregion Microsoft game media, types 130 to 149 - #region Sega game media + #region Sega game media, types 150 to 169 /// Sega MegaCD - MEGACD, + MEGACD = 150, /// Sega Saturn disc - SATURNCD, + SATURNCD = 151, /// Sega/Yamaha Gigabyte Disc - GDROM, + GDROM = 152, /// Sega/Yamaha recordable Gigabyte Disc - GDR, - SegaCard, - #endregion Sega game media + GDR = 153, + SegaCard = 154, + #endregion Sega game media, types 150 to 169 - #region Other game media + #region Other game media, types 170 to 179 /// PC-Engine / TurboGrafx cartridge - HuCard, + HuCard = 170, /// PC-Engine / TurboGrafx CD - SuperCDROM2, + SuperCDROM2 = 171, /// Atari Jaguar CD - JaguarCD, + JaguarCD = 172, /// 3DO CD - ThreeDO, - #endregion Other game media + ThreeDO = 173, + #endregion Other game media, types 170 to 179 - #region Apple standard floppy format + #region Apple standard floppy format, types 180 to 189 /// 5.25", SS, DD, 35 tracks, 13 spt, 256 bytes/sector, GCR - Apple32SS, + Apple32SS = 180, /// 5.25", DS, DD, 35 tracks, 13 spt, 256 bytes/sector, GCR - Apple32DS, + Apple32DS = 181, /// 5.25", SS, DD, 35 tracks, 16 spt, 256 bytes/sector, GCR - Apple33SS, + Apple33SS = 182, /// 5.25", DS, DD, 35 tracks, 16 spt, 256 bytes/sector, GCR - Apple33DS, + Apple33DS = 183, /// 3.5", SS, DD, 80 tracks, 8 to 12 spt, 512 bytes/sector, GCR - AppleSonySS, + AppleSonySS = 184, /// 3.5", DS, DD, 80 tracks, 8 to 12 spt, 512 bytes/sector, GCR - AppleSonyDS, + AppleSonyDS = 185, /// 5.25", DS, ?D, ?? tracks, ?? spt, 512 bytes/sector, GCR, opposite side heads, aka Twiggy - AppleFileWare, + AppleFileWare = 186, #endregion Apple standard floppy format - #region IBM/Microsoft PC standard floppy formats + #region IBM/Microsoft PC floppy formats, types 190 to 209 /// 5.25", SS, DD, 40 tracks, 8 spt, 512 bytes/sector, MFM - DOS_525_SS_DD_8, + DOS_525_SS_DD_8 = 190, /// 5.25", SS, DD, 40 tracks, 9 spt, 512 bytes/sector, MFM - DOS_525_SS_DD_9, + DOS_525_SS_DD_9 = 191, /// 5.25", DS, DD, 40 tracks, 8 spt, 512 bytes/sector, MFM - DOS_525_DS_DD_8, + DOS_525_DS_DD_8 = 192, /// 5.25", DS, DD, 40 tracks, 9 spt, 512 bytes/sector, MFM - DOS_525_DS_DD_9, + DOS_525_DS_DD_9 = 193, /// 5.25", DS, HD, 80 tracks, 15 spt, 512 bytes/sector, MFM - DOS_525_HD, + DOS_525_HD = 194, /// 3.5", SS, DD, 80 tracks, 8 spt, 512 bytes/sector, MFM - DOS_35_SS_DD_8, + DOS_35_SS_DD_8 = 195, /// 3.5", SS, DD, 80 tracks, 9 spt, 512 bytes/sector, MFM - DOS_35_SS_DD_9, + DOS_35_SS_DD_9 = 196, /// 3.5", DS, DD, 80 tracks, 8 spt, 512 bytes/sector, MFM - DOS_35_DS_DD_8, + DOS_35_DS_DD_8 = 197, /// 3.5", DS, DD, 80 tracks, 9 spt, 512 bytes/sector, MFM - DOS_35_DS_DD_9, + DOS_35_DS_DD_9 = 198, /// 3.5", DS, HD, 80 tracks, 18 spt, 512 bytes/sector, MFM - DOS_35_HD, + DOS_35_HD = 199, /// 3.5", DS, ED, 80 tracks, 36 spt, 512 bytes/sector, MFM - DOS_35_ED, - #endregion IBM/Microsoft PC standard floppy formats - - #region Microsoft non standard floppy formats + DOS_35_ED = 200, /// 3.5", DS, HD, 80 tracks, 21 spt, 512 bytes/sector, MFM - DMF, + DMF = 201, /// 3.5", DS, HD, 82 tracks, 21 spt, 512 bytes/sector, MFM - DMF_82, - #endregion Microsoft non standard floppy formats - - #region IBM non standard floppy formats + DMF_82 = 202, /// /// 5.25", DS, HD, 80 tracks, ? spt, ??? + ??? + ??? bytes/sector, MFM track 0 = ??15 sectors, 512 /// bytes/sector, falsified to DOS as 19 spt, 512 bps /// - XDF_525, + XDF_525 = 203, /// /// 3.5", DS, HD, 80 tracks, 4 spt, 8192 + 2048 + 1024 + 512 bytes/sector, MFM track 0 = 19 sectors, 512 /// bytes/sector, falsified to DOS as 23 spt, 512 bps /// - XDF_35, - #endregion IBM non standard floppy formats + XDF_35 = 204, + #endregion IBM/Microsoft PC standard floppy formats, types 190 to 209 - #region IBM standard floppy formats + #region IBM standard floppy formats, types 210 to 219 /// 8", SS, SD, 32 tracks, 8 spt, 319 bytes/sector, FM - IBM23FD, + IBM23FD = 210, /// 8", SS, SD, 73 tracks, 26 spt, 128 bytes/sector, FM - IBM33FD_128, + IBM33FD_128 = 211, /// 8", SS, SD, 74 tracks, 15 spt, 256 bytes/sector, FM, track 0 = 26 sectors, 128 bytes/sector - IBM33FD_256, + IBM33FD_256 = 212, /// 8", SS, SD, 74 tracks, 8 spt, 512 bytes/sector, FM, track 0 = 26 sectors, 128 bytes/sector - IBM33FD_512, + IBM33FD_512 = 213, /// 8", DS, SD, 74 tracks, 26 spt, 128 bytes/sector, FM, track 0 = 26 sectors, 128 bytes/sector - IBM43FD_128, + IBM43FD_128 = 214, /// 8", DS, SD, 74 tracks, 26 spt, 256 bytes/sector, FM, track 0 = 26 sectors, 128 bytes/sector - IBM43FD_256, + IBM43FD_256 = 215, /// /// 8", DS, DD, 74 tracks, 26 spt, 256 bytes/sector, MFM, track 0 side 0 = 26 sectors, 128 bytes/sector, track 0 /// side 1 = 26 sectors, 256 bytes/sector /// - IBM53FD_256, + IBM53FD_256 = 216, /// /// 8", DS, DD, 74 tracks, 15 spt, 512 bytes/sector, MFM, track 0 side 0 = 26 sectors, 128 bytes/sector, track 0 /// side 1 = 26 sectors, 256 bytes/sector /// - IBM53FD_512, + IBM53FD_512 = 217, /// /// 8", DS, DD, 74 tracks, 8 spt, 1024 bytes/sector, MFM, track 0 side 0 = 26 sectors, 128 bytes/sector, track 0 /// side 1 = 26 sectors, 256 bytes/sector /// - IBM53FD_1024, - #endregion IBM standard floppy formats + IBM53FD_1024 = 218, + #endregion IBM standard floppy formats, types 210 to 219 - #region DEC standard floppy formats + #region DEC standard floppy formats, types 220 to 229 /// 8", SS, DD, 77 tracks, 26 spt, 128 bytes/sector, FM - RX01, + RX01 = 220, /// 8", SS, DD, 77 tracks, 26 spt, 256 bytes/sector, FM/MFM - RX02, + RX02 = 221, /// 8", DS, DD, 77 tracks, 26 spt, 256 bytes/sector, FM/MFM - RX03, + RX03 = 222, /// 5.25", SS, DD, 80 tracks, 10 spt, 512 bytes/sector, MFM - RX50, - #endregion DEC standard floppy formats + RX50 = 223, + #endregion DEC standard floppy formats, types 220 to 229 - #region Acorn standard floppy formats + #region Acorn standard floppy formats, types 230 to 239 /// 5,25", SS, SD, 40 tracks, 10 spt, 256 bytes/sector, FM - ACORN_525_SS_SD_40, + ACORN_525_SS_SD_40 = 230, /// 5,25", SS, SD, 80 tracks, 10 spt, 256 bytes/sector, FM - ACORN_525_SS_SD_80, + ACORN_525_SS_SD_80 = 231, /// 5,25", SS, DD, 40 tracks, 16 spt, 256 bytes/sector, MFM - ACORN_525_SS_DD_40, + ACORN_525_SS_DD_40 = 232, /// 5,25", SS, DD, 80 tracks, 16 spt, 256 bytes/sector, MFM - ACORN_525_SS_DD_80, + ACORN_525_SS_DD_80 = 233, /// 5,25", DS, DD, 80 tracks, 16 spt, 256 bytes/sector, MFM - ACORN_525_DS_DD, + ACORN_525_DS_DD = 234, /// 3,5", DS, DD, 80 tracks, 5 spt, 1024 bytes/sector, MFM - ACORN_35_DS_DD, + ACORN_35_DS_DD = 235, /// 3,5", DS, HD, 80 tracks, 10 spt, 1024 bytes/sector, MFM - ACORN_35_DS_HD, - #endregion Acorn standard floppy formats + ACORN_35_DS_HD = 236, + #endregion Acorn standard floppy formats, types 230 to 239 - #region Atari standard floppy formats + #region Atari standard floppy formats, types 240 to 249 /// 5,25", SS, SD, 40 tracks, 18 spt, 128 bytes/sector, FM - ATARI_525_SD, + ATARI_525_SD = 240, /// 5,25", SS, ED, 40 tracks, 26 spt, 128 bytes/sector, MFM - ATARI_525_ED, + ATARI_525_ED = 241, /// 5,25", SS, DD, 40 tracks, 18 spt, 256 bytes/sector, MFM - ATARI_525_DD, + ATARI_525_DD = 242, /// 3,5", SS, DD, 80 tracks, 10 spt, 512 bytes/sector, MFM - ATARI_35_SS_DD, + ATARI_35_SS_DD = 243, /// 3,5", DS, DD, 80 tracks, 10 spt, 512 bytes/sector, MFM - ATARI_35_DS_DD, + ATARI_35_DS_DD = 244, /// 3,5", SS, DD, 80 tracks, 11 spt, 512 bytes/sector, MFM - ATARI_35_SS_DD_11, + ATARI_35_SS_DD_11 = 245, /// 3,5", DS, DD, 80 tracks, 11 spt, 512 bytes/sector, MFM - ATARI_35_DS_DD_11, - #endregion Atari standard floppy formats + ATARI_35_DS_DD_11 = 246, + #endregion Atari standard floppy formats, types 240 to 249 - #region Commodore standard floppy formats + #region Commodore standard floppy formats, types 250 to 259 /// 3,5", DS, DD, 80 tracks, 10 spt, 512 bytes/sector, MFM (1581) - CBM_35_DD, + CBM_35_DD = 250, /// 3,5", DS, DD, 80 tracks, 11 spt, 512 bytes/sector, MFM (Amiga) - CBM_AMIGA_35_DD, + CBM_AMIGA_35_DD = 251, /// 3,5", DS, HD, 80 tracks, 22 spt, 512 bytes/sector, MFM (Amiga) - CBM_AMIGA_35_HD, + CBM_AMIGA_35_HD = 252, /// 5,25", SS, DD, 35 tracks, GCR - CBM_1540, + CBM_1540 = 253, /// 5,25", SS, DD, 40 tracks, GCR - CBM_1540_Ext, + CBM_1540_Ext = 254, /// 5,25", DS, DD, 35 tracks, GCR - CBM_1571, - #endregion Commodore standard floppy formats + CBM_1571 = 255, + #endregion Commodore standard floppy formats, types 250 to 259 - #region NEC standard floppy formats + #region NEC/SHARP standard floppy formats, types 260 to 269 /// 8", DS, SD, 77 tracks, 26 spt, 128 bytes/sector, FM - NEC_8_SD, + NEC_8_SD = 260, /// 8", DS, DD, 77 tracks, 26 spt, 256 bytes/sector, MFM - NEC_8_DD, + NEC_8_DD = 261, /// 5.25", SS, SD, 80 tracks, 16 spt, 256 bytes/sector, FM - NEC_525_SS, + NEC_525_SS = 262, /// 5.25", DS, SD, 80 tracks, 16 spt, 256 bytes/sector, MFM - NEC_525_DS, + NEC_525_DS = 263, /// 5,25", DS, HD, 77 tracks, 8 spt, 1024 bytes/sector, MFM - NEC_525_HD, + NEC_525_HD = 264, /// 3,5", DS, HD, 77 tracks, 8 spt, 1024 bytes/sector, MFM, aka mode 3 - NEC_35_HD_8, + NEC_35_HD_8 = 265, /// 3,5", DS, HD, 80 tracks, 15 spt, 512 bytes/sector, MFM - NEC_35_HD_15, + NEC_35_HD_15 = 266, /// 3,5", DS, TD, 240 tracks, 38 spt, 512 bytes/sector, MFM - NEC_35_TD, - #endregion NEC standard floppy formats - - #region SHARP standard floppy formats + NEC_35_TD = 267, /// 5,25", DS, HD, 77 tracks, 8 spt, 1024 bytes/sector, MFM - SHARP_525, + SHARP_525 = NEC_525_HD, /// 3,5", DS, HD, 80 tracks, 9 spt, 1024 bytes/sector, MFM - SHARP_525_9, + SHARP_525_9 = 268, /// 3,5", DS, HD, 77 tracks, 8 spt, 1024 bytes/sector, MFM - SHARP_35, + SHARP_35 = NEC_35_HD_8, /// 3,5", DS, HD, 80 tracks, 9 spt, 1024 bytes/sector, MFM - SHARP_35_9, - #endregion SHARP standard floppy formats + SHARP_35_9 = 269, + #endregion NEC/SHARP standard floppy formats, types 260 to 269 - #region ECMA floppy standards + #region ECMA floppy standards, types 270 to 289 /// /// 5,25", DS, DD, 80 tracks, 8 spt, 1024 bytes/sector, MFM, track 0 side 0 = 26 sectors, 128 bytes/sector, track /// 0 side 1 = 26 sectors, 256 bytes/sector /// - ECMA_99_8, + ECMA_99_8 = 270, /// /// 5,25", DS, DD, 77 tracks, 15 spt, 512 bytes/sector, MFM, track 0 side 0 = 26 sectors, 128 bytes/sector, track /// 0 side 1 = 26 sectors, 256 bytes/sector /// - ECMA_99_15, + ECMA_99_15 = 271, /// /// 5,25", DS, DD, 77 tracks, 26 spt, 256 bytes/sector, MFM, track 0 side 0 = 26 sectors, 128 bytes/sector, track /// 0 side 1 = 26 sectors, 256 bytes/sector /// - ECMA_99_26, + ECMA_99_26 = 272, /// 3,5", DS, DD, 80 tracks, 9 spt, 512 bytes/sector, MFM - ECMA_100, + ECMA_100 = DOS_35_DS_DD_9, /// 3,5", DS, HD, 80 tracks, 18 spt, 512 bytes/sector, MFM - ECMA_125, + ECMA_125 = DOS_35_HD, /// 3,5", DS, ED, 80 tracks, 36 spt, 512 bytes/sector, MFM - ECMA_147, + ECMA_147 = DOS_35_ED, /// 8", SS, SD, 77 tracks, 26 spt, 128 bytes/sector, FM - ECMA_54, + ECMA_54 = 273, /// 8", DS, SD, 77 tracks, 26 spt, 128 bytes/sector, FM - ECMA_59, + ECMA_59 = 274, /// 5,25", SS, DD, 35 tracks, 9 spt, 256 bytes/sector, FM, track 0 side 0 = 16 sectors, 128 bytes/sector - ECMA_66, + ECMA_66 = 275, /// /// 8", DS, DD, 77 tracks, 8 spt, 1024 bytes/sector, FM, track 0 side 0 = 26 sectors, 128 bytes/sector, track 0 /// side 1 = 26 sectors, 256 bytes/sector /// - ECMA_69_8, + ECMA_69_8 = 276, /// /// 8", DS, DD, 77 tracks, 15 spt, 512 bytes/sector, FM, track 0 side 0 = 26 sectors, 128 bytes/sector, track 0 /// side 1 = 26 sectors, 256 bytes/sector /// - ECMA_69_15, + ECMA_69_15 = 277, /// /// 8", DS, DD, 77 tracks, 26 spt, 256 bytes/sector, FM, track 0 side 0 = 26 sectors, 128 bytes/sector, track 0 /// side 1 = 26 sectors, 256 bytes/sector /// - ECMA_69_26, + ECMA_69_26 = 278, /// /// 5,25", DS, DD, 40 tracks, 16 spt, 256 bytes/sector, FM, track 0 side 0 = 16 sectors, 128 bytes/sector, track 0 /// side 1 = 16 sectors, 256 bytes/sector /// - ECMA_70, + ECMA_70 = 279, /// /// 5,25", DS, DD, 80 tracks, 16 spt, 256 bytes/sector, FM, track 0 side 0 = 16 sectors, 128 bytes/sector, track 0 /// side 1 = 16 sectors, 256 bytes/sector /// - ECMA_78, + ECMA_78 = 280, /// 5,25", DS, DD, 80 tracks, 9 spt, 512 bytes/sector, FM - ECMA_78_2, - #endregion ECMA floppy standards + ECMA_78_2 = 281, + #endregion ECMA floppy standards, types 270 to 289 - #region FDFORMAT, non-standard floppy formats + #region Non-standard PC formats (FDFORMAT, 2M, etc), types 290 to 308 /// 5,25", DS, DD, 82 tracks, 10 spt, 512 bytes/sector, MFM - FDFORMAT_525_DD, + FDFORMAT_525_DD = 290, /// 5,25", DS, HD, 82 tracks, 17 spt, 512 bytes/sector, MFM - FDFORMAT_525_HD, + FDFORMAT_525_HD = 291, /// 3,5", DS, DD, 82 tracks, 10 spt, 512 bytes/sector, MFM - FDFORMAT_35_DD, + FDFORMAT_35_DD = 292, /// 3,5", DS, HD, 82 tracks, 21 spt, 512 bytes/sector, MFM - FDFORMAT_35_HD, - #endregion FDFORMAT, non-standard floppy formats + FDFORMAT_35_HD = 293, + #endregion Non-standard PC formats (FDFORMAT, 2M, etc), types 290 to 308 - #region Apricot ACT standard floppy formats + #region Apricot ACT standard floppy formats, type 309 /// 3.5", DS, DD, 70 tracks, 9 spt, 512 bytes/sector, MFM - Apricot_35, - #endregion Apricot ACT standard floppy formats + Apricot_35 = 309, + #endregion Apricot ACT standard floppy formats, type 309 - #region OnStream ADR - ADR2120, - ADR260, - ADR30, - ADR50, - #endregion OnStream ADR + #region OnStream ADR, types 310 to 319 + ADR2120 = 310, + ADR260 = 311, + ADR30 = 312, + ADR50 = 313, + #endregion OnStream ADR, types 310 to 319 - #region Advanced Intelligent Tape - AIT1, - AIT1Turbo, - AIT2, - AIT2Turbo, - AIT3, - AIT3Ex, - AIT3Turbo, - AIT4, - AIT5, - AITETurbo, - SAIT1, - SAIT2, - #endregion Advanced Intelligent Tape + #region Advanced Intelligent Tape, types 320 to 339 + AIT1 = 320, + AIT1Turbo = 321, + AIT2 = 322, + AIT2Turbo = 323, + AIT3 = 324, + AIT3Ex = 325, + AIT3Turbo = 326, + AIT4 = 327, + AIT5 = 328, + AITETurbo = 329, + SAIT1 = 330, + SAIT2 = 331, + #endregion Advanced Intelligent Tape, types 320 to 339 - #region Iomega - Bernoulli, - Bernoulli2, - Ditto, - DittoMax, - Jaz, - Jaz2, - PocketZip, - REV120, - REV35, - REV70, - ZIP100, - ZIP250, - ZIP750, - #endregion Iomega + #region Iomega, types 340 to 359 + Bernoulli = 340, + Bernoulli2 = 341, + Ditto = 342, + DittoMax = 343, + Jaz = 344, + Jaz2 = 345, + PocketZip = 346, + REV120 = 347, + REV35 = 348, + REV70 = 349, + ZIP100 = 350, + ZIP250 = 351, + ZIP750 = 352, + #endregion Iomega, types 340 to 359 - #region Audio or video media - CompactCassette, - Data8, - MiniDV, - #endregion Audio media + #region Audio or video media, types 360 to 369 + CompactCassette = 360, + Data8 = 361, + MiniDV = 362, + #endregion Audio media, types 360 to 369 - #region CompactFlash Association - CFast, - CompactFlash, - CompactFlashType2, - #endregion CompactFlash Association + #region CompactFlash Association, types 370 to 379 + CFast = 370, + CompactFlash = 371, + CompactFlashType2 = 372, + #endregion CompactFlash Association, types 370 to 379 - #region Digital Audio Tape / Digital Data Storage - DigitalAudioTape, - DAT160, - DAT320, - DAT72, - DDS1, - DDS2, - DDS3, - DDS4, - #endregion Digital Audio Tape / Digital Data Storage + #region Digital Audio Tape / Digital Data Storage, types 380 to 389 + DigitalAudioTape = 380, + DAT160 = 381, + DAT320 = 382, + DAT72 = 383, + DDS1 = 384, + DDS2 = 385, + DDS3 = 386, + DDS4 = 387, + #endregion Digital Audio Tape / Digital Data Storage, types 380 to 389 - #region DEC - CompactTapeI, - CompactTapeII, - DECtapeII, - DLTtapeIII, - DLTtapeIIIxt, - DLTtapeIV, - DLTtapeS4, - SDLT1, - SDLT2, - VStapeI, - #endregion DEC + #region DEC, types 390 to 399 + CompactTapeI = 390, + CompactTapeII = 391, + DECtapeII = 392, + DLTtapeIII = 393, + DLTtapeIIIxt = 394, + DLTtapeIV = 395, + DLTtapeS4 = 396, + SDLT1 = 397, + SDLT2 = 398, + VStapeI = 399, + #endregion DEC, types 390 to 399 - #region Exatape - Exatape15m, - Exatape22m, - Exatape22mAME, - Exatape28m, - Exatape40m, - Exatape45m, - Exatape54m, - Exatape75m, - Exatape76m, - Exatape80m, - Exatape106m, - Exatape160mXL, - Exatape112m, - Exatape125m, - Exatape150m, - Exatape170m, - Exatape225m, - #endregion Exatape + #region Exatape, types 400 to 419 + Exatape15m = 400, + Exatape22m = 401, + Exatape22mAME = 402, + Exatape28m = 403, + Exatape40m = 404, + Exatape45m = 405, + Exatape54m = 406, + Exatape75m = 407, + Exatape76m = 408, + Exatape80m = 409, + Exatape106m = 410, + Exatape160mXL = 411, + Exatape112m = 412, + Exatape125m = 413, + Exatape150m = 414, + Exatape170m = 415, + Exatape225m = 416, + #endregion Exatape, types 400 to 419 - #region PCMCIA / ExpressCard - ExpressCard34, - ExpressCard54, - PCCardTypeI, - PCCardTypeII, - PCCardTypeIII, - PCCardTypeIV, - #endregion PCMCIA / ExpressCard + #region PCMCIA / ExpressCard, types 420 to 429 + ExpressCard34 = 420, + ExpressCard54 = 421, + PCCardTypeI = 422, + PCCardTypeII = 423, + PCCardTypeIII = 424, + PCCardTypeIV = 425, + #endregion PCMCIA / ExpressCard, types 420 to 429 - #region SyQuest - EZ135, - EZ230, - Quest, - SparQ, - SQ100, - SQ200, - SQ300, - SQ310, - SQ327, - SQ400, - SQ800, - SQ1500, - SQ2000, - SyJet, - #endregion SyQuest + #region SyQuest, types 430 to 449 + EZ135 = 430, + EZ230 = 431, + Quest = 432, + SparQ = 433, + SQ100 = 434, + SQ200 = 435, + SQ300 = 436, + SQ310 = 437, + SQ327 = 438, + SQ400 = 439, + SQ800 = 440, + SQ1500 = 441, + SQ2000 = 442, + SyJet = 443, + #endregion SyQuest, types 430 to 449 - #region Nintendo - FamicomGamePak, - GameBoyAdvanceGamePak, - GameBoyGamePak, + #region Nintendo, types 450 to 469 + FamicomGamePak = 450, + GameBoyAdvanceGamePak = 451, + GameBoyGamePak = 452, /// Nintendo GameCube Optical Disc - GOD, - N64DD, - N64GamePak, - NESGamePak, - Nintendo3DSGameCard, - NintendoDiskCard, - NintendoDSGameCard, - NintendoDSiGameCard, - SNESGamePak, - SNESGamePakUS, + GOD = 453, + N64DD = 454, + N64GamePak = 455, + NESGamePak = 456, + Nintendo3DSGameCard = 457, + NintendoDiskCard = 458, + NintendoDSGameCard = 459, + NintendoDSiGameCard = 460, + SNESGamePak = 461, + SNESGamePakUS = 462, /// Nintendo Wii Optical Disc - WOD, + WOD = 463, /// Nintendo Wii U Optical Disc - WUOD, - #endregion Nintendo + WUOD = 464, + SwitchGameCard = 465, + #endregion Nintendo, types 450 to 469 - #region IBM Tapes - IBM3470, - IBM3480, - IBM3490, - IBM3490E, - IBM3592, - #endregion IBM Tapes + #region IBM Tapes, types 470 to 479 + IBM3470 = 470, + IBM3480 = 471, + IBM3490 = 472, + IBM3490E = 473, + IBM3592 = 474, + #endregion IBM Tapes, types 470 to 479 - #region LTO Ultrium - LTO, - LTO2, - LTO3, - LTO3WORM, - LTO4, - LTO4WORM, - LTO5, - LTO5WORM, - LTO6, - LTO6WORM, - LTO7, - LTO7WORM, - #endregion LTO Ultrium + #region LTO Ultrium, types 480 to 509 + LTO = 480, + LTO2 = 481, + LTO3 = 482, + LTO3WORM = 483, + LTO4 = 484, + LTO4WORM = 485, + LTO5 = 486, + LTO5WORM = 487, + LTO6 = 488, + LTO6WORM = 489, + LTO7 = 490, + LTO7WORM = 491, + #endregion LTO Ultrium, types 480 to 509 - #region MemoryStick - MemoryStick, - MemoryStickDuo, - MemoryStickMicro, - MemoryStickPro, - MemoryStickProDuo, - #endregion MemoryStick + #region MemoryStick, types 510 to 519 + MemoryStick = 510, + MemoryStickDuo = 511, + MemoryStickMicro = 512, + MemoryStickPro = 513, + MemoryStickProDuo = 514, + #endregion MemoryStick, types 510 to 519 - #region SecureDigital - microSD, - miniSD, - SecureDigital, - #endregion SecureDigital + #region SecureDigital, types 520 to 529 + microSD = 520, + miniSD = 521, + SecureDigital = 522, + #endregion SecureDigital, types 520 to 529 - #region MultiMediaCard - MMC, - MMCmicro, - RSMMC, - MMCplus, - MMCmobile, - #endregion MultiMediaCard + #region MultiMediaCard, types 530 to 539 + MMC = 530, + MMCmicro = 531, + RSMMC = 532, + MMCplus = 533, + MMCmobile = 534, + #endregion MultiMediaCard, types 530 to 539 - #region SLR - MLR1, - MLR1SL, - MLR3, - SLR1, - SLR2, - SLR3, - SLR32, - SLR32SL, - SLR4, - SLR5, - SLR5SL, - SLR6, - SLRtape7, - SLRtape7SL, - SLRtape24, - SLRtape24SL, - SLRtape40, - SLRtape50, - SLRtape60, - SLRtape75, - SLRtape100, - SLRtape140, - #endregion SLR + #region SLR, types 540 to 569 + MLR1 = 540, + MLR1SL = 541, + MLR3 = 542, + SLR1 = 543, + SLR2 = 544, + SLR3 = 545, + SLR32 = 546, + SLR32SL = 547, + SLR4 = 548, + SLR5 = 549, + SLR5SL = 550, + SLR6 = 551, + SLRtape7 = 552, + SLRtape7SL = 553, + SLRtape24 = 554, + SLRtape24SL = 555, + SLRtape40 = 556, + SLRtape50 = 557, + SLRtape60 = 558, + SLRtape75 = 559, + SLRtape100 = 560, + SLRtape140 = 561, + #endregion SLR, types 540 to 569 - #region QIC - QIC11, - QIC120, - QIC1350, - QIC150, - QIC24, - QIC3010, - QIC3020, - QIC3080, - QIC3095, - QIC320, - QIC40, - QIC525, - QIC80, - #endregion QIC + #region QIC, types 570 to 589 + QIC11 = 570, + QIC120 = 571, + QIC1350 = 572, + QIC150 = 573, + QIC24 = 574, + QIC3010 = 575, + QIC3020 = 576, + QIC3080 = 577, + QIC3095 = 578, + QIC320 = 579, + QIC40 = 580, + QIC525 = 581, + QIC80 = 582, + #endregion QIC, types 570 to 589 - #region StorageTek tapes - STK4480, - STK4490, - STK9490, - T9840A, - T9840B, - T9840C, - T9840D, - T9940A, - T9940B, - T10000A, - T10000B, - T10000C, - T10000D, - #endregion StorageTek tapes + #region StorageTek tapes, types 590 to 609 + STK4480 = 590, + STK4490 = 591, + STK9490 = 592, + T9840A = 593, + T9840B = 594, + T9840C = 595, + T9840D = 596, + T9940A = 597, + T9940B = 598, + T10000A = 599, + T10000B = 600, + T10000C = 601, + T10000D = 602, + #endregion StorageTek tapes, types 590 to 609 - #region Travan - Travan, - Travan1Ex, - Travan3, - Travan3Ex, - Travan4, - Travan5, - Travan7, - #endregion Travan + #region Travan, types 610 to 619 + Travan = 610, + Travan1Ex = 611, + Travan3 = 612, + Travan3Ex = 613, + Travan4 = 614, + Travan5 = 615, + Travan7 = 616, + #endregion Travan, types 610 to 619 - #region VXA - VXA1, - VXA2, - VXA3, - #endregion VXA + #region VXA, types 620 to 629 + VXA1 = 620, + VXA2 = 621, + VXA3 = 622, + #endregion VXA, types 620 to 629 - #region Magneto-optical + #region Magneto-optical, types 630 to 659 /// 5,25", M.O., ??? sectors, 1024 bytes/sector, ECMA-153, ISO 11560 - ECMA_153, + ECMA_153 = 630, /// 5,25", M.O., ??? sectors, 512 bytes/sector, ECMA-153, ISO 11560 - ECMA_153_512, + ECMA_153_512 = 631, /// 3,5", M.O., 249850 sectors, 512 bytes/sector, ECMA-154, ISO 10090 - ECMA_154, + ECMA_154 = 632, /// 5,25", M.O., 904995 sectors, 512 bytes/sector, ECMA-183, ISO 13481 - ECMA_183_512, + ECMA_183_512 = 633, /// 5,25", M.O., 498526 sectors, 1024 bytes/sector, ECMA-183, ISO 13481 - ECMA_183, + ECMA_183 = 634, /// 5,25", M.O., 1128772 or 1163337 sectors, 512 bytes/sector, ECMA-183, ISO 13549 - ECMA_184_512, + ECMA_184_512 = 635, /// 5,25", M.O., 603466 or 637041 sectors, 1024 bytes/sector, ECMA-183, ISO 13549 - ECMA_184, + ECMA_184 = 636, /// 300mm, M.O., ??? sectors, 1024 bytes/sector, ECMA-189, ISO 13614 - ECMA_189, + ECMA_189 = 637, /// 300mm, M.O., ??? sectors, 1024 bytes/sector, ECMA-190, ISO 13403 - ECMA_190, + ECMA_190 = 638, /// 5,25", M.O., 936921 or 948770 sectors, 1024 bytes/sector, ECMA-195, ISO 13842 - ECMA_195, + ECMA_195 = 639, /// 5,25", M.O., 1644581 or 1647371 sectors, 512 bytes/sector, ECMA-195, ISO 13842 - ECMA_195_512, + ECMA_195_512 = 640, /// 3,5", M.O., 446325 sectors, 512 bytes/sector, ECMA-201, ISO 13963 - ECMA_201, + ECMA_201 = 641, /// 3,5", M.O., 429975 sectors, 512 bytes/sector, embossed, ISO 13963 - ECMA_201_ROM, + ECMA_201_ROM = 642, /// 3,5", M.O., 371371 sectors, 1024 bytes/sector, ECMA-223 - ECMA_223, + ECMA_223 = 643, /// 3,5", M.O., 694929 sectors, 512 bytes/sector, ECMA-223 - ECMA_223_512, + ECMA_223_512 = 644, /// 5,25", M.O., 1244621 sectors, 1024 bytes/sector, ECMA-238, ISO 15486 - ECMA_238, + ECMA_238 = 645, /// 3,5", M.O., 318988, 320332 or 321100 sectors, 2048 bytes/sector, ECMA-239, ISO 15498 - ECMA_239, + ECMA_239 = 646, /// 356mm, M.O., 14476734 sectors, 1024 bytes/sector, ECMA-260, ISO 15898 - ECMA_260, + ECMA_260 = 647, /// 356mm, M.O., 24445990 sectors, 1024 bytes/sector, ECMA-260, ISO 15898 - ECMA_260_Double, + ECMA_260_Double = 648, /// 5,25", M.O., 1128134 sectors, 2048 bytes/sector, ECMA-280, ISO 18093 - ECMA_280, + ECMA_280 = 649, /// 300mm, M.O., 7355716 sectors, 2048 bytes/sector, ECMA-317, ISO 20162 - ECMA_317, + ECMA_317 = 650, /// 5,25", M.O., 1095840 sectors, 4096 bytes/sector, ECMA-322, ISO 22092 - ECMA_322, + ECMA_322 = 651, /// 5,25", M.O., 2043664 sectors, 2048 bytes/sector, ECMA-322, ISO 22092 - ECMA_322_2k, + ECMA_322_2k = 652, /// 3,5", M.O., 605846 sectors, 2048 bytes/sector, Cherry Book, GigaMo, ECMA-351, ISO 17346 - GigaMo, + GigaMo = 653, /// 3,5", M.O., 1063146 sectors, 2048 bytes/sector, Cherry Book 2, GigaMo 2, ECMA-353, ISO 22533 - GigaMo2, - UnknownMO, - #endregion Magneto-optical + GigaMo2 = 654, + #endregion Magneto-optical, types 630 to 659 - #region Other floppy standards - CompactFloppy, - DemiDiskette, + #region Other floppy standards, types 660 to 689 + CompactFloppy = 660, + DemiDiskette = 661, /// 3.5", 652 tracks, 2 sides, 512 bytes/sector, Floptical, ECMA-207, ISO 14169 - Floptical, - HiFD, - LS120, - LS240, - FD32MB, - QuickDisk, - UHD144, - VideoFloppy, - Wafer, - ZXMicrodrive, - #endregion Other floppy standards + Floptical = 662, + HiFD = 663, + QuickDisk = 664, + UHD144 = 665, + VideoFloppy = 666, + Wafer = 667, + ZXMicrodrive = 668, + #endregion Other floppy standards, types 660 to 669 - #region Miscellaneous - BeeCard, - Borsu, - DataStore, - DIR, - DST, - DTF, - DTF2, - Flextra3020, - Flextra3225, - HiTC1, - HiTC2, - LT1, - MiniCard, - Orb, - Orb5, - SmartMedia, - xD, - XQD, - DataPlay, - /// 120mm, Phase-Change, 1298496 sectors, 512 bytes/sector, PD650, ECMA-240, ISO 15485 - PD650, - /// 120mm, Write-Once, 1281856 sectors, 512 bytes/sector, PD650, ECMA-240, ISO 15485 - PD650_WORM, - #endregion Miscellaneous + #region Miscellaneous, types 670 to 689 + BeeCard = 670, + Borsu = 671, + DataStore = 672, + DIR = 673, + DST = 674, + DTF = 675, + DTF2 = 676, + Flextra3020 = 677, + Flextra3225 = 678, + HiTC1 = 679, + HiTC2 = 680, + LT1 = 681, + MiniCard = 872, + Orb = 683, + Orb5 = 684, + SmartMedia = 685, + xD = 686, + XQD = 687, + DataPlay = 688, + #endregion Miscellaneous, types 670 to 689 - #region Apple Hard Disks - AppleProfile, - AppleWidget, - AppleHD20, - #endregion Apple Hard Disks + #region Apple Hard Disks, types 690 to 699 + AppleProfile = 690, + AppleWidget = 691, + AppleHD20 = 692, + PriamDataTower = 693, + #endregion Apple Hard Disks, types 690 to 699 - #region DEC hard disks + #region DEC hard disks, types 700 to 729 /// /// 2382 cylinders, 4 tracks/cylinder, 42 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, /// 204890112 bytes /// - RA60, + RA60 = 700, /// /// 546 cylinders, 14 tracks/cylinder, 31 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, /// 121325568 bytes /// - RA80, + RA80 = 701, /// /// 1248 cylinders, 14 tracks/cylinder, 51 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, /// 456228864 bytes /// - RA81, + RA81 = 702, /// /// 302 cylinders, 4 tracks/cylinder, 42 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, 25976832 /// bytes /// - RC25, + RC25 = 703, /// /// 615 cylinders, 4 tracks/cylinder, 17 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, 21411840 /// bytes /// - RD31, + RD31 = 704, /// /// 820 cylinders, 6 tracks/cylinder, 17 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, 42823680 /// bytes /// - RD32, + RD32 = 705, /// /// 306 cylinders, 4 tracks/cylinder, 17 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, 10653696 /// bytes /// - RD51, + RD51 = 706, /// /// 480 cylinders, 7 tracks/cylinder, 18 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, 30965760 /// bytes /// - RD52, + RD52 = 707, /// /// 1024 cylinders, 7 tracks/cylinder, 18 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, /// 75497472 bytes /// - RD53, + RD53 = 708, /// /// 1225 cylinders, 8 tracks/cylinder, 18 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, /// 159936000 bytes /// - RD54, + RD54 = 709, /// /// 411 cylinders, 3 tracks/cylinder, 22 sectors/track, 256 words/sector, 16 bits/word, 512 bytes/sector, 13888512 /// bytes /// - RK06, + RK06 = 710, /// /// 411 cylinders, 3 tracks/cylinder, 20 sectors/track, 256 words/sector, 18 bits/word, 576 bytes/sector, 14204160 /// bytes /// - RK06_18, + RK06_18 = 711, /// /// 815 cylinders, 3 tracks/cylinder, 22 sectors/track, 256 words/sector, 16 bits/word, 512 bytes/sector, 27540480 /// bytes /// - RK07, + RK07 = 712, /// /// 815 cylinders, 3 tracks/cylinder, 20 sectors/track, 256 words/sector, 18 bits/word, 576 bytes/sector, 28166400 /// bytes /// - RK07_18, + RK07_18 = 713, /// /// 823 cylinders, 5 tracks/cylinder, 32 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, 67420160 /// bytes /// - RM02, + RM02 = 714, /// /// 823 cylinders, 5 tracks/cylinder, 32 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, 67420160 /// bytes /// - RM03, + RM03 = 715, /// /// 823 cylinders, 19 tracks/cylinder, 32 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, /// 256196608 bytes /// - RM05, + RM05 = 716, /// /// 203 cylinders, 10 tracks/cylinder, 22 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, /// 22865920 bytes /// - RP02, + RP02 = 717, /// /// 203 cylinders, 10 tracks/cylinder, 20 sectors/track, 128 words/sector, 36 bits/word, 576 bytes/sector, /// 23385600 bytes /// - RP02_18, + RP02_18 = 718, /// /// 400 cylinders, 10 tracks/cylinder, 22 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, /// 45056000 bytes /// - RP03, + RP03 = 719, /// /// 400 cylinders, 10 tracks/cylinder, 20 sectors/track, 128 words/sector, 36 bits/word, 576 bytes/sector, /// 46080000 bytes /// - RP03_18, + RP03_18 = 720, /// /// 411 cylinders, 19 tracks/cylinder, 22 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, /// 87960576 bytes /// - RP04, + RP04 = 721, /// /// 411 cylinders, 19 tracks/cylinder, 20 sectors/track, 128 words/sector, 36 bits/word, 576 bytes/sector, /// 89959680 bytes /// - RP04_18, + RP04_18 = 722, /// /// 411 cylinders, 19 tracks/cylinder, 22 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, /// 87960576 bytes /// - RP05, + RP05 = 723, /// /// 411 cylinders, 19 tracks/cylinder, 20 sectors/track, 128 words/sector, 36 bits/word, 576 bytes/sector, /// 89959680 bytes /// - RP05_18, + RP05_18 = 724, /// /// 815 cylinders, 19 tracks/cylinder, 22 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, /// 174423040 bytes /// - RP06, + RP06 = 725, /// /// 815 cylinders, 19 tracks/cylinder, 20 sectors/track, 128 words/sector, 36 bits/word, 576 bytes/sector, /// 178387200 bytes /// - RP06_18, - #endregion + RP06_18 = 726, + #endregion DEC hard disks, types 700 to 729 - #region Generic hard disks - Microdrive, - PriamDataTower, - RDX, + #region Imation, types 730 to 739 + LS120 = 730, + LS240 = 731, + FD32MB = 732, + RDX = 733, /// Imation 320Gb RDX - RDX320, - GENERIC_HDD, - Zone_HDD, - // USB flash drives - FlashDrive - #endregion Generic hard disks + RDX320 = 734, + #endregion Imation, types 730 to 739 } } \ No newline at end of file diff --git a/DiscImageChef.Compression/DiscImageChef.Compression.csproj b/DiscImageChef.Compression/DiscImageChef.Compression.csproj index f21d3635..d79c07ec 100644 --- a/DiscImageChef.Compression/DiscImageChef.Compression.csproj +++ b/DiscImageChef.Compression/DiscImageChef.Compression.csproj @@ -21,6 +21,7 @@ DEBUG;TRACE prompt 4 + true AnyCPU @@ -30,6 +31,7 @@ TRACE prompt 4 + true @@ -38,11 +40,185 @@ + + CUETools.Codecs.FLAKE/ChannelMode.cs + + + CUETools.Codecs.FLAKE/FlacFrame.cs + + + CUETools.Codecs.FLAKE/FlacSubframe.cs + + + CUETools.Codecs.FLAKE/FlacSubframeInfo.cs + + + CUETools.Codecs.FLAKE/Flake.cs + + + CUETools.Codecs.FLAKE/FlakeReader.cs + + + CUETools.Codecs.FLAKE/FlakeWriter.cs + + + CUETools.Codecs.FLAKE/MetadataType.cs + + + CUETools.Codecs.FLAKE/OrderMethod.cs + + + CUETools.Codecs.FLAKE/PredictionType.cs + + + CUETools.Codecs.FLAKE/Properties/Resources.Designer.cs + + + CUETools.Codecs.FLAKE/RiceContext.cs + + + CUETools.Codecs.FLAKE/SeekPoint.cs + + + CUETools.Codecs.FLAKE/StereoMethod.cs + + + CUETools.Codecs.FLAKE/SubframeType.cs + + + CUETools.Codecs.FLAKE/WindowFunction.cs + + + CUETools.Codecs.FLAKE/WindowMethod.cs + + + CUETools.Codecs/AudioBuffer.cs + + + CUETools.Codecs/AudioDecoderClass.cs + + + CUETools.Codecs/AudioEncoderClass.cs + + + CUETools.Codecs/AudioEncoderSettings.cs + + + CUETools.Codecs/AudioPCMConfig.cs + + + CUETools.Codecs/AudioPipe.cs + + + CUETools.Codecs/AudioSamples.cs + + + CUETools.Codecs/BitReader.cs + + + CUETools.Codecs/BitWriter.cs + + + CUETools.Codecs/CRC/CRC16.cs + + + CUETools.Codecs/CRC/CRC16CCITT.cs + + + CUETools.Codecs/CRC/CRC32.cs + + + CUETools.Codecs/CRC/CRC8.cs + + + CUETools.Codecs/CUEToolsCodecsConfig.cs + + + CUETools.Codecs/CUEToolsFormat.cs + + + CUETools.Codecs/CUEToolsTagger.cs + + + CUETools.Codecs/CUEToolsUDC.cs + + + CUETools.Codecs/CUEToolsUDCList.cs + + + CUETools.Codecs/CyclicBuffer.cs + + + CUETools.Codecs/CyclicBufferInputStream.cs + + + CUETools.Codecs/CyclicBufferOutputStream.cs + + + CUETools.Codecs/DefaultValueForMode.cs + + + CUETools.Codecs/DummyWriter.cs + + + CUETools.Codecs/IAudioDest.cs + + + CUETools.Codecs/IAudioFilter.cs + + + CUETools.Codecs/IAudioSource.cs + + + CUETools.Codecs/IWavePlayer.cs + + + CUETools.Codecs/LPC.cs + + + CUETools.Codecs/LpcContext.cs + + + CUETools.Codecs/NullStream.cs + + + CUETools.Codecs/PlaybackState.cs + + + CUETools.Codecs/SilenceGenerator.cs + + + CUETools.Codecs/SRDescriptionAttribute.cs + + + CUETools.Codecs/UserDefinedEncoderSettings.cs + + + CUETools.Codecs/UserDefinedReader.cs + + + CUETools.Codecs/UserDefinedWriter.cs + + + CUETools.Codecs/WAVReader.cs + + + CUETools.Codecs/WAVWriter.cs + + + CUETools.Codecs/WAVWriterSettings.cs + + + CUETools.Codecs.FLAKE/Properties/Resources.resx + + + CUETools.Codecs.FLAKE/Properties/Resources.ru-RU.resx + LICENSE.LGPL diff --git a/DiscImageChef.Core/Sidecar/BlockMedia.cs b/DiscImageChef.Core/Sidecar/BlockMedia.cs index c1ada6cb..e108147c 100644 --- a/DiscImageChef.Core/Sidecar/BlockMedia.cs +++ b/DiscImageChef.Core/Sidecar/BlockMedia.cs @@ -504,15 +504,10 @@ namespace DiscImageChef.Core case CommonTypes.MediaType.CBM_1571: trkFormat = "Commodore GCR"; break; - case CommonTypes.MediaType.SHARP_525: case CommonTypes.MediaType.SHARP_525_9: - case CommonTypes.MediaType.SHARP_35: break; case CommonTypes.MediaType.SHARP_35_9: break; case CommonTypes.MediaType.ECMA_99_15: case CommonTypes.MediaType.ECMA_99_26: - case CommonTypes.MediaType.ECMA_100: - case CommonTypes.MediaType.ECMA_125: - case CommonTypes.MediaType.ECMA_147: case CommonTypes.MediaType.ECMA_99_8: trkFormat = "ISO MFM"; break; diff --git a/DiscImageChef.Decoders/LisaTag.cs b/DiscImageChef.Decoders/LisaTag.cs index 0af0a097..c09caf07 100644 --- a/DiscImageChef.Decoders/LisaTag.cs +++ b/DiscImageChef.Decoders/LisaTag.cs @@ -115,7 +115,7 @@ namespace DiscImageChef.Decoders /// /// Converts this tag to Sony format /// - public SonyTag Sony() + public SonyTag ToSony() { return new SonyTag { diff --git a/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj b/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj index 7f7d70fd..bc9fa999 100644 --- a/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj +++ b/DiscImageChef.DiscImages/DiscImageChef.DiscImages.csproj @@ -54,6 +54,7 @@ + diff --git a/DiscImageChef.DiscImages/DiscImageChef.cs b/DiscImageChef.DiscImages/DiscImageChef.cs new file mode 100644 index 00000000..035513e8 --- /dev/null +++ b/DiscImageChef.DiscImages/DiscImageChef.cs @@ -0,0 +1,4235 @@ +// /*************************************************************************** +// The Disc Image Chef +// ---------------------------------------------------------------------------- +// +// Filename : DiscImageChef.cs +// Author(s) : Natalia Portillo +// +// Component : Disk image plugins. +// +// --[ Description ] ---------------------------------------------------------- +// +// Manages DiscImageChef format disk images. +// +// --[ License ] -------------------------------------------------------------- +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 2.1 of the +// License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, see . +// +// ---------------------------------------------------------------------------- +// Copyright © 2011-2018 Natalia Portillo +// ****************************************************************************/ + +/* + The idea of the format is being able to easily store, retrieve, and access any data that can be read from media. + + At the start of a file there's a header that contains a format version, application creator name, and a pointer to + the index. + + The index points to one or several DeDuplication Tables, or media tag blocks. + + A deduplication table is a table of offsets to blocks and sectors inside blocks. Each entry equals to an LBA and points + to a byte offset in the file shift left to the number of sectors contained in a block, plus the number of sector inside + the block. + Each block must contain sectors of equal size, but that size can be different between blocks. + The deduplication table should be stored decompressed if its size is too big to be stored on-memory. This is chosen at + creation time but it is a good idea to set the limit to 256MiB (this allows for a device of 33 million sectors, + 17Gb at 512 bps, 68Gb at 2048 bps and 137Gb at 4096 bps). + + Sector tags that are simply too small to be deduplicated are contained in a single block pointed by the index (e.g. + Apple GCR sector tags). + + Optical disks contain a track block that describes the tracks. + TODO: Streaming tapes contain a file block that describes the files and an optional partition block that describes the tape + partitions. + + TODO: There are also blocks for image metadata, contents metadata and dump hardware information. + + A differencing image will have all the metadata and deduplication tables, but the entries in these ones will be set to + 0 if the block is stored in the parent image. TODO: This is not yet implemented. + + Also because the file becomes useless without the index and deduplication table, each can be stored twice. In case of + the index it should just be searched for. In case of deduplication tables, both copies should be indexed. + + Finally, writing new data to an existing image is just Copy-On-Write. Create a new block with the modified data, change + the pointer in the corresponding deduplication table. + + P.S.: Data Position Measurement is doable, as soon as I know how to do it. + P.S.2: Support for floppy image containg bitslices and/or fluxes will be added soon. +*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text; +using CUETools.Codecs; +using CUETools.Codecs.FLAKE; +using DiscImageChef.Checksums; +using DiscImageChef.CommonTypes; +using DiscImageChef.Console; +using DiscImageChef.Decoders; +using DiscImageChef.Decoders.ATA; +using DiscImageChef.Decoders.SCSI; +using DiscImageChef.Decoders.SecureDigital; +using DiscImageChef.Filters; +using SharpCompress.Compressors.LZMA; +using VendorString = DiscImageChef.Decoders.SecureDigital.VendorString; + +namespace DiscImageChef.DiscImages +{ + public class DiscImageChef : IWritableImage + { + /// Magic identidier = "DICMFMT". + const ulong DIC_MAGIC = 0x544D52464D434944; + /// + /// Image format version. A change in this number indicates an incompatible change to the format that + /// prevents older implementations from reading it correctly, if at all. + /// + const byte DICF_VERSION = 1; + /// Maximum read cache size, 256MiB. + const uint MAX_CACHE_SIZE = 256 * 1024 * 1024; + /// Size in bytes of LZMA properties. + const int LZMA_PROPERTIES_LENGTH = 5; + /// Maximum number of entries for the DDT cache. + const int MAX_DDT_ENTRY_CACHE = 16000000; + /// How many samples are contained in a RedBook sector. + const int SAMPLES_PER_SECTOR = 588; + /// Maximum number of samples for a FLAC block. Bigger than 4608 gives no benefit. + const int MAX_FLAKE_BLOCK = 4608; + /// + /// Minimum number of samples for a FLAC block. does not support it to be + /// smaller than 256. + /// + const int MIN_FLAKE_BLOCK = 256; + + /// Cache of uncompressed blocks. + Dictionary blockCache; + /// Cache of block headers. + Dictionary blockHeaderCache; + /// Stream used for writing blocks. + MemoryStream blockStream; + /// Provides checksum for deduplication of sectors. + SHA256 checksumProvider; + /// Provides CRC64. + Crc64Context crc64; + /// Header of the currently writing block. + BlockHeader currentBlockHeader; + /// Sector offset of writing position in currently writing block. + uint currentBlockOffset; + /// Current size in bytes of the block cache + uint currentCacheSize; + /// Cache of DDT entries. + Dictionary ddtEntryCache; + /// On-memory deduplication table indexed by checksum. + Dictionary deduplicationTable; + /// writer. + FlakeWriter flakeWriter; + /// settings. + FlakeWriterSettings flakeWriterSettings; + /// Block with logical geometry. + GeometryBlock geometryBlock; + /// Image header. + DicHeader header; + /// Image information. + ImageInfo imageInfo; + /// Image data stream. + Stream imageStream; + /// Index. + List index; + /// If set to true, the DDT entries are in-memory. + bool inMemoryDdt; + /// LZMA stream. + LzmaStream lzmaBlockStream; + /// LZMA properties. + LzmaEncoderProperties lzmaEncoderProperties; + /// Cache of media tags. + Dictionary mediaTags; + /// If DDT is on-disk, this is the image stream offset at which it starts. + long outMemoryDdtPosition; + /// Cache for data that prefixes the user data on a sector (e.g. sync). + byte[] sectorPrefix; + /// Cache for data that goes side by side with user data (e.g. CompactDisc subchannel). + byte[] sectorSubchannel; + /// Cache for data that suffixes the user data on a sector (e.g. edc, ecc). + byte[] sectorSuffix; + /// Shift for calculating number of sectors in a block. + byte shift; + /// Cache for bytes to write/rad on-disk. + byte[] structureBytes; + /// Cache for pointer for marshaling structures. + IntPtr structurePointer; + /// Cache of CompactDisc track's flags + Dictionary trackFlags; + /// Cache of CompactDisc track's ISRC + Dictionary trackIsrcs; + /// In-memory deduplication table + ulong[] userDataDdt; + + public DiscImageChef() + { + imageInfo = new ImageInfo + { + ReadableSectorTags = new List(), + ReadableMediaTags = new List(), + HasPartitions = false, + HasSessions = false, + Version = null, + Application = "DiscImageChef", + ApplicationVersion = null, + Creator = null, + Comments = null, + MediaManufacturer = null, + MediaModel = null, + MediaSerialNumber = null, + MediaBarcode = null, + MediaPartNumber = null, + MediaSequence = 0, + LastMediaSequence = 0, + DriveManufacturer = null, + DriveModel = null, + DriveSerialNumber = null, + DriveFirmwareRevision = null + }; + } + + public ImageInfo Info => imageInfo; + public string Name => "DiscImageChef format"; + public Guid Id => new Guid("49360069-1784-4A2F-B723-0C844D610B0A"); + public string Format => "DiscImageChef"; + public List Partitions { get; private set; } + public List Tracks { get; private set; } + public List Sessions { get; private set; } + + public bool Identify(IFilter imageFilter) + { + imageStream = imageFilter.GetDataForkStream(); + imageStream.Seek(0, SeekOrigin.Begin); + + if(imageStream.Length < 512) return false; + + header = new DicHeader(); + structureBytes = new byte[Marshal.SizeOf(header)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(header)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(header)); + header = (DicHeader)Marshal.PtrToStructure(structurePointer, typeof(DicHeader)); + Marshal.FreeHGlobal(structurePointer); + + return header.identifier == DIC_MAGIC && header.imageMajorVersion == 0; + } + + public bool Open(IFilter imageFilter) + { + imageStream = imageFilter.GetDataForkStream(); + imageStream.Seek(0, SeekOrigin.Begin); + + if(imageStream.Length < 512) return false; + + header = new DicHeader(); + structureBytes = new byte[Marshal.SizeOf(header)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(header)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(header)); + header = (DicHeader)Marshal.PtrToStructure(structurePointer, typeof(DicHeader)); + Marshal.FreeHGlobal(structurePointer); + + if(header.imageMajorVersion > DICF_VERSION) + throw new FeatureUnsupportedImageException($"Image version {header.imageMajorVersion} not recognized."); + + imageInfo.Application = header.application; + imageInfo.ApplicationVersion = $"{header.applicationMajorVersion}.{header.applicationMinorVersion}"; + imageInfo.Version = $"{header.imageMajorVersion}.{header.imageMinorVersion}"; + imageInfo.MediaType = header.mediaType; + + // Read the index header + imageStream.Position = (long)header.indexOffset; + IndexHeader idxHeader = new IndexHeader(); + structureBytes = new byte[Marshal.SizeOf(idxHeader)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(idxHeader)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(idxHeader)); + idxHeader = (IndexHeader)Marshal.PtrToStructure(structurePointer, typeof(IndexHeader)); + Marshal.FreeHGlobal(structurePointer); + + if(idxHeader.identifier != BlockType.Index) throw new FeatureUnsupportedImageException("Index not found!"); + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Index at {0} contains {1} entries", + header.indexOffset, idxHeader.entries); + + // Fill in-memory index + index = new List(); + for(ushort i = 0; i < idxHeader.entries; i++) + { + IndexEntry entry = new IndexEntry(); + structureBytes = new byte[Marshal.SizeOf(entry)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(entry)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(entry)); + entry = (IndexEntry)Marshal.PtrToStructure(structurePointer, typeof(IndexEntry)); + Marshal.FreeHGlobal(structurePointer); + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Block type {0} with data type {1} is indexed to be at {2}", entry.blockType, + entry.dataType, entry.offset); + index.Add(entry); + } + + imageInfo.ImageSize = 0; + + bool foundUserDataDdt = false; + mediaTags = new Dictionary(); + foreach(IndexEntry entry in index) + { + imageStream.Position = (long)entry.offset; + switch(entry.blockType) + { + case BlockType.DataBlock: + // NOP block, skip + if(entry.dataType == DataType.NoData) break; + + imageStream.Position = (long)entry.offset; + + BlockHeader blockHeader = new BlockHeader(); + structureBytes = new byte[Marshal.SizeOf(blockHeader)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(blockHeader)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(blockHeader)); + blockHeader = (BlockHeader)Marshal.PtrToStructure(structurePointer, typeof(BlockHeader)); + Marshal.FreeHGlobal(structurePointer); + imageInfo.ImageSize += blockHeader.cmpLength; + + // Unused, skip + if(entry.dataType == DataType.UserData) + { + if(blockHeader.sectorSize > imageInfo.SectorSize) + imageInfo.SectorSize = blockHeader.sectorSize; + break; + } + + if(blockHeader.identifier != entry.blockType) + { + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Incorrect identifier for data block at position {0}", + entry.offset); + break; + } + + if(blockHeader.type != entry.dataType) + { + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Expected block with data type {0} at position {1} but found data type {2}", + entry.dataType, entry.offset, blockHeader.type); + break; + } + + byte[] data; + + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Found data block type {0} at position {1}", entry.dataType, + entry.offset); + + // Decompress media tag + if(blockHeader.compression == CompressionType.Lzma) + { + byte[] compressedTag = new byte[blockHeader.cmpLength - LZMA_PROPERTIES_LENGTH]; + byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH]; + imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH); + imageStream.Read(compressedTag, 0, compressedTag.Length); + MemoryStream compressedTagMs = new MemoryStream(compressedTag); + LzmaStream lzmaBlock = new LzmaStream(lzmaProperties, compressedTagMs); + data = new byte[blockHeader.length]; + lzmaBlock.Read(data, 0, (int)blockHeader.length); + lzmaBlock.Close(); + compressedTagMs.Close(); + } + else if(blockHeader.compression == CompressionType.None) + { + data = new byte[blockHeader.length]; + imageStream.Read(data, 0, (int)blockHeader.length); + } + else + { + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Found unknown compression type {0}, continuing...", + (ushort)blockHeader.compression); + break; + } + + // Check CRC, if not correct, skip it + Crc64Context.Data(data, out byte[] blockCrc); + blockCrc = blockCrc.Reverse().ToArray(); + if(BitConverter.ToUInt64(blockCrc, 0) != blockHeader.crc64) + { + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Incorrect CRC found: 0x{0:X16} found, expected 0x{1:X16}, continuing...", + BitConverter.ToUInt64(blockCrc, 0), blockHeader.crc64); + break; + } + + // Check if it's not a media tag, but a sector tag, and fill the appropriate table then + switch(entry.dataType) + { + case DataType.CdSectorPrefix: + sectorPrefix = data; + if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSync)) + imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSync); + if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorHeader)) + imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorHeader); + break; + case DataType.CdSectorSuffix: + sectorSuffix = data; + if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSubHeader)) + imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSubHeader); + if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEcc)) + imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEcc); + if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEccP)) + imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEccP); + if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEccQ)) + imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEccQ); + if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEdc)) + imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEdc); + break; + case DataType.CdSectorSubchannel: + sectorSubchannel = data; + if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSubchannel)) + imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSubchannel); + break; + case DataType.AppleProfileTag: + case DataType.AppleSonyTag: + case DataType.PriamDataTowerTag: + sectorSubchannel = data; + if(!imageInfo.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag)) + imageInfo.ReadableSectorTags.Add(SectorTagType.AppleSectorTag); + break; + default: + MediaTagType mediaTagType = GetMediaTagTypeForDataType(blockHeader.type); + + if(mediaTags.ContainsKey(mediaTagType)) + { + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Media tag type {0} duplicated, removing previous entry...", + mediaTagType); + + mediaTags.Remove(mediaTagType); + } + + mediaTags.Add(mediaTagType, data); + break; + } + + break; + case BlockType.DeDuplicationTable: + // Only user data deduplication tables are used right now + if(entry.dataType != DataType.UserData) break; + + DdtHeader ddtHeader = new DdtHeader(); + structureBytes = new byte[Marshal.SizeOf(ddtHeader)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(ddtHeader)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(ddtHeader)); + ddtHeader = (DdtHeader)Marshal.PtrToStructure(structurePointer, typeof(DdtHeader)); + Marshal.FreeHGlobal(structurePointer); + imageInfo.ImageSize += ddtHeader.cmpLength; + + if(ddtHeader.identifier != BlockType.DeDuplicationTable) break; + + imageInfo.Sectors = ddtHeader.entries; + shift = ddtHeader.shift; + + // Check for DDT compression + switch(ddtHeader.compression) + { + case CompressionType.Lzma: + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Decompressing DDT..."); + DateTime ddtStart = DateTime.UtcNow; + byte[] compressedDdt = new byte[ddtHeader.cmpLength - LZMA_PROPERTIES_LENGTH]; + byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH]; + imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH); + imageStream.Read(compressedDdt, 0, compressedDdt.Length); + MemoryStream compressedDdtMs = new MemoryStream(compressedDdt); + LzmaStream lzmaDdt = new LzmaStream(lzmaProperties, compressedDdtMs); + byte[] decompressedDdt = new byte[ddtHeader.length]; + lzmaDdt.Read(decompressedDdt, 0, (int)ddtHeader.length); + lzmaDdt.Close(); + compressedDdtMs.Close(); + userDataDdt = new ulong[ddtHeader.entries]; + for(ulong i = 0; i < ddtHeader.entries; i++) + userDataDdt[i] = BitConverter.ToUInt64(decompressedDdt, (int)(i * sizeof(ulong))); + DateTime ddtEnd = DateTime.UtcNow; + inMemoryDdt = true; + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Took {0} seconds to decompress DDT", + (ddtEnd - ddtStart).TotalSeconds); + break; + case CompressionType.None: + inMemoryDdt = false; + outMemoryDdtPosition = (long)entry.offset; + break; + default: + throw new + ImageNotSupportedException($"Found unsupported compression algorithm {(ushort)ddtHeader.compression}"); + } + + foundUserDataDdt = true; + break; + // Logical geometry block. It doesn't have a CRC coz, well, it's not so important + case BlockType.GeometryBlock: + geometryBlock = new GeometryBlock(); + structureBytes = new byte[Marshal.SizeOf(geometryBlock)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(geometryBlock)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(geometryBlock)); + geometryBlock = (GeometryBlock)Marshal.PtrToStructure(structurePointer, typeof(GeometryBlock)); + Marshal.FreeHGlobal(structurePointer); + if(geometryBlock.identifier == BlockType.GeometryBlock) + { + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Geometry set to {0} cylinders {1} heads {2} sectors per track", + geometryBlock.cylinders, geometryBlock.heads, + geometryBlock.sectorsPerTrack); + imageInfo.Cylinders = geometryBlock.cylinders; + imageInfo.Heads = geometryBlock.heads; + imageInfo.SectorsPerTrack = geometryBlock.sectorsPerTrack; + } + + break; + // Metadata block + case BlockType.MetadataBlock: + MetadataBlock metadataBlock = new MetadataBlock(); + structureBytes = new byte[Marshal.SizeOf(metadataBlock)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(metadataBlock)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(metadataBlock)); + metadataBlock = (MetadataBlock)Marshal.PtrToStructure(structurePointer, typeof(MetadataBlock)); + Marshal.FreeHGlobal(structurePointer); + + if(metadataBlock.identifier != entry.blockType) + { + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Incorrect identifier for data block at position {0}", + entry.offset); + break; + } + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Found metadata block at position {0}", + entry.offset); + + byte[] metadata = new byte[metadataBlock.blockSize]; + imageStream.Position = (long)entry.offset; + imageStream.Read(metadata, 0, metadata.Length); + + if(metadataBlock.mediaSequence > 0 && metadataBlock.lastMediaSequence > 0) + { + imageInfo.MediaSequence = metadataBlock.mediaSequence; + imageInfo.LastMediaSequence = metadataBlock.lastMediaSequence; + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Setting media sequence as {0} of {1}", imageInfo.MediaSequence, + imageInfo.LastMediaSequence); + } + + if(metadataBlock.creatorLength > 0 && + metadataBlock.creatorLength + metadataBlock.creatorOffset <= metadata.Length) + { + imageInfo.Creator = Encoding.Unicode.GetString(metadata, (int)metadataBlock.creatorOffset, + (int)(metadataBlock.creatorLength - 2)); + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Setting creator: {0}", + imageInfo.Creator); + } + + if(metadataBlock.commentsOffset > 0 && + metadataBlock.commentsLength + metadataBlock.commentsOffset <= metadata.Length) + { + imageInfo.Comments = + Encoding.Unicode.GetString(metadata, (int)metadataBlock.commentsOffset, + (int)(metadataBlock.commentsLength - 2)); + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Setting comments: {0}", + imageInfo.Comments); + } + + if(metadataBlock.mediaTitleOffset > 0 && + metadataBlock.mediaTitleLength + metadataBlock.mediaTitleOffset <= metadata.Length) + { + imageInfo.MediaTitle = + Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaTitleOffset, + (int)(metadataBlock.mediaTitleLength - 2)); + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Setting media title: {0}", + imageInfo.MediaTitle); + } + + if(metadataBlock.mediaManufacturerOffset > 0 && + metadataBlock.mediaManufacturerLength + metadataBlock.mediaManufacturerOffset <= + metadata.Length) + { + imageInfo.MediaManufacturer = + Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaManufacturerOffset, + (int)(metadataBlock.mediaManufacturerLength - 2)); + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Setting media manufacturer: {0}", + imageInfo.MediaManufacturer); + } + + if(metadataBlock.mediaModelOffset > 0 && + metadataBlock.mediaModelLength + metadataBlock.mediaModelOffset <= metadata.Length) + { + imageInfo.MediaModel = + Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaModelOffset, + (int)(metadataBlock.mediaModelLength - 2)); + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Setting media model: {0}", + imageInfo.MediaModel); + } + + if(metadataBlock.mediaSerialNumberOffset > 0 && + metadataBlock.mediaSerialNumberLength + metadataBlock.mediaSerialNumberOffset <= + metadata.Length) + { + imageInfo.MediaSerialNumber = + Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaSerialNumberOffset, + (int)(metadataBlock.mediaSerialNumberLength - 2)); + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Setting media serial number: {0}", + imageInfo.MediaSerialNumber); + } + + if(metadataBlock.mediaBarcodeOffset > 0 && + metadataBlock.mediaBarcodeLength + metadataBlock.mediaBarcodeOffset <= metadata.Length) + { + imageInfo.MediaBarcode = + Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaBarcodeOffset, + (int)(metadataBlock.mediaBarcodeLength - 2)); + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Setting media barcode: {0}", + imageInfo.MediaBarcode); + } + + if(metadataBlock.mediaPartNumberOffset > 0 && + metadataBlock.mediaPartNumberLength + metadataBlock.mediaPartNumberOffset <= metadata.Length) + { + imageInfo.MediaPartNumber = + Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaPartNumberOffset, + (int)(metadataBlock.mediaPartNumberLength - 2)); + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Setting media part number: {0}", + imageInfo.MediaPartNumber); + } + + if(metadataBlock.driveManufacturerOffset > 0 && + metadataBlock.driveManufacturerLength + metadataBlock.driveManufacturerOffset <= + metadata.Length) + { + imageInfo.DriveManufacturer = + Encoding.Unicode.GetString(metadata, (int)metadataBlock.driveManufacturerOffset, + (int)(metadataBlock.driveManufacturerLength - 2)); + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Setting drive manufacturer: {0}", + imageInfo.DriveManufacturer); + } + + if(metadataBlock.driveModelOffset > 0 && + metadataBlock.driveModelLength + metadataBlock.driveModelOffset <= metadata.Length) + { + imageInfo.DriveModel = + Encoding.Unicode.GetString(metadata, (int)metadataBlock.driveModelOffset, + (int)(metadataBlock.driveModelLength - 2)); + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Setting drive model: {0}", + imageInfo.DriveModel); + } + + if(metadataBlock.driveSerialNumberOffset > 0 && + metadataBlock.driveSerialNumberLength + metadataBlock.driveSerialNumberOffset <= + metadata.Length) + { + imageInfo.DriveSerialNumber = + Encoding.Unicode.GetString(metadata, (int)metadataBlock.driveSerialNumberOffset, + (int)(metadataBlock.driveSerialNumberLength - 2)); + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Setting drive serial number: {0}", + imageInfo.DriveSerialNumber); + } + + if(metadataBlock.driveFirmwareRevisionOffset > 0 && metadataBlock.driveFirmwareRevisionLength + + metadataBlock.driveFirmwareRevisionOffset <= metadata.Length) + { + imageInfo.DriveFirmwareRevision = + Encoding.Unicode.GetString(metadata, (int)metadataBlock.driveFirmwareRevisionOffset, + (int)(metadataBlock.driveFirmwareRevisionLength - 2)); + + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Setting drive firmware revision: {0}", + imageInfo.DriveFirmwareRevision); + } + + break; + // Optical disc tracks block + case BlockType.TracksBlock: + TracksHeader tracksHeader = new TracksHeader(); + structureBytes = new byte[Marshal.SizeOf(tracksHeader)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(tracksHeader)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(tracksHeader)); + tracksHeader = (TracksHeader)Marshal.PtrToStructure(structurePointer, typeof(TracksHeader)); + Marshal.FreeHGlobal(structurePointer); + if(tracksHeader.identifier != BlockType.TracksBlock) + { + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Incorrect identifier for tracks block at position {0}", + entry.offset); + break; + } + + structureBytes = new byte[Marshal.SizeOf(typeof(TrackEntry)) * tracksHeader.entries]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + Crc64Context.Data(structureBytes, out byte[] trksCrc); + if(BitConverter.ToUInt64(trksCrc, 0) != tracksHeader.crc64) + { + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Incorrect CRC found: 0x{0:X16} found, expected 0x{1:X16}, continuing...", + BitConverter.ToUInt64(trksCrc, 0), tracksHeader.crc64); + break; + } + + imageStream.Position -= structureBytes.Length; + + Tracks = new List(); + trackFlags = new Dictionary(); + trackIsrcs = new Dictionary(); + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Found {0} tracks at position {0}", + tracksHeader.entries, entry.offset); + + for(ushort i = 0; i < tracksHeader.entries; i++) + { + TrackEntry trackEntry = new TrackEntry(); + structureBytes = new byte[Marshal.SizeOf(trackEntry)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(trackEntry)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(trackEntry)); + trackEntry = (TrackEntry)Marshal.PtrToStructure(structurePointer, typeof(TrackEntry)); + Marshal.FreeHGlobal(structurePointer); + + Tracks.Add(new Track + { + TrackSequence = trackEntry.sequence, + TrackType = trackEntry.type, + TrackStartSector = (ulong)trackEntry.start, + TrackEndSector = (ulong)trackEntry.end, + TrackPregap = (ulong)trackEntry.pregap, + TrackSession = trackEntry.session, + TrackFile = imageFilter.GetFilename(), + TrackFileType = "BINARY", + TrackFilter = imageFilter + }); + + trackFlags.Add(trackEntry.sequence, trackEntry.flags); + trackIsrcs.Add(trackEntry.sequence, trackEntry.isrc); + } + + imageInfo.HasPartitions = true; + imageInfo.HasSessions = true; + break; + } + } + + if(!foundUserDataDdt) throw new ImageNotSupportedException("Could not find user data deduplication table."); + + imageInfo.CreationTime = DateTime.FromFileTimeUtc(header.creationTime); + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Image created on {0}", imageInfo.CreationTime); + imageInfo.LastModificationTime = DateTime.FromFileTimeUtc(header.lastWrittenTime); + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Image last written on {0}", + imageInfo.LastModificationTime); + + if(geometryBlock.identifier != BlockType.GeometryBlock && imageInfo.XmlMediaType == XmlMediaType.BlockMedia) + { + imageInfo.Cylinders = (uint)(imageInfo.Sectors / 16 / 63); + imageInfo.Heads = 16; + imageInfo.SectorsPerTrack = 63; + } + + imageInfo.XmlMediaType = GetXmlMediaType(header.mediaType); + imageInfo.ReadableMediaTags.AddRange(mediaTags.Keys); + + // Initialize caches + blockCache = new Dictionary(); + blockHeaderCache = new Dictionary(); + currentCacheSize = 0; + if(!inMemoryDdt) ddtEntryCache = new Dictionary(); + + // Initialize tracks, sessions and partitions + if(imageInfo.XmlMediaType == XmlMediaType.OpticalDisc) + { + if(Tracks == null || Tracks.Count == 0) + { + Tracks = new List + { + new Track + { + Indexes = new Dictionary(), + TrackBytesPerSector = (int)imageInfo.SectorSize, + TrackEndSector = imageInfo.Sectors - 1, + TrackFile = imageFilter.GetFilename(), + TrackFileType = "BINARY", + TrackFilter = imageFilter, + TrackRawBytesPerSector = (int)imageInfo.SectorSize, + TrackSession = 1, + TrackSequence = 1, + TrackType = TrackType.Data + } + }; + + trackFlags = new Dictionary {{1, (byte)CdFlags.DataTrack}}; + trackIsrcs = new Dictionary(); + } + + Sessions = new List(); + for(int i = 1; i <= Tracks.Max(t => t.TrackSession); i++) + Sessions.Add(new Session + { + SessionSequence = (ushort)i, + StartTrack = Tracks.Where(t => t.TrackSession == i).Max(t => t.TrackSequence), + EndTrack = Tracks.Where(t => t.TrackSession == i).Min(t => t.TrackSequence), + StartSector = Tracks.Where(t => t.TrackSession == i).Min(t => t.TrackStartSector), + EndSector = Tracks.Where(t => t.TrackSession == i).Max(t => t.TrackEndSector) + }); + + ulong currentTrackOffset = 0; + Partitions = new List(); + foreach(Track track in Tracks.OrderBy(t => t.TrackStartSector)) + { + Partitions.Add(new Partition + { + Sequence = track.TrackSequence, + Type = track.TrackType.ToString(), + Name = $"Track {track.TrackSequence}", + Offset = currentTrackOffset, + Start = track.TrackStartSector, + Size = (track.TrackEndSector - track.TrackStartSector + 1) * + (ulong)track.TrackBytesPerSector, + Length = track.TrackEndSector - track.TrackStartSector + 1, + Scheme = "Optical disc track" + }); + currentTrackOffset += (track.TrackEndSector - track.TrackStartSector + 1) * + (ulong)track.TrackBytesPerSector; + } + + Track[] tracks = Tracks.ToArray(); + for(int i = 0; i < tracks.Length; i++) + { + byte[] sector = ReadSector(tracks[i].TrackStartSector); + tracks[i].TrackBytesPerSector = sector.Length; + tracks[i].TrackRawBytesPerSector = + sectorPrefix != null && sectorSuffix != null ? 2352 : sector.Length; + + if(sectorSubchannel == null) continue; + + tracks[i].TrackSubchannelFile = tracks[i].TrackFile; + tracks[i].TrackSubchannelFilter = tracks[i].TrackFilter; + tracks[i].TrackSubchannelType = TrackSubchannelType.Raw; + } + + Tracks = tracks.ToList(); + } + else + { + Tracks = null; + Sessions = null; + Partitions = null; + } + + SetMetadataFromTags(); + + return true; + } + + public byte[] ReadDiskTag(MediaTagType tag) + { + if(mediaTags.TryGetValue(tag, out byte[] data)) return data; + + throw new FeatureNotPresentImageException("Requested tag is not present in image"); + } + + public byte[] ReadSector(ulong sectorAddress) + { + if(sectorAddress > imageInfo.Sectors - 1) + throw new ArgumentOutOfRangeException(nameof(sectorAddress), + $"Sector address {sectorAddress} not found"); + + ulong ddtEntry = GetDdtEntry(sectorAddress); + uint offsetMask = (uint)((1 << shift) - 1); + ulong offset = ddtEntry & offsetMask; + ulong blockOffset = ddtEntry >> shift; + + // Partially written image... as we can't know the real sector size just assume it's common :/ + if(ddtEntry == 0) return new byte[imageInfo.SectorSize]; + + byte[] sector; + + // Check if block is cached + if(blockCache.TryGetValue(blockOffset, out byte[] block) && + blockHeaderCache.TryGetValue(blockOffset, out BlockHeader blockHeader)) + { + sector = new byte[blockHeader.sectorSize]; + Array.Copy(block, (long)(offset * blockHeader.sectorSize), sector, 0, blockHeader.sectorSize); + return sector; + } + + // Read block header + imageStream.Position = (long)blockOffset; + blockHeader = new BlockHeader(); + structureBytes = new byte[Marshal.SizeOf(blockHeader)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(blockHeader)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(blockHeader)); + blockHeader = (BlockHeader)Marshal.PtrToStructure(structurePointer, typeof(BlockHeader)); + Marshal.FreeHGlobal(structurePointer); + + // Decompress block + switch(blockHeader.compression) + { + case CompressionType.None: + block = new byte[blockHeader.length]; + imageStream.Read(block, 0, (int)blockHeader.length); + break; + case CompressionType.Lzma: + byte[] compressedBlock = new byte[blockHeader.cmpLength - LZMA_PROPERTIES_LENGTH]; + byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH]; + imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH); + imageStream.Read(compressedBlock, 0, compressedBlock.Length); + MemoryStream compressedBlockMs = new MemoryStream(compressedBlock); + LzmaStream lzmaBlock = new LzmaStream(lzmaProperties, compressedBlockMs); + block = new byte[blockHeader.length]; + lzmaBlock.Read(block, 0, (int)blockHeader.length); + lzmaBlock.Close(); + compressedBlockMs.Close(); + break; + case CompressionType.Flac: + byte[] flacBlock = new byte[blockHeader.cmpLength]; + imageStream.Read(flacBlock, 0, flacBlock.Length); + MemoryStream flacMs = new MemoryStream(flacBlock); + FlakeReader flakeReader = new FlakeReader("", flacMs); + block = new byte[blockHeader.length]; + int samples = (int)(block.Length / blockHeader.sectorSize * 588); + AudioBuffer audioBuffer = new AudioBuffer(AudioPCMConfig.RedBook, block, samples); + flakeReader.Read(audioBuffer, samples); + flakeReader.Close(); + flacMs.Close(); + break; + default: + throw new + ImageNotSupportedException($"Found unsupported compression algorithm {(ushort)blockHeader.compression}"); + } + + // Check if cache needs to be emptied + if(currentCacheSize + blockHeader.length >= MAX_CACHE_SIZE) + { + currentCacheSize = 0; + blockHeaderCache = new Dictionary(); + blockCache = new Dictionary(); + } + + // Add block to cache + currentCacheSize += blockHeader.length; + blockHeaderCache.Add(blockOffset, blockHeader); + blockCache.Add(blockOffset, block); + + sector = new byte[blockHeader.sectorSize]; + Array.Copy(block, (long)(offset * blockHeader.sectorSize), sector, 0, blockHeader.sectorSize); + return sector; + } + + public byte[] ReadSectorTag(ulong sectorAddress, SectorTagType tag) + { + return ReadSectorsTag(sectorAddress, 1, tag); + } + + public byte[] ReadSector(ulong sectorAddress, uint track) + { + if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) + throw new FeatureNotPresentImageException("Feature not present in image"); + + Track trk = Tracks.FirstOrDefault(t => t.TrackSequence == track); + if(trk.TrackSequence != track) + throw new ArgumentOutOfRangeException(nameof(track), "Track does not exist in disc image"); + + return ReadSector(trk.TrackStartSector + sectorAddress); + } + + public byte[] ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag) + { + if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) + throw new FeatureNotPresentImageException("Feature not present in image"); + + Track trk = Tracks.FirstOrDefault(t => t.TrackSequence == track); + if(trk.TrackSequence != track) + throw new ArgumentOutOfRangeException(nameof(track), "Track does not exist in disc image"); + + return ReadSectorTag(trk.TrackStartSector + sectorAddress, tag); + } + + public byte[] ReadSectors(ulong sectorAddress, uint length) + { + if(sectorAddress > imageInfo.Sectors - 1) + throw new ArgumentOutOfRangeException(nameof(sectorAddress), + $"Sector address {sectorAddress} not found"); + + if(sectorAddress + length > imageInfo.Sectors) + throw new ArgumentOutOfRangeException(nameof(length), "Requested more sectors than available"); + + MemoryStream ms = new MemoryStream(); + + for(uint i = 0; i < length; i++) + { + byte[] sector = ReadSector(sectorAddress + i); + ms.Write(sector, 0, sector.Length); + } + + return ms.ToArray(); + } + + public byte[] ReadSectorsTag(ulong sectorAddress, uint length, SectorTagType tag) + { + uint sectorOffset; + uint sectorSize; + uint sectorSkip; + byte[] dataSource; + + if(imageInfo.XmlMediaType == XmlMediaType.OpticalDisc) + { + Track trk = Tracks.FirstOrDefault(t => sectorAddress >= t.TrackStartSector && + sectorAddress <= t.TrackEndSector); + if(trk.TrackSequence == 0) + throw new ArgumentOutOfRangeException(nameof(sectorAddress), + "Can't found track containing requested sector"); + + if(trk.TrackType == TrackType.Data) + throw new ArgumentException("Unsupported tag requested", nameof(tag)); + + switch(tag) + { + case SectorTagType.CdSectorEcc: + case SectorTagType.CdSectorEccP: + case SectorTagType.CdSectorEccQ: + case SectorTagType.CdSectorEdc: + case SectorTagType.CdSectorHeader: + case SectorTagType.CdSectorSubchannel: + case SectorTagType.CdSectorSubHeader: + case SectorTagType.CdSectorSync: break; + case SectorTagType.CdTrackFlags: + return trackFlags.TryGetValue((byte)trk.TrackSequence, out byte flags) ? new[] {flags} : null; + case SectorTagType.CdTrackIsrc: + return trackIsrcs.TryGetValue((byte)trk.TrackSequence, out string isrc) + ? Encoding.UTF8.GetBytes(isrc) + : null; + default: throw new ArgumentException("Unsupported tag requested", nameof(tag)); + } + + switch(trk.TrackType) + { + case TrackType.CdMode1: + switch(tag) + { + case SectorTagType.CdSectorSync: + { + sectorOffset = 0; + sectorSize = 12; + sectorSkip = 4; + dataSource = sectorPrefix; + break; + } + case SectorTagType.CdSectorHeader: + { + sectorOffset = 12; + sectorSize = 4; + sectorSkip = 2336; + dataSource = sectorPrefix; + break; + } + case SectorTagType.CdSectorSubHeader: + throw new ArgumentException("Unsupported tag requested for this track", nameof(tag)); + case SectorTagType.CdSectorEcc: + { + sectorOffset = 12; + sectorSize = 276; + sectorSkip = 0; + dataSource = sectorSuffix; + break; + } + case SectorTagType.CdSectorEccP: + { + sectorOffset = 12; + sectorSize = 172; + sectorSkip = 104; + dataSource = sectorSuffix; + break; + } + case SectorTagType.CdSectorEccQ: + { + sectorOffset = 184; + sectorSize = 104; + sectorSkip = 0; + dataSource = sectorSuffix; + break; + } + case SectorTagType.CdSectorEdc: + { + sectorOffset = 0; + sectorSize = 4; + sectorSkip = 284; + dataSource = sectorSuffix; + break; + } + case SectorTagType.CdSectorSubchannel: + { + sectorOffset = 0; + sectorSize = 96; + sectorSkip = 0; + dataSource = sectorSubchannel; + break; + } + default: throw new ArgumentException("Unsupported tag requested", nameof(tag)); + } + + break; + case TrackType.CdMode2Formless: + case TrackType.CdMode2Form1: + case TrackType.CdMode2Form2: + { + switch(tag) + { + case SectorTagType.CdSectorSync: + { + sectorOffset = 0; + sectorSize = 12; + sectorSkip = 4; + dataSource = sectorPrefix; + break; + } + case SectorTagType.CdSectorHeader: + { + sectorOffset = 12; + sectorSize = 4; + sectorSkip = 2336; + dataSource = sectorPrefix; + break; + } + // These could be implemented + case SectorTagType.CdSectorEcc: + case SectorTagType.CdSectorEccP: + case SectorTagType.CdSectorEccQ: + case SectorTagType.CdSectorSubHeader: + case SectorTagType.CdSectorEdc: + throw new ArgumentException("Unsupported tag requested for this track", nameof(tag)); + case SectorTagType.CdSectorSubchannel: + { + sectorOffset = 0; + sectorSize = 96; + sectorSkip = 0; + dataSource = sectorSubchannel; + break; + } + default: throw new ArgumentException("Unsupported tag requested", nameof(tag)); + } + + break; + } + case TrackType.Audio: + { + switch(tag) + { + case SectorTagType.CdSectorSubchannel: + { + sectorOffset = 0; + sectorSize = 96; + sectorSkip = 0; + dataSource = sectorSubchannel; + break; + } + default: throw new ArgumentException("Unsupported tag requested", nameof(tag)); + } + + break; + } + default: throw new FeatureSupportedButNotImplementedImageException("Unsupported track type"); + } + } + else throw new FeatureNotPresentImageException("Feature not present in image"); + + if(dataSource == null) throw new ArgumentException("Unsupported tag requested", nameof(tag)); + + byte[] data = new byte[sectorSize * length]; + + if(sectorOffset == 0 && sectorSkip == 0) + { + Array.Copy(dataSource, (long)(sectorAddress * sectorSize), data, 0, length * sectorSize); + return data; + } + + for(int i = 0; i < length; i++) + Array.Copy(dataSource, (long)(sectorAddress * (sectorOffset + sectorSize + sectorSkip)), data, + i * sectorSize, sectorSize); + + return data; + } + + public byte[] ReadSectors(ulong sectorAddress, uint length, uint track) + { + if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) + throw new FeatureNotPresentImageException("Feature not present in image"); + + Track trk = Tracks.FirstOrDefault(t => t.TrackSequence == track); + if(trk.TrackSequence != track) + throw new ArgumentOutOfRangeException(nameof(track), "Track does not exist in disc image"); + + if(trk.TrackStartSector + sectorAddress + length > trk.TrackEndSector + 1) + throw new ArgumentOutOfRangeException(nameof(length), + $"Requested more sectors ({length + sectorAddress}) than present in track ({trk.TrackEndSector - trk.TrackStartSector + 1}), won't cross tracks"); + + return ReadSectors(trk.TrackStartSector + sectorAddress, length); + } + + public byte[] ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag) + { + if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) + throw new FeatureNotPresentImageException("Feature not present in image"); + + Track trk = Tracks.FirstOrDefault(t => t.TrackSequence == track); + if(trk.TrackSequence != track) + throw new ArgumentOutOfRangeException(nameof(track), "Track does not exist in disc image"); + + if(trk.TrackStartSector + sectorAddress + length > trk.TrackEndSector + 1) + throw new ArgumentOutOfRangeException(nameof(length), + $"Requested more sectors ({length + sectorAddress}) than present in track ({trk.TrackEndSector - trk.TrackStartSector + 1}), won't cross tracks"); + + return ReadSectorsTag(trk.TrackStartSector + sectorAddress, length, tag); + } + + public byte[] ReadSectorLong(ulong sectorAddress) + { + switch(imageInfo.XmlMediaType) + { + case XmlMediaType.OpticalDisc: + Track trk = Tracks.FirstOrDefault(t => sectorAddress >= t.TrackStartSector && + sectorAddress <= t.TrackEndSector); + if(trk.TrackSequence == 0) + throw new ArgumentOutOfRangeException(nameof(sectorAddress), + "Can't found track containing requested sector"); + + if(sectorSuffix == null || sectorPrefix == null) return ReadSector(sectorAddress); + + byte[] sector = new byte[2352]; + byte[] data = ReadSector(sectorAddress); + + switch(trk.TrackType) + { + case TrackType.Audio: + case TrackType.Data: return data; + case TrackType.CdMode1: + Array.Copy(sectorPrefix, (int)sectorAddress * 16, sector, 0, 16); + Array.Copy(data, 0, sector, 16, 2048); + Array.Copy(sectorSuffix, (int)sectorAddress * 288, sector, 2064, 288); + return sector; + case TrackType.CdMode2Formless: + case TrackType.CdMode2Form1: + case TrackType.CdMode2Form2: + Array.Copy(sectorPrefix, (int)sectorAddress * 16, sector, 0, 16); + Array.Copy(data, 0, sector, 16, 2336); + return sector; + } + + break; + case XmlMediaType.BlockMedia: + switch(imageInfo.MediaType) + { + case MediaType.AppleFileWare: + case MediaType.AppleProfile: + case MediaType.AppleSonySS: + case MediaType.AppleSonyDS: + case MediaType.AppleWidget: + case MediaType.PriamDataTower: return ReadSectorsLong(sectorAddress, 1); + } + + break; + } + + throw new FeatureNotPresentImageException("Feature not present in image"); + } + + public byte[] ReadSectorLong(ulong sectorAddress, uint track) + { + if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) + throw new FeatureNotPresentImageException("Feature not present in image"); + + Track trk = Tracks.FirstOrDefault(t => t.TrackSequence == track); + if(trk.TrackSequence != track) + throw new ArgumentOutOfRangeException(nameof(track), "Track does not exist in disc image"); + + return ReadSectorLong(trk.TrackStartSector + sectorAddress); + } + + public byte[] ReadSectorsLong(ulong sectorAddress, uint length) + { + byte[] sectors; + byte[] data; + + switch(imageInfo.XmlMediaType) + { + case XmlMediaType.OpticalDisc: + Track trk = Tracks.FirstOrDefault(t => sectorAddress >= t.TrackStartSector && + sectorAddress <= t.TrackEndSector); + if(trk.TrackSequence == 0) + throw new ArgumentOutOfRangeException(nameof(sectorAddress), + "Can't found track containing requested sector"); + + if(trk.TrackStartSector + sectorAddress + length > trk.TrackEndSector + 1) + throw new ArgumentOutOfRangeException(nameof(length), + $"Requested more sectors ({length + sectorAddress}) than present in track ({trk.TrackEndSector - trk.TrackStartSector + 1}), won't cross tracks"); + + switch(trk.TrackType) + { + // These types only contain user data + case TrackType.Audio: + case TrackType.Data: return ReadSectors(sectorAddress, length); + // Join prefix (sync, header) with user data with suffix (edc, ecc p, ecc q) + case TrackType.CdMode1: + if(sectorPrefix == null || sectorSuffix == null) return ReadSectors(sectorAddress, length); + + sectors = new byte[2352 * length]; + data = ReadSectors(sectorAddress, length); + for(uint i = 0; i < length; i++) + { + Array.Copy(sectorPrefix, (int)((sectorAddress + i) * 16), sectors, + (int)((sectorAddress + i) * 2352), 16); + Array.Copy(data, (int)((sectorAddress + i) * 2048), sectors, + (int)((sectorAddress + i) * 2352) + 16, 2048); + Array.Copy(sectorSuffix, (int)((sectorAddress + i) * 288), sectors, + (int)((sectorAddress + i) * 2352) + 2064, 288); + } + + return sectors; + // Join prefix (sync, header) with user data + case TrackType.CdMode2Formless: + case TrackType.CdMode2Form1: + case TrackType.CdMode2Form2: + if(sectorPrefix == null || sectorSuffix == null) return ReadSectors(sectorAddress, length); + + sectors = new byte[2352 * length]; + data = ReadSectors(sectorAddress, length); + for(uint i = 0; i < length; i++) + { + Array.Copy(sectorPrefix, (int)((sectorAddress + i) * 16), sectors, + (int)((sectorAddress + i) * 2352), 16); + Array.Copy(data, (int)((sectorAddress + i) * 2336), sectors, + (int)((sectorAddress + i) * 2352) + 16, 2336); + } + + return sectors; + } + + break; + case XmlMediaType.BlockMedia: + switch(imageInfo.MediaType) + { + // Join user data with tags + case MediaType.AppleFileWare: + case MediaType.AppleProfile: + case MediaType.AppleSonySS: + case MediaType.AppleSonyDS: + case MediaType.AppleWidget: + case MediaType.PriamDataTower: + if(sectorSubchannel == null) return ReadSector(sectorAddress); + + uint tagSize = 0; + switch(imageInfo.MediaType) + { + case MediaType.AppleFileWare: + case MediaType.AppleProfile: + case MediaType.AppleWidget: + tagSize = 20; + break; + case MediaType.AppleSonySS: + case MediaType.AppleSonyDS: + tagSize = 12; + break; + case MediaType.PriamDataTower: + tagSize = 24; + break; + } + + uint sectorSize = 512 + tagSize; + data = ReadSectors(sectorAddress, length); + sectors = new byte[(sectorSize + 512) * length]; + for(uint i = 0; i < length; i++) + { + Array.Copy(sectorSubchannel, (int)((sectorAddress + i) * tagSize), sectors, + (int)((sectorAddress + i) * sectorSize + 512), tagSize); + Array.Copy(data, (int)((sectorAddress + i) * 512), sectors, + (int)((sectorAddress + i) * 512), 512); + } + + return sectors; + } + + break; + } + + throw new FeatureNotPresentImageException("Feature not present in image"); + } + + public byte[] ReadSectorsLong(ulong sectorAddress, uint length, uint track) + { + if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) + throw new FeatureNotPresentImageException("Feature not present in image"); + + Track trk = Tracks.FirstOrDefault(t => t.TrackSequence == track); + if(trk.TrackSequence != track) + throw new ArgumentOutOfRangeException(nameof(track), "Track does not exist in disc image"); + + if(trk.TrackStartSector + sectorAddress + length > trk.TrackEndSector + 1) + throw new ArgumentOutOfRangeException(nameof(length), + $"Requested more sectors ({length + sectorAddress}) than present in track ({trk.TrackEndSector - trk.TrackStartSector + 1}), won't cross tracks"); + + return ReadSectorsLong(trk.TrackStartSector + sectorAddress, length); + } + + public List GetSessionTracks(Session session) + { + return Tracks.Where(t => t.TrackSequence == session.SessionSequence).ToList(); + } + + public List GetSessionTracks(ushort session) + { + return Tracks.Where(t => t.TrackSequence == session).ToList(); + } + + public bool? VerifySector(ulong sectorAddress) + { + if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) return null; + + byte[] buffer = ReadSectorLong(sectorAddress); + return CdChecksums.CheckCdSector(buffer); + } + + public bool? VerifySector(ulong sectorAddress, uint track) + { + if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) return null; + + byte[] buffer = ReadSectorLong(sectorAddress, track); + return CdChecksums.CheckCdSector(buffer); + } + + public bool? VerifySectors(ulong sectorAddress, uint length, out List failingLbas, + out List unknownLbas) + { + failingLbas = new List(); + unknownLbas = new List(); + + // Right now only CompactDisc sectors are verifyable + if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) + { + for(ulong i = sectorAddress; i < sectorAddress + length; i++) unknownLbas.Add(i); + + return null; + } + + byte[] buffer = ReadSectorsLong(sectorAddress, length); + int bps = (int)(buffer.Length / length); + byte[] sector = new byte[bps]; + failingLbas = new List(); + unknownLbas = new List(); + + for(int i = 0; i < length; i++) + { + Array.Copy(buffer, i * bps, sector, 0, bps); + bool? sectorStatus = CdChecksums.CheckCdSector(sector); + + switch(sectorStatus) + { + case null: + unknownLbas.Add((ulong)i + sectorAddress); + break; + case false: + failingLbas.Add((ulong)i + sectorAddress); + break; + } + } + + if(unknownLbas.Count > 0) return null; + + return failingLbas.Count <= 0; + } + + public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List failingLbas, + out List unknownLbas) + { + // Right now only CompactDisc sectors are verifyable + if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) + { + failingLbas = new List(); + unknownLbas = new List(); + + for(ulong i = sectorAddress; i < sectorAddress + length; i++) unknownLbas.Add(i); + + return null; + } + + byte[] buffer = ReadSectorsLong(sectorAddress, length, track); + int bps = (int)(buffer.Length / length); + byte[] sector = new byte[bps]; + failingLbas = new List(); + unknownLbas = new List(); + + for(int i = 0; i < length; i++) + { + Array.Copy(buffer, i * bps, sector, 0, bps); + bool? sectorStatus = CdChecksums.CheckCdSector(sector); + + switch(sectorStatus) + { + case null: + unknownLbas.Add((ulong)i + sectorAddress); + break; + case false: + failingLbas.Add((ulong)i + sectorAddress); + break; + } + } + + if(unknownLbas.Count > 0) return null; + + return failingLbas.Count <= 0; + } + + public bool? VerifyMediaImage() + { + // This will traverse all blocks and check their CRC64 without uncompressing them + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Checking index integrity at {0}", + header.indexOffset); + imageStream.Position = (long)header.indexOffset; + + IndexHeader idxHeader = new IndexHeader(); + structureBytes = new byte[Marshal.SizeOf(idxHeader)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(idxHeader)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(idxHeader)); + idxHeader = (IndexHeader)Marshal.PtrToStructure(structurePointer, typeof(IndexHeader)); + Marshal.FreeHGlobal(structurePointer); + + if(idxHeader.identifier != BlockType.Index) + { + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Incorrect index identifier"); + return false; + } + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Index at {0} contains {1} entries", + header.indexOffset, idxHeader.entries); + + structureBytes = new byte[Marshal.SizeOf(typeof(IndexEntry)) * idxHeader.entries]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + Crc64Context.Data(structureBytes, out byte[] verifyCrc); + + if(BitConverter.ToUInt64(verifyCrc, 0) != idxHeader.crc64) + { + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Expected index CRC {0:X16} but got {1:X16}", + idxHeader.crc64, BitConverter.ToUInt64(verifyCrc, 0)); + return false; + } + + imageStream.Position -= structureBytes.Length; + + List vrIndex = new List(); + for(ushort i = 0; i < idxHeader.entries; i++) + { + IndexEntry entry = new IndexEntry(); + structureBytes = new byte[Marshal.SizeOf(entry)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(entry)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(entry)); + entry = (IndexEntry)Marshal.PtrToStructure(structurePointer, typeof(IndexEntry)); + Marshal.FreeHGlobal(structurePointer); + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Block type {0} with data type {1} is indexed to be at {2}", entry.blockType, + entry.dataType, entry.offset); + vrIndex.Add(entry); + } + + // Read up to 1MiB at a time for verification + const int VERIFY_SIZE = 1024 * 1024; + + foreach(IndexEntry entry in vrIndex) + { + imageStream.Position = (long)entry.offset; + Crc64Context crcVerify; + ulong readBytes; + byte[] verifyBytes; + + switch(entry.blockType) + { + case BlockType.DataBlock: + BlockHeader blockHeader = new BlockHeader(); + structureBytes = new byte[Marshal.SizeOf(blockHeader)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(blockHeader)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(blockHeader)); + blockHeader = (BlockHeader)Marshal.PtrToStructure(structurePointer, typeof(BlockHeader)); + Marshal.FreeHGlobal(structurePointer); + + crcVerify = new Crc64Context(); + crcVerify.Init(); + readBytes = 0; + + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Verifying data block type {0} at position {1}", entry.dataType, + entry.offset); + + while(readBytes + VERIFY_SIZE < blockHeader.cmpLength) + { + verifyBytes = new byte[readBytes]; + imageStream.Read(verifyBytes, 0, verifyBytes.Length); + crcVerify.Update(verifyBytes); + readBytes += (ulong)verifyBytes.LongLength; + } + + verifyBytes = new byte[blockHeader.cmpLength - readBytes]; + imageStream.Read(verifyBytes, 0, verifyBytes.Length); + crcVerify.Update(verifyBytes); + + verifyCrc = crcVerify.Final(); + + if(BitConverter.ToUInt64(verifyCrc, 0) != blockHeader.cmpCrc64) + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Expected block CRC {0:X16} but got {1:X16}", + blockHeader.cmpCrc64, BitConverter.ToUInt64(verifyCrc, 0)); + + break; + case BlockType.DeDuplicationTable: + DdtHeader ddtHeader = new DdtHeader(); + structureBytes = new byte[Marshal.SizeOf(ddtHeader)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(ddtHeader)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(ddtHeader)); + ddtHeader = (DdtHeader)Marshal.PtrToStructure(structurePointer, typeof(DdtHeader)); + Marshal.FreeHGlobal(structurePointer); + + crcVerify = new Crc64Context(); + crcVerify.Init(); + readBytes = 0; + + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Verifying deduplication table type {0} at position {1}", + entry.dataType, entry.offset); + + while(readBytes + VERIFY_SIZE < ddtHeader.cmpLength) + { + verifyBytes = new byte[readBytes]; + imageStream.Read(verifyBytes, 0, verifyBytes.Length); + crcVerify.Update(verifyBytes); + readBytes += (ulong)verifyBytes.LongLength; + } + + verifyBytes = new byte[ddtHeader.cmpLength - readBytes]; + imageStream.Read(verifyBytes, 0, verifyBytes.Length); + crcVerify.Update(verifyBytes); + + verifyCrc = crcVerify.Final(); + + if(BitConverter.ToUInt64(verifyCrc, 0) != ddtHeader.cmpCrc64) + { + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Expected block CRC {0:X16} but got {1:X16}", ddtHeader.cmpCrc64, + BitConverter.ToUInt64(verifyCrc, 0)); + return false; + } + + break; + case BlockType.TracksBlock: + TracksHeader trkHeader = new TracksHeader(); + structureBytes = new byte[Marshal.SizeOf(trkHeader)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(trkHeader)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(trkHeader)); + trkHeader = (TracksHeader)Marshal.PtrToStructure(structurePointer, typeof(IndexHeader)); + Marshal.FreeHGlobal(structurePointer); + + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Track block at {0} contains {1} entries", header.indexOffset, + trkHeader.entries); + + structureBytes = new byte[Marshal.SizeOf(typeof(TrackEntry)) * trkHeader.entries]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + Crc64Context.Data(structureBytes, out verifyCrc); + + if(BitConverter.ToUInt64(verifyCrc, 0) != trkHeader.crc64) + { + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Expected index CRC {0:X16} but got {1:X16}", trkHeader.crc64, + BitConverter.ToUInt64(verifyCrc, 0)); + return false; + } + + break; + default: + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Ignored field type {0}", + entry.blockType); + break; + } + } + + return true; + } + + public IEnumerable SupportedMediaTags => + Enum.GetValues(typeof(MediaTagType)).Cast(); + public IEnumerable SupportedSectorTags => + Enum.GetValues(typeof(SectorTagType)).Cast(); + public IEnumerable SupportedMediaTypes => + Enum.GetValues(typeof(MediaType)).Cast(); + public IEnumerable<(string name, Type type, string description)> SupportedOptions => + new[] + { + ("sectors_per_block", typeof(uint), + "How many sectors to store per block (will be rounded to next power of two)"), + ("dictionary", typeof(uint), "Size, in bytes, of the LZMA dictionary"), + ("max_ddt_size", typeof(uint), + "Maximum size, in mebibytes, for in-memory DDT. If image needs a bigger one, it will be on-disk") + }; + public IEnumerable KnownExtensions => new[] {".dicf"}; + public bool IsWriting { get; private set; } + public string ErrorMessage { get; private set; } + + public bool Create(string path, MediaType mediaType, Dictionary options, ulong sectors, + uint sectorSize) + { + uint sectorsPerBlock; + uint dictionary; + uint maxDdtSize; + + if(options != null) + { + if(options.TryGetValue("sectors_per_block", out string tmpValue)) + { + if(!uint.TryParse(tmpValue, out sectorsPerBlock)) + { + ErrorMessage = "Invalid value for sectors_per_block option"; + return false; + } + } + else sectorsPerBlock = 4096; + + if(options.TryGetValue("dictionary", out tmpValue)) + { + if(!uint.TryParse(tmpValue, out dictionary)) + { + ErrorMessage = "Invalid value for dictionary option"; + return false; + } + } + else dictionary = 1 << 25; + + if(options.TryGetValue("max_ddt_size", out tmpValue)) + { + if(!uint.TryParse(tmpValue, out maxDdtSize)) + { + ErrorMessage = "Invalid value for max_ddt_size option"; + return false; + } + } + else maxDdtSize = 256; + } + else + { + sectorsPerBlock = 4096; + dictionary = 1 << 25; + maxDdtSize = 256; + } + + // This really, cannot happen + if(!SupportedMediaTypes.Contains(mediaType)) + { + ErrorMessage = $"Unsupport media format {mediaType}"; + return false; + } + + // Calculate shift + shift = 0; + uint oldSectorsPerBlock = sectorsPerBlock; + while(sectorsPerBlock > 1) + { + sectorsPerBlock >>= 1; + shift++; + } + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Got a shift of {0} for {1} sectors per block", + shift, oldSectorsPerBlock); + + imageInfo = new ImageInfo + { + MediaType = mediaType, + SectorSize = sectorSize, + Sectors = sectors, + XmlMediaType = GetXmlMediaType(mediaType) + }; + + try { imageStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); } + catch(IOException e) + { + ErrorMessage = $"Could not create new image file, exception {e.Message}"; + return false; + } + + // Check if appending to an existing image + if(imageStream.Length > Marshal.SizeOf(typeof(DicHeader))) + { + header = new DicHeader(); + structureBytes = new byte[Marshal.SizeOf(header)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(header)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(header)); + header = (DicHeader)Marshal.PtrToStructure(structurePointer, typeof(DicHeader)); + Marshal.FreeHGlobal(structurePointer); + + if(header.identifier != DIC_MAGIC) + { + ErrorMessage = "Cannot append to a non DiscImageChef format image"; + return false; + } + + if(header.imageMajorVersion > DICF_VERSION) + { + ErrorMessage = $"Cannot append to an unknown image version {header.imageMajorVersion}"; + return false; + } + + if(header.mediaType != mediaType) + { + ErrorMessage = + $"Cannot write a media with type {mediaType} to an image with type {header.mediaType}"; + return false; + } + } + else + { + header = new DicHeader + { + identifier = DIC_MAGIC, + mediaType = mediaType, + creationTime = DateTime.UtcNow.ToFileTimeUtc() + }; + + imageStream.Write(new byte[Marshal.SizeOf(typeof(DicHeader))], 0, Marshal.SizeOf(typeof(DicHeader))); + } + + header.application = "DiscImageChef"; + header.imageMajorVersion = DICF_VERSION; + header.imageMinorVersion = 0; + header.applicationMajorVersion = (byte)typeof(DiscImageChef).Assembly.GetName().Version.Major; + header.applicationMinorVersion = (byte)typeof(DiscImageChef).Assembly.GetName().Version.Minor; + + index = new List(); + + // If there exists an index, we are appending, so read index + if(header.indexOffset > 0) + { + imageStream.Position = (long)header.indexOffset; + IndexHeader idxHeader = new IndexHeader(); + structureBytes = new byte[Marshal.SizeOf(idxHeader)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(idxHeader)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(idxHeader)); + idxHeader = (IndexHeader)Marshal.PtrToStructure(structurePointer, typeof(IndexHeader)); + Marshal.FreeHGlobal(structurePointer); + + if(idxHeader.identifier != BlockType.Index) + { + ErrorMessage = "Index not found in existing image, cannot continue"; + return false; + } + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Index at {0} contains {1} entries", + header.indexOffset, idxHeader.entries); + + for(ushort i = 0; i < idxHeader.entries; i++) + { + IndexEntry entry = new IndexEntry(); + structureBytes = new byte[Marshal.SizeOf(entry)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(entry)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(entry)); + entry = (IndexEntry)Marshal.PtrToStructure(structurePointer, typeof(IndexEntry)); + Marshal.FreeHGlobal(structurePointer); + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Block type {0} with data type {1} is indexed to be at {2}", + entry.blockType, entry.dataType, entry.offset); + index.Add(entry); + } + + bool foundUserDataDdt = false; + foreach(IndexEntry entry in index) + { + imageStream.Position = (long)entry.offset; + switch(entry.blockType) + { + case BlockType.DataBlock: + switch(entry.dataType) + { + case DataType.CdSectorPrefix: + case DataType.CdSectorSuffix: + case DataType.CdSectorSubchannel: + case DataType.AppleProfileTag: + case DataType.AppleSonyTag: + case DataType.PriamDataTowerTag: break; + default: continue; + } + + BlockHeader blockHeader = new BlockHeader(); + structureBytes = new byte[Marshal.SizeOf(blockHeader)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(blockHeader)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(blockHeader)); + blockHeader = (BlockHeader)Marshal.PtrToStructure(structurePointer, typeof(BlockHeader)); + Marshal.FreeHGlobal(structurePointer); + imageInfo.ImageSize += blockHeader.cmpLength; + + if(blockHeader.identifier != entry.blockType) + { + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Incorrect identifier for data block at position {0}", + entry.offset); + break; + } + + if(blockHeader.type != entry.dataType) + { + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Expected block with data type {0} at position {1} but found data type {2}", + entry.dataType, entry.offset, blockHeader.type); + break; + } + + byte[] data; + + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Found data block type {0} at position {1}", entry.dataType, + entry.offset); + + if(blockHeader.compression == CompressionType.Lzma) + { + byte[] compressedTag = new byte[blockHeader.cmpLength - LZMA_PROPERTIES_LENGTH]; + byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH]; + imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH); + imageStream.Read(compressedTag, 0, compressedTag.Length); + MemoryStream compressedTagMs = new MemoryStream(compressedTag); + LzmaStream lzmaBlock = new LzmaStream(lzmaProperties, compressedTagMs); + data = new byte[blockHeader.length]; + lzmaBlock.Read(data, 0, (int)blockHeader.length); + lzmaBlock.Close(); + compressedTagMs.Close(); + } + else if(blockHeader.compression == CompressionType.None) + { + data = new byte[blockHeader.length]; + imageStream.Read(data, 0, (int)blockHeader.length); + } + else + { + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Found unknown compression type {0}, continuing...", + (ushort)blockHeader.compression); + break; + } + + Crc64Context.Data(data, out byte[] blockCrc); + blockCrc = blockCrc.Reverse().ToArray(); + if(BitConverter.ToUInt64(blockCrc, 0) != blockHeader.crc64) + { + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Incorrect CRC found: 0x{0:X16} found, expected 0x{1:X16}, continuing...", + BitConverter.ToUInt64(blockCrc, 0), blockHeader.crc64); + break; + } + + switch(entry.dataType) + { + case DataType.CdSectorPrefix: + sectorPrefix = data; + break; + case DataType.CdSectorSuffix: + sectorSuffix = data; + break; + case DataType.CdSectorSubchannel: + case DataType.AppleProfileTag: + case DataType.AppleSonyTag: + case DataType.PriamDataTowerTag: + sectorSubchannel = data; + break; + } + + break; + case BlockType.DeDuplicationTable: + // Only user data deduplication tables are used right now + if(entry.dataType != DataType.UserData) break; + + DdtHeader ddtHeader = new DdtHeader(); + structureBytes = new byte[Marshal.SizeOf(ddtHeader)]; + imageStream.Read(structureBytes, 0, structureBytes.Length); + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(ddtHeader)); + Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(ddtHeader)); + ddtHeader = (DdtHeader)Marshal.PtrToStructure(structurePointer, typeof(DdtHeader)); + Marshal.FreeHGlobal(structurePointer); + + if(ddtHeader.identifier != BlockType.DeDuplicationTable) break; + + if(ddtHeader.entries != imageInfo.Sectors) + { + ErrorMessage = + $"Trying to write a media with {imageInfo.Sectors} sectors to an image with {ddtHeader.entries} sectors, not continuing..."; + return false; + } + + shift = ddtHeader.shift; + + switch(ddtHeader.compression) + { + case CompressionType.Lzma: + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Decompressing DDT..."); + DateTime ddtStart = DateTime.UtcNow; + byte[] compressedDdt = new byte[ddtHeader.cmpLength - LZMA_PROPERTIES_LENGTH]; + byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH]; + imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH); + imageStream.Read(compressedDdt, 0, compressedDdt.Length); + MemoryStream compressedDdtMs = new MemoryStream(compressedDdt); + LzmaStream lzmaDdt = new LzmaStream(lzmaProperties, compressedDdtMs); + byte[] decompressedDdt = new byte[ddtHeader.length]; + lzmaDdt.Read(decompressedDdt, 0, (int)ddtHeader.length); + lzmaDdt.Close(); + compressedDdtMs.Close(); + userDataDdt = new ulong[ddtHeader.entries]; + for(ulong i = 0; i < ddtHeader.entries; i++) + userDataDdt[i] = + BitConverter.ToUInt64(decompressedDdt, (int)(i * sizeof(ulong))); + DateTime ddtEnd = DateTime.UtcNow; + inMemoryDdt = true; + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Took {0} seconds to decompress DDT", + (ddtEnd - ddtStart).TotalSeconds); + break; + case CompressionType.None: + inMemoryDdt = false; + outMemoryDdtPosition = (long)entry.offset; + break; + default: + throw new + ImageNotSupportedException($"Found unsupported compression algorithm {(ushort)ddtHeader.compression}"); + } + + foundUserDataDdt = true; + break; + } + } + + if(!foundUserDataDdt) + { + ErrorMessage = "Could not find user data deduplication table."; + return false; + } + } + // Creating new + else + { + // Checking that DDT is smaller than requested size + inMemoryDdt = sectors <= maxDdtSize * 1024 * 1024 / sizeof(ulong); + + // If in memory, easy + if(inMemoryDdt) userDataDdt = new ulong[sectors]; + // If not, create the block, add to index, and enlarge the file to allow the DDT to exist on-disk + else + { + outMemoryDdtPosition = imageStream.Position; + index.Add(new IndexEntry + { + blockType = BlockType.DeDuplicationTable, + dataType = DataType.UserData, + offset = (ulong)outMemoryDdtPosition + }); + + // CRC64 will be calculated later + DdtHeader ddtHeader = new DdtHeader + { + identifier = BlockType.DeDuplicationTable, + type = DataType.UserData, + compression = CompressionType.None, + shift = shift, + entries = sectors, + cmpLength = sectors * sizeof(ulong), + length = sectors * sizeof(ulong) + }; + + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(ddtHeader)); + structureBytes = new byte[Marshal.SizeOf(ddtHeader)]; + Marshal.StructureToPtr(ddtHeader, structurePointer, true); + Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); + Marshal.FreeHGlobal(structurePointer); + imageStream.Write(structureBytes, 0, structureBytes.Length); + structureBytes = null; + + imageStream.Position += (long)(sectors * sizeof(ulong)) - 1; + imageStream.WriteByte(0); + } + } + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "In memory DDT?: {0}", inMemoryDdt); + + // Initialize tables + imageStream.Seek(0, SeekOrigin.End); + mediaTags = new Dictionary(); + checksumProvider = SHA256.Create(); + deduplicationTable = new Dictionary(); + trackIsrcs = new Dictionary(); + trackFlags = new Dictionary(); + + // Initialize compressors properties (all maxed) + lzmaEncoderProperties = new LzmaEncoderProperties(true, (int)dictionary, 273); + flakeWriterSettings = new FlakeWriterSettings + { + PCM = AudioPCMConfig.RedBook, + DoMD5 = false, + BlockSize = (1 << shift) * SAMPLES_PER_SECTOR, + MinFixedOrder = 0, + MaxFixedOrder = 4, + MinLPCOrder = 1, + MaxLPCOrder = 32, + MaxPartitionOrder = 8, + StereoMethod = StereoMethod.Evaluate, + PredictionType = PredictionType.Search, + WindowMethod = WindowMethod.EvaluateN, + EstimationDepth = 5, + MinPrecisionSearch = 1, + MaxPrecisionSearch = 1, + TukeyParts = 0, + TukeyOverlap = 1.0, + TukeyP = 1.0, + AllowNonSubset = true + }; + + // Check if FLAKE's block size is bigger than what we want + if(flakeWriterSettings.BlockSize > MAX_FLAKE_BLOCK) flakeWriterSettings.BlockSize = MAX_FLAKE_BLOCK; + if(flakeWriterSettings.BlockSize < MIN_FLAKE_BLOCK) flakeWriterSettings.BlockSize = MIN_FLAKE_BLOCK; + FlakeWriter.Vendor = "DiscImageChef"; + + IsWriting = true; + ErrorMessage = null; + return true; + } + + public bool WriteMediaTag(byte[] data, MediaTagType tag) + { + if(!IsWriting) + { + ErrorMessage = "Tried to write on a non-writable image"; + return false; + } + + if(mediaTags.ContainsKey(tag)) mediaTags.Remove(tag); + + mediaTags.Add(tag, data); + + ErrorMessage = ""; + return true; + } + + public bool WriteSector(byte[] data, ulong sectorAddress) + { + if(!IsWriting) + { + ErrorMessage = "Tried to write on a non-writable image"; + return false; + } + + if(sectorAddress >= Info.Sectors) + { + ErrorMessage = "Tried to write past image size"; + return false; + } + + byte[] hash = checksumProvider.ComputeHash(data); + + if(deduplicationTable.TryGetValue(hash, out ulong pointer)) + { + SetDdtEntry(sectorAddress, pointer); + ErrorMessage = ""; + return true; + } + + Track trk = new Track(); + + // If optical disc check track + if(imageInfo.XmlMediaType == XmlMediaType.OpticalDisc) + { + trk = Tracks.FirstOrDefault(t => sectorAddress >= t.TrackStartSector && + sectorAddress <= t.TrackEndSector); + if(trk.TrackSequence == 0) + throw new ArgumentOutOfRangeException(nameof(sectorAddress), + "Can't found track containing requested sector"); + } + + // Close current block first + if(blockStream != null && + // When sector siz changes + (currentBlockHeader.sectorSize != data.Length || + // When block if filled + currentBlockOffset == 1 << shift || + // When we change to/from CompactDisc audio + currentBlockHeader.compression == CompressionType.Flac && trk.TrackType != TrackType.Audio)) + { + currentBlockHeader.length = currentBlockOffset * currentBlockHeader.sectorSize; + currentBlockHeader.crc64 = BitConverter.ToUInt64(crc64.Final(), 0); + + Crc64Context cmpCrc64Context = new Crc64Context(); + cmpCrc64Context.Init(); + + byte[] lzmaProperties = new byte[0]; + + if(currentBlockHeader.compression == CompressionType.Flac) + { + long remaining = currentBlockOffset * SAMPLES_PER_SECTOR % flakeWriter.Settings.BlockSize; + // Fill FLAC block + if(remaining != 0) + { + AudioBuffer audioBuffer = + new AudioBuffer(AudioPCMConfig.RedBook, new byte[remaining * 4], (int)remaining); + flakeWriter.Write(audioBuffer); + } + + // This trick because CUETools.Codecs.Flake closes the underlying stream + long realLength = blockStream.Length; + byte[] buffer = new byte[realLength]; + flakeWriter.Close(); + Array.Copy(blockStream.GetBuffer(), 0, buffer, 0, realLength); + blockStream = new MemoryStream(buffer); + } + else + { + lzmaProperties = lzmaBlockStream.Properties; + lzmaBlockStream.Close(); + cmpCrc64Context.Update(lzmaProperties); + } + + currentBlockHeader.cmpLength = (uint)blockStream.Length; + if(currentBlockHeader.compression == CompressionType.Lzma) + currentBlockHeader.cmpLength += LZMA_PROPERTIES_LENGTH; + cmpCrc64Context.Update(blockStream.ToArray()); + currentBlockHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64Context.Final(), 0); + + index.Add(new IndexEntry + { + blockType = BlockType.DataBlock, + dataType = DataType.UserData, + offset = (ulong)imageStream.Position + }); + + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(currentBlockHeader)); + structureBytes = new byte[Marshal.SizeOf(currentBlockHeader)]; + Marshal.StructureToPtr(currentBlockHeader, structurePointer, true); + Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); + Marshal.FreeHGlobal(structurePointer); + imageStream.Write(structureBytes, 0, structureBytes.Length); + structureBytes = null; + if(currentBlockHeader.compression == CompressionType.Lzma) + imageStream.Write(lzmaProperties, 0, lzmaProperties.Length); + imageStream.Write(blockStream.ToArray(), 0, (int)blockStream.Length); + blockStream = null; + currentBlockOffset = 0; + } + + // No block set + if(blockStream == null) + { + currentBlockHeader = new BlockHeader + { + identifier = BlockType.DataBlock, + type = DataType.UserData, + compression = CompressionType.Lzma, + sectorSize = (uint)data.Length + }; + + if(imageInfo.XmlMediaType == XmlMediaType.OpticalDisc && trk.TrackType == TrackType.Audio) + currentBlockHeader.compression = CompressionType.Flac; + + blockStream = new MemoryStream(); + if(currentBlockHeader.compression == CompressionType.Flac) + flakeWriter = new FlakeWriter("", blockStream, flakeWriterSettings) {DoSeekTable = false}; + else lzmaBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); + crc64 = new Crc64Context(); + crc64.Init(); + } + + ulong ddtEntry = (ulong)((imageStream.Position << shift) + currentBlockOffset); + deduplicationTable.Add(hash, ddtEntry); + if(currentBlockHeader.compression == CompressionType.Flac) + { + AudioBuffer audioBuffer = new AudioBuffer(AudioPCMConfig.RedBook, data, SAMPLES_PER_SECTOR); + flakeWriter.Write(audioBuffer); + } + else lzmaBlockStream.Write(data, 0, data.Length); + + SetDdtEntry(sectorAddress, ddtEntry); + crc64.Update(data); + currentBlockOffset++; + + ErrorMessage = ""; + return true; + } + + public bool WriteSectors(byte[] data, ulong sectorAddress, uint length) + { + if(!IsWriting) + { + ErrorMessage = "Tried to write on a non-writable image"; + return false; + } + + if(sectorAddress + length > Info.Sectors) + { + ErrorMessage = "Tried to write past image size"; + return false; + } + + uint sectorSize = (uint)(data.Length / length); + + for(uint i = 0; i < length; i++) + { + byte[] tmp = new byte[sectorSize]; + Array.Copy(data, i * sectorSize, tmp, 0, sectorSize); + if(!WriteSector(tmp, sectorAddress + i)) return false; + } + + ErrorMessage = ""; + return true; + } + + public bool WriteSectorLong(byte[] data, ulong sectorAddress) + { + if(!IsWriting) + { + ErrorMessage = "Tried to write on a non-writable image"; + return false; + } + + byte[] sector; + + switch(imageInfo.XmlMediaType) + { + case XmlMediaType.OpticalDisc: + Track track = + Tracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector && + sectorAddress <= trk.TrackEndSector); + + if(track.TrackSequence == 0) + { + ErrorMessage = $"Can't found track containing {sectorAddress}"; + return false; + } + + if(data.Length != 2352) + { + ErrorMessage = "Incorrect data size"; + return false; + } + + // Split raw cd sector data in prefix (sync, header), user data and suffix (edc, ecc p, ecc q) + switch(track.TrackType) + { + case TrackType.Audio: + case TrackType.Data: return WriteSector(data, sectorAddress); + case TrackType.CdMode1: + if(sectorPrefix == null) sectorPrefix = new byte[imageInfo.Sectors * 16]; + if(sectorSuffix == null) sectorSuffix = new byte[imageInfo.Sectors * 288]; + sector = new byte[2048]; + Array.Copy(data, 0, sectorPrefix, (int)sectorAddress * 16, 16); + Array.Copy(data, 16, sector, 0, 2048); + Array.Copy(data, 2064, sectorSuffix, (int)sectorAddress * 288, 288); + return WriteSector(sector, sectorAddress); + case TrackType.CdMode2Formless: + case TrackType.CdMode2Form1: + case TrackType.CdMode2Form2: + if(sectorPrefix == null) sectorPrefix = new byte[imageInfo.Sectors * 16]; + if(sectorSuffix == null) sectorSuffix = new byte[imageInfo.Sectors * 288]; + sector = new byte[2336]; + Array.Copy(data, 0, sectorPrefix, (int)sectorAddress * 16, 16); + Array.Copy(data, 16, sector, 0, 2336); + return WriteSector(sector, sectorAddress); + } + + break; + case XmlMediaType.BlockMedia: + switch(imageInfo.MediaType) + { + // Split user data from Apple tags + case MediaType.AppleFileWare: + case MediaType.AppleProfile: + case MediaType.AppleSonyDS: + case MediaType.AppleSonySS: + case MediaType.AppleWidget: + case MediaType.PriamDataTower: + byte[] oldTag; + byte[] newTag; + + switch(data.Length - 512) + { + // Sony tag, convert to Profile + case 12 when imageInfo.MediaType == MediaType.AppleProfile || + imageInfo.MediaType == MediaType.AppleFileWare: + oldTag = new byte[12]; + Array.Copy(data, 512, oldTag, 0, 12); + newTag = LisaTag.DecodeSonyTag(oldTag)?.ToProfile().GetBytes(); + break; + // Sony tag, convert to Priam + case 12 when imageInfo.MediaType == MediaType.PriamDataTower: + oldTag = new byte[12]; + Array.Copy(data, 512, oldTag, 0, 12); + newTag = LisaTag.DecodeSonyTag(oldTag)?.ToPriam().GetBytes(); + break; + // Sony tag, copy to Sony + case 12 when imageInfo.MediaType == MediaType.AppleSonySS || + imageInfo.MediaType == MediaType.AppleSonySS: + newTag = new byte[12]; + Array.Copy(data, 512, newTag, 0, 12); + break; + // Profile tag, copy to Profile + case 20 when imageInfo.MediaType == MediaType.AppleProfile || + imageInfo.MediaType == MediaType.AppleFileWare: + newTag = new byte[20]; + Array.Copy(data, 512, newTag, 0, 20); + break; + // Profile tag, convert to Priam + case 20 when imageInfo.MediaType == MediaType.PriamDataTower: + oldTag = new byte[20]; + Array.Copy(data, 512, oldTag, 0, 20); + newTag = LisaTag.DecodeProfileTag(oldTag)?.ToPriam().GetBytes(); + break; + // Profile tag, convert to Sony + case 20 when imageInfo.MediaType == MediaType.AppleSonySS || + imageInfo.MediaType == MediaType.AppleSonySS: + oldTag = new byte[20]; + Array.Copy(data, 512, oldTag, 0, 20); + newTag = LisaTag.DecodeProfileTag(oldTag)?.ToSony().GetBytes(); + break; + // Priam tag, convert to Profile + case 24 when imageInfo.MediaType == MediaType.AppleProfile || + imageInfo.MediaType == MediaType.AppleFileWare: + oldTag = new byte[24]; + Array.Copy(data, 512, oldTag, 0, 24); + newTag = LisaTag.DecodePriamTag(oldTag)?.ToProfile().GetBytes(); + break; + // Priam tag, copy to Priam + case 12 when imageInfo.MediaType == MediaType.PriamDataTower: + newTag = new byte[24]; + Array.Copy(data, 512, newTag, 0, 24); + break; + // Priam tag, convert to Sony + case 24 when imageInfo.MediaType == MediaType.AppleSonySS || + imageInfo.MediaType == MediaType.AppleSonySS: + oldTag = new byte[24]; + Array.Copy(data, 512, oldTag, 0, 24); + newTag = LisaTag.DecodePriamTag(oldTag)?.ToSony().GetBytes(); + break; + case 0: + newTag = null; + break; + default: + ErrorMessage = "Incorrect data size"; + return false; + } + + sector = new byte[512]; + Array.Copy(data, 0, sector, 0, 512); + + if(newTag == null) return WriteSector(sector, sectorAddress); + + if(sectorSubchannel == null) + sectorSubchannel = new byte[newTag.Length * (int)imageInfo.Sectors]; + Array.Copy(newTag, 0, sectorSubchannel, newTag.Length * (int)sectorAddress, newTag.Length); + + return WriteSector(sector, sectorAddress); + } + + break; + } + + ErrorMessage = "Unknown long sector type, cannot write."; + return false; + } + + public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length) + { + byte[] sector; + switch(imageInfo.XmlMediaType) + { + case XmlMediaType.OpticalDisc: + Track track = + Tracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector && + sectorAddress <= trk.TrackEndSector); + + if(track.TrackSequence == 0) + { + ErrorMessage = $"Can't found track containing {sectorAddress}"; + return false; + } + + if(data.Length % 2352 != 0) + { + ErrorMessage = "Incorrect data size"; + return false; + } + + if(sectorAddress + length > track.TrackEndSector + 1) + { + ErrorMessage = "Can't cross tracks"; + return false; + } + + sector = new byte[2352]; + for(uint i = 0; i < length; i++) + { + Array.Copy(data, 2352 * i, sector, 0, 2352); + if(!WriteSectorLong(sector, sectorAddress + i)) return false; + } + + ErrorMessage = ""; + return true; + case XmlMediaType.BlockMedia: + switch(imageInfo.MediaType) + { + case MediaType.AppleFileWare: + case MediaType.AppleProfile: + case MediaType.AppleSonyDS: + case MediaType.AppleSonySS: + case MediaType.AppleWidget: + case MediaType.PriamDataTower: + int sectorSize = 0; + if(data.Length % 524 == 0) sectorSize = 524; + else if(data.Length % 532 == 0) + sectorSize = 532; + else if(data.Length % 536 == 0) + sectorSize = 536; + + if(sectorSize == 0) + { + ErrorMessage = "Incorrect data size"; + return false; + } + + sector = new byte[sectorSize]; + for(uint i = 0; i < length; i++) + { + Array.Copy(data, sectorSize * i, sector, 0, sectorSize); + if(!WriteSectorLong(sector, sectorAddress + i)) return false; + } + + ErrorMessage = ""; + return true; + } + + break; + } + + ErrorMessage = "Unknown long sector type, cannot write."; + return false; + } + + public bool SetTracks(List tracks) + { + if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) + { + ErrorMessage = "Unsupported feature"; + return false; + } + + if(!IsWriting) + { + ErrorMessage = "Tried to write on a non-writable image"; + return false; + } + + Tracks = tracks; + ErrorMessage = ""; + return true; + } + + public bool Close() + { + if(!IsWriting) + { + ErrorMessage = "Image is not opened for writing"; + return false; + } + + // Close current block first + if(blockStream != null) + { + currentBlockHeader.length = currentBlockOffset * currentBlockHeader.sectorSize; + currentBlockHeader.crc64 = BitConverter.ToUInt64(crc64.Final(), 0); + + Crc64Context cmpCrc64Context = new Crc64Context(); + cmpCrc64Context.Init(); + + byte[] lzmaProperties = new byte[0]; + + if(currentBlockHeader.compression == CompressionType.Flac) + { + long remaining = currentBlockOffset * SAMPLES_PER_SECTOR % flakeWriter.Settings.BlockSize; + // Fill FLAC block + if(remaining != 0) + { + AudioBuffer audioBuffer = + new AudioBuffer(AudioPCMConfig.RedBook, new byte[remaining * 4], (int)remaining); + flakeWriter.Write(audioBuffer); + } + + // This trick because CUETools.Codecs.Flake closes the underlying stream + long realLength = blockStream.Length; + byte[] buffer = new byte[realLength]; + flakeWriter.Close(); + Array.Copy(blockStream.GetBuffer(), 0, buffer, 0, realLength); + blockStream = new MemoryStream(buffer); + } + else + { + lzmaProperties = lzmaBlockStream.Properties; + lzmaBlockStream.Close(); + cmpCrc64Context.Update(lzmaProperties); + } + + currentBlockHeader.cmpLength = (uint)blockStream.Length; + if(currentBlockHeader.compression == CompressionType.Lzma) + currentBlockHeader.cmpLength += LZMA_PROPERTIES_LENGTH; + cmpCrc64Context.Update(blockStream.ToArray()); + currentBlockHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64Context.Final(), 0); + + index.Add(new IndexEntry + { + blockType = BlockType.DataBlock, + dataType = DataType.UserData, + offset = (ulong)imageStream.Position + }); + + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(currentBlockHeader)); + structureBytes = new byte[Marshal.SizeOf(currentBlockHeader)]; + Marshal.StructureToPtr(currentBlockHeader, structurePointer, true); + Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); + Marshal.FreeHGlobal(structurePointer); + imageStream.Write(structureBytes, 0, structureBytes.Length); + structureBytes = null; + if(currentBlockHeader.compression == CompressionType.Lzma) + imageStream.Write(lzmaProperties, 0, lzmaProperties.Length); + imageStream.Write(blockStream.ToArray(), 0, (int)blockStream.Length); + } + + IndexEntry idxEntry; + + // Write media tag blocks + foreach(KeyValuePair mediaTag in mediaTags) + { + DataType dataType = GetDataTypeForMediaTag(mediaTag.Key); + idxEntry = new IndexEntry + { + blockType = BlockType.DataBlock, + dataType = dataType, + offset = (ulong)imageStream.Position + }; + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Writing tag type {0} to position {1}", + mediaTag.Key, idxEntry.offset); + + Crc64Context.Data(mediaTag.Value, out byte[] tagCrc); + + BlockHeader tagBlock = new BlockHeader + { + identifier = BlockType.DataBlock, + type = dataType, + length = (uint)mediaTag.Value.Length, + crc64 = BitConverter.ToUInt64(tagCrc, 0) + }; + + blockStream = new MemoryStream(); + lzmaBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); + lzmaBlockStream.Write(mediaTag.Value, 0, mediaTag.Value.Length); + byte[] lzmaProperties = lzmaBlockStream.Properties; + lzmaBlockStream.Close(); + byte[] tagData; + + // Not compressible + if(blockStream.Length + LZMA_PROPERTIES_LENGTH >= mediaTag.Value.Length) + { + tagBlock.cmpLength = tagBlock.length; + tagBlock.cmpCrc64 = tagBlock.crc64; + tagData = mediaTag.Value; + tagBlock.compression = CompressionType.None; + } + else + { + tagData = blockStream.ToArray(); + Crc64Context.Data(tagData, out tagCrc); + tagBlock.cmpLength = (uint)tagData.Length + LZMA_PROPERTIES_LENGTH; + tagBlock.cmpCrc64 = BitConverter.ToUInt64(tagCrc, 0); + tagBlock.compression = CompressionType.Lzma; + } + + lzmaBlockStream = null; + blockStream = null; + + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(tagBlock)); + structureBytes = new byte[Marshal.SizeOf(tagBlock)]; + Marshal.StructureToPtr(tagBlock, structurePointer, true); + Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); + Marshal.FreeHGlobal(structurePointer); + imageStream.Write(structureBytes, 0, structureBytes.Length); + if(tagBlock.compression == CompressionType.Lzma) + imageStream.Write(lzmaProperties, 0, lzmaProperties.Length); + imageStream.Write(tagData, 0, tagData.Length); + + index.RemoveAll(t => t.blockType == BlockType.DataBlock && t.dataType == dataType); + + index.Add(idxEntry); + } + + // If we have set the geometry block, write it + if(geometryBlock.identifier == BlockType.GeometryBlock) + { + idxEntry = new IndexEntry + { + blockType = BlockType.GeometryBlock, + dataType = DataType.NoData, + offset = (ulong)imageStream.Position + }; + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Writing geometry block to position {0}", + idxEntry.offset); + + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(geometryBlock)); + structureBytes = new byte[Marshal.SizeOf(geometryBlock)]; + Marshal.StructureToPtr(geometryBlock, structurePointer, true); + Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); + Marshal.FreeHGlobal(structurePointer); + imageStream.Write(structureBytes, 0, structureBytes.Length); + + index.RemoveAll(t => t.blockType == BlockType.GeometryBlock && t.dataType == DataType.NoData); + + index.Add(idxEntry); + } + + // If the DDT is in-memory, write it to disk + if(inMemoryDdt) + { + idxEntry = new IndexEntry + { + blockType = BlockType.DeDuplicationTable, + dataType = DataType.UserData, + offset = (ulong)imageStream.Position + }; + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Writing user data DDT to position {0}", + idxEntry.offset); + + DdtHeader ddtHeader = new DdtHeader + { + identifier = BlockType.DeDuplicationTable, + type = DataType.UserData, + compression = CompressionType.Lzma, + shift = shift, + entries = (ulong)userDataDdt.LongLength, + length = (ulong)(userDataDdt.LongLength * sizeof(ulong)) + }; + + blockStream = new MemoryStream(); + lzmaBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); + crc64 = new Crc64Context(); + crc64.Init(); + for(ulong i = 0; i < (ulong)userDataDdt.LongLength; i++) + { + byte[] ddtEntry = BitConverter.GetBytes(userDataDdt[i]); + crc64.Update(ddtEntry); + lzmaBlockStream.Write(ddtEntry, 0, ddtEntry.Length); + } + + byte[] lzmaProperties = lzmaBlockStream.Properties; + lzmaBlockStream.Close(); + ddtHeader.cmpLength = (uint)blockStream.Length + LZMA_PROPERTIES_LENGTH; + Crc64Context cmpCrc64Context = new Crc64Context(); + cmpCrc64Context.Init(); + cmpCrc64Context.Update(lzmaProperties); + cmpCrc64Context.Update(blockStream.ToArray()); + ddtHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64Context.Final(), 0); + + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(ddtHeader)); + structureBytes = new byte[Marshal.SizeOf(ddtHeader)]; + Marshal.StructureToPtr(ddtHeader, structurePointer, true); + Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); + Marshal.FreeHGlobal(structurePointer); + imageStream.Write(structureBytes, 0, structureBytes.Length); + structureBytes = null; + imageStream.Write(lzmaProperties, 0, lzmaProperties.Length); + imageStream.Write(blockStream.ToArray(), 0, (int)blockStream.Length); + blockStream = null; + lzmaBlockStream = null; + + index.RemoveAll(t => t.blockType == BlockType.DeDuplicationTable && t.dataType == DataType.UserData); + + index.Add(idxEntry); + } + + // Write the sector prefix, suffix and subchannels if present + switch(imageInfo.XmlMediaType) + { + case XmlMediaType.OpticalDisc when Tracks != null && Tracks.Count > 0: + if(sectorPrefix != null && sectorSuffix != null) + { + idxEntry = new IndexEntry + { + blockType = BlockType.DataBlock, + dataType = DataType.CdSectorPrefix, + offset = (ulong)imageStream.Position + }; + + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Writing CD sector prefix block to position {0}", idxEntry.offset); + + Crc64Context.Data(sectorPrefix, out byte[] blockCrc); + + BlockHeader prefixBlock = new BlockHeader + { + identifier = BlockType.DataBlock, + type = DataType.CdSectorPrefix, + length = (uint)sectorPrefix.Length, + crc64 = BitConverter.ToUInt64(blockCrc, 0) + }; + + blockStream = new MemoryStream(); + lzmaBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); + lzmaBlockStream.Write(sectorPrefix, 0, sectorPrefix.Length); + byte[] lzmaProperties = lzmaBlockStream.Properties; + lzmaBlockStream.Close(); + + Crc64Context.Data(blockStream.ToArray(), out blockCrc); + prefixBlock.cmpLength = (uint)blockStream.Length + LZMA_PROPERTIES_LENGTH; + prefixBlock.cmpCrc64 = BitConverter.ToUInt64(blockCrc, 0); + prefixBlock.compression = CompressionType.Lzma; + + lzmaBlockStream = null; + + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(prefixBlock)); + structureBytes = new byte[Marshal.SizeOf(prefixBlock)]; + Marshal.StructureToPtr(prefixBlock, structurePointer, true); + Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); + Marshal.FreeHGlobal(structurePointer); + imageStream.Write(structureBytes, 0, structureBytes.Length); + if(prefixBlock.compression == CompressionType.Lzma) + imageStream.Write(lzmaProperties, 0, lzmaProperties.Length); + imageStream.Write(blockStream.ToArray(), 0, (int)blockStream.Length); + + index.RemoveAll(t => t.blockType == BlockType.DataBlock && + t.dataType == DataType.CdSectorPrefix); + + index.Add(idxEntry); + + idxEntry = new IndexEntry + { + blockType = BlockType.DataBlock, + dataType = DataType.CdSectorSuffix, + offset = (ulong)imageStream.Position + }; + + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Writing CD sector suffix block to position {0}", idxEntry.offset); + + Crc64Context.Data(sectorSuffix, out blockCrc); + + prefixBlock = new BlockHeader + { + identifier = BlockType.DataBlock, + type = DataType.CdSectorSuffix, + length = (uint)sectorSuffix.Length, + crc64 = BitConverter.ToUInt64(blockCrc, 0) + }; + + blockStream = new MemoryStream(); + lzmaBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); + lzmaBlockStream.Write(sectorSuffix, 0, sectorSuffix.Length); + lzmaProperties = lzmaBlockStream.Properties; + lzmaBlockStream.Close(); + + Crc64Context.Data(blockStream.ToArray(), out blockCrc); + prefixBlock.cmpLength = (uint)blockStream.Length + LZMA_PROPERTIES_LENGTH; + prefixBlock.cmpCrc64 = BitConverter.ToUInt64(blockCrc, 0); + prefixBlock.compression = CompressionType.Lzma; + + lzmaBlockStream = null; + + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(prefixBlock)); + structureBytes = new byte[Marshal.SizeOf(prefixBlock)]; + Marshal.StructureToPtr(prefixBlock, structurePointer, true); + Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); + Marshal.FreeHGlobal(structurePointer); + imageStream.Write(structureBytes, 0, structureBytes.Length); + if(prefixBlock.compression == CompressionType.Lzma) + imageStream.Write(lzmaProperties, 0, lzmaProperties.Length); + imageStream.Write(blockStream.ToArray(), 0, (int)blockStream.Length); + + index.RemoveAll(t => t.blockType == BlockType.DataBlock && + t.dataType == DataType.CdSectorSuffix); + + index.Add(idxEntry); + blockStream = null; + } + + if(sectorSubchannel != null) + { + idxEntry = new IndexEntry + { + blockType = BlockType.DataBlock, + dataType = DataType.CdSectorSubchannel, + offset = (ulong)imageStream.Position + }; + + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Writing CD subchannel block to position {0}", idxEntry.offset); + + Crc64Context.Data(sectorSubchannel, out byte[] blockCrc); + + BlockHeader subchannelBlock = new BlockHeader + { + identifier = BlockType.DataBlock, + type = DataType.CdSectorSubchannel, + length = (uint)sectorSubchannel.Length, + crc64 = BitConverter.ToUInt64(blockCrc, 0) + }; + + blockStream = new MemoryStream(); + lzmaBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); + lzmaBlockStream.Write(sectorSubchannel, 0, sectorSubchannel.Length); + byte[] lzmaProperties = lzmaBlockStream.Properties; + lzmaBlockStream.Close(); + + Crc64Context.Data(blockStream.ToArray(), out blockCrc); + subchannelBlock.cmpLength = (uint)blockStream.Length + LZMA_PROPERTIES_LENGTH; + subchannelBlock.cmpCrc64 = BitConverter.ToUInt64(blockCrc, 0); + subchannelBlock.compression = CompressionType.Lzma; + + lzmaBlockStream = null; + + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(subchannelBlock)); + structureBytes = new byte[Marshal.SizeOf(subchannelBlock)]; + Marshal.StructureToPtr(subchannelBlock, structurePointer, true); + Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); + Marshal.FreeHGlobal(structurePointer); + imageStream.Write(structureBytes, 0, structureBytes.Length); + if(subchannelBlock.compression == CompressionType.Lzma) + imageStream.Write(lzmaProperties, 0, lzmaProperties.Length); + imageStream.Write(blockStream.ToArray(), 0, (int)blockStream.Length); + + index.RemoveAll(t => t.blockType == BlockType.DataBlock && + t.dataType == DataType.CdSectorSubchannel); + + index.Add(idxEntry); + blockStream = null; + } + + List trackEntries = new List(); + foreach(Track track in Tracks) + { + trackFlags.TryGetValue((byte)track.TrackSequence, out byte flags); + trackIsrcs.TryGetValue((byte)track.TrackSequence, out string isrc); + + if((flags & (int)CdFlags.DataTrack) == 0 && track.TrackType != TrackType.Audio) + flags += (byte)CdFlags.DataTrack; + + trackEntries.Add(new TrackEntry + { + sequence = (byte)track.TrackSequence, + type = track.TrackType, + start = (long)track.TrackStartSector, + end = (long)track.TrackEndSector, + pregap = (long)track.TrackPregap, + session = (byte)track.TrackSession, + isrc = isrc, + flags = flags + }); + } + + // If there are tracks build the tracks block + if(trackEntries.Count > 0) + { + blockStream = new MemoryStream(); + + foreach(TrackEntry entry in trackEntries) + { + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(entry)); + structureBytes = new byte[Marshal.SizeOf(entry)]; + Marshal.StructureToPtr(entry, structurePointer, true); + Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); + Marshal.FreeHGlobal(structurePointer); + blockStream.Write(structureBytes, 0, structureBytes.Length); + } + + Crc64Context.Data(blockStream.ToArray(), out byte[] trksCrc); + TracksHeader trkHeader = new TracksHeader + { + identifier = BlockType.TracksBlock, + entries = (ushort)trackEntries.Count, + crc64 = BitConverter.ToUInt64(trksCrc, 0) + }; + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Writing tracks to position {0}", + imageStream.Position); + + index.RemoveAll(t => t.blockType == BlockType.TracksBlock && t.dataType == DataType.NoData); + + index.Add(new IndexEntry + { + blockType = BlockType.TracksBlock, + dataType = DataType.NoData, + offset = (ulong)imageStream.Position + }); + + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(trkHeader)); + structureBytes = new byte[Marshal.SizeOf(trkHeader)]; + Marshal.StructureToPtr(trkHeader, structurePointer, true); + Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); + Marshal.FreeHGlobal(structurePointer); + imageStream.Write(structureBytes, 0, structureBytes.Length); + imageStream.Write(blockStream.ToArray(), 0, (int)blockStream.Length); + } + + break; + case XmlMediaType.BlockMedia: + if(sectorSubchannel != null && + (imageInfo.MediaType == MediaType.AppleFileWare || + imageInfo.MediaType == MediaType.AppleSonySS || + imageInfo.MediaType == MediaType.AppleSonyDS || + imageInfo.MediaType == MediaType.AppleProfile || + imageInfo.MediaType == MediaType.AppleWidget || + imageInfo.MediaType == MediaType.PriamDataTower)) + { + DataType tagType = DataType.NoData; + + switch(imageInfo.MediaType) + { + case MediaType.AppleSonySS: + case MediaType.AppleSonyDS: + tagType = DataType.AppleSonyTag; + break; + case MediaType.AppleFileWare: + case MediaType.AppleProfile: + case MediaType.AppleWidget: + tagType = DataType.AppleProfileTag; + break; + case MediaType.PriamDataTower: + tagType = DataType.PriamDataTowerTag; + break; + } + + idxEntry = new IndexEntry + { + blockType = BlockType.DataBlock, + dataType = tagType, + offset = (ulong)imageStream.Position + }; + + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Writing apple sector tag block to position {0}", idxEntry.offset); + + Crc64Context.Data(sectorSubchannel, out byte[] blockCrc); + + BlockHeader subchannelBlock = new BlockHeader + { + identifier = BlockType.DataBlock, + type = tagType, + length = (uint)sectorSubchannel.Length, + crc64 = BitConverter.ToUInt64(blockCrc, 0) + }; + + blockStream = new MemoryStream(); + lzmaBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); + lzmaBlockStream.Write(sectorSubchannel, 0, sectorSubchannel.Length); + byte[] lzmaProperties = lzmaBlockStream.Properties; + lzmaBlockStream.Close(); + + Crc64Context.Data(blockStream.ToArray(), out blockCrc); + subchannelBlock.cmpLength = (uint)blockStream.Length + LZMA_PROPERTIES_LENGTH; + subchannelBlock.cmpCrc64 = BitConverter.ToUInt64(blockCrc, 0); + subchannelBlock.compression = CompressionType.Lzma; + + lzmaBlockStream = null; + + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(subchannelBlock)); + structureBytes = new byte[Marshal.SizeOf(subchannelBlock)]; + Marshal.StructureToPtr(subchannelBlock, structurePointer, true); + Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); + Marshal.FreeHGlobal(structurePointer); + imageStream.Write(structureBytes, 0, structureBytes.Length); + if(subchannelBlock.compression == CompressionType.Lzma) + imageStream.Write(lzmaProperties, 0, lzmaProperties.Length); + imageStream.Write(blockStream.ToArray(), 0, (int)blockStream.Length); + + index.RemoveAll(t => t.blockType == BlockType.DataBlock && t.dataType == tagType); + + index.Add(idxEntry); + blockStream = null; + } + + break; + } + + // Write metadata if present + SetMetadataFromTags(); + MetadataBlock metadataBlock = new MetadataBlock(); + blockStream = new MemoryStream(); + blockStream.Write(new byte[Marshal.SizeOf(metadataBlock)], 0, Marshal.SizeOf(metadataBlock)); + byte[] tmpUtf16Le; + + if(imageInfo.MediaSequence > 0 && imageInfo.LastMediaSequence > 0) + { + metadataBlock.identifier = BlockType.MetadataBlock; + metadataBlock.mediaSequence = imageInfo.MediaSequence; + metadataBlock.lastMediaSequence = imageInfo.LastMediaSequence; + } + + if(!string.IsNullOrWhiteSpace(imageInfo.Creator)) + { + tmpUtf16Le = Encoding.Unicode.GetBytes(imageInfo.Creator); + metadataBlock.identifier = BlockType.MetadataBlock; + metadataBlock.creatorOffset = (uint)blockStream.Position; + metadataBlock.creatorLength = (uint)(tmpUtf16Le.Length + 2); + blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length); + blockStream.Write(new byte[] {0, 0}, 0, 2); + } + + if(!string.IsNullOrWhiteSpace(imageInfo.Comments)) + { + tmpUtf16Le = Encoding.Unicode.GetBytes(imageInfo.Comments); + metadataBlock.identifier = BlockType.MetadataBlock; + metadataBlock.commentsOffset = (uint)blockStream.Position; + metadataBlock.commentsLength = (uint)(tmpUtf16Le.Length + 2); + blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length); + blockStream.Write(new byte[] {0, 0}, 0, 2); + } + + if(!string.IsNullOrWhiteSpace(imageInfo.MediaTitle)) + { + tmpUtf16Le = Encoding.Unicode.GetBytes(imageInfo.MediaTitle); + metadataBlock.identifier = BlockType.MetadataBlock; + metadataBlock.mediaTitleOffset = (uint)blockStream.Position; + metadataBlock.mediaTitleLength = (uint)(tmpUtf16Le.Length + 2); + blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length); + blockStream.Write(new byte[] {0, 0}, 0, 2); + } + + if(!string.IsNullOrWhiteSpace(imageInfo.MediaManufacturer)) + { + tmpUtf16Le = Encoding.Unicode.GetBytes(imageInfo.MediaManufacturer); + metadataBlock.identifier = BlockType.MetadataBlock; + metadataBlock.mediaManufacturerOffset = (uint)blockStream.Position; + metadataBlock.mediaManufacturerLength = (uint)(tmpUtf16Le.Length + 2); + blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length); + blockStream.Write(new byte[] {0, 0}, 0, 2); + } + + if(!string.IsNullOrWhiteSpace(imageInfo.MediaModel)) + { + tmpUtf16Le = Encoding.Unicode.GetBytes(imageInfo.MediaModel); + metadataBlock.identifier = BlockType.MetadataBlock; + metadataBlock.mediaModelOffset = (uint)blockStream.Position; + metadataBlock.mediaModelLength = (uint)(tmpUtf16Le.Length + 2); + blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length); + blockStream.Write(new byte[] {0, 0}, 0, 2); + } + + if(!string.IsNullOrWhiteSpace(imageInfo.MediaSerialNumber)) + { + tmpUtf16Le = Encoding.Unicode.GetBytes(imageInfo.MediaSerialNumber); + metadataBlock.identifier = BlockType.MetadataBlock; + metadataBlock.mediaSerialNumberOffset = (uint)blockStream.Position; + metadataBlock.mediaSerialNumberLength = (uint)(tmpUtf16Le.Length + 2); + blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length); + blockStream.Write(new byte[] {0, 0}, 0, 2); + } + + if(!string.IsNullOrWhiteSpace(imageInfo.MediaBarcode)) + { + tmpUtf16Le = Encoding.Unicode.GetBytes(imageInfo.MediaBarcode); + metadataBlock.identifier = BlockType.MetadataBlock; + metadataBlock.mediaBarcodeOffset = (uint)blockStream.Position; + metadataBlock.mediaBarcodeLength = (uint)(tmpUtf16Le.Length + 2); + blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length); + blockStream.Write(new byte[] {0, 0}, 0, 2); + } + + if(!string.IsNullOrWhiteSpace(imageInfo.MediaPartNumber)) + { + tmpUtf16Le = Encoding.Unicode.GetBytes(imageInfo.MediaPartNumber); + metadataBlock.identifier = BlockType.MetadataBlock; + metadataBlock.mediaPartNumberOffset = (uint)blockStream.Position; + metadataBlock.mediaPartNumberLength = (uint)(tmpUtf16Le.Length + 2); + blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length); + blockStream.Write(new byte[] {0, 0}, 0, 2); + } + + if(!string.IsNullOrWhiteSpace(imageInfo.DriveManufacturer)) + { + tmpUtf16Le = Encoding.Unicode.GetBytes(imageInfo.DriveManufacturer); + metadataBlock.identifier = BlockType.MetadataBlock; + metadataBlock.driveManufacturerOffset = (uint)blockStream.Position; + metadataBlock.driveManufacturerLength = (uint)(tmpUtf16Le.Length + 2); + blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length); + blockStream.Write(new byte[] {0, 0}, 0, 2); + } + + if(!string.IsNullOrWhiteSpace(imageInfo.DriveModel)) + { + tmpUtf16Le = Encoding.Unicode.GetBytes(imageInfo.DriveModel); + metadataBlock.identifier = BlockType.MetadataBlock; + metadataBlock.driveModelOffset = (uint)blockStream.Position; + metadataBlock.driveModelLength = (uint)(tmpUtf16Le.Length + 2); + blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length); + blockStream.Write(new byte[] {0, 0}, 0, 2); + } + + if(!string.IsNullOrWhiteSpace(imageInfo.DriveSerialNumber)) + { + tmpUtf16Le = Encoding.Unicode.GetBytes(imageInfo.DriveSerialNumber); + metadataBlock.identifier = BlockType.MetadataBlock; + metadataBlock.driveSerialNumberOffset = (uint)blockStream.Position; + metadataBlock.driveSerialNumberLength = (uint)(tmpUtf16Le.Length + 2); + blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length); + blockStream.Write(new byte[] {0, 0}, 0, 2); + } + + if(!string.IsNullOrWhiteSpace(imageInfo.DriveFirmwareRevision)) + { + tmpUtf16Le = Encoding.Unicode.GetBytes(imageInfo.DriveFirmwareRevision); + metadataBlock.identifier = BlockType.MetadataBlock; + metadataBlock.driveFirmwareRevisionOffset = (uint)blockStream.Position; + metadataBlock.driveFirmwareRevisionLength = (uint)(tmpUtf16Le.Length + 2); + blockStream.Write(tmpUtf16Le, 0, tmpUtf16Le.Length); + blockStream.Write(new byte[] {0, 0}, 0, 2); + } + + // Check if we set up any metadata earlier, then write its block + if(metadataBlock.identifier == BlockType.MetadataBlock) + { + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Writing metadata to position {0}", + imageStream.Position); + metadataBlock.blockSize = (uint)blockStream.Length; + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(metadataBlock)); + structureBytes = new byte[Marshal.SizeOf(metadataBlock)]; + Marshal.StructureToPtr(metadataBlock, structurePointer, true); + Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); + Marshal.FreeHGlobal(structurePointer); + blockStream.Position = 0; + blockStream.Write(structureBytes, 0, structureBytes.Length); + index.RemoveAll(t => t.blockType == BlockType.MetadataBlock && t.dataType == DataType.NoData); + + index.Add(new IndexEntry + { + blockType = BlockType.MetadataBlock, + dataType = DataType.NoData, + offset = (ulong)imageStream.Position + }); + imageStream.Write(blockStream.ToArray(), 0, (int)blockStream.Length); + } + + header.indexOffset = (ulong)imageStream.Position; + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Writing index to position {0}", + header.indexOffset); + + blockStream = new MemoryStream(); + + // Write index to memory + foreach(IndexEntry entry in index) + { + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(entry)); + structureBytes = new byte[Marshal.SizeOf(entry)]; + Marshal.StructureToPtr(entry, structurePointer, true); + Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); + Marshal.FreeHGlobal(structurePointer); + blockStream.Write(structureBytes, 0, structureBytes.Length); + } + + Crc64Context.Data(blockStream.ToArray(), out byte[] idxCrc); + + IndexHeader idxHeader = new IndexHeader + { + identifier = BlockType.Index, + entries = (ushort)index.Count, + crc64 = BitConverter.ToUInt64(idxCrc, 0) + }; + + // Write index to disk + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(idxHeader)); + structureBytes = new byte[Marshal.SizeOf(idxHeader)]; + Marshal.StructureToPtr(idxHeader, structurePointer, true); + Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); + Marshal.FreeHGlobal(structurePointer); + imageStream.Write(structureBytes, 0, structureBytes.Length); + imageStream.Write(blockStream.ToArray(), 0, (int)blockStream.Length); + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Writing header"); + header.lastWrittenTime = DateTime.UtcNow.ToFileTimeUtc(); + imageStream.Position = 0; + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(header)); + structureBytes = new byte[Marshal.SizeOf(header)]; + Marshal.StructureToPtr(header, structurePointer, true); + Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); + Marshal.FreeHGlobal(structurePointer); + imageStream.Write(structureBytes, 0, structureBytes.Length); + + imageStream.Flush(); + imageStream.Close(); + + IsWriting = false; + ErrorMessage = ""; + return true; + } + + public bool SetMetadata(ImageInfo metadata) + { + imageInfo.Creator = metadata.Creator; + imageInfo.Comments = metadata.Comments; + imageInfo.MediaManufacturer = metadata.MediaManufacturer; + imageInfo.MediaModel = metadata.MediaModel; + imageInfo.MediaSerialNumber = metadata.MediaSerialNumber; + imageInfo.MediaBarcode = metadata.MediaBarcode; + imageInfo.MediaPartNumber = metadata.MediaPartNumber; + imageInfo.MediaSequence = metadata.MediaSequence; + imageInfo.LastMediaSequence = metadata.LastMediaSequence; + imageInfo.DriveManufacturer = metadata.DriveManufacturer; + imageInfo.DriveModel = metadata.DriveModel; + imageInfo.DriveSerialNumber = metadata.DriveSerialNumber; + imageInfo.DriveFirmwareRevision = metadata.DriveFirmwareRevision; + + return true; + } + + public bool SetGeometry(uint cylinders, uint heads, uint sectorsPerTrack) + { + if(!IsWriting) + { + ErrorMessage = "Tried to write on a non-writable image"; + return false; + } + + if(imageInfo.XmlMediaType != XmlMediaType.BlockMedia) + { + ErrorMessage = "Tried to set geometry on a media that doesn't suppport it"; + return false; + } + + geometryBlock = new GeometryBlock + { + identifier = BlockType.GeometryBlock, + cylinders = cylinders, + heads = heads, + sectorsPerTrack = sectorsPerTrack + }; + + ErrorMessage = ""; + return true; + } + + public bool WriteSectorTag(byte[] data, ulong sectorAddress, SectorTagType tag) + { + if(!IsWriting) + { + ErrorMessage = "Tried to write on a non-writable image"; + return false; + } + + if(sectorAddress >= imageInfo.Sectors) + { + ErrorMessage = "Tried to write past image size"; + return false; + } + + Track track = new Track(); + switch(tag) + { + case SectorTagType.CdTrackFlags: + case SectorTagType.CdTrackIsrc: + case SectorTagType.CdSectorSubchannel: + if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) + { + ErrorMessage = "Incorrect tag for disk type"; + return false; + } + + track = Tracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector && + sectorAddress <= trk.TrackEndSector); + if(track.TrackSequence == 0) + { + ErrorMessage = $"Can't found track containing {sectorAddress}"; + return false; + } + + break; + } + + switch(tag) + { + case SectorTagType.CdTrackFlags: + { + if(data.Length != 1) + { + ErrorMessage = "Incorrect data size for track flags"; + return false; + } + + trackFlags.Add((byte)track.TrackSequence, data[0]); + + return true; + } + case SectorTagType.CdTrackIsrc: + { + if(data != null) trackIsrcs.Add((byte)track.TrackSequence, Encoding.UTF8.GetString(data)); + return true; + } + case SectorTagType.CdSectorSubchannel: + { + if(data.Length != 96) + { + ErrorMessage = "Incorrect data size for subchannel"; + return false; + } + + if(sectorSubchannel == null) sectorSubchannel = new byte[imageInfo.Sectors * 96]; + + Array.Copy(data, 0, sectorSubchannel, (int)(96 * sectorAddress), 96); + + return true; + } + default: + ErrorMessage = $"Don't know how to write sector tag type {tag}"; + return false; + } + } + + public bool WriteSectorsTag(byte[] data, ulong sectorAddress, uint length, SectorTagType tag) + { + if(!IsWriting) + { + ErrorMessage = "Tried to write on a non-writable image"; + return false; + } + + if(sectorAddress + length > imageInfo.Sectors) + { + ErrorMessage = "Tried to write past image size"; + return false; + } + + switch(tag) + { + case SectorTagType.CdTrackFlags: + case SectorTagType.CdTrackIsrc: return WriteSectorTag(data, sectorAddress, tag); + case SectorTagType.CdSectorSubchannel: + { + if(data.Length % 96 != 0) + { + ErrorMessage = "Incorrect data size for subchannel"; + return false; + } + + if(sectorSubchannel == null) sectorSubchannel = new byte[imageInfo.Sectors * 96]; + + if(sectorAddress * 96 + length * 96 > (ulong)sectorSubchannel.LongLength) + { + ErrorMessage = "Tried to write more data than possible"; + return false; + } + + Array.Copy(data, 0, sectorSubchannel, (int)(96 * sectorAddress), 96 * length); + + return true; + } + + default: + ErrorMessage = $"Don't know how to write sector tag type {tag}"; + return false; + } + } + + /// + /// Checks for media tags that may contain metadata and sets it up if not already set + /// + void SetMetadataFromTags() + { + // Search for SecureDigital CID + if(mediaTags.TryGetValue(MediaTagType.SD_CID, out byte[] sdCid)) + { + CID decoded = Decoders.SecureDigital.Decoders.DecodeCID(sdCid); + if(string.IsNullOrWhiteSpace(imageInfo.DriveManufacturer)) + imageInfo.DriveManufacturer = + VendorString.Prettify(decoded.Manufacturer); + if(string.IsNullOrWhiteSpace(imageInfo.DriveModel)) imageInfo.DriveModel = decoded.ProductName; + if(string.IsNullOrWhiteSpace(imageInfo.DriveFirmwareRevision)) + imageInfo.DriveFirmwareRevision = + $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}"; + if(string.IsNullOrWhiteSpace(imageInfo.DriveSerialNumber)) + imageInfo.DriveSerialNumber = $"{decoded.ProductSerialNumber}"; + } + + // Search for MultiMediaCard CID + if(mediaTags.TryGetValue(MediaTagType.MMC_CID, out byte[] mmcCid)) + { + Decoders.MMC.CID decoded = Decoders.MMC.Decoders.DecodeCID(mmcCid); + if(string.IsNullOrWhiteSpace(imageInfo.DriveManufacturer)) + imageInfo.DriveManufacturer = + Decoders.MMC.VendorString.Prettify(decoded.Manufacturer); + if(string.IsNullOrWhiteSpace(imageInfo.DriveModel)) imageInfo.DriveModel = decoded.ProductName; + if(string.IsNullOrWhiteSpace(imageInfo.DriveFirmwareRevision)) + imageInfo.DriveFirmwareRevision = + $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}"; + if(string.IsNullOrWhiteSpace(imageInfo.DriveSerialNumber)) + imageInfo.DriveSerialNumber = $"{decoded.ProductSerialNumber}"; + } + + // Search for SCSI INQUIRY + if(mediaTags.TryGetValue(MediaTagType.SCSI_INQUIRY, out byte[] scsiInquiry)) + { + Inquiry.SCSIInquiry? nullableInquiry = Inquiry.Decode(scsiInquiry); + + if(nullableInquiry.HasValue) + { + Inquiry.SCSIInquiry inquiry = nullableInquiry.Value; + if(string.IsNullOrWhiteSpace(imageInfo.DriveManufacturer)) + imageInfo.DriveManufacturer = StringHandlers.CToString(inquiry.VendorIdentification)?.Trim(); + if(string.IsNullOrWhiteSpace(imageInfo.DriveModel)) + imageInfo.DriveModel = StringHandlers.CToString(inquiry.ProductIdentification)?.Trim(); + if(string.IsNullOrWhiteSpace(imageInfo.DriveFirmwareRevision)) + imageInfo.DriveFirmwareRevision = + StringHandlers.CToString(inquiry.ProductRevisionLevel)?.Trim(); + } + } + + // Search for ATA or ATAPI IDENTIFY + if(!mediaTags.TryGetValue(MediaTagType.ATA_IDENTIFY, out byte[] ataIdentify) && + !mediaTags.TryGetValue(MediaTagType.ATAPI_IDENTIFY, out ataIdentify)) return; + + Identify.IdentifyDevice? nullableIdentify = Decoders.ATA.Identify.Decode(ataIdentify); + + if(!nullableIdentify.HasValue) return; + + Identify.IdentifyDevice identify = nullableIdentify.Value; + + string[] separated = identify.Model.Split(' '); + + if(separated.Length == 1) + if(string.IsNullOrWhiteSpace(imageInfo.DriveModel)) + imageInfo.DriveModel = separated[0]; + else + { + if(string.IsNullOrWhiteSpace(imageInfo.DriveManufacturer)) + imageInfo.DriveManufacturer = separated[0]; + if(string.IsNullOrWhiteSpace(imageInfo.DriveModel)) + imageInfo.DriveModel = separated[separated.Length - 1]; + } + + if(string.IsNullOrWhiteSpace(imageInfo.DriveFirmwareRevision)) + imageInfo.DriveFirmwareRevision = identify.FirmwareRevision; + if(string.IsNullOrWhiteSpace(imageInfo.DriveSerialNumber)) + imageInfo.DriveSerialNumber = identify.SerialNumber; + } + + // Get the CICM XML media type from DIC media type + static XmlMediaType GetXmlMediaType(MediaType type) + { + switch(type) + { + case MediaType.CD: + case MediaType.CDDA: + case MediaType.CDG: + case MediaType.CDEG: + case MediaType.CDI: + case MediaType.CDROM: + case MediaType.CDROMXA: + case MediaType.CDPLUS: + case MediaType.CDMO: + case MediaType.CDR: + case MediaType.CDRW: + case MediaType.CDMRW: + case MediaType.VCD: + case MediaType.SVCD: + case MediaType.PCD: + case MediaType.SACD: + case MediaType.DDCD: + case MediaType.DDCDR: + case MediaType.DDCDRW: + case MediaType.DTSCD: + case MediaType.CDMIDI: + case MediaType.CDV: + case MediaType.DVDROM: + case MediaType.DVDR: + case MediaType.DVDRW: + case MediaType.DVDPR: + case MediaType.DVDPRW: + case MediaType.DVDPRWDL: + case MediaType.DVDRDL: + case MediaType.DVDPRDL: + case MediaType.DVDRAM: + case MediaType.DVDRWDL: + case MediaType.DVDDownload: + case MediaType.HDDVDROM: + case MediaType.HDDVDRAM: + case MediaType.HDDVDR: + case MediaType.HDDVDRW: + case MediaType.HDDVDRDL: + case MediaType.HDDVDRWDL: + case MediaType.BDROM: + case MediaType.BDR: + case MediaType.BDRE: + case MediaType.BDRXL: + case MediaType.BDREXL: + case MediaType.EVD: + case MediaType.FVD: + case MediaType.HVD: + case MediaType.CBHD: + case MediaType.HDVMD: + case MediaType.VCDHD: + case MediaType.SVOD: + case MediaType.FDDVD: + case MediaType.LD: + case MediaType.LDROM: + case MediaType.LDROM2: + case MediaType.LVROM: + case MediaType.MegaLD: + case MediaType.PS1CD: + case MediaType.PS2CD: + case MediaType.PS2DVD: + case MediaType.PS3DVD: + case MediaType.PS3BD: + case MediaType.PS4BD: + case MediaType.UMD: + case MediaType.XGD: + case MediaType.XGD2: + case MediaType.XGD3: + case MediaType.XGD4: + case MediaType.MEGACD: + case MediaType.SATURNCD: + case MediaType.GDROM: + case MediaType.GDR: + case MediaType.SuperCDROM2: + case MediaType.JaguarCD: + case MediaType.ThreeDO: + case MediaType.GOD: + case MediaType.WOD: + case MediaType.WUOD: return XmlMediaType.OpticalDisc; + default: return XmlMediaType.BlockMedia; + } + } + + // Gets a DDT entry + ulong GetDdtEntry(ulong sectorAddress) + { + if(inMemoryDdt) return userDataDdt[sectorAddress]; + + if(ddtEntryCache.TryGetValue(sectorAddress, out ulong entry)) return entry; + + long oldPosition = imageStream.Position; + imageStream.Position = outMemoryDdtPosition + Marshal.SizeOf(typeof(DdtHeader)); + imageStream.Position += (long)(sectorAddress * sizeof(ulong)); + byte[] temp = new byte[sizeof(ulong)]; + imageStream.Read(temp, 0, sizeof(ulong)); + imageStream.Position = oldPosition; + entry = BitConverter.ToUInt64(temp, 0); + + if(ddtEntryCache.Count >= MAX_DDT_ENTRY_CACHE) ddtEntryCache.Clear(); + + ddtEntryCache.Add(sectorAddress, entry); + return entry; + } + + // Sets a DDT entry + void SetDdtEntry(ulong sectorAddress, ulong pointer) + { + if(inMemoryDdt) + { + userDataDdt[sectorAddress] = pointer; + return; + } + + long oldPosition = imageStream.Position; + imageStream.Position = outMemoryDdtPosition + Marshal.SizeOf(typeof(DdtHeader)); + imageStream.Position += (long)(sectorAddress * sizeof(ulong)); + imageStream.Write(BitConverter.GetBytes(pointer), 0, sizeof(ulong)); + imageStream.Position = oldPosition; + } + + // Converts between image data type and dic media tag type + static MediaTagType GetMediaTagTypeForDataType(DataType type) + { + switch(type) + { + case DataType.CompactDiscPartialToc: return MediaTagType.CD_TOC; + case DataType.CompactDiscSessionInfo: return MediaTagType.CD_SessionInfo; + case DataType.CompactDiscToc: return MediaTagType.CD_FullTOC; + case DataType.CompactDiscPma: return MediaTagType.CD_PMA; + case DataType.CompactDiscAtip: return MediaTagType.CD_ATIP; + case DataType.CompactDiscLeadInCdText: return MediaTagType.CD_TEXT; + case DataType.DvdPfi: return MediaTagType.DVD_PFI; + case DataType.DvdLeadInCmi: return MediaTagType.DVD_CMI; + case DataType.DvdDiscKey: return MediaTagType.DVD_DiscKey; + case DataType.DvdBca: return MediaTagType.DVD_BCA; + case DataType.DvdDmi: return MediaTagType.DVD_DMI; + case DataType.DvdMediaIdentifier: return MediaTagType.DVD_MediaIdentifier; + case DataType.DvdMediaKeyBlock: return MediaTagType.DVD_MKB; + case DataType.DvdRamDds: return MediaTagType.DVDRAM_DDS; + case DataType.DvdRamMediumStatus: return MediaTagType.DVDRAM_MediumStatus; + case DataType.DvdRamSpareArea: return MediaTagType.DVDRAM_SpareArea; + case DataType.DvdRRmd: return MediaTagType.DVDR_RMD; + case DataType.DvdRPrerecordedInfo: return MediaTagType.DVDR_PreRecordedInfo; + case DataType.DvdRMediaIdentifier: return MediaTagType.DVDR_MediaIdentifier; + case DataType.DvdRPfi: return MediaTagType.DVDR_PFI; + case DataType.DvdAdip: return MediaTagType.DVD_ADIP; + case DataType.HdDvdCpi: return MediaTagType.HDDVD_CPI; + case DataType.HdDvdMediumStatus: return MediaTagType.HDDVD_MediumStatus; + case DataType.DvdDlLayerCapacity: return MediaTagType.DVDDL_LayerCapacity; + case DataType.DvdDlMiddleZoneAddress: return MediaTagType.DVDDL_MiddleZoneAddress; + case DataType.DvdDlJumpIntervalSize: return MediaTagType.DVDDL_JumpIntervalSize; + case DataType.DvdDlManualLayerJumpLba: return MediaTagType.DVDDL_ManualLayerJumpLBA; + case DataType.BlurayDi: return MediaTagType.BD_DI; + case DataType.BlurayBca: return MediaTagType.BD_BCA; + case DataType.BlurayDds: return MediaTagType.BD_DDS; + case DataType.BlurayCartridgeStatus: return MediaTagType.BD_CartridgeStatus; + case DataType.BluraySpareArea: return MediaTagType.BD_SpareArea; + case DataType.AacsVolumeIdentifier: return MediaTagType.AACS_VolumeIdentifier; + case DataType.AacsSerialNumber: return MediaTagType.AACS_SerialNumber; + case DataType.AacsMediaIdentifier: return MediaTagType.AACS_MediaIdentifier; + case DataType.AacsMediaKeyBlock: return MediaTagType.AACS_MKB; + case DataType.AacsDataKeys: return MediaTagType.AACS_DataKeys; + case DataType.AacsLbaExtents: return MediaTagType.AACS_LBAExtents; + case DataType.CprmMediaKeyBlock: return MediaTagType.AACS_CPRM_MKB; + case DataType.HybridRecognizedLayers: return MediaTagType.Hybrid_RecognizedLayers; + case DataType.ScsiMmcWriteProtection: return MediaTagType.MMC_WriteProtection; + case DataType.ScsiMmcDiscInformation: return MediaTagType.MMC_DiscInformation; + case DataType.ScsiMmcTrackResourcesInformation: return MediaTagType.MMC_TrackResourcesInformation; + case DataType.ScsiMmcPowResourcesInformation: return MediaTagType.MMC_POWResourcesInformation; + case DataType.ScsiInquiry: return MediaTagType.SCSI_INQUIRY; + case DataType.ScsiModePage2A: return MediaTagType.SCSI_MODEPAGE_2A; + case DataType.AtaIdentify: return MediaTagType.ATA_IDENTIFY; + case DataType.AtapiIdentify: return MediaTagType.ATAPI_IDENTIFY; + case DataType.PcmciaCis: return MediaTagType.PCMCIA_CIS; + case DataType.SecureDigitalCid: return MediaTagType.SD_CID; + case DataType.SecureDigitalCsd: return MediaTagType.SD_CSD; + case DataType.SecureDigitalScr: return MediaTagType.SD_SCR; + case DataType.SecureDigitalOcr: return MediaTagType.SD_OCR; + case DataType.MultiMediaCardCid: return MediaTagType.MMC_CID; + case DataType.MultiMediaCardCsd: return MediaTagType.MMC_CSD; + case DataType.MultiMediaCardOcr: return MediaTagType.MMC_OCR; + case DataType.MultiMediaCardExtendedCsd: return MediaTagType.MMC_ExtendedCSD; + case DataType.XboxSecuritySector: return MediaTagType.Xbox_SecuritySector; + case DataType.FloppyLeadOut: return MediaTagType.Floppy_LeadOut; + case DataType.DvdDiscControlBlock: return MediaTagType.DCB; + case DataType.CompactDiscLeadIn: return MediaTagType.CD_LeadIn; + case DataType.CompactDiscLeadOut: return MediaTagType.CD_LeadOut; + case DataType.ScsiModeSense6: return MediaTagType.SCSI_MODESENSE_6; + case DataType.ScsiModeSense10: return MediaTagType.SCSI_MODESENSE_10; + case DataType.UsbDescriptors: return MediaTagType.USB_Descriptors; + case DataType.XboxDmi: return MediaTagType.Xbox_DMI; + case DataType.XboxPfi: return MediaTagType.Xbox_PFI; + default: throw new ArgumentOutOfRangeException(); + } + } + + // Converts between dic media tag type and image data type + static DataType GetDataTypeForMediaTag(MediaTagType tag) + { + switch(tag) + { + case MediaTagType.CD_TOC: return DataType.CompactDiscPartialToc; + case MediaTagType.CD_SessionInfo: return DataType.CompactDiscSessionInfo; + case MediaTagType.CD_FullTOC: return DataType.CompactDiscToc; + case MediaTagType.CD_PMA: return DataType.CompactDiscPma; + case MediaTagType.CD_ATIP: return DataType.CompactDiscAtip; + case MediaTagType.CD_TEXT: return DataType.CompactDiscLeadInCdText; + case MediaTagType.DVD_PFI: return DataType.DvdPfi; + case MediaTagType.DVD_CMI: return DataType.DvdLeadInCmi; + case MediaTagType.DVD_DiscKey: return DataType.DvdDiscKey; + case MediaTagType.DVD_BCA: return DataType.DvdBca; + case MediaTagType.DVD_DMI: return DataType.DvdDmi; + case MediaTagType.DVD_MediaIdentifier: return DataType.DvdMediaIdentifier; + case MediaTagType.DVD_MKB: return DataType.DvdMediaKeyBlock; + case MediaTagType.DVDRAM_DDS: return DataType.DvdRamDds; + case MediaTagType.DVDRAM_MediumStatus: return DataType.DvdRamMediumStatus; + case MediaTagType.DVDRAM_SpareArea: return DataType.DvdRamSpareArea; + case MediaTagType.DVDR_RMD: return DataType.DvdRRmd; + case MediaTagType.DVDR_PreRecordedInfo: return DataType.DvdRPrerecordedInfo; + case MediaTagType.DVDR_MediaIdentifier: return DataType.DvdRMediaIdentifier; + case MediaTagType.DVDR_PFI: return DataType.DvdRPfi; + case MediaTagType.DVD_ADIP: return DataType.DvdAdip; + case MediaTagType.HDDVD_CPI: return DataType.HdDvdCpi; + case MediaTagType.HDDVD_MediumStatus: return DataType.HdDvdMediumStatus; + case MediaTagType.DVDDL_LayerCapacity: return DataType.DvdDlLayerCapacity; + case MediaTagType.DVDDL_MiddleZoneAddress: return DataType.DvdDlMiddleZoneAddress; + case MediaTagType.DVDDL_JumpIntervalSize: return DataType.DvdDlJumpIntervalSize; + case MediaTagType.DVDDL_ManualLayerJumpLBA: return DataType.DvdDlManualLayerJumpLba; + case MediaTagType.BD_DI: return DataType.BlurayDi; + case MediaTagType.BD_BCA: return DataType.BlurayBca; + case MediaTagType.BD_DDS: return DataType.BlurayDds; + case MediaTagType.BD_CartridgeStatus: return DataType.BlurayCartridgeStatus; + case MediaTagType.BD_SpareArea: return DataType.BluraySpareArea; + case MediaTagType.AACS_VolumeIdentifier: return DataType.AacsVolumeIdentifier; + case MediaTagType.AACS_SerialNumber: return DataType.AacsSerialNumber; + case MediaTagType.AACS_MediaIdentifier: return DataType.AacsMediaIdentifier; + case MediaTagType.AACS_MKB: return DataType.AacsMediaKeyBlock; + case MediaTagType.AACS_DataKeys: return DataType.AacsDataKeys; + case MediaTagType.AACS_LBAExtents: return DataType.AacsLbaExtents; + case MediaTagType.AACS_CPRM_MKB: return DataType.CprmMediaKeyBlock; + case MediaTagType.Hybrid_RecognizedLayers: return DataType.HybridRecognizedLayers; + case MediaTagType.MMC_WriteProtection: return DataType.ScsiMmcWriteProtection; + case MediaTagType.MMC_DiscInformation: return DataType.ScsiMmcDiscInformation; + case MediaTagType.MMC_TrackResourcesInformation: return DataType.ScsiMmcTrackResourcesInformation; + case MediaTagType.MMC_POWResourcesInformation: return DataType.ScsiMmcPowResourcesInformation; + case MediaTagType.SCSI_INQUIRY: return DataType.ScsiInquiry; + case MediaTagType.SCSI_MODEPAGE_2A: return DataType.ScsiModePage2A; + case MediaTagType.ATA_IDENTIFY: return DataType.AtaIdentify; + case MediaTagType.ATAPI_IDENTIFY: return DataType.AtapiIdentify; + case MediaTagType.PCMCIA_CIS: return DataType.PcmciaCis; + case MediaTagType.SD_CID: return DataType.SecureDigitalCid; + case MediaTagType.SD_CSD: return DataType.SecureDigitalCsd; + case MediaTagType.SD_SCR: return DataType.SecureDigitalScr; + case MediaTagType.SD_OCR: return DataType.SecureDigitalOcr; + case MediaTagType.MMC_CID: return DataType.MultiMediaCardCid; + case MediaTagType.MMC_CSD: return DataType.MultiMediaCardCsd; + case MediaTagType.MMC_OCR: return DataType.MultiMediaCardOcr; + case MediaTagType.MMC_ExtendedCSD: return DataType.MultiMediaCardExtendedCsd; + case MediaTagType.Xbox_SecuritySector: return DataType.XboxSecuritySector; + case MediaTagType.Floppy_LeadOut: return DataType.FloppyLeadOut; + case MediaTagType.DCB: return DataType.DvdDiscControlBlock; + case MediaTagType.CD_LeadIn: return DataType.CompactDiscLeadIn; + case MediaTagType.CD_LeadOut: return DataType.CompactDiscLeadOut; + case MediaTagType.SCSI_MODESENSE_6: return DataType.ScsiModeSense6; + case MediaTagType.SCSI_MODESENSE_10: return DataType.ScsiModeSense10; + case MediaTagType.USB_Descriptors: return DataType.UsbDescriptors; + case MediaTagType.Xbox_DMI: return DataType.XboxDmi; + case MediaTagType.Xbox_PFI: return DataType.XboxPfi; + default: + throw new ArgumentOutOfRangeException(nameof(tag), tag, null); + } + } + + /// List of known compression types + enum CompressionType : ushort + { + /// Not compressed + None = 0, + /// LZMA + Lzma = 1, + /// FLAC + Flac = 2 + } + + /// List of known data types + enum DataType : ushort + { + /// No data + NoData = 0, + /// User data + UserData = 1, + /// CompactDisc partial Table of Contents + CompactDiscPartialToc = 2, + /// CompactDisc session information + CompactDiscSessionInfo = 3, + /// CompactDisc Table of Contents + CompactDiscToc = 4, + /// CompactDisc Power Management Area + CompactDiscPma = 5, + /// CompactDisc Absolute Time In Pregroove + CompactDiscAtip = 6, + /// CompactDisc Lead-in's CD-Text + CompactDiscLeadInCdText = 7, + /// DVD Physical Format Information + DvdPfi = 8, + /// DVD Lead-in's Copyright Management Information + DvdLeadInCmi = 9, + /// DVD Disc Key + DvdDiscKey = 10, + /// DVD Burst Cutting Area + DvdBca = 11, + /// DVD DMI + DvdDmi = 12, + /// DVD Media Identifier + DvdMediaIdentifier = 13, + /// DVD Media Key Block + DvdMediaKeyBlock = 14, + /// DVD-RAM Disc Definition Structure + DvdRamDds = 15, + /// DVD-RAM Medium Status + DvdRamMediumStatus = 16, + /// DVD-RAM Spare Area Information + DvdRamSpareArea = 17, + /// DVD-R RMD + DvdRRmd = 18, + /// DVD-R Pre-recorded Information + DvdRPrerecordedInfo = 19, + /// DVD-R Media Identifier + DvdRMediaIdentifier = 20, + /// DVD-R Physical Format Information + DvdRPfi = 21, + /// DVD ADress In Pregroove + DvdAdip = 22, + /// HD DVD Copy Protection Information + HdDvdCpi = 23, + /// HD DVD Medium Status + HdDvdMediumStatus = 24, + /// DVD DL Layer Capacity + DvdDlLayerCapacity = 25, + /// DVD DL Middle Zone Address + DvdDlMiddleZoneAddress = 26, + /// DVD DL Jump Interval Size + DvdDlJumpIntervalSize = 27, + /// DVD DL Manual Layer Jump LBA + DvdDlManualLayerJumpLba = 28, + /// Bluray Disc Information + BlurayDi = 29, + /// Bluray Burst Cutting Area + BlurayBca = 30, + /// Bluray Disc Definition Structure + BlurayDds = 31, + /// Bluray Cartridge Status + BlurayCartridgeStatus = 32, + /// Bluray Spare Area Information + BluraySpareArea = 33, + /// AACS Volume Identifier + AacsVolumeIdentifier = 34, + /// AACS Serial Number + AacsSerialNumber = 35, + /// AACS Media Identifier + AacsMediaIdentifier = 36, + /// AACS Media Key Block + AacsMediaKeyBlock = 37, + /// AACS Data Keys + AacsDataKeys = 38, + /// AACS LBA Extents + AacsLbaExtents = 39, + /// CPRM Media Key Block + CprmMediaKeyBlock = 40, + /// Recognized Layers + HybridRecognizedLayers = 41, + /// MMC Write Protection + ScsiMmcWriteProtection = 42, + /// MMC Disc Information + ScsiMmcDiscInformation = 43, + /// MMC Track Resources Information + ScsiMmcTrackResourcesInformation = 44, + /// MMC POW Resources Information + ScsiMmcPowResourcesInformation = 45, + /// SCSI INQUIRY RESPONSE + ScsiInquiry = 46, + /// SCSI MODE PAGE 2Ah + ScsiModePage2A = 47, + /// ATA IDENTIFY response + AtaIdentify = 48, + /// ATAPI IDENTIFY response + AtapiIdentify = 49, + /// PCMCIA CIS + PcmciaCis = 50, + /// SecureDigital CID + SecureDigitalCid = 51, + /// SecureDigital CSD + SecureDigitalCsd = 52, + /// SecureDigital SCR + SecureDigitalScr = 53, + /// SecureDigital OCR + SecureDigitalOcr = 54, + /// MultiMediaCard CID + MultiMediaCardCid = 55, + /// MultiMediaCard CSD + MultiMediaCardCsd = 56, + /// MultiMediaCard OCR + MultiMediaCardOcr = 57, + /// MultiMediaCard Extended CSD + MultiMediaCardExtendedCsd = 58, + /// Xbox Security Sector + XboxSecuritySector = 59, + /// Floppy Lead-out + FloppyLeadOut = 60, + /// Dvd Disc Control Block + DvdDiscControlBlock = 61, + /// CompactDisc Lead-in + CompactDiscLeadIn = 62, + /// CompactDisc Lead-out + CompactDiscLeadOut = 63, + /// SCSI MODE SENSE (6) response + ScsiModeSense6 = 64, + /// SCSI MODE SENSE (10) response + ScsiModeSense10 = 65, + /// USB descriptors + UsbDescriptors = 66, + /// Xbox DMI + XboxDmi = 67, + /// Xbox Physical Format Information + XboxPfi = 68, + /// CompactDisc sector prefix (sync, header + CdSectorPrefix = 69, + /// CompactDisc sector suffix (edc, ecc p, ecc q) + CdSectorSuffix = 70, + /// CompactDisc subchannel + CdSectorSubchannel = 71, + /// Apple Profile (20 byte) tag + AppleProfileTag = 72, + /// Apple Sony (12 byte) tag + AppleSonyTag = 73, + /// Priam Data Tower (24 byte) tag + PriamDataTowerTag = 74 + } + + /// List of known blocks types + enum BlockType : uint + { + /// Block containing data + DataBlock = 0x4B4C4244, + /// Block containing a deduplication table + DeDuplicationTable = 0X2A544444, + /// Block containing the index + Index = 0X58444E49, + /// Block containing logical geometry + GeometryBlock = 0x4D4F4547, + /// Block containing metadata + MetadataBlock = 0x4154454D, + /// Block containing optical disc tracks + TracksBlock = 0x534B5254, + /// TODO: Block containing CICM XML metadata + CicmBlock = 0x4D434943, + /// TODO: Block containing contents checksums + ChecksumBlock = 0x4D534B43, + /// TODO: Block containing data position measurements + DataPositionMeasurementBlock = 0x2A4D5044, + /// TODO: Block containing a snapshot index + SnapshotBlock = 0x50414E53, + /// TODO: Block containing how to locate the parent image + ParentBlock = 0x50524E54, + /// TODO: Block containing an array of hardware used to create the image + DumpHardwareBlock = 0x2A504D44, + /// TODO: Block containing list of files for a tape image + TapeFileBlock = 0x454C4654 + } + + /// Header, at start of file + [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)] + struct DicHeader + { + /// Header identifier, + public ulong identifier; + /// UTF-16LE name of the application that created the image + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string application; + /// Image format major version. A new major version means a possibly incompatible change of format + public byte imageMajorVersion; + /// Image format minor version. A new minor version indicates a compatible change of format + public byte imageMinorVersion; + /// Major version of the application that created the image + public byte applicationMajorVersion; + /// Minor version of the application that created the image + public byte applicationMinorVersion; + /// Type of media contained on image + public MediaType mediaType; + /// Offset to index + public ulong indexOffset; + /// Windows filetime (100 nanoseconds since 1601/01/01 00:00:00 UTC) of image creation time + public long creationTime; + /// Windows filetime (100 nanoseconds since 1601/01/01 00:00:00 UTC) of image last written time + public long lastWrittenTime; + } + + /// Header for a deduplication table. Table follows it + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct DdtHeader + { + /// Identifier, + public BlockType identifier; + /// Type of data pointed by this DDT + public DataType type; + /// Compression algorithm used to compress the DDT + public CompressionType compression; + /// Each entry is ((byte offset in file) << shift) + (sector offset in block) + public byte shift; + /// How many entries are in the table + public ulong entries; + /// Compressed length for the DDT + public ulong cmpLength; + /// Uncompressed length for the DDT + public ulong length; + /// CRC64-ECMA of the compressed DDT + public ulong cmpCrc64; + /// CRC64-ECMA of the uncompressed DDT + public ulong crc64; + } + + /// Header for the index, followed by entries + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct IndexHeader + { + /// Identifier, + public BlockType identifier; + /// How many entries follow this header + public ushort entries; + /// CRC64-ECMA of the index + public ulong crc64; + } + + /// Index entry + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct IndexEntry + { + /// Type of item pointed by this entry + public BlockType blockType; + /// Type of data contained by the block pointed by this entry + public DataType dataType; + /// Offset in file where item is stored + public ulong offset; + } + + /// Block header, precedes block data + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct BlockHeader + { + /// Identifier, + public BlockType identifier; + /// Type of data contained by this block + public DataType type; + /// Compression algorithm used to compress the block + public CompressionType compression; + /// Size in bytes of each sector contained in this block + public uint sectorSize; + /// Compressed length for the block + public uint cmpLength; + /// Uncompressed length for the block + public uint length; + /// CRC64-ECMA of the compressed block + public ulong cmpCrc64; + /// CRC64-ECMA of the uncompressed block + public ulong crc64; + } + + /// Geometry block, contains physical geometry information + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct GeometryBlock + { + /// Identifier, + public BlockType identifier; + public uint cylinders; + public uint heads; + public uint sectorsPerTrack; + } + + /// Metadata block, contains metadata + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct MetadataBlock + { + /// Identifier, + public BlockType identifier; + /// Size in bytes of this whole metadata block + public uint blockSize; + /// Sequence of media set this media belongs to + public int mediaSequence; + /// Total number of media on the media set this media belongs to + public int lastMediaSequence; + /// Offset to start of creator string from start of this block + public uint creatorOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint creatorLength; + /// Offset to start of creator string from start of this block + public uint commentsOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint commentsLength; + /// Offset to start of creator string from start of this block + public uint mediaTitleOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint mediaTitleLength; + /// Offset to start of creator string from start of this block + public uint mediaManufacturerOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint mediaManufacturerLength; + /// Offset to start of creator string from start of this block + public uint mediaModelOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint mediaModelLength; + /// Offset to start of creator string from start of this block + public uint mediaSerialNumberOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint mediaSerialNumberLength; + /// Offset to start of creator string from start of this block + public uint mediaBarcodeOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint mediaBarcodeLength; + /// Offset to start of creator string from start of this block + public uint mediaPartNumberOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint mediaPartNumberLength; + /// Offset to start of creator string from start of this block + public uint driveManufacturerOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint driveManufacturerLength; + /// Offset to start of creator string from start of this block + public uint driveModelOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint driveModelLength; + /// Offset to start of creator string from start of this block + public uint driveSerialNumberOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint driveSerialNumberLength; + /// Offset to start of creator string from start of this block + public uint driveFirmwareRevisionOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint driveFirmwareRevisionLength; + } + + /// Contains list of optical disc tracks + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct TracksHeader + { + /// Identifier, + public BlockType identifier; + /// How many entries follow this header + public ushort entries; + /// CRC64-ECMA of the block + public ulong crc64; + } + + /// Optical disc track + [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)] + struct TrackEntry + { + /// Track sequence + public byte sequence; + /// Track type + public TrackType type; + /// Track starting LBA + public long start; + /// Track last LBA + public long end; + /// Track pregap in sectors + public long pregap; + /// Track session + public byte session; + /// Track's ISRC in ASCII + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 13)] + public string isrc; + /// Track flags + public byte flags; + } + } +} \ No newline at end of file diff --git a/DiscImageChef.DiscImages/Enums.cs b/DiscImageChef.DiscImages/Enums.cs index 3eaa33db..5a8826b5 100644 --- a/DiscImageChef.DiscImages/Enums.cs +++ b/DiscImageChef.DiscImages/Enums.cs @@ -37,20 +37,20 @@ namespace DiscImageChef.DiscImages /// /// Track (as partitioning element) types. /// - public enum TrackType + public enum TrackType : byte { /// Audio track - Audio, + Audio = 0, /// Data track (not any of the below defined ones) - Data, + Data = 1, /// Data track, compact disc mode 1 - CdMode1, + CdMode1 = 2, /// Data track, compact disc mode 2, formless - CdMode2Formless, + CdMode2Formless = 3, /// Data track, compact disc mode 2, form 1 - CdMode2Form1, + CdMode2Form1 = 4, /// Data track, compact disc mode 2, form 2 - CdMode2Form2 + CdMode2Form2 = 5 } /// diff --git a/DiscImageChef.Filesystems/CPM/Info.cs b/DiscImageChef.Filesystems/CPM/Info.cs index 9241de3e..7e2342ae 100644 --- a/DiscImageChef.Filesystems/CPM/Info.cs +++ b/DiscImageChef.Filesystems/CPM/Info.cs @@ -98,16 +98,11 @@ namespace DiscImageChef.Filesystems.CPM case MediaType.NEC_525_HD: case MediaType.NEC_35_HD_8: case MediaType.NEC_35_HD_15: - case MediaType.SHARP_525: case MediaType.SHARP_525_9: - case MediaType.SHARP_35: case MediaType.SHARP_35_9: case MediaType.ECMA_99_8: case MediaType.ECMA_99_15: case MediaType.ECMA_99_26: - case MediaType.ECMA_100: - case MediaType.ECMA_125: - case MediaType.ECMA_147: case MediaType.ECMA_54: case MediaType.ECMA_59: case MediaType.ECMA_66: diff --git a/DiscImageChef.Metadata/Dimensions.cs b/DiscImageChef.Metadata/Dimensions.cs index 6daca6e1..bc88491a 100644 --- a/DiscImageChef.Metadata/Dimensions.cs +++ b/DiscImageChef.Metadata/Dimensions.cs @@ -75,7 +75,6 @@ namespace DiscImageChef.Metadata case CommonTypes.MediaType.ECMA_99_26: case CommonTypes.MediaType.FDFORMAT_525_DD: case CommonTypes.MediaType.FDFORMAT_525_HD: - case CommonTypes.MediaType.SHARP_525: // According to ECMA-99 et al dmns.Height = 133.3; dmns.HeightSpecified = true; @@ -101,14 +100,10 @@ namespace DiscImageChef.Metadata case CommonTypes.MediaType.CBM_35_DD: case CommonTypes.MediaType.CBM_AMIGA_35_DD: case CommonTypes.MediaType.CBM_AMIGA_35_HD: - case CommonTypes.MediaType.ECMA_100: - case CommonTypes.MediaType.ECMA_125: - case CommonTypes.MediaType.ECMA_147: case CommonTypes.MediaType.FDFORMAT_35_DD: case CommonTypes.MediaType.FDFORMAT_35_HD: case CommonTypes.MediaType.NEC_35_HD_8: case CommonTypes.MediaType.NEC_35_HD_15: - case CommonTypes.MediaType.SHARP_35: case CommonTypes.MediaType.Floptical: case CommonTypes.MediaType.HiFD: case CommonTypes.MediaType.UHD144: diff --git a/DiscImageChef.Metadata/MediaType.cs b/DiscImageChef.Metadata/MediaType.cs index 710330e2..01c79790 100644 --- a/DiscImageChef.Metadata/MediaType.cs +++ b/DiscImageChef.Metadata/MediaType.cs @@ -603,14 +603,6 @@ namespace DiscImageChef.Metadata discType = "3.5\" floppy"; discSubType = "NEC triple-density"; break; - case CommonTypes.MediaType.SHARP_525: - discType = "5.25\" floppy"; - discSubType = "Sharp"; - break; - case CommonTypes.MediaType.SHARP_35: - discType = "3.5\" floppy"; - discSubType = "Sharp"; - break; case CommonTypes.MediaType.SHARP_525_9: discType = "5.25\" floppy"; discSubType = "Sharp (9 sectors per track)"; @@ -652,18 +644,6 @@ namespace DiscImageChef.Metadata discType = "5.25\" floppy"; discSubType = "ECMA-99"; break; - case CommonTypes.MediaType.ECMA_100: - discType = "3.5\" floppy"; - discSubType = "ECMA-99"; - break; - case CommonTypes.MediaType.ECMA_125: - discType = "3.5\" floppy"; - discSubType = "ECMA-125"; - break; - case CommonTypes.MediaType.ECMA_147: - discType = "3.5\" floppy"; - discSubType = "ECMA-147"; - break; case CommonTypes.MediaType.FDFORMAT_525_DD: discType = "5.25\" floppy"; discSubType = "FDFORMAT double-density"; diff --git a/cuetoolsnet b/cuetoolsnet new file mode 160000 index 00000000..42adab01 --- /dev/null +++ b/cuetoolsnet @@ -0,0 +1 @@ +Subproject commit 42adab0138833031c3b564ec002267da568d04b2 diff --git a/templates/dicformat.bt b/templates/dicformat.bt new file mode 100644 index 00000000..cf34e2f9 --- /dev/null +++ b/templates/dicformat.bt @@ -0,0 +1,853 @@ +//------------------------------------------------ +//--- 010 Editor v8.0.1 Binary Template +// +// File: dicformat.bt +// Authors: Natalia Portillo +// Version: 1.0 +// Purpose: DiscImageChef format +// Category: Misc +// File Mask: *.dicf +// ID Bytes: 44 49 43 4D 46 52 4D 54 // DICMFRMT +// History: +// 1.0 2018-01-26 Natalia Portillo: Initial release +//------------------------------------------------ + +#define DIC_MAGIC 0x544D52464D434944 + +enum MediaType +{ + Unknown = 0, + UnknownMO = 1, + GENERIC_HDD = 2, + Microdrive = 3, + Zone_HDD = 4, + FlashDrive = 5, + + CD = 10, + CDDA = 11, + CDG = 12, + CDEG = 13, + CDI = 14, + CDROM = 15, + CDROMXA = 16, + CDPLUS = 17, + CDMO = 18, + CDR = 19, + CDRW = 20, + CDMRW = 21, + VCD = 22, + SVCD = 23, + PCD = 24, + SACD = 25, + DDCD = 26, + DDCDR = 27, + DDCDRW = 28, + DTSCD = 29, + CDMIDI = 30, + CDV = 31, + PD650 = 32, + PD650_WORM = 33, + + DVDROM = 40, + DVDR = 41, + DVDRW = 42, + DVDPR = 43, + DVDPRW = 44, + DVDPRWDL = 45, + DVDRDL = 46, + DVDPRDL = 47, + DVDRAM = 48, + DVDRWDL = 49, + DVDDownload = 50, + + HDDVDROM = 51, + HDDVDRAM = 52, + HDDVDR = 53, + HDDVDRW = 54, + HDDVDRDL = 55, + HDDVDRWDL = 56, + + BDROM = 60, + BDR = 61, + BDRE = 62, + BDRXL = 63, + BDREXL = 64, + + EVD = 70, + FVD = 71, + HVD = 72, + CBHD = 73, + HDVMD = 74, + VCDHD = 75, + SVOD = 76, + FDDVD = 77, + + LD = 80, + LDROM = 81, + LDROM2 = 82, + LVROM = 83, + MegaLD = 84, + + HiMD = 90, + MD = 91, + MDData = 92, + MDData2 = 93, + + UDO = 100, + UDO2 = 101, + UDO2_WORM = 102, + + PlayStationMemoryCard = 110, + PlayStationMemoryCard2 = 111, + PS1CD = 112, + PS2CD = 113, + PS2DVD = 114, + PS3DVD = 115, + PS3BD = 116, + PS4BD = 117, + UMD = 118, + + XGD = 130, + XGD2 = 131, + XGD3 = 132, + XGD4 = 133, + + MEGACD = 150, + SATURNCD = 151, + GDROM = 152, + GDR = 153, + SegaCard = 154, + + HuCard = 170, + SuperCDROM2 = 171, + JaguarCD = 172, + ThreeDO = 173, + + Apple32SS = 180, + Apple32DS = 181, + Apple33SS = 182, + Apple33DS = 183, + AppleSonySS = 184, + AppleSonyDS = 185, + AppleFileWare = 186, + + DOS_525_SS_DD_8 = 190, + DOS_525_SS_DD_9 = 191, + DOS_525_DS_DD_8 = 192, + DOS_525_DS_DD_9 = 193, + DOS_525_HD = 194, + DOS_35_SS_DD_8 = 195, + DOS_35_SS_DD_9 = 196, + DOS_35_DS_DD_8 = 197, + DOS_35_DS_DD_9 = 198, + DOS_35_HD = 199, + DOS_35_ED = 200, + DMF = 201, + DMF_82 = 202, + XDF_525 = 203, + XDF_35 = 204, + + IBM23FD = 210, + IBM33FD_128 = 211, + IBM33FD_256 = 212, + IBM33FD_512 = 213, + IBM43FD_128 = 214, + IBM43FD_256 = 215, + IBM53FD_256 = 216, + IBM53FD_512 = 217, + IBM53FD_1024 = 218, + + RX01 = 220, + RX02 = 221, + RX03 = 222, + RX50 = 223, + + ACORN_525_SS_SD_40 = 230, + ACORN_525_SS_SD_80 = 231, + ACORN_525_SS_DD_40 = 232, + ACORN_525_SS_DD_80 = 233, + ACORN_525_DS_DD = 234, + ACORN_35_DS_DD = 235, + ACORN_35_DS_HD = 236, + + ATARI_525_SD = 240, + ATARI_525_ED = 241, + ATARI_525_DD = 242, + ATARI_35_SS_DD = 243, + ATARI_35_DS_DD = 244, + ATARI_35_SS_DD_11 = 245, + ATARI_35_DS_DD_11 = 246, + + CBM_35_DD = 250, + CBM_AMIGA_35_DD = 251, + CBM_AMIGA_35_HD = 252, + CBM_1540 = 253, + CBM_1540_Ext = 254, + CBM_1571 = 255, + + NEC_8_SD = 260, + NEC_8_DD = 261, + NEC_525_SS = 262, + NEC_525_DS = 263, + NEC_525_HD = 264, + NEC_35_HD_8 = 265, + NEC_35_HD_15 = 266, + NEC_35_TD = 267, + SHARP_525 = NEC_525_HD, + SHARP_525_9 = 268, + SHARP_35 = NEC_35_HD_8, + SHARP_35_9 = 269, + + ECMA_99_8 = 270, + ECMA_99_15 = 271, + ECMA_99_26 = 272, + ECMA_100 = DOS_35_DS_DD_9, + ECMA_125 = DOS_35_HD, + ECMA_147 = DOS_35_ED, + ECMA_54 = 273, + ECMA_59 = 274, + ECMA_66 = 275, + ECMA_69_8 = 276, + ECMA_69_15 = 277, + ECMA_69_26 = 278, + ECMA_70 = 279, + ECMA_78 = 280, + ECMA_78_2 = 281, + + FDFORMAT_525_DD = 290, + FDFORMAT_525_HD = 291, + FDFORMAT_35_DD = 292, + FDFORMAT_35_HD = 293, + + Apricot_35 = 309, + + ADR2120 = 310, + ADR260 = 311, + ADR30 = 312, + ADR50 = 313, + + AIT1 = 320, + AIT1Turbo = 321, + AIT2 = 322, + AIT2Turbo = 323, + AIT3 = 324, + AIT3Ex = 325, + AIT3Turbo = 326, + AIT4 = 327, + AIT5 = 328, + AITETurbo = 329, + SAIT1 = 330, + SAIT2 = 331, + + Bernoulli = 340, + Bernoulli2 = 341, + Ditto = 342, + DittoMax = 343, + Jaz = 344, + Jaz2 = 345, + PocketZip = 346, + REV120 = 347, + REV35 = 348, + REV70 = 349, + ZIP100 = 350, + ZIP250 = 351, + ZIP750 = 352, + + CompactCassette = 360, + Data8 = 361, + MiniDV = 362, + + CFast = 370, + CompactFlash = 371, + CompactFlashType2 = 372, + + DigitalAudioTape = 380, + DAT160 = 381, + DAT320 = 382, + DAT72 = 383, + DDS1 = 384, + DDS2 = 385, + DDS3 = 386, + DDS4 = 387, + + CompactTapeI = 390, + CompactTapeII = 391, + DECtapeII = 392, + DLTtapeIII = 393, + DLTtapeIIIxt = 394, + DLTtapeIV = 395, + DLTtapeS4 = 396, + SDLT1 = 397, + SDLT2 = 398, + VStapeI = 399, + + Exatape15m = 400, + Exatape22m = 401, + Exatape22mAME = 402, + Exatape28m = 403, + Exatape40m = 404, + Exatape45m = 405, + Exatape54m = 406, + Exatape75m = 407, + Exatape76m = 408, + Exatape80m = 409, + Exatape106m = 410, + Exatape160mXL = 411, + Exatape112m = 412, + Exatape125m = 413, + Exatape150m = 414, + Exatape170m = 415, + Exatape225m = 416, + + ExpressCard34 = 420, + ExpressCard54 = 421, + PCCardTypeI = 422, + PCCardTypeII = 423, + PCCardTypeIII = 424, + PCCardTypeIV = 425, + + EZ135 = 430, + EZ230 = 431, + Quest = 432, + SparQ = 433, + SQ100 = 434, + SQ200 = 435, + SQ300 = 436, + SQ310 = 437, + SQ327 = 438, + SQ400 = 439, + SQ800 = 440, + SQ1500 = 441, + SQ2000 = 442, + SyJet = 443, + + FamicomGamePak = 450, + GameBoyAdvanceGamePak = 451, + GameBoyGamePak = 452, + GOD = 453, + N64DD = 454, + N64GamePak = 455, + NESGamePak = 456, + Nintendo3DSGameCard = 457, + NintendoDiskCard = 458, + NintendoDSGameCard = 459, + NintendoDSiGameCard = 460, + SNESGamePak = 461, + SNESGamePakUS = 462, + WOD = 463, + WUOD = 464, + SwitchGameCard = 465, + + IBM3470 = 470, + IBM3480 = 471, + IBM3490 = 472, + IBM3490E = 473, + IBM3592 = 474, + + LTO = 480, + LTO2 = 481, + LTO3 = 482, + LTO3WORM = 483, + LTO4 = 484, + LTO4WORM = 485, + LTO5 = 486, + LTO5WORM = 487, + LTO6 = 488, + LTO6WORM = 489, + LTO7 = 490, + LTO7WORM = 491, + + MemoryStick = 510, + MemoryStickDuo = 511, + MemoryStickMicro = 512, + MemoryStickPro = 513, + MemoryStickProDuo = 514, + + microSD = 520, + miniSD = 521, + SecureDigital = 522, + + MMC = 530, + MMCmicro = 531, + RSMMC = 532, + MMCplus = 533, + MMCmobile = 534, + + MLR1 = 540, + MLR1SL = 541, + MLR3 = 542, + SLR1 = 543, + SLR2 = 544, + SLR3 = 545, + SLR32 = 546, + SLR32SL = 547, + SLR4 = 548, + SLR5 = 549, + SLR5SL = 550, + SLR6 = 551, + SLRtape7 = 552, + SLRtape7SL = 553, + SLRtape24 = 554, + SLRtape24SL = 555, + SLRtape40 = 556, + SLRtape50 = 557, + SLRtape60 = 558, + SLRtape75 = 559, + SLRtape100 = 560, + SLRtape140 = 561, + + QIC11 = 570, + QIC120 = 571, + QIC1350 = 572, + QIC150 = 573, + QIC24 = 574, + QIC3010 = 575, + QIC3020 = 576, + QIC3080 = 577, + QIC3095 = 578, + QIC320 = 579, + QIC40 = 580, + QIC525 = 581, + QIC80 = 582, + + STK4480 = 590, + STK4490 = 591, + STK9490 = 592, + T9840A = 593, + T9840B = 594, + T9840C = 595, + T9840D = 596, + T9940A = 597, + T9940B = 598, + T10000A = 599, + T10000B = 600, + T10000C = 601, + T10000D = 602, + + Travan = 610, + Travan1Ex = 611, + Travan3 = 612, + Travan3Ex = 613, + Travan4 = 614, + Travan5 = 615, + Travan7 = 616, + + VXA1 = 620, + VXA2 = 621, + VXA3 = 622, + + ECMA_153 = 630, + ECMA_153_512 = 631, + ECMA_154 = 632, + ECMA_183_512 = 633, + ECMA_183 = 634, + ECMA_184_512 = 635, + ECMA_184 = 636, + ECMA_189 = 637, + ECMA_190 = 638, + ECMA_195 = 639, + ECMA_195_512 = 640, + ECMA_201 = 641, + ECMA_201_ROM = 642, + ECMA_223 = 643, + ECMA_223_512 = 644, + ECMA_238 = 645, + ECMA_239 = 646, + ECMA_260 = 647, + ECMA_260_Double = 648, + ECMA_280 = 649, + ECMA_317 = 650, + ECMA_322 = 651, + ECMA_322_2k = 652, + GigaMo = 653, + GigaMo2 = 654, + + CompactFloppy = 660, + DemiDiskette = 661, + Floptical = 662, + HiFD = 663, + QuickDisk = 664, + UHD144 = 665, + VideoFloppy = 666, + Wafer = 667, + ZXMicrodrive = 668, + + BeeCard = 670, + Borsu = 671, + DataStore = 672, + DIR = 673, + DST = 674, + DTF = 675, + DTF2 = 676, + Flextra3020 = 677, + Flextra3225 = 678, + HiTC1 = 679, + HiTC2 = 680, + LT1 = 681, + MiniCard = 872, + Orb = 683, + Orb5 = 684, + SmartMedia = 685, + xD = 686, + XQD = 687, + DataPlay = 688, + + AppleProfile = 690, + AppleWidget = 691, + AppleHD20 = 692, + PriamDataTower = 693, + + RA60 = 700, + RA80 = 701, + RA81 = 702, + RC25 = 703, + RD31 = 704, + RD32 = 705, + RD51 = 706, + RD52 = 707, + RD53 = 708, + RD54 = 709, + RK06 = 710, + RK06_18 = 711, + RK07 = 712, + RK07_18 = 713, + RM02 = 714, + RM03 = 715, + RM05 = 716, + RP02 = 717, + RP02_18 = 718, + RP03 = 719, + RP03_18 = 720, + RP04 = 721, + RP04_18 = 722, + RP05 = 723, + RP05_18 = 724, + RP06 = 725, + RP06_18 = 726, + + LS120 = 730, + LS240 = 731, + FD32MB = 732, + RDX = 733, + RDX320 = 734, +}; + +enum CompressionType +{ + None = 0, + Lzma = 1, + Flac = 2 +}; + +enum DataType +{ + NoData = 0, + UserData = 1, + CompactDiscPartialToc = 2, + CompactDiscSessionInfo = 3, + CompactDiscToc = 4, + CompactDiscPma = 5, + CompactDiscAtip = 6, + CompactDiscLeadInCdText = 7, + DvdPfi = 8, + DvdLeadInCmi = 9, + DvdDiscKey = 10, + DvdBca = 11, + DvdDmi = 12, + DvdMediaIdentifier = 13, + DvdMediaKeyBlock = 14, + DvdRamDds = 15, + DvdRamMediumStatus = 16, + DvdRamSpareArea = 17, + DvdRRmd = 18, + DvdRPrerecordedInfo = 19, + DvdRMediaIdentifier = 20, + DvdRPfi = 21, + DvdAdip = 22, + HdDvdCpi = 23, + HdDvdMediumStatus = 24, + DvdDlLayerCapacity = 25, + DvdDlMiddleZoneAddress = 26, + DvdDlJumpIntervalSize = 27, + DvdDlManualLayerJumpLba = 28, + BlurayDi = 29, + BlurayBca = 30, + BlurayDds = 31, + BlurayCartridgeStatus = 32, + BluraySpareArea = 33, + AacsVolumeIdentifier = 34, + AacsSerialNumber = 35, + AacsMediaIdentifier = 36, + AacsMediaKeyBlock = 37, + AacsDataKeys = 38, + AacsLbaExtents = 39, + CprmMediaKeyBlock = 40, + HybridRecognizedLayers = 41, + ScsiMmcWriteProtection = 42, + ScsiMmcDiscInformation = 43, + ScsiMmcTrackResourcesInformation = 44, + ScsiMmcPowResourcesInformation = 45, + ScsiInquiry = 46, + ScsiModePage2A = 47, + AtaIdentify = 48, + AtapiIdentify = 49, + PcmciaCis = 50, + SecureDigitalCid = 51, + SecureDigitalCsd = 52, + SecureDigitalScr = 53, + SecureDigitalOcr = 54, + MultiMediaCardCid = 55, + MultiMediaCardCsd = 56, + MultiMediaCardOcr = 57, + MultiMediaCardExtendedCsd = 58, + XboxSecuritySector = 59, + FloppyLeadOut = 60, + DvdDiscControlBlock = 61, + CompactDiscLeadIn = 62, + CompactDiscLeadOut = 63, + ScsiModeSense6 = 64, + ScsiModeSense10 = 65, + UsbDescriptors = 66, + XboxDmi = 67, + XboxPfi = 68, + CdSectorPrefix = 69, + CdSectorSuffix = 70, + CdSectorSubchannel = 71, + AppleProfileTag = 72, + AppleSonyTag = 73, + PriamDataTowerTag = 74 +}; + +enum BlockType +{ + DataBlock = 0x4B4C4244, + DeDuplicationTable = 0X2A544444, + Index = 0X58444E49, + GeometryBlock = 0x4D4F4547, + MetadataBlock = 0x4154454D, + TracksBlock = 0x534B5254, + CicmBlock = 0x4D434943, + ChecksumBlock = 0x4D534B43, + DataPositionMeasurementBlock = 0x2A4D5044, + SnapshotBlock = 0x50414E53, + ParentBlock = 0x50524E54, + DumpHardwareBlock = 0x2A504D44, + TapeFileBlock = 0x454C4654 +}; + +enum TrackType +{ + Audio = 0, + Data = 1, + CdMode1 = 2, + CdMode2Formless = 3, + CdMode2Form1 = 4, + CdMode2Form2 = 5 +}; + +typedef struct +{ + char identifier[8]; + wchar_t application[32]; + byte imageMajorVersion; + byte imageMinorVersion; + byte applicationMajorVersion; + byte applicationMinorVersion; + MediaType mediaType; + uint64 indexOffset; + FILETIME creationTime; + FILETIME lastWrittenTime; +} DicHeader; + +typedef struct +{ + BlockType identifier; + DataType type; + CompressionType compression; + byte shift; + uint64 entries; + uint64 cmpLength; + uint64 length; + uint64 cmpCrc64 ; + uint64 crc64 ; +} DdtHeader; + +typedef struct +{ + BlockType blockType; + DataType dataType; + uint64 offset; +} IndexEntry; + +typedef struct +{ + BlockType identifier; + ushort entries; + uint64 crc64 ; + IndexEntry items[entries]; +} Index; + +typedef struct +{ + BlockType identifier; + DataType type; + CompressionType compression; + uint sectorSize; + uint cmpLength; + uint length; + uint64 cmpCrc64 ; + uint64 crc64 ; +} BlockHeader; + +typedef struct +{ + BlockType identifier; + uint cylinders; + uint heads; + uint sectorsPerTrack; +} GeometryBlock; + +typedef struct +{ + BlockType identifier; + uint blockSize; + int mediaSequence; + int lastMediaSequence; + uint creatorOffset; + uint creatorLength; + uint commentsOffset; + uint commentsLength; + uint mediaTitleOffset; + uint mediaTitleLength; + uint mediaManufacturerOffset; + uint mediaManufacturerLength; + uint mediaModelOffset; + uint mediaModelLength; + uint mediaSerialNumberOffset; + uint mediaSerialNumberLength; + uint mediaBarcodeOffset; + uint mediaBarcodeLength; + uint mediaPartNumberOffset; + uint mediaPartNumberLength; + uint driveManufacturerOffset; + uint driveManufacturerLength; + uint driveModelOffset; + uint driveModelLength; + uint driveSerialNumberOffset; + uint driveSerialNumberLength; + uint driveFirmwareRevisionOffset; + uint driveFirmwareRevisionLength; +} MetadataBlock; + +typedef struct +{ + byte sequence; + TrackType type; + long start; + long end; + long pregap; + byte session; + char isrc[13]; + byte flags; +} TrackEntry; + +typedef struct +{ + BlockType identifier; + ushort entries; + uint64 crc64; + TrackEntry tracks[entries]; +} TracksHeader; + +LittleEndian(); + +local int i; + +Assert(ReadUInt64() == DIC_MAGIC, "Incorrect signature!"); + +DicHeader header; + +FSeek(header.indexOffset); +Index index; +for(i = 0; i < index.entries; i++) +{ + FSeek(index.items[i].offset); + switch(index.items[i].blockType) + { + case 0x4B4C4244: + BlockHeader dataBlock; + break; + case 0x2A544444: + DdtHeader deduplicationTable; + break; + case 0x4D4F4547: + GeometryBlock geometry; + break; + case 0x4154454D: + MetadataBlock metadata; + if(metadata.creatorOffset > 0) + { + FSeek(index.items[i].offset + metadata.creatorOffset); + wchar_t Creator[metadata.creatorLength / 2]; + } + if(metadata.commentsOffset > 0) + { + FSeek(index.items[i].offset + metadata.commentsOffset); + wchar_t Comments[metadata.commentsLength / 2]; + } + if(metadata.mediaTitleOffset > 0) + { + FSeek(index.items[i].offset + metadata.mediaTitleOffset); + wchar_t MediaTitle[metadata.mediaTitleLength / 2]; + } + if(metadata.mediaManufacturerOffset > 0) + { + FSeek(index.items[i].offset + metadata.mediaManufacturerOffset); + wchar_t MediaManufacturer[metadata.mediaManufacturerLength / 2]; + } + if(metadata.mediaModelOffset > 0) + { + FSeek(index.items[i].offset + metadata.mediaModelOffset); + wchar_t MediaModel[metadata.mediaModelLength / 2]; + } + if(metadata.mediaSerialNumberOffset > 0) + { + FSeek(index.items[i].offset + metadata.mediaSerialNumberOffset); + wchar_t MediaSerialNumber[metadata.mediaSerialNumberLength / 2]; + } + if(metadata.mediaBarcodeOffset > 0) + { + FSeek(index.items[i].offset + metadata.mediaBarcodeOffset); + wchar_t MediaBarcode[metadata.mediaBarcodeLength / 2]; + } + if(metadata.mediaPartNumberOffset > 0) + { + FSeek(index.items[i].offset + metadata.mediaPartNumberOffset); + wchar_t MediaPartNumber[metadata.mediaPartNumberLength / 2]; + } + if(metadata.driveManufacturerOffset > 0) + { + FSeek(index.items[i].offset + metadata.driveManufacturerOffset); + wchar_t DriveManufacturer[metadata.driveManufacturerLength / 2]; + } + if(metadata.driveModelOffset > 0) + { + FSeek(index.items[i].offset + metadata.driveModelOffset); + wchar_t DriveModel[metadata.driveModelLength / 2]; + } + if(metadata.driveSerialNumberOffset > 0) + { + FSeek(index.items[i].offset + metadata.driveSerialNumberOffset); + wchar_t DriveSerialNumber[metadata.driveSerialNumberLength / 2]; + } + if(metadata.driveFirmwareRevisionOffset > 0) + { + FSeek(index.items[i].offset + metadata.driveFirmwareRevisionOffset); + wchar_t DriveFirmwareRevision[metadata.driveFirmwareRevisionLength / 2]; + } + break; + case 0x534B5254: + TracksHeader tracks; + break; + } +} \ No newline at end of file