From f9d5028cf0a7194d259763d6b6973a725dc084ce Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Tue, 23 Jan 2018 15:49:49 +0000 Subject: [PATCH 01/20] Add numeric values to media types. --- DiscImageChef.CommonTypes/MediaType.cs | 1190 +++++++++++----------- DiscImageChef.Core/Sidecar/BlockMedia.cs | 5 - DiscImageChef.Filesystems/CPM/Info.cs | 5 - DiscImageChef.Metadata/Dimensions.cs | 5 - DiscImageChef.Metadata/MediaType.cs | 20 - 5 files changed, 594 insertions(+), 631 deletions(-) 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.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.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"; From ef73b7b43e5fba579c552a3ef89050a7dc95c381 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Tue, 23 Jan 2018 15:50:19 +0000 Subject: [PATCH 02/20] Add writing support for DiscImageChef format. --- .../.idea/contentModel.xml | 1 + .../DiscImageChef.DiscImages.csproj | 1 + DiscImageChef.DiscImages/DiscImageChef.cs | 1077 +++++++++++++++++ 3 files changed, 1079 insertions(+) create mode 100644 DiscImageChef.DiscImages/DiscImageChef.cs diff --git a/.idea/.idea.DiscImageChef/.idea/contentModel.xml b/.idea/.idea.DiscImageChef/.idea/contentModel.xml index a21793c4..0dd37636 100644 --- a/.idea/.idea.DiscImageChef/.idea/contentModel.xml +++ b/.idea/.idea.DiscImageChef/.idea/contentModel.xml @@ -423,6 +423,7 @@ + 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..d83d1ee6 --- /dev/null +++ b/DiscImageChef.DiscImages/DiscImageChef.cs @@ -0,0 +1,1077 @@ +// /*************************************************************************** +// 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. + Streaming tapes contain a file block that describes the files and an optional partition block that describes the tape + partitions. + + 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. 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. +*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using DiscImageChef.Checksums; +using DiscImageChef.CommonTypes; +using DiscImageChef.Console; +using DiscImageChef.Filters; +using SharpCompress.Compressors.LZMA; + +namespace DiscImageChef.DiscImages +{ + // TODO: Work in progress + public class DiscImageChef : IWritableImage + { + const ulong DIC_MAGIC = 0x544D464444434944; + MemoryStream blockStream; + SHA256 checksumProvider; + LzmaStream compressedBlockStream; + + Crc64Context crc64; + BlockHeader currentBlockHeader; + Dictionary deduplicationTable; + GeometryBlock geometryBlock; + DicHeader header; + Stream imageStream; + List index; + bool inMemoryDdt; + Dictionary mediaTags; + long outMemoryDdtPosition; + + byte shift; + byte[] structureBytes; + IntPtr structurePointer; + ulong[] userDataDdt; + + public DiscImageChef() + { + Info = 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 { get; private set; } + public string Name => "DiscImageChef format plugin"; + public Guid Id => new Guid("49360069-1784-4A2F-B723-0C844D610B0A"); + public string Format => "DiscImageChef"; + public List Partitions { get; } + public List Tracks { get; private set; } + public List Sessions { get; } + + 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) + { + throw new NotImplementedException(); + } + + 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) + { + throw new NotImplementedException(); + } + + public byte[] ReadSectorTag(ulong sectorAddress, SectorTagType tag) + { + throw new NotImplementedException(); + } + + public byte[] ReadSector(ulong sectorAddress, uint track) + { + throw new NotImplementedException(); + } + + public byte[] ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag) + { + throw new NotImplementedException(); + } + + public byte[] ReadSectors(ulong sectorAddress, uint length) + { + throw new NotImplementedException(); + } + + public byte[] ReadSectorsTag(ulong sectorAddress, uint length, SectorTagType tag) + { + throw new NotImplementedException(); + } + + public byte[] ReadSectors(ulong sectorAddress, uint length, uint track) + { + throw new NotImplementedException(); + } + + public byte[] ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag) + { + throw new NotImplementedException(); + } + + public byte[] ReadSectorLong(ulong sectorAddress) + { + throw new NotImplementedException(); + } + + public byte[] ReadSectorLong(ulong sectorAddress, uint track) + { + throw new NotImplementedException(); + } + + public byte[] ReadSectorsLong(ulong sectorAddress, uint length) + { + throw new NotImplementedException(); + } + + public byte[] ReadSectorsLong(ulong sectorAddress, uint length, uint track) + { + throw new NotImplementedException(); + } + + 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) + { + throw new NotImplementedException(); + } + + public bool? VerifySector(ulong sectorAddress, uint track) + { + throw new NotImplementedException(); + } + + public bool? VerifySectors(ulong sectorAddress, uint length, out List failingLbas, + out List unknownLbas) + { + throw new NotImplementedException(); + } + + public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List failingLbas, + out List unknownLbas) + { + throw new NotImplementedException(); + } + + public bool? VerifyMediaImage() + { + throw new NotImplementedException(); + } + + public IEnumerable SupportedMediaTags => Enum.GetValues(typeof(MediaType)).Cast(); + public IEnumerable SupportedSectorTags => + Enum.GetValues(typeof(MediaType)).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)") + }; + public IEnumerable KnownExtensions => new[] {".dicf"}; + public bool IsWriting { get; private set; } + public string ErrorMessage { get; private set; } + + // TODO: Support resume + public bool Create(string path, MediaType mediaType, Dictionary options, ulong sectors, + uint sectorSize) + { + uint sectorsPerBlock; + + 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; + else sectorsPerBlock = 4096; + + if(!SupportedMediaTypes.Contains(mediaType)) + { + ErrorMessage = $"Unsupport media format {mediaType}"; + return false; + } + + 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); + + Info = new ImageInfo {MediaType = mediaType, SectorSize = sectorSize, Sectors = sectors}; + + 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; + } + + index = new List(); + + // TODO: Set correct version + header = new DicHeader + { + identifier = DIC_MAGIC, + application = "DiscImageChef", + imageMajorVersion = 0, + imageMinorVersion = 0, + applicationMajorVersion = 4, + applicationMinorVersion = 0, + mediaType = mediaType + }; + + // TODO: Settable + inMemoryDdt = sectors <= 256 * 1024 * 1024 / sizeof(ulong); + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "In memory DDT?: {0}", inMemoryDdt); + + imageStream.Write(new byte[Marshal.SizeOf(typeof(DicHeader))], 0, Marshal.SizeOf(typeof(DicHeader))); + + if(inMemoryDdt) userDataDdt = new ulong[sectors]; + 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; + + // TODO: Can be changed to a seek? + imageStream.Write(new byte[sectors * sizeof(ulong)], 0, (int)(sectors * sizeof(ulong))); + } + + mediaTags = new Dictionary(); + checksumProvider = SHA256.Create(); + deduplicationTable = new Dictionary(); + + 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; + } + + // TODO: Resume + 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; + } + + // Close current block first + if(blockStream != null && + (currentBlockHeader.sectorSize != data.Length || + compressedBlockStream.Length / currentBlockHeader.sectorSize == 1 << shift)) + { + currentBlockHeader.length = (uint)compressedBlockStream.Length; + currentBlockHeader.crc64 = BitConverter.ToUInt64(crc64.Final(), 0); + compressedBlockStream.Close(); + currentBlockHeader.cmpLength = (uint)blockStream.Length; + Crc64Context.Data(blockStream.ToArray(), out byte[] cmpCrc64); + currentBlockHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64, 0); + + 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; + imageStream.Write(blockStream.ToArray(), 0, (int)blockStream.Length); + blockStream = null; + } + + // No block set + if(blockStream == null) + { + blockStream = new MemoryStream(); + compressedBlockStream = new LzmaStream(new LzmaEncoderProperties(), false, blockStream); + currentBlockHeader = new BlockHeader + { + identifier = BlockType.DataBlock, + type = DataType.UserData, + compression = CompressionType.Lzma, + sectorSize = (uint)data.Length + }; + crc64 = new Crc64Context(); + crc64.Init(); + } + + long blockOffset = compressedBlockStream.Position / currentBlockHeader.sectorSize; + ulong ddtEntry = (ulong)((imageStream.Position << shift) + blockOffset); + deduplicationTable.Add(hash, ddtEntry); + compressedBlockStream.Write(data, 0, data.Length); + SetDdtEntry(sectorAddress, ddtEntry); + crc64.Update(data); + + ErrorMessage = ""; + return true; + } + + // TODO: Optimize this + 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; + } + + // TODO: Implement + public bool WriteSectorLong(byte[] data, ulong sectorAddress) + { + ErrorMessage = "Writing sectors with tags is not yet implemented."; + return false; + } + + // TODO: Implement + public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length) + { + ErrorMessage = "Writing sectors with tags is not yet implemented."; + return false; + } + + public bool SetTracks(List tracks) + { + 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 = (uint)compressedBlockStream.Length; + currentBlockHeader.crc64 = BitConverter.ToUInt64(crc64.Final(), 0); + compressedBlockStream.Close(); + currentBlockHeader.cmpLength = (uint)blockStream.Length; + Crc64Context.Data(blockStream.ToArray(), out byte[] cmpCrc64); + currentBlockHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64, 0); + + 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; + imageStream.Write(blockStream.ToArray(), 0, (int)blockStream.Length); + blockStream = null; + } + + IndexEntry idxEntry; + + 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(); + compressedBlockStream = new LzmaStream(new LzmaEncoderProperties(), false, blockStream); + compressedBlockStream.Write(mediaTag.Value, 0, mediaTag.Value.Length); + compressedBlockStream.Close(); + byte[] tagData; + + // Not compressible + if(blockStream.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; + tagBlock.cmpCrc64 = BitConverter.ToUInt64(tagCrc, 0); + tagBlock.compression = CompressionType.Lzma; + } + + compressedBlockStream = 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); + imageStream.Write(tagData, 0, tagData.Length); + + index.Add(idxEntry); + } + + 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.Add(idxEntry); + } + + if(inMemoryDdt) + { + idxEntry = new IndexEntry + { + blockType = BlockType.DeDuplicationTable, + dataType = DataType.NoData, + 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(); + compressedBlockStream = new LzmaStream(new 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); + compressedBlockStream.Write(ddtEntry, 0, ddtEntry.Length); + } + + compressedBlockStream.Close(); + ddtHeader.cmpLength = (uint)blockStream.Length; + Crc64Context.Data(blockStream.ToArray(), out byte[] cmpCrc64); + ddtHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64, 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(blockStream.ToArray(), 0, (int)blockStream.Length); + blockStream = null; + compressedBlockStream = null; + + index.Add(idxEntry); + } + + header.indexOffset = (ulong)imageStream.Position; + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Writing index to position {0}", + header.indexOffset); + + blockStream = new MemoryStream(); + + 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) + }; + + 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"); + 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; + } + + // TODO: Implement + public bool SetMetadata(ImageInfo metadata) + { + return true; + } + + public bool SetGeometry(uint cylinders, uint heads, uint sectorsPerTrack) + { + if(!IsWriting) + { + ErrorMessage = "Tried to write on a non-writable image"; + return false; + } + + geometryBlock = new GeometryBlock + { + identifier = BlockType.GeometryBlock, + cylinders = cylinders, + heads = heads, + sectorsPerTrack = sectorsPerTrack + }; + + ErrorMessage = ""; + return true; + } + + // TODO: Implement + public bool WriteSectorTag(byte[] data, ulong sectorAddress, SectorTagType tag) + { + ErrorMessage = "Writing sectors with tags is not yet implemented."; + return false; + } + + // TODO: Implement + public bool WriteSectorsTag(byte[] data, ulong sectorAddress, uint length, SectorTagType tag) + { + ErrorMessage = "Writing sectors with tags is not yet implemented."; + return false; + } + + void SetDdtEntry(ulong sectorAddress, ulong pointer) + { + if(inMemoryDdt) + { + userDataDdt[sectorAddress] = pointer; + return; + } + + long oldPosition = imageStream.Position; + imageStream.Position = outMemoryDdtPosition; + imageStream.Position += (long)(sectorAddress * sizeof(ulong)); + imageStream.Write(BitConverter.GetBytes(pointer), 0, sizeof(ulong)); + imageStream.Position = oldPosition; + } + + 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); + } + } + + enum CompressionType : ushort + { + None = 0, + Lzma = 1 + } + + enum DataType : ushort + { + 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 + } + + enum BlockType : uint + { + DataBlock = 0x484B4C42, + DeDuplicationTable = 0x48544444, + Index = 0x48584449, + GeometryBlock = 0x4D4F4547 + } + + /// Header, at start of file + [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)] + struct DicHeader + { + /// Header identifier, + public ulong identifier; + /// UTF-16 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; + } + + /// 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; + } + } +} \ No newline at end of file From f4180a7e6d952d8e0fdf6e32a1b553a8de0d4f11 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Tue, 23 Jan 2018 20:55:28 +0000 Subject: [PATCH 03/20] Add reading support for DiscImageChef format. --- DiscImageChef.DiscImages/DiscImageChef.cs | 503 ++++++++++++++++++++-- 1 file changed, 474 insertions(+), 29 deletions(-) diff --git a/DiscImageChef.DiscImages/DiscImageChef.cs b/DiscImageChef.DiscImages/DiscImageChef.cs index d83d1ee6..84f66165 100644 --- a/DiscImageChef.DiscImages/DiscImageChef.cs +++ b/DiscImageChef.DiscImages/DiscImageChef.cs @@ -84,30 +84,39 @@ namespace DiscImageChef.DiscImages // TODO: Work in progress public class DiscImageChef : IWritableImage { - const ulong DIC_MAGIC = 0x544D464444434944; - MemoryStream blockStream; - SHA256 checksumProvider; - LzmaStream compressedBlockStream; + const ulong DIC_MAGIC = 0x544D464444434944; + const byte DICF_VERSION = 0; + const uint MAX_CACHE_SIZE = 256 * 1024 * 1024; + const int LZMA_PROPERTIES_LENGTH = 5; + const int MAX_DDT_ENTRY_CACHE = 16000000; + Dictionary blockCache; + Dictionary blockHeaderCache; + MemoryStream blockStream; + SHA256 checksumProvider; + LzmaStream compressedBlockStream; Crc64Context crc64; BlockHeader currentBlockHeader; + uint currentBlockOffset; + uint currentCacheSize; + Dictionary ddtEntryCache; Dictionary deduplicationTable; GeometryBlock geometryBlock; DicHeader header; + ImageInfo imageInfo; Stream imageStream; List index; bool inMemoryDdt; Dictionary mediaTags; long outMemoryDdtPosition; - - byte shift; - byte[] structureBytes; - IntPtr structurePointer; - ulong[] userDataDdt; + byte shift; + byte[] structureBytes; + IntPtr structurePointer; + ulong[] userDataDdt; public DiscImageChef() { - Info = new ImageInfo + imageInfo = new ImageInfo { ReadableSectorTags = new List(), ReadableMediaTags = new List(), @@ -132,8 +141,8 @@ namespace DiscImageChef.DiscImages }; } - public ImageInfo Info { get; private set; } - public string Name => "DiscImageChef format plugin"; + 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; } @@ -160,7 +169,256 @@ namespace DiscImageChef.DiscImages public bool Open(IFilter imageFilter) { - throw new NotImplementedException(); + 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; + + 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); + + 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); + } + + bool foundUserDataDdt = false; + mediaTags = new Dictionary(); + foreach(IndexEntry entry in index) + { + imageStream.Position = (long)entry.offset; + switch(entry.blockType) + { + // TODO: Non-deduplicatable sector tags are data blocks + case BlockType.DataBlock: + // NOP block, skip + if(entry.dataType == DataType.NoData || + // Unused, skip + entry.dataType == DataType.UserData) 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; + + 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; + MediaTagType mediaTagType = GetMediaTagTypeForDataType(blockHeader.type); + + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Found media tag {0} at position {1}", + mediaTagType, entry.offset); + + if(blockHeader.compression == CompressionType.Lzma) + { + byte[] compressedTag = new byte[blockHeader.cmpLength]; + byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH]; + imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH); + imageStream.Read(compressedTag, 0, (int)blockHeader.cmpLength); + 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(); + compressedTag = null; + } + 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); + 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; + } + + 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; + 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; + + // TODO: Check CRC64 + imageInfo.Sectors = ddtHeader.entries; + 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]; + byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH]; + imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH); + imageStream.Read(compressedDdt, 0, (int)ddtHeader.cmpLength); + 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(); + compressedDdt = null; + userDataDdt = new ulong[ddtHeader.entries]; + for(ulong i = 0; i < ddtHeader.entries; i++) + userDataDdt[i] = BitConverter.ToUInt64(decompressedDdt, (int)(i * sizeof(ulong))); + decompressedDdt = null; + 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; + 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; + } + } + + if(!foundUserDataDdt) throw new ImageNotSupportedException("Could not find user data deduplication table."); + + // TODO: Sector size! + imageInfo.SectorSize = 512; + + // TODO: Timestamps in header? + imageInfo.CreationTime = imageFilter.GetCreationTime(); + imageInfo.LastModificationTime = imageFilter.GetLastWriteTime(); + // TODO: Metadata + imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename()); + // TODO: Get from media type + imageInfo.XmlMediaType = XmlMediaType.BlockMedia; + // TODO: Calculate + //imageInfo.ImageSize = qHdr.size; + // TODO: If no geometry + /*imageInfo.Cylinders = (uint)(imageInfo.Sectors / 16 / 63); + imageInfo.Heads = 16; + imageInfo.SectorsPerTrack = 63;*/ + + // Initialize caches + blockCache = new Dictionary(); + blockHeaderCache = new Dictionary(); + currentCacheSize = 0; + if(!inMemoryDdt) ddtEntryCache = new Dictionary(); + + return true; } public byte[] ReadDiskTag(MediaTagType tag) @@ -172,7 +430,75 @@ namespace DiscImageChef.DiscImages public byte[] ReadSector(ulong sectorAddress) { - throw new NotImplementedException(); + 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; + + 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; + } + + 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); + + 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]; + byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH]; + imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH); + imageStream.Read(compressedBlock, 0, (int)blockHeader.cmpLength); + 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(); + compressedBlock = null; + break; + default: + throw new + ImageNotSupportedException($"Found unsupported compression algorithm {(ushort)blockHeader.compression}"); + } + + if(currentCacheSize + blockHeader.length >= MAX_CACHE_SIZE) + { + currentCacheSize = 0; + blockHeaderCache = new Dictionary(); + blockCache = new Dictionary(); + } + + 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) @@ -192,7 +518,22 @@ namespace DiscImageChef.DiscImages public byte[] ReadSectors(ulong sectorAddress, uint length) { - throw new NotImplementedException(); + 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) @@ -318,7 +659,7 @@ namespace DiscImageChef.DiscImages DicConsole.DebugWriteLine("DiscImageChef format plugin", "Got a shift of {0} for {1} sectors per block", shift, oldSectorsPerBlock); - Info = new ImageInfo {MediaType = mediaType, SectorSize = sectorSize, Sectors = sectors}; + imageInfo = new ImageInfo {MediaType = mediaType, SectorSize = sectorSize, Sectors = sectors}; try { imageStream = new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); } catch(IOException e) @@ -334,7 +675,7 @@ namespace DiscImageChef.DiscImages { identifier = DIC_MAGIC, application = "DiscImageChef", - imageMajorVersion = 0, + imageMajorVersion = DICF_VERSION, imageMinorVersion = 0, applicationMajorVersion = 4, applicationMinorVersion = 0, @@ -433,12 +774,12 @@ namespace DiscImageChef.DiscImages } // Close current block first - if(blockStream != null && - (currentBlockHeader.sectorSize != data.Length || - compressedBlockStream.Length / currentBlockHeader.sectorSize == 1 << shift)) + if(blockStream != null && + (currentBlockHeader.sectorSize != data.Length || currentBlockOffset == 1 << shift)) { - currentBlockHeader.length = (uint)compressedBlockStream.Length; + currentBlockHeader.length = currentBlockOffset * currentBlockHeader.sectorSize; currentBlockHeader.crc64 = BitConverter.ToUInt64(crc64.Final(), 0); + byte[] lzmaProperties = compressedBlockStream.Properties; compressedBlockStream.Close(); currentBlockHeader.cmpLength = (uint)blockStream.Length; Crc64Context.Data(blockStream.ToArray(), out byte[] cmpCrc64); @@ -451,8 +792,10 @@ namespace DiscImageChef.DiscImages 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; + blockStream = null; + currentBlockOffset = 0; } // No block set @@ -471,12 +814,12 @@ namespace DiscImageChef.DiscImages crc64.Init(); } - long blockOffset = compressedBlockStream.Position / currentBlockHeader.sectorSize; - ulong ddtEntry = (ulong)((imageStream.Position << shift) + blockOffset); + ulong ddtEntry = (ulong)((imageStream.Position << shift) + currentBlockOffset); deduplicationTable.Add(hash, ddtEntry); compressedBlockStream.Write(data, 0, data.Length); SetDdtEntry(sectorAddress, ddtEntry); crc64.Update(data); + currentBlockOffset++; ErrorMessage = ""; return true; @@ -548,8 +891,9 @@ namespace DiscImageChef.DiscImages // Close current block first if(blockStream != null) { - currentBlockHeader.length = (uint)compressedBlockStream.Length; + currentBlockHeader.length = currentBlockOffset * currentBlockHeader.sectorSize; currentBlockHeader.crc64 = BitConverter.ToUInt64(crc64.Final(), 0); + byte[] lzmaProperties = compressedBlockStream.Properties; compressedBlockStream.Close(); currentBlockHeader.cmpLength = (uint)blockStream.Length; Crc64Context.Data(blockStream.ToArray(), out byte[] cmpCrc64); @@ -562,6 +906,7 @@ namespace DiscImageChef.DiscImages 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; } @@ -594,11 +939,12 @@ namespace DiscImageChef.DiscImages blockStream = new MemoryStream(); compressedBlockStream = new LzmaStream(new LzmaEncoderProperties(), false, blockStream); compressedBlockStream.Write(mediaTag.Value, 0, mediaTag.Value.Length); + byte[] lzmaProperties = compressedBlockStream.Properties; compressedBlockStream.Close(); byte[] tagData; // Not compressible - if(blockStream.Length >= mediaTag.Value.Length) + if(blockStream.Length + LZMA_PROPERTIES_LENGTH >= mediaTag.Value.Length) { tagBlock.cmpLength = tagBlock.length; tagBlock.cmpCrc64 = tagBlock.crc64; @@ -623,7 +969,9 @@ namespace DiscImageChef.DiscImages Marshal.Copy(structurePointer, structureBytes, 0, structureBytes.Length); Marshal.FreeHGlobal(structurePointer); imageStream.Write(structureBytes, 0, structureBytes.Length); - imageStream.Write(tagData, 0, tagData.Length); + if(tagBlock.compression == CompressionType.Lzma) + imageStream.Write(lzmaProperties, 0, lzmaProperties.Length); + imageStream.Write(tagData, 0, tagData.Length); index.Add(idxEntry); } @@ -655,7 +1003,7 @@ namespace DiscImageChef.DiscImages idxEntry = new IndexEntry { blockType = BlockType.DeDuplicationTable, - dataType = DataType.NoData, + dataType = DataType.UserData, offset = (ulong)imageStream.Position }; @@ -683,6 +1031,7 @@ namespace DiscImageChef.DiscImages compressedBlockStream.Write(ddtEntry, 0, ddtEntry.Length); } + byte[] lzmaProperties = compressedBlockStream.Properties; compressedBlockStream.Close(); ddtHeader.cmpLength = (uint)blockStream.Length; Crc64Context.Data(blockStream.ToArray(), out byte[] cmpCrc64); @@ -695,6 +1044,7 @@ namespace DiscImageChef.DiscImages 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; compressedBlockStream = null; @@ -792,6 +1142,26 @@ namespace DiscImageChef.DiscImages return false; } + 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; + } + void SetDdtEntry(ulong sectorAddress, ulong pointer) { if(inMemoryDdt) @@ -801,12 +1171,87 @@ namespace DiscImageChef.DiscImages } long oldPosition = imageStream.Position; - imageStream.Position = outMemoryDdtPosition; + imageStream.Position = outMemoryDdtPosition + Marshal.SizeOf(typeof(DdtHeader)); imageStream.Position += (long)(sectorAddress * sizeof(ulong)); imageStream.Write(BitConverter.GetBytes(pointer), 0, sizeof(ulong)); imageStream.Position = oldPosition; } + 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(); + } + } + static DataType GetDataTypeForMediaTag(MediaTagType tag) { switch(tag) From 34bceca8fb8419e7ff838537ae414c08ae11a001 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Wed, 24 Jan 2018 13:49:21 +0000 Subject: [PATCH 04/20] For DiscImageChef format, compressend length should include the lzma properties 5-byte field. --- DiscImageChef.DiscImages/DiscImageChef.cs | 26 ++++++++++++----------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/DiscImageChef.DiscImages/DiscImageChef.cs b/DiscImageChef.DiscImages/DiscImageChef.cs index 84f66165..0fbcf8c1 100644 --- a/DiscImageChef.DiscImages/DiscImageChef.cs +++ b/DiscImageChef.DiscImages/DiscImageChef.cs @@ -220,6 +220,8 @@ namespace DiscImageChef.DiscImages index.Add(entry); } + imageInfo.ImageSize = 0; + bool foundUserDataDdt = false; mediaTags = new Dictionary(); foreach(IndexEntry entry in index) @@ -243,7 +245,7 @@ namespace DiscImageChef.DiscImages Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(blockHeader)); blockHeader = (BlockHeader)Marshal.PtrToStructure(structurePointer, typeof(BlockHeader)); Marshal.FreeHGlobal(structurePointer); - imageInfo.ImageSize = blockHeader.cmpLength; + imageInfo.ImageSize += blockHeader.cmpLength; if(blockHeader.identifier != entry.blockType) { @@ -269,10 +271,10 @@ namespace DiscImageChef.DiscImages if(blockHeader.compression == CompressionType.Lzma) { - byte[] compressedTag = new byte[blockHeader.cmpLength]; + 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, (int)blockHeader.cmpLength); + imageStream.Read(compressedTag, 0, compressedTag.Length); MemoryStream compressedTagMs = new MemoryStream(compressedTag); LzmaStream lzmaBlock = new LzmaStream(lzmaProperties, compressedTagMs); data = new byte[blockHeader.length]; @@ -325,7 +327,7 @@ namespace DiscImageChef.DiscImages Marshal.Copy(structureBytes, 0, structurePointer, Marshal.SizeOf(ddtHeader)); ddtHeader = (DdtHeader)Marshal.PtrToStructure(structurePointer, typeof(DdtHeader)); Marshal.FreeHGlobal(structurePointer); - imageInfo.ImageSize = ddtHeader.cmpLength; + imageInfo.ImageSize += ddtHeader.cmpLength; if(ddtHeader.identifier != BlockType.DeDuplicationTable) break; @@ -338,10 +340,10 @@ namespace DiscImageChef.DiscImages case CompressionType.Lzma: DicConsole.DebugWriteLine("DiscImageChef format plugin", "Decompressing DDT..."); DateTime ddtStart = DateTime.UtcNow; - byte[] compressedDdt = new byte[ddtHeader.cmpLength]; + 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, (int)ddtHeader.cmpLength); + imageStream.Read(compressedDdt, 0, compressedDdt.Length); MemoryStream compressedDdtMs = new MemoryStream(compressedDdt); LzmaStream lzmaDdt = new LzmaStream(lzmaProperties, compressedDdtMs); byte[] decompressedDdt = new byte[ddtHeader.length]; @@ -468,10 +470,10 @@ namespace DiscImageChef.DiscImages imageStream.Read(block, 0, (int)blockHeader.length); break; case CompressionType.Lzma: - byte[] compressedBlock = new byte[blockHeader.cmpLength]; + 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, (int)blockHeader.cmpLength); + imageStream.Read(compressedBlock, 0, compressedBlock.Length); MemoryStream compressedBlockMs = new MemoryStream(compressedBlock); LzmaStream lzmaBlock = new LzmaStream(lzmaProperties, compressedBlockMs); block = new byte[blockHeader.length]; @@ -781,7 +783,7 @@ namespace DiscImageChef.DiscImages currentBlockHeader.crc64 = BitConverter.ToUInt64(crc64.Final(), 0); byte[] lzmaProperties = compressedBlockStream.Properties; compressedBlockStream.Close(); - currentBlockHeader.cmpLength = (uint)blockStream.Length; + currentBlockHeader.cmpLength = (uint)blockStream.Length + LZMA_PROPERTIES_LENGTH; Crc64Context.Data(blockStream.ToArray(), out byte[] cmpCrc64); currentBlockHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64, 0); @@ -895,7 +897,7 @@ namespace DiscImageChef.DiscImages currentBlockHeader.crc64 = BitConverter.ToUInt64(crc64.Final(), 0); byte[] lzmaProperties = compressedBlockStream.Properties; compressedBlockStream.Close(); - currentBlockHeader.cmpLength = (uint)blockStream.Length; + currentBlockHeader.cmpLength = (uint)blockStream.Length + LZMA_PROPERTIES_LENGTH; Crc64Context.Data(blockStream.ToArray(), out byte[] cmpCrc64); currentBlockHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64, 0); @@ -955,7 +957,7 @@ namespace DiscImageChef.DiscImages { tagData = blockStream.ToArray(); Crc64Context.Data(tagData, out tagCrc); - tagBlock.cmpLength = (uint)tagData.Length; + tagBlock.cmpLength = (uint)tagData.Length + LZMA_PROPERTIES_LENGTH; tagBlock.cmpCrc64 = BitConverter.ToUInt64(tagCrc, 0); tagBlock.compression = CompressionType.Lzma; } @@ -1033,7 +1035,7 @@ namespace DiscImageChef.DiscImages byte[] lzmaProperties = compressedBlockStream.Properties; compressedBlockStream.Close(); - ddtHeader.cmpLength = (uint)blockStream.Length; + ddtHeader.cmpLength = (uint)blockStream.Length + LZMA_PROPERTIES_LENGTH; Crc64Context.Data(blockStream.ToArray(), out byte[] cmpCrc64); ddtHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64, 0); From 9c6df48e87b4e91bb917a4f08e5922ffa0912af3 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Wed, 24 Jan 2018 15:01:58 +0000 Subject: [PATCH 05/20] For DiscImageChef format, add metadata block. --- DiscImageChef.DiscImages/DiscImageChef.cs | 535 +++++++++++++++++++++- 1 file changed, 518 insertions(+), 17 deletions(-) diff --git a/DiscImageChef.DiscImages/DiscImageChef.cs b/DiscImageChef.DiscImages/DiscImageChef.cs index 0fbcf8c1..0fd387f5 100644 --- a/DiscImageChef.DiscImages/DiscImageChef.cs +++ b/DiscImageChef.DiscImages/DiscImageChef.cs @@ -73,6 +73,7 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Security.Cryptography; +using System.Text; using DiscImageChef.Checksums; using DiscImageChef.CommonTypes; using DiscImageChef.Console; @@ -391,6 +392,176 @@ namespace DiscImageChef.DiscImages imageInfo.SectorsPerTrack = geometryBlock.sectorsPerTrack; } + break; + 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; } } @@ -400,19 +571,24 @@ namespace DiscImageChef.DiscImages // TODO: Sector size! imageInfo.SectorSize = 512; - // TODO: Timestamps in header? - imageInfo.CreationTime = imageFilter.GetCreationTime(); - imageInfo.LastModificationTime = imageFilter.GetLastWriteTime(); - // TODO: Metadata - imageInfo.MediaTitle = Path.GetFileNameWithoutExtension(imageFilter.GetFilename()); - // TODO: Get from media type - imageInfo.XmlMediaType = XmlMediaType.BlockMedia; + imageInfo.CreationTime = DateTime.FromFileTimeUtc(header.creationTime); + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Image created on", imageInfo.CreationTime); + imageInfo.LastModificationTime = DateTime.FromFileTimeUtc(header.lastWrittenTime); + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Image last written on", + imageInfo.LastModificationTime); + // TODO: Calculate //imageInfo.ImageSize = qHdr.size; - // TODO: If no geometry - /*imageInfo.Cylinders = (uint)(imageInfo.Sectors / 16 / 63); - imageInfo.Heads = 16; - imageInfo.SectorsPerTrack = 63;*/ + + 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(); @@ -681,7 +857,8 @@ namespace DiscImageChef.DiscImages imageMinorVersion = 0, applicationMajorVersion = 4, applicationMinorVersion = 0, - mediaType = mediaType + mediaType = mediaType, + creationTime = DateTime.UtcNow.ToFileTimeUtc() }; // TODO: Settable @@ -1054,6 +1231,159 @@ namespace DiscImageChef.DiscImages index.Add(idxEntry); } + 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); + } + + 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.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); @@ -1088,9 +1418,10 @@ namespace DiscImageChef.DiscImages imageStream.Write(blockStream.ToArray(), 0, (int)blockStream.Length); DicConsole.DebugWriteLine("DiscImageChef format plugin", "Writing header"); - imageStream.Position = 0; - structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(header)); - structureBytes = new byte[Marshal.SizeOf(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); @@ -1107,6 +1438,20 @@ namespace DiscImageChef.DiscImages // TODO: Implement 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; } @@ -1144,6 +1489,92 @@ namespace DiscImageChef.DiscImages return false; } + 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; + } + } + ulong GetDdtEntry(ulong sectorAddress) { if(inMemoryDdt) return userDataDdt[sectorAddress]; @@ -1414,7 +1845,8 @@ namespace DiscImageChef.DiscImages DataBlock = 0x484B4C42, DeDuplicationTable = 0x48544444, Index = 0x48584449, - GeometryBlock = 0x4D4F4547 + GeometryBlock = 0x4D4F4547, + MetadataBlock = 0x5444545D } /// Header, at start of file @@ -1423,7 +1855,7 @@ namespace DiscImageChef.DiscImages { /// Header identifier, public ulong identifier; - /// UTF-16 name of the application that created the image + /// 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 @@ -1438,6 +1870,14 @@ namespace DiscImageChef.DiscImages 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 @@ -1520,5 +1960,66 @@ namespace DiscImageChef.DiscImages public uint heads; public uint sectorsPerTrack; } + + [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; + } } } \ No newline at end of file From 18067f8485e05ef49b8f2cf4a6b2d32caaf46753 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Wed, 24 Jan 2018 15:35:13 +0000 Subject: [PATCH 06/20] For DiscImageChef format, calculate image size without headers and sector size (biggest found), by indexing all data blocks. --- DiscImageChef.DiscImages/DiscImageChef.cs | 32 ++++++++++++++++------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/DiscImageChef.DiscImages/DiscImageChef.cs b/DiscImageChef.DiscImages/DiscImageChef.cs index 0fd387f5..9da94dd2 100644 --- a/DiscImageChef.DiscImages/DiscImageChef.cs +++ b/DiscImageChef.DiscImages/DiscImageChef.cs @@ -233,9 +233,7 @@ namespace DiscImageChef.DiscImages // TODO: Non-deduplicatable sector tags are data blocks case BlockType.DataBlock: // NOP block, skip - if(entry.dataType == DataType.NoData || - // Unused, skip - entry.dataType == DataType.UserData) break; + if(entry.dataType == DataType.NoData) break; imageStream.Position = (long)entry.offset; @@ -248,6 +246,14 @@ namespace DiscImageChef.DiscImages 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", @@ -568,18 +574,12 @@ namespace DiscImageChef.DiscImages if(!foundUserDataDdt) throw new ImageNotSupportedException("Could not find user data deduplication table."); - // TODO: Sector size! - imageInfo.SectorSize = 512; - imageInfo.CreationTime = DateTime.FromFileTimeUtc(header.creationTime); DicConsole.DebugWriteLine("DiscImageChef format plugin", "Image created on", imageInfo.CreationTime); imageInfo.LastModificationTime = DateTime.FromFileTimeUtc(header.lastWrittenTime); DicConsole.DebugWriteLine("DiscImageChef format plugin", "Image last written on", imageInfo.LastModificationTime); - // TODO: Calculate - //imageInfo.ImageSize = qHdr.size; - if(geometryBlock.identifier != BlockType.GeometryBlock && imageInfo.XmlMediaType == XmlMediaType.BlockMedia) { imageInfo.Cylinders = (uint)(imageInfo.Sectors / 16 / 63); @@ -964,6 +964,13 @@ namespace DiscImageChef.DiscImages Crc64Context.Data(blockStream.ToArray(), out byte[] cmpCrc64); currentBlockHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64, 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); @@ -1078,6 +1085,13 @@ namespace DiscImageChef.DiscImages Crc64Context.Data(blockStream.ToArray(), out byte[] cmpCrc64); currentBlockHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64, 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); From db281a09865d0497607896f38b1cdcf55b721097 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Wed, 24 Jan 2018 17:56:50 +0000 Subject: [PATCH 07/20] For DiscImageChef format, add support for tracks. --- DiscImageChef.DiscImages/DiscImageChef.cs | 412 +++++++++++++++++++++- DiscImageChef.DiscImages/Enums.cs | 14 +- 2 files changed, 400 insertions(+), 26 deletions(-) diff --git a/DiscImageChef.DiscImages/DiscImageChef.cs b/DiscImageChef.DiscImages/DiscImageChef.cs index 9da94dd2..f277ca2e 100644 --- a/DiscImageChef.DiscImages/DiscImageChef.cs +++ b/DiscImageChef.DiscImages/DiscImageChef.cs @@ -113,6 +113,8 @@ namespace DiscImageChef.DiscImages byte shift; byte[] structureBytes; IntPtr structurePointer; + Dictionary trackFlags; + Dictionary trackIsrcs; ulong[] userDataDdt; public DiscImageChef() @@ -146,9 +148,9 @@ namespace DiscImageChef.DiscImages public string Name => "DiscImageChef format"; public Guid Id => new Guid("49360069-1784-4A2F-B723-0C844D610B0A"); public string Format => "DiscImageChef"; - public List Partitions { get; } + public List Partitions { get; private set; } public List Tracks { get; private set; } - public List Sessions { get; } + public List Sessions { get; private set; } public bool Identify(IFilter imageFilter) { @@ -568,6 +570,59 @@ namespace DiscImageChef.DiscImages imageInfo.DriveFirmwareRevision); } + break; + 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; + } + + // TODO: Check CRC64 + + 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); + } + break; } } @@ -596,6 +651,79 @@ namespace DiscImageChef.DiscImages currentCacheSize = 0; if(!inMemoryDdt) ddtEntryCache = new Dictionary(); + 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 = sector.Length; + } + + Tracks = tracks.ToList(); + } + else + { + Tracks = null; + Sessions = null; + Partitions = null; + } + return true; } @@ -686,12 +814,26 @@ namespace DiscImageChef.DiscImages public byte[] ReadSector(ulong sectorAddress, uint track) { - throw new NotImplementedException(); + 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) { - throw new NotImplementedException(); + 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) @@ -721,12 +863,34 @@ namespace DiscImageChef.DiscImages public byte[] ReadSectors(ulong sectorAddress, uint length, uint track) { - throw new NotImplementedException(); + 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) { - throw new NotImplementedException(); + 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) @@ -736,7 +900,14 @@ namespace DiscImageChef.DiscImages public byte[] ReadSectorLong(ulong sectorAddress, uint track) { - throw new NotImplementedException(); + 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) @@ -746,7 +917,18 @@ namespace DiscImageChef.DiscImages public byte[] ReadSectorsLong(ulong sectorAddress, uint length, uint track) { - throw new NotImplementedException(); + 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) @@ -766,7 +948,14 @@ namespace DiscImageChef.DiscImages public bool? VerifySector(ulong sectorAddress, uint track) { - throw new NotImplementedException(); + 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 VerifySector(trk.TrackStartSector + sectorAddress); } public bool? VerifySectors(ulong sectorAddress, uint length, out List failingLbas, @@ -778,7 +967,18 @@ namespace DiscImageChef.DiscImages public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List failingLbas, out List unknownLbas) { - throw new NotImplementedException(); + 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 VerifySectors(trk.TrackStartSector + sectorAddress, length, out failingLbas, out unknownLbas); } public bool? VerifyMediaImage() @@ -837,7 +1037,13 @@ namespace DiscImageChef.DiscImages 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}; + 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) @@ -906,6 +1112,8 @@ namespace DiscImageChef.DiscImages mediaTags = new Dictionary(); checksumProvider = SHA256.Create(); deduplicationTable = new Dictionary(); + trackIsrcs = new Dictionary(); + trackFlags = new Dictionary(); IsWriting = true; ErrorMessage = null; @@ -970,7 +1178,7 @@ namespace DiscImageChef.DiscImages dataType = DataType.UserData, offset = (ulong)imageStream.Position }); - + structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(currentBlockHeader)); structureBytes = new byte[Marshal.SizeOf(currentBlockHeader)]; Marshal.StructureToPtr(currentBlockHeader, structurePointer, true); @@ -1055,6 +1263,12 @@ namespace DiscImageChef.DiscImages 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"; @@ -1245,6 +1459,72 @@ namespace DiscImageChef.DiscImages index.Add(idxEntry); } + if(imageInfo.XmlMediaType == XmlMediaType.OpticalDisc && Tracks != null && Tracks.Count > 0) + { + 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(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.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); + } + } + MetadataBlock metadataBlock = new MetadataBlock(); blockStream = new MemoryStream(); blockStream.Write(new byte[Marshal.SizeOf(metadataBlock)], 0, Marshal.SizeOf(metadataBlock)); @@ -1489,18 +1769,85 @@ namespace DiscImageChef.DiscImages return true; } - // TODO: Implement public bool WriteSectorTag(byte[] data, ulong sectorAddress, SectorTagType tag) { - ErrorMessage = "Writing sectors with tags is not yet implemented."; - return false; + 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: + 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; + } + default: throw new NotImplementedException(); + } } - // TODO: Implement public bool WriteSectorsTag(byte[] data, ulong sectorAddress, uint length, SectorTagType tag) { - ErrorMessage = "Writing sectors with tags is not yet implemented."; - return false; + 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); + default: throw new NotImplementedException(); + } } static XmlMediaType GetXmlMediaType(MediaType type) @@ -1860,7 +2207,8 @@ namespace DiscImageChef.DiscImages DeDuplicationTable = 0x48544444, Index = 0x48584449, GeometryBlock = 0x4D4F4547, - MetadataBlock = 0x5444545D + MetadataBlock = 0x5444545D, + TracksBlock = 0x534B5254 } /// Header, at start of file @@ -1975,6 +2323,7 @@ namespace DiscImageChef.DiscImages public uint sectorsPerTrack; } + /// Metadata block, contains metadata [StructLayout(LayoutKind.Sequential, Pack = 1)] struct MetadataBlock { @@ -2035,5 +2384,30 @@ namespace DiscImageChef.DiscImages /// Length in bytes of the null-terminated UTF-16LE creator string public uint driveFirmwareRevisionLength; } + + [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; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)] + struct TrackEntry + { + public byte sequence; + public TrackType type; + public long start; + public long end; + public long pregap; + public byte session; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 13)] + public string isrc; + 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 } /// From 56bf811ef8f4831654da1f4c294b1706731250bb Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Wed, 24 Jan 2018 21:12:14 +0000 Subject: [PATCH 08/20] For DiscImageChef format, add support CompactDisc long sectors and subchannel. --- DiscImageChef.DiscImages/DiscImageChef.cs | 579 +++++++++++++++++++++- 1 file changed, 555 insertions(+), 24 deletions(-) diff --git a/DiscImageChef.DiscImages/DiscImageChef.cs b/DiscImageChef.DiscImages/DiscImageChef.cs index f277ca2e..40e4fa26 100644 --- a/DiscImageChef.DiscImages/DiscImageChef.cs +++ b/DiscImageChef.DiscImages/DiscImageChef.cs @@ -83,6 +83,7 @@ using SharpCompress.Compressors.LZMA; namespace DiscImageChef.DiscImages { // TODO: Work in progress + // TODO: Get manufacurer, model, firmware, from tags, if available public class DiscImageChef : IWritableImage { const ulong DIC_MAGIC = 0x544D464444434944; @@ -110,6 +111,9 @@ namespace DiscImageChef.DiscImages bool inMemoryDdt; Dictionary mediaTags; long outMemoryDdtPosition; + byte[] sectorPrefix; + byte[] sectorSubchannel; + byte[] sectorSuffix; byte shift; byte[] structureBytes; IntPtr structurePointer; @@ -232,7 +236,6 @@ namespace DiscImageChef.DiscImages imageStream.Position = (long)entry.offset; switch(entry.blockType) { - // TODO: Non-deduplicatable sector tags are data blocks case BlockType.DataBlock: // NOP block, skip if(entry.dataType == DataType.NoData) break; @@ -272,11 +275,11 @@ namespace DiscImageChef.DiscImages break; } - byte[] data; - MediaTagType mediaTagType = GetMediaTagTypeForDataType(blockHeader.type); + byte[] data; - DicConsole.DebugWriteLine("DiscImageChef format plugin", "Found media tag {0} at position {1}", - mediaTagType, entry.offset); + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Found data block type {0} at position {1}", entry.dataType, + entry.offset); if(blockHeader.compression == CompressionType.Lzma) { @@ -306,6 +309,7 @@ namespace DiscImageChef.DiscImages } Crc64Context.Data(data, out byte[] blockCrc); + blockCrc = blockCrc.Reverse().ToArray(); if(BitConverter.ToUInt64(blockCrc, 0) != blockHeader.crc64) { DicConsole.DebugWriteLine("DiscImageChef format plugin", @@ -314,16 +318,49 @@ namespace DiscImageChef.DiscImages break; } - if(mediaTags.ContainsKey(mediaTagType)) + switch(entry.dataType) { - DicConsole.DebugWriteLine("DiscImageChef format plugin", - "Media tag type {0} duplicated, removing previous entry...", - mediaTagType); + 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; + default: + MediaTagType mediaTagType = GetMediaTagTypeForDataType(blockHeader.type); - mediaTags.Remove(mediaTagType); + 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; } - mediaTags.Add(mediaTagType, data); break; case BlockType.DeDuplicationTable: // Only user data deduplication tables are used right now @@ -809,7 +846,7 @@ namespace DiscImageChef.DiscImages public byte[] ReadSectorTag(ulong sectorAddress, SectorTagType tag) { - throw new NotImplementedException(); + return ReadSectorsTag(sectorAddress, 1, tag); } public byte[] ReadSector(ulong sectorAddress, uint track) @@ -858,7 +895,186 @@ namespace DiscImageChef.DiscImages public byte[] ReadSectorsTag(ulong sectorAddress, uint length, SectorTagType tag) { - throw new NotImplementedException(); + 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; + } + 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 NotImplementedException(); + + 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) @@ -895,7 +1111,38 @@ namespace DiscImageChef.DiscImages public byte[] ReadSectorLong(ulong sectorAddress) { - throw new NotImplementedException(); + 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(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; + } + } + + throw new FeatureNotPresentImageException("Feature not present in image"); } public byte[] ReadSectorLong(ulong sectorAddress, uint track) @@ -912,7 +1159,20 @@ namespace DiscImageChef.DiscImages public byte[] ReadSectorsLong(ulong sectorAddress, uint length) { - throw new NotImplementedException(); + if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) + throw new FeatureNotPresentImageException("Feature not present in image"); + + 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"); + + return ReadSectorsLong(trk.TrackStartSector + sectorAddress, length); } public byte[] ReadSectorsLong(ulong sectorAddress, uint length, uint track) @@ -986,9 +1246,10 @@ namespace DiscImageChef.DiscImages throw new NotImplementedException(); } - public IEnumerable SupportedMediaTags => Enum.GetValues(typeof(MediaType)).Cast(); + public IEnumerable SupportedMediaTags => + Enum.GetValues(typeof(MediaTagType)).Cast(); public IEnumerable SupportedSectorTags => - Enum.GetValues(typeof(MediaType)).Cast(); + Enum.GetValues(typeof(SectorTagType)).Cast(); public IEnumerable SupportedMediaTypes => Enum.GetValues(typeof(MediaType)).Cast(); public IEnumerable<(string name, Type type, string description)> SupportedOptions => @@ -1247,17 +1508,100 @@ namespace DiscImageChef.DiscImages return true; } - // TODO: Implement public bool WriteSectorLong(byte[] data, ulong sectorAddress) { - ErrorMessage = "Writing sectors with tags is not yet implemented."; + if(!IsWriting) + { + ErrorMessage = "Tried to write on a non-writable image"; + return false; + } + + byte[] sector; + + if(imageInfo.XmlMediaType == 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; + } + + 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); + } + } + + // TODO: Implement + ErrorMessage = "Unknown sector tag type, cannot write."; return false; } - // TODO: Implement public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length) { - ErrorMessage = "Writing sectors with tags is not yet implemented."; + if(imageInfo.XmlMediaType == 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(track.TrackStartSector + sectorAddress + length > track.TrackEndSector + 1) + throw new ArgumentOutOfRangeException(nameof(length), + $"Requested more sectors ({length + sectorAddress}) than present in track ({track.TrackEndSector - track.TrackStartSector + 1}), won't cross tracks"); + + byte[] 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; + } + + // TODO: Implement + ErrorMessage = "Unknown sector tag type, cannot write."; return false; } @@ -1461,6 +1805,149 @@ namespace DiscImageChef.DiscImages if(imageInfo.XmlMediaType == XmlMediaType.OpticalDisc && 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(); + compressedBlockStream = new LzmaStream(new LzmaEncoderProperties(), false, blockStream); + compressedBlockStream.Write(sectorPrefix, 0, sectorPrefix.Length); + byte[] lzmaProperties = compressedBlockStream.Properties; + compressedBlockStream.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; + + compressedBlockStream = 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.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(); + compressedBlockStream = new LzmaStream(new LzmaEncoderProperties(), false, blockStream); + compressedBlockStream.Write(sectorSuffix, 0, sectorSuffix.Length); + lzmaProperties = compressedBlockStream.Properties; + compressedBlockStream.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; + + compressedBlockStream = 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.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(); + compressedBlockStream = new LzmaStream(new LzmaEncoderProperties(), false, blockStream); + compressedBlockStream.Write(sectorSubchannel, 0, sectorSubchannel.Length); + byte[] lzmaProperties = compressedBlockStream.Properties; + compressedBlockStream.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; + + compressedBlockStream = 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.Add(idxEntry); + blockStream = null; + } + List trackEntries = new List(); foreach(Track track in Tracks) { @@ -1729,7 +2216,6 @@ namespace DiscImageChef.DiscImages return true; } - // TODO: Implement public bool SetMetadata(ImageInfo metadata) { imageInfo.Creator = metadata.Creator; @@ -1757,6 +2243,12 @@ namespace DiscImageChef.DiscImages 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, @@ -1788,6 +2280,7 @@ namespace DiscImageChef.DiscImages { case SectorTagType.CdTrackFlags: case SectorTagType.CdTrackIsrc: + case SectorTagType.CdSectorSubchannel: if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) { ErrorMessage = "Incorrect tag for disk type"; @@ -1824,6 +2317,20 @@ namespace DiscImageChef.DiscImages 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: throw new NotImplementedException(); } } @@ -1846,7 +2353,28 @@ namespace DiscImageChef.DiscImages { case SectorTagType.CdTrackFlags: case SectorTagType.CdTrackIsrc: return WriteSectorTag(data, sectorAddress, tag); - default: throw new NotImplementedException(); + 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: throw new NotImplementedException(); } } @@ -2198,7 +2726,10 @@ namespace DiscImageChef.DiscImages ScsiModeSense10 = 65, UsbDescriptors = 66, XboxDmi = 67, - XboxPfi = 68 + XboxPfi = 68, + CdSectorPrefix = 69, + CdSectorSuffix = 70, + CdSectorSubchannel = 71 } enum BlockType : uint From ac211cc8b502802ebba3f25de5a73aaa574e6cfb Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Wed, 24 Jan 2018 22:53:19 +0000 Subject: [PATCH 09/20] For DiscImageChef format, add support for Apple long sectors (with tags). --- DiscImageChef.Decoders/LisaTag.cs | 2 +- DiscImageChef.DiscImages/DiscImageChef.cs | 539 +++++++++++++++++----- 2 files changed, 433 insertions(+), 108 deletions(-) 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.cs b/DiscImageChef.DiscImages/DiscImageChef.cs index 40e4fa26..c9827d14 100644 --- a/DiscImageChef.DiscImages/DiscImageChef.cs +++ b/DiscImageChef.DiscImages/DiscImageChef.cs @@ -77,6 +77,7 @@ using System.Text; using DiscImageChef.Checksums; using DiscImageChef.CommonTypes; using DiscImageChef.Console; +using DiscImageChef.Decoders; using DiscImageChef.Filters; using SharpCompress.Compressors.LZMA; @@ -345,6 +346,13 @@ namespace DiscImageChef.DiscImages 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); @@ -749,7 +757,14 @@ namespace DiscImageChef.DiscImages { byte[] sector = ReadSector(tracks[i].TrackStartSector); tracks[i].TrackBytesPerSector = sector.Length; - tracks[i].TrackRawBytesPerSector = 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(); @@ -1058,7 +1073,7 @@ namespace DiscImageChef.DiscImages default: throw new FeatureSupportedButNotImplementedImageException("Unsupported track type"); } } - else throw new NotImplementedException(); + else throw new FeatureNotPresentImageException("Feature not present in image"); if(dataSource == null) throw new ArgumentException("Unsupported tag requested", nameof(tag)); @@ -1111,35 +1126,50 @@ namespace DiscImageChef.DiscImages public byte[] ReadSectorLong(ulong sectorAddress) { - if(imageInfo.XmlMediaType == XmlMediaType.OpticalDisc) + switch(imageInfo.XmlMediaType) { - 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"); + 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); + if(sectorSuffix == null || sectorPrefix == null) return ReadSector(sectorAddress); - byte[] sector = new byte[2352]; - byte[] data = 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; - } + 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"); @@ -1159,20 +1189,107 @@ namespace DiscImageChef.DiscImages public byte[] ReadSectorsLong(ulong sectorAddress, uint length) { - if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) - throw new FeatureNotPresentImageException("Feature not present in image"); + byte[] sectors; + byte[] data; - 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"); + 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"); + 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); + switch(trk.TrackType) + { + case TrackType.Audio: + case TrackType.Data: return ReadSectors(sectorAddress, length); + 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; + 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) + { + 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) @@ -1480,7 +1597,6 @@ namespace DiscImageChef.DiscImages return true; } - // TODO: Optimize this public bool WriteSectors(byte[] data, ulong sectorAddress, uint length) { if(!IsWriting) @@ -1518,90 +1634,219 @@ namespace DiscImageChef.DiscImages byte[] sector; - if(imageInfo.XmlMediaType == XmlMediaType.OpticalDisc) + switch(imageInfo.XmlMediaType) { - Track track = - Tracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector && - sectorAddress <= trk.TrackEndSector); + 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(track.TrackSequence == 0) + { + ErrorMessage = $"Can't found track containing {sectorAddress}"; + return false; + } - if(data.Length != 2352) - { - ErrorMessage = "Incorrect data size"; - return false; - } + if(data.Length != 2352) + { + ErrorMessage = "Incorrect data size"; + return false; + } - 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); - } + 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) + { + 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; } - // TODO: Implement - ErrorMessage = "Unknown sector tag type, cannot write."; + ErrorMessage = "Unknown long sector type, cannot write."; return false; } public bool WriteSectorsLong(byte[] data, ulong sectorAddress, uint length) { - if(imageInfo.XmlMediaType == XmlMediaType.OpticalDisc) + byte[] sector; + switch(imageInfo.XmlMediaType) { - Track track = - Tracks.FirstOrDefault(trk => sectorAddress >= trk.TrackStartSector && - sectorAddress <= trk.TrackEndSector); + 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(track.TrackSequence == 0) + { + ErrorMessage = $"Can't found track containing {sectorAddress}"; + return false; + } - if(data.Length % 2352 != 0) - { - ErrorMessage = "Incorrect data size"; - return false; - } + if(data.Length % 2352 != 0) + { + ErrorMessage = "Incorrect data size"; + return false; + } - if(track.TrackStartSector + sectorAddress + length > track.TrackEndSector + 1) - throw new ArgumentOutOfRangeException(nameof(length), - $"Requested more sectors ({length + sectorAddress}) than present in track ({track.TrackEndSector - track.TrackStartSector + 1}), won't cross tracks"); + if(track.TrackStartSector + sectorAddress + length > track.TrackEndSector + 1) + throw new ArgumentOutOfRangeException(nameof(length), + $"Requested more sectors ({length + sectorAddress}) than present in track ({track.TrackEndSector - track.TrackStartSector + 1}), won't cross tracks"); - byte[] 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; - } + 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; + 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; } - // TODO: Implement - ErrorMessage = "Unknown sector tag type, cannot write."; + ErrorMessage = "Unknown long sector type, cannot write."; return false; } @@ -2011,6 +2256,79 @@ namespace DiscImageChef.DiscImages imageStream.Write(blockStream.ToArray(), 0, (int)blockStream.Length); } } + else if(imageInfo.XmlMediaType == 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(); + compressedBlockStream = new LzmaStream(new LzmaEncoderProperties(), false, blockStream); + compressedBlockStream.Write(sectorSubchannel, 0, sectorSubchannel.Length); + byte[] lzmaProperties = compressedBlockStream.Properties; + compressedBlockStream.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; + + compressedBlockStream = 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.Add(idxEntry); + blockStream = null; + } MetadataBlock metadataBlock = new MetadataBlock(); blockStream = new MemoryStream(); @@ -2331,7 +2649,9 @@ namespace DiscImageChef.DiscImages return true; } - default: throw new NotImplementedException(); + default: + ErrorMessage = $"Don't know how to write sector tag type {tag}"; + return false; } } @@ -2374,7 +2694,9 @@ namespace DiscImageChef.DiscImages return true; } - default: throw new NotImplementedException(); + default: + ErrorMessage = $"Don't know how to write sector tag type {tag}"; + return false; } } @@ -2729,7 +3051,10 @@ namespace DiscImageChef.DiscImages XboxPfi = 68, CdSectorPrefix = 69, CdSectorSuffix = 70, - CdSectorSubchannel = 71 + CdSectorSubchannel = 71, + AppleProfileTag = 72, + AppleSonyTag = 73, + PriamDataTowerTag = 74 } enum BlockType : uint From 2888aa73aee9920e67a2dfcee4bfc6a0a1a6b61c Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Thu, 25 Jan 2018 00:29:07 +0000 Subject: [PATCH 10/20] For DiscImageChef format, add options for dictionary and in-memory ddt sizes. --- DiscImageChef.DiscImages/DiscImageChef.cs | 582 ++++++++++++---------- 1 file changed, 311 insertions(+), 271 deletions(-) diff --git a/DiscImageChef.DiscImages/DiscImageChef.cs b/DiscImageChef.DiscImages/DiscImageChef.cs index c9827d14..de7db82c 100644 --- a/DiscImageChef.DiscImages/DiscImageChef.cs +++ b/DiscImageChef.DiscImages/DiscImageChef.cs @@ -65,6 +65,7 @@ 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; @@ -110,6 +111,7 @@ namespace DiscImageChef.DiscImages Stream imageStream; List index; bool inMemoryDdt; + LzmaEncoderProperties lzmaEncoderProperties; Dictionary mediaTags; long outMemoryDdtPosition; byte[] sectorPrefix; @@ -1373,7 +1375,11 @@ namespace DiscImageChef.DiscImages new[] { ("sectors_per_block", typeof(uint), - "How many sectors to store per block (will be rounded to next power of two)") + "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; } @@ -1384,8 +1390,11 @@ namespace DiscImageChef.DiscImages 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)) @@ -1394,9 +1403,34 @@ namespace DiscImageChef.DiscImages return false; } } - else - sectorsPerBlock = 4096; - else sectorsPerBlock = 4096; + 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("dictionary", 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; + } if(!SupportedMediaTypes.Contains(mediaType)) { @@ -1445,8 +1479,7 @@ namespace DiscImageChef.DiscImages creationTime = DateTime.UtcNow.ToFileTimeUtc() }; - // TODO: Settable - inMemoryDdt = sectors <= 256 * 1024 * 1024 / sizeof(ulong); + inMemoryDdt = sectors <= maxDdtSize * 1024 * 1024 / sizeof(ulong); DicConsole.DebugWriteLine("DiscImageChef format plugin", "In memory DDT?: {0}", inMemoryDdt); @@ -1493,6 +1526,8 @@ namespace DiscImageChef.DiscImages trackIsrcs = new Dictionary(); trackFlags = new Dictionary(); + lzmaEncoderProperties = new LzmaEncoderProperties(true, (int)dictionary, 255); + IsWriting = true; ErrorMessage = null; return true; @@ -1574,7 +1609,7 @@ namespace DiscImageChef.DiscImages if(blockStream == null) { blockStream = new MemoryStream(); - compressedBlockStream = new LzmaStream(new LzmaEncoderProperties(), false, blockStream); + compressedBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); currentBlockHeader = new BlockHeader { identifier = BlockType.DataBlock, @@ -1933,7 +1968,7 @@ namespace DiscImageChef.DiscImages }; blockStream = new MemoryStream(); - compressedBlockStream = new LzmaStream(new LzmaEncoderProperties(), false, blockStream); + compressedBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); compressedBlockStream.Write(mediaTag.Value, 0, mediaTag.Value.Length); byte[] lzmaProperties = compressedBlockStream.Properties; compressedBlockStream.Close(); @@ -2017,7 +2052,7 @@ namespace DiscImageChef.DiscImages }; blockStream = new MemoryStream(); - compressedBlockStream = new LzmaStream(new LzmaEncoderProperties(), false, blockStream); + compressedBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); crc64 = new Crc64Context(); crc64.Init(); for(ulong i = 0; i < (ulong)userDataDdt.LongLength; i++) @@ -2048,287 +2083,292 @@ namespace DiscImageChef.DiscImages index.Add(idxEntry); } - if(imageInfo.XmlMediaType == XmlMediaType.OpticalDisc && Tracks != null && Tracks.Count > 0) + switch(imageInfo.XmlMediaType) { - if(sectorPrefix != null && sectorSuffix != null) - { - idxEntry = new IndexEntry + case XmlMediaType.OpticalDisc when Tracks != null && Tracks.Count > 0: + if(sectorPrefix != null && sectorSuffix != null) { - blockType = BlockType.DataBlock, - dataType = DataType.CdSectorPrefix, - offset = (ulong)imageStream.Position - }; + 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); + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Writing CD sector prefix block to position {0}", idxEntry.offset); - Crc64Context.Data(sectorPrefix, out byte[] blockCrc); + 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) - }; + BlockHeader prefixBlock = new BlockHeader + { + identifier = BlockType.DataBlock, + type = DataType.CdSectorPrefix, + length = (uint)sectorPrefix.Length, + crc64 = BitConverter.ToUInt64(blockCrc, 0) + }; - blockStream = new MemoryStream(); - compressedBlockStream = new LzmaStream(new LzmaEncoderProperties(), false, blockStream); - compressedBlockStream.Write(sectorPrefix, 0, sectorPrefix.Length); - byte[] lzmaProperties = compressedBlockStream.Properties; - compressedBlockStream.Close(); + blockStream = new MemoryStream(); + compressedBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); + compressedBlockStream.Write(sectorPrefix, 0, sectorPrefix.Length); + byte[] lzmaProperties = compressedBlockStream.Properties; + compressedBlockStream.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; + Crc64Context.Data(blockStream.ToArray(), out blockCrc); + prefixBlock.cmpLength = (uint)blockStream.Length + LZMA_PROPERTIES_LENGTH; + prefixBlock.cmpCrc64 = BitConverter.ToUInt64(blockCrc, 0); + prefixBlock.compression = CompressionType.Lzma; - compressedBlockStream = null; + compressedBlockStream = 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.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(); - compressedBlockStream = new LzmaStream(new LzmaEncoderProperties(), false, blockStream); - compressedBlockStream.Write(sectorSuffix, 0, sectorSuffix.Length); - lzmaProperties = compressedBlockStream.Properties; - compressedBlockStream.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; - - compressedBlockStream = 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.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(); - compressedBlockStream = new LzmaStream(new LzmaEncoderProperties(), false, blockStream); - compressedBlockStream.Write(sectorSubchannel, 0, sectorSubchannel.Length); - byte[] lzmaProperties = compressedBlockStream.Properties; - compressedBlockStream.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; - - compressedBlockStream = 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.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(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); + 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); - blockStream.Write(structureBytes, 0, structureBytes.Length); + 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.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(); + compressedBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); + compressedBlockStream.Write(sectorSuffix, 0, sectorSuffix.Length); + lzmaProperties = compressedBlockStream.Properties; + compressedBlockStream.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; + + compressedBlockStream = 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.Add(idxEntry); + blockStream = null; } - Crc64Context.Data(blockStream.ToArray(), out byte[] trksCrc); - TracksHeader trkHeader = new TracksHeader + if(sectorSubchannel != null) { - identifier = BlockType.TracksBlock, - entries = (ushort)trackEntries.Count, - crc64 = BitConverter.ToUInt64(trksCrc, 0) - }; + idxEntry = new IndexEntry + { + blockType = BlockType.DataBlock, + dataType = DataType.CdSectorSubchannel, + offset = (ulong)imageStream.Position + }; - DicConsole.DebugWriteLine("DiscImageChef format plugin", "Writing tracks to position {0}", - imageStream.Position); + DicConsole.DebugWriteLine("DiscImageChef format plugin", + "Writing CD subchannel block to position {0}", idxEntry.offset); - index.Add(new IndexEntry + 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(); + compressedBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); + compressedBlockStream.Write(sectorSubchannel, 0, sectorSubchannel.Length); + byte[] lzmaProperties = compressedBlockStream.Properties; + compressedBlockStream.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; + + compressedBlockStream = 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.Add(idxEntry); + blockStream = null; + } + + List trackEntries = new List(); + foreach(Track track in Tracks) { - blockType = BlockType.TracksBlock, - dataType = DataType.NoData, - offset = (ulong)imageStream.Position - }); + trackFlags.TryGetValue((byte)track.TrackSequence, out byte flags); + trackIsrcs.TryGetValue((byte)track.TrackSequence, out string isrc); - 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); - } + 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(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.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(); + compressedBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); + compressedBlockStream.Write(sectorSubchannel, 0, sectorSubchannel.Length); + byte[] lzmaProperties = compressedBlockStream.Properties; + compressedBlockStream.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; + + compressedBlockStream = 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.Add(idxEntry); + blockStream = null; + } + + break; } - else if(imageInfo.XmlMediaType == 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(); - compressedBlockStream = new LzmaStream(new LzmaEncoderProperties(), false, blockStream); - compressedBlockStream.Write(sectorSubchannel, 0, sectorSubchannel.Length); - byte[] lzmaProperties = compressedBlockStream.Properties; - compressedBlockStream.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; - - compressedBlockStream = 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.Add(idxEntry); - blockStream = null; - } MetadataBlock metadataBlock = new MetadataBlock(); blockStream = new MemoryStream(); From 144b177e7790a12c976f44a7afb90c1db004bff5 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Thu, 25 Jan 2018 16:16:16 +0000 Subject: [PATCH 11/20] For DiscImageChef format, add image and sector verification. --- DiscImageChef.DiscImages/DiscImageChef.cs | 313 +++++++++++++++++++--- 1 file changed, 278 insertions(+), 35 deletions(-) diff --git a/DiscImageChef.DiscImages/DiscImageChef.cs b/DiscImageChef.DiscImages/DiscImageChef.cs index de7db82c..d8492a18 100644 --- a/DiscImageChef.DiscImages/DiscImageChef.cs +++ b/DiscImageChef.DiscImages/DiscImageChef.cs @@ -296,7 +296,6 @@ namespace DiscImageChef.DiscImages lzmaBlock.Read(data, 0, (int)blockHeader.length); lzmaBlock.Close(); compressedTagMs.Close(); - compressedTag = null; } else if(blockHeader.compression == CompressionType.None) { @@ -387,7 +386,6 @@ namespace DiscImageChef.DiscImages if(ddtHeader.identifier != BlockType.DeDuplicationTable) break; - // TODO: Check CRC64 imageInfo.Sectors = ddtHeader.entries; shift = ddtHeader.shift; @@ -406,11 +404,9 @@ namespace DiscImageChef.DiscImages lzmaDdt.Read(decompressedDdt, 0, (int)ddtHeader.length); lzmaDdt.Close(); compressedDdtMs.Close(); - compressedDdt = null; userDataDdt = new ulong[ddtHeader.entries]; for(ulong i = 0; i < ddtHeader.entries; i++) userDataDdt[i] = BitConverter.ToUInt64(decompressedDdt, (int)(i * sizeof(ulong))); - decompressedDdt = null; DateTime ddtEnd = DateTime.UtcNow; inMemoryDdt = true; DicConsole.DebugWriteLine("DiscImageChef format plugin", @@ -634,7 +630,18 @@ namespace DiscImageChef.DiscImages break; } - // TODO: Check CRC64 + 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(); @@ -838,7 +845,6 @@ namespace DiscImageChef.DiscImages lzmaBlock.Read(block, 0, (int)blockHeader.length); lzmaBlock.Close(); compressedBlockMs.Close(); - compressedBlock = null; break; default: throw new @@ -1322,47 +1328,276 @@ namespace DiscImageChef.DiscImages public bool? VerifySector(ulong sectorAddress) { - throw new NotImplementedException(); + 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) - throw new FeatureNotPresentImageException("Feature not present in image"); + if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) return null; - 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 VerifySector(trk.TrackStartSector + sectorAddress); + byte[] buffer = ReadSectorLong(sectorAddress, track); + return CdChecksums.CheckCdSector(buffer); } public bool? VerifySectors(ulong sectorAddress, uint length, out List failingLbas, out List unknownLbas) { - throw new NotImplementedException(); + failingLbas = new List(); + unknownLbas = new List(); + + 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) { if(imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) - throw new FeatureNotPresentImageException("Feature not present in image"); + { + failingLbas = new List(); + unknownLbas = new List(); - Track trk = Tracks.FirstOrDefault(t => t.TrackSequence == track); - if(trk.TrackSequence != track) - throw new ArgumentOutOfRangeException(nameof(track), "Track does not exist in disc image"); + for(ulong i = sectorAddress; i < sectorAddress + length; i++) unknownLbas.Add(i); - 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 null; + } - return VerifySectors(trk.TrackStartSector + sectorAddress, length, out failingLbas, out unknownLbas); + 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() { - throw new NotImplementedException(); + 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); + } + + 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); + readBytes += (ulong)verifyBytes.LongLength; + System.Console.WriteLine("Read {0} bytes of {1}", readBytes, blockHeader.cmpLength); + + 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 => @@ -1376,8 +1611,7 @@ namespace DiscImageChef.DiscImages { ("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"), + ("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") }; @@ -1428,8 +1662,8 @@ namespace DiscImageChef.DiscImages else { sectorsPerBlock = 4096; - dictionary = 1 << 25; - maxDdtSize = 256; + dictionary = 1 << 25; + maxDdtSize = 256; } if(!SupportedMediaTypes.Contains(mediaType)) @@ -1582,8 +1816,11 @@ namespace DiscImageChef.DiscImages byte[] lzmaProperties = compressedBlockStream.Properties; compressedBlockStream.Close(); currentBlockHeader.cmpLength = (uint)blockStream.Length + LZMA_PROPERTIES_LENGTH; - Crc64Context.Data(blockStream.ToArray(), out byte[] cmpCrc64); - currentBlockHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64, 0); + Crc64Context cmpCrc64Context = new Crc64Context(); + cmpCrc64Context.Init(); + cmpCrc64Context.Update(lzmaProperties); + cmpCrc64Context.Update(blockStream.ToArray()); + currentBlockHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64Context.Final(), 0); index.Add(new IndexEntry { @@ -1920,8 +2157,11 @@ namespace DiscImageChef.DiscImages byte[] lzmaProperties = compressedBlockStream.Properties; compressedBlockStream.Close(); currentBlockHeader.cmpLength = (uint)blockStream.Length + LZMA_PROPERTIES_LENGTH; - Crc64Context.Data(blockStream.ToArray(), out byte[] cmpCrc64); - currentBlockHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64, 0); + Crc64Context cmpCrc64Context = new Crc64Context(); + cmpCrc64Context.Init(); + cmpCrc64Context.Update(lzmaProperties); + cmpCrc64Context.Update(blockStream.ToArray()); + currentBlockHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64Context.Final(), 0); index.Add(new IndexEntry { @@ -2064,9 +2304,12 @@ namespace DiscImageChef.DiscImages byte[] lzmaProperties = compressedBlockStream.Properties; compressedBlockStream.Close(); - ddtHeader.cmpLength = (uint)blockStream.Length + LZMA_PROPERTIES_LENGTH; - Crc64Context.Data(blockStream.ToArray(), out byte[] cmpCrc64); - ddtHeader.cmpCrc64 = BitConverter.ToUInt64(cmpCrc64, 0); + 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)]; From 2f252709a094d28f38d5a9bce99eab9aa10757af Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Thu, 25 Jan 2018 17:33:03 +0000 Subject: [PATCH 12/20] For DiscImageChef format, enable resume appending. --- DiscImageChef.DiscImages/DiscImageChef.cs | 369 +++++++++++++++++++--- 1 file changed, 318 insertions(+), 51 deletions(-) diff --git a/DiscImageChef.DiscImages/DiscImageChef.cs b/DiscImageChef.DiscImages/DiscImageChef.cs index d8492a18..2de002c3 100644 --- a/DiscImageChef.DiscImages/DiscImageChef.cs +++ b/DiscImageChef.DiscImages/DiscImageChef.cs @@ -1619,7 +1619,6 @@ namespace DiscImageChef.DiscImages public bool IsWriting { get; private set; } public string ErrorMessage { get; private set; } - // TODO: Support resume public bool Create(string path, MediaType mediaType, Dictionary options, ulong sectors, uint sectorSize) { @@ -1698,62 +1697,304 @@ namespace DiscImageChef.DiscImages return false; } + 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; + } + + // TODO: Set correct version + header.application = "DiscImageChef"; + header.imageMajorVersion = DICF_VERSION; + header.imageMinorVersion = 0; + header.applicationMajorVersion = 4; + header.applicationMinorVersion = 0; + } + else + { + // TODO: Set correct version + header = new DicHeader + { + identifier = DIC_MAGIC, + application = "DiscImageChef", + imageMajorVersion = DICF_VERSION, + imageMinorVersion = 0, + applicationMajorVersion = 4, + applicationMinorVersion = 0, + mediaType = mediaType, + creationTime = DateTime.UtcNow.ToFileTimeUtc() + }; + + imageStream.Write(new byte[Marshal.SizeOf(typeof(DicHeader))], 0, Marshal.SizeOf(typeof(DicHeader))); + } + index = new List(); - // TODO: Set correct version - header = new DicHeader + if(header.indexOffset > 0) { - identifier = DIC_MAGIC, - application = "DiscImageChef", - imageMajorVersion = DICF_VERSION, - imageMinorVersion = 0, - applicationMajorVersion = 4, - applicationMinorVersion = 0, - mediaType = mediaType, - creationTime = DateTime.UtcNow.ToFileTimeUtc() - }; + 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); - inMemoryDdt = sectors <= maxDdtSize * 1024 * 1024 / sizeof(ulong); + 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); + imageInfo.ImageSize += ddtHeader.cmpLength; + + if(ddtHeader.identifier != BlockType.DeDuplicationTable) break; + + imageInfo.Sectors = ddtHeader.entries; + 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; + } + } + else + { + inMemoryDdt = sectors <= maxDdtSize * 1024 * 1024 / sizeof(ulong); + + if(inMemoryDdt) userDataDdt = new ulong[sectors]; + 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; + + // TODO: Can be changed to a seek? + imageStream.Write(new byte[sectors * sizeof(ulong)], 0, (int)(sectors * sizeof(ulong))); + } + } DicConsole.DebugWriteLine("DiscImageChef format plugin", "In memory DDT?: {0}", inMemoryDdt); - imageStream.Write(new byte[Marshal.SizeOf(typeof(DicHeader))], 0, Marshal.SizeOf(typeof(DicHeader))); - - if(inMemoryDdt) userDataDdt = new ulong[sectors]; - 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; - - // TODO: Can be changed to a seek? - imageStream.Write(new byte[sectors * sizeof(ulong)], 0, (int)(sectors * sizeof(ulong))); - } - + imageStream.Seek(0, SeekOrigin.End); mediaTags = new Dictionary(); checksumProvider = SHA256.Create(); deduplicationTable = new Dictionary(); @@ -1783,7 +2024,6 @@ namespace DiscImageChef.DiscImages return true; } - // TODO: Resume public bool WriteSector(byte[] data, ulong sectorAddress) { if(!IsWriting) @@ -2244,6 +2484,9 @@ namespace DiscImageChef.DiscImages 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); } @@ -2266,6 +2509,9 @@ namespace DiscImageChef.DiscImages Marshal.FreeHGlobal(structurePointer); imageStream.Write(structureBytes, 0, structureBytes.Length); + index.RemoveAll(t => t.blockType == BlockType.GeometryBlock && + t.dataType == DataType.NoData); + index.Add(idxEntry); } @@ -2323,6 +2569,9 @@ namespace DiscImageChef.DiscImages blockStream = null; compressedBlockStream = null; + index.RemoveAll(t => t.blockType == BlockType.DeDuplicationTable && + t.dataType == DataType.UserData); + index.Add(idxEntry); } @@ -2374,6 +2623,9 @@ namespace DiscImageChef.DiscImages 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 @@ -2419,6 +2671,9 @@ namespace DiscImageChef.DiscImages 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; } @@ -2468,6 +2723,9 @@ namespace DiscImageChef.DiscImages 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; } @@ -2519,6 +2777,9 @@ namespace DiscImageChef.DiscImages 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, @@ -2606,6 +2867,9 @@ namespace DiscImageChef.DiscImages 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; } @@ -2757,6 +3021,9 @@ namespace DiscImageChef.DiscImages 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, From 0946db6a8869247428f722dd282e8401d05065ce Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Thu, 25 Jan 2018 19:37:52 +0000 Subject: [PATCH 13/20] For DiscImageChef format, solve minor implementation bugs. --- DiscImageChef.DiscImages/DiscImageChef.cs | 68 ++++++++++------------- 1 file changed, 30 insertions(+), 38 deletions(-) diff --git a/DiscImageChef.DiscImages/DiscImageChef.cs b/DiscImageChef.DiscImages/DiscImageChef.cs index 2de002c3..faed6d6d 100644 --- a/DiscImageChef.DiscImages/DiscImageChef.cs +++ b/DiscImageChef.DiscImages/DiscImageChef.cs @@ -404,7 +404,7 @@ namespace DiscImageChef.DiscImages lzmaDdt.Read(decompressedDdt, 0, (int)ddtHeader.length); lzmaDdt.Close(); compressedDdtMs.Close(); - userDataDdt = new ulong[ddtHeader.entries]; + 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; @@ -677,6 +677,8 @@ namespace DiscImageChef.DiscImages trackIsrcs.Add(trackEntry.sequence, trackEntry.isrc); } + imageInfo.HasPartitions = true; + imageInfo.HasSessions = true; break; } } @@ -684,9 +686,9 @@ namespace DiscImageChef.DiscImages 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", imageInfo.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", + DicConsole.DebugWriteLine("DiscImageChef format plugin", "Image last written on {0}", imageInfo.LastModificationTime); if(geometryBlock.identifier != BlockType.GeometryBlock && imageInfo.XmlMediaType == XmlMediaType.BlockMedia) @@ -1721,35 +1723,29 @@ namespace DiscImageChef.DiscImages if(header.mediaType != mediaType) { - ErrorMessage = $"Cannot write a media with type {mediaType} to an image with type {header.mediaType}"; + ErrorMessage = + $"Cannot write a media with type {mediaType} to an image with type {header.mediaType}"; return false; } - - // TODO: Set correct version - header.application = "DiscImageChef"; - header.imageMajorVersion = DICF_VERSION; - header.imageMinorVersion = 0; - header.applicationMajorVersion = 4; - header.applicationMinorVersion = 0; } else { - // TODO: Set correct version header = new DicHeader { - identifier = DIC_MAGIC, - application = "DiscImageChef", - imageMajorVersion = DICF_VERSION, - imageMinorVersion = 0, - applicationMajorVersion = 4, - applicationMinorVersion = 0, - mediaType = mediaType, - creationTime = DateTime.UtcNow.ToFileTimeUtc() + 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(header.indexOffset > 0) @@ -1955,7 +1951,7 @@ namespace DiscImageChef.DiscImages else { inMemoryDdt = sectors <= maxDdtSize * 1024 * 1024 / sizeof(ulong); - + if(inMemoryDdt) userDataDdt = new ulong[sectors]; else { @@ -1987,8 +1983,8 @@ namespace DiscImageChef.DiscImages imageStream.Write(structureBytes, 0, structureBytes.Length); structureBytes = null; - // TODO: Can be changed to a seek? - imageStream.Write(new byte[sectors * sizeof(ulong)], 0, (int)(sectors * sizeof(ulong))); + imageStream.Position += (long)(sectors * sizeof(ulong)) - 1; + imageStream.WriteByte(0); } } @@ -2309,9 +2305,11 @@ namespace DiscImageChef.DiscImages return false; } - if(track.TrackStartSector + sectorAddress + length > track.TrackEndSector + 1) - throw new ArgumentOutOfRangeException(nameof(length), - $"Requested more sectors ({length + sectorAddress}) than present in track ({track.TrackEndSector - track.TrackStartSector + 1}), won't cross tracks"); + if(sectorAddress + length > track.TrackEndSector + 1) + { + ErrorMessage = "Can't cross tracks"; + return false; + } sector = new byte[2352]; for(uint i = 0; i < length; i++) @@ -2484,8 +2482,7 @@ namespace DiscImageChef.DiscImages imageStream.Write(lzmaProperties, 0, lzmaProperties.Length); imageStream.Write(tagData, 0, tagData.Length); - index.RemoveAll(t => t.blockType == BlockType.DataBlock && - t.dataType == dataType); + index.RemoveAll(t => t.blockType == BlockType.DataBlock && t.dataType == dataType); index.Add(idxEntry); } @@ -2509,8 +2506,7 @@ namespace DiscImageChef.DiscImages Marshal.FreeHGlobal(structurePointer); imageStream.Write(structureBytes, 0, structureBytes.Length); - index.RemoveAll(t => t.blockType == BlockType.GeometryBlock && - t.dataType == DataType.NoData); + index.RemoveAll(t => t.blockType == BlockType.GeometryBlock && t.dataType == DataType.NoData); index.Add(idxEntry); } @@ -2569,8 +2565,7 @@ namespace DiscImageChef.DiscImages blockStream = null; compressedBlockStream = null; - index.RemoveAll(t => t.blockType == BlockType.DeDuplicationTable && - t.dataType == DataType.UserData); + index.RemoveAll(t => t.blockType == BlockType.DeDuplicationTable && t.dataType == DataType.UserData); index.Add(idxEntry); } @@ -2777,8 +2772,7 @@ namespace DiscImageChef.DiscImages DicConsole.DebugWriteLine("DiscImageChef format plugin", "Writing tracks to position {0}", imageStream.Position); - index.RemoveAll(t => t.blockType == BlockType.TracksBlock && - t.dataType == DataType.NoData); + index.RemoveAll(t => t.blockType == BlockType.TracksBlock && t.dataType == DataType.NoData); index.Add(new IndexEntry { @@ -2867,8 +2861,7 @@ namespace DiscImageChef.DiscImages 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.RemoveAll(t => t.blockType == BlockType.DataBlock && t.dataType == tagType); index.Add(idxEntry); blockStream = null; @@ -3021,8 +3014,7 @@ namespace DiscImageChef.DiscImages Marshal.FreeHGlobal(structurePointer); blockStream.Position = 0; blockStream.Write(structureBytes, 0, structureBytes.Length); - index.RemoveAll(t => t.blockType == BlockType.MetadataBlock && - t.dataType == DataType.NoData); + index.RemoveAll(t => t.blockType == BlockType.MetadataBlock && t.dataType == DataType.NoData); index.Add(new IndexEntry { From 7c62f0ebd030a8fb6404443aa67f6009386a9974 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Thu, 25 Jan 2018 20:53:52 +0000 Subject: [PATCH 14/20] For DiscImageChef format, get metadata from drive descriptors if present. --- DiscImageChef.DiscImages/DiscImageChef.cs | 83 ++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/DiscImageChef.DiscImages/DiscImageChef.cs b/DiscImageChef.DiscImages/DiscImageChef.cs index faed6d6d..da6b6341 100644 --- a/DiscImageChef.DiscImages/DiscImageChef.cs +++ b/DiscImageChef.DiscImages/DiscImageChef.cs @@ -79,13 +79,16 @@ 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 { // TODO: Work in progress - // TODO: Get manufacurer, model, firmware, from tags, if available public class DiscImageChef : IWritableImage { const ulong DIC_MAGIC = 0x544D464444434944; @@ -787,6 +790,8 @@ namespace DiscImageChef.DiscImages Partitions = null; } + SetMetadataFromTags(); + return true; } @@ -2870,6 +2875,7 @@ namespace DiscImageChef.DiscImages break; } + SetMetadataFromTags(); MetadataBlock metadataBlock = new MetadataBlock(); blockStream = new MemoryStream(); blockStream.Write(new byte[Marshal.SizeOf(metadataBlock)], 0, Marshal.SizeOf(metadataBlock)); @@ -3242,6 +3248,81 @@ namespace DiscImageChef.DiscImages } } + void SetMetadataFromTags() + { + 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}"; + } + + 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}"; + } + + 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(); + } + } + + 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; + } + static XmlMediaType GetXmlMediaType(MediaType type) { switch(type) From fd6090296fb29a87fe05803e5d5d9b8bc3b37bfe Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Thu, 25 Jan 2018 20:55:06 +0000 Subject: [PATCH 15/20] For DiscImageChef format, added template. --- templates/dicformat.bt | 846 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 846 insertions(+) create mode 100644 templates/dicformat.bt diff --git a/templates/dicformat.bt b/templates/dicformat.bt new file mode 100644 index 00000000..8d01bbdb --- /dev/null +++ b/templates/dicformat.bt @@ -0,0 +1,846 @@ +//------------------------------------------------ +//--- 010 Editor v8.0.1 Binary Template +// +// File: dicformat.bt +// Authors: Natalia Portillo +// Version: 0.0 +// Purpose: DiscImageChef format +// Category: Misc +// File Mask: *.dicf +// ID Bytes: 44 49 43 44 44 46 4D 54 // DICDDFMT +// History: +// 0.0 2018-01-25 Natalia Portillo: Initial release +//------------------------------------------------ + +#define DIC_MAGIC 0x544D464444434944 + +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 +}; + +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 = 0x484B4C42, + DeDuplicationTable = 0x48544444, + Index = 0x48584449, + GeometryBlock = 0x4D4F4547, + MetadataBlock = 0x5444545D, + TracksBlock = 0x534B5254 +}; + +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; + +DisplayFormatHex(); +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 0x484B4C42: + BlockHeader dataBlock; + break; + case 0x48544444: + DdtHeader deduplicationTable; + break; + case 0x4D4F4547: + GeometryBlock geometry; + break; + case 0x5444545D: + 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 From c9349fc0e02048329499d6b9c41f4f2fab5bae76 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Thu, 25 Jan 2018 21:39:56 +0000 Subject: [PATCH 16/20] Add FLAC library: CUETools.NET's FLAKE implementation. --- .gitmodules | 3 + .../.idea/contentModel.xml | 64 +++++++ .idea/.idea.DiscImageChef/.idea/vcs.xml | 1 + .../DiscImageChef.Compression.csproj | 176 ++++++++++++++++++ 4 files changed, 244 insertions(+) diff --git a/.gitmodules b/.gitmodules index b0e82270..54f800a2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "CICMMetadata"] path = CICMMetadata url = https://github.com/claunia/CICMMetadata +[submodule "cuetoolsnet"] + path = cuetoolsnet + url = git@github.com:claunia/cuetoolsnet.git diff --git a/.idea/.idea.DiscImageChef/.idea/contentModel.xml b/.idea/.idea.DiscImageChef/.idea/contentModel.xml index 0dd37636..c06e9f14 100644 --- a/.idea/.idea.DiscImageChef/.idea/contentModel.xml +++ b/.idea/.idea.DiscImageChef/.idea/contentModel.xml @@ -86,6 +86,12 @@ + + + + + + @@ -969,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.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 From 04ba5a9820f9f4d1fb131ce5c06ba53223572f14 Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Thu, 25 Jan 2018 22:20:34 +0000 Subject: [PATCH 17/20] Add FLAC library: CUETools.NET's FLAKE implementation. --- cuetoolsnet | 1 + 1 file changed, 1 insertion(+) create mode 160000 cuetoolsnet diff --git a/cuetoolsnet b/cuetoolsnet new file mode 160000 index 00000000..42adab01 --- /dev/null +++ b/cuetoolsnet @@ -0,0 +1 @@ +Subproject commit 42adab0138833031c3b564ec002267da568d04b2 From 96cea8b821101cf06829f8fa38ddc5743a58921b Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Thu, 25 Jan 2018 22:23:54 +0000 Subject: [PATCH 18/20] Change submodule URL. --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 54f800a2..1cf09b6d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,4 +3,4 @@ url = https://github.com/claunia/CICMMetadata [submodule "cuetoolsnet"] path = cuetoolsnet - url = git@github.com:claunia/cuetoolsnet.git + url = https://github.com/claunia/cuetoolsnet.git From 2c700d17982ef6062901a745652869d9ff98cd4d Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Fri, 26 Jan 2018 18:36:42 +0000 Subject: [PATCH 19/20] For DiscImageChef format, use FLAC for audio tracks. --- DiscImageChef.DiscImages/DiscImageChef.cs | 208 ++++++++++++++++++---- templates/dicformat.bt | 4 +- 2 files changed, 173 insertions(+), 39 deletions(-) diff --git a/DiscImageChef.DiscImages/DiscImageChef.cs b/DiscImageChef.DiscImages/DiscImageChef.cs index da6b6341..68725e8f 100644 --- a/DiscImageChef.DiscImages/DiscImageChef.cs +++ b/DiscImageChef.DiscImages/DiscImageChef.cs @@ -75,6 +75,8 @@ 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; @@ -97,17 +99,24 @@ namespace DiscImageChef.DiscImages const int LZMA_PROPERTIES_LENGTH = 5; const int MAX_DDT_ENTRY_CACHE = 16000000; - Dictionary blockCache; - Dictionary blockHeaderCache; - MemoryStream blockStream; - SHA256 checksumProvider; - LzmaStream compressedBlockStream; - Crc64Context crc64; - BlockHeader currentBlockHeader; - uint currentBlockOffset; - uint currentCacheSize; - Dictionary ddtEntryCache; - Dictionary deduplicationTable; + const int SAMPLES_PER_SECTOR = 588; + const int MAX_FLAKE_BLOCK = 4608; + const int MIN_FLAKE_BLOCK = 256; + + Dictionary blockCache; + Dictionary blockHeaderCache; + MemoryStream blockStream; + SHA256 checksumProvider; + LzmaStream compressedBlockStream; + Crc64Context crc64; + BlockHeader currentBlockHeader; + uint currentBlockOffset; + uint currentCacheSize; + Dictionary ddtEntryCache; + Dictionary deduplicationTable; + FlakeWriter flakeWriter; + + FlakeWriterSettings flakeWriterSettings; GeometryBlock geometryBlock; DicHeader header; ImageInfo imageInfo; @@ -853,6 +862,18 @@ namespace DiscImageChef.DiscImages 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}"); @@ -1520,8 +1541,6 @@ namespace DiscImageChef.DiscImages verifyBytes = new byte[blockHeader.cmpLength - readBytes]; imageStream.Read(verifyBytes, 0, verifyBytes.Length); crcVerify.Update(verifyBytes); - readBytes += (ulong)verifyBytes.LongLength; - System.Console.WriteLine("Read {0} bytes of {1}", readBytes, blockHeader.cmpLength); verifyCrc = crcVerify.Final(); @@ -1655,7 +1674,7 @@ namespace DiscImageChef.DiscImages } else dictionary = 1 << 25; - if(options.TryGetValue("dictionary", out tmpValue)) + if(options.TryGetValue("max_ddt_size", out tmpValue)) { if(!uint.TryParse(tmpValue, out maxDdtSize)) { @@ -1901,12 +1920,17 @@ namespace DiscImageChef.DiscImages 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; + 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) { @@ -2002,7 +2026,32 @@ namespace DiscImageChef.DiscImages trackIsrcs = new Dictionary(); trackFlags = new Dictionary(); - lzmaEncoderProperties = new LzmaEncoderProperties(true, (int)dictionary, 255); + 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 + }; + + 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; @@ -2048,18 +2097,60 @@ namespace DiscImageChef.DiscImages return true; } + Track trk = new 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 && - (currentBlockHeader.sectorSize != data.Length || currentBlockOffset == 1 << shift)) + if(blockStream != null && + (currentBlockHeader.sectorSize != data.Length || + currentBlockOffset == 1 << shift || + currentBlockHeader.compression == CompressionType.Flac && + trk.TrackType != TrackType.Audio)) { currentBlockHeader.length = currentBlockOffset * currentBlockHeader.sectorSize; currentBlockHeader.crc64 = BitConverter.ToUInt64(crc64.Final(), 0); - byte[] lzmaProperties = compressedBlockStream.Properties; - compressedBlockStream.Close(); - currentBlockHeader.cmpLength = (uint)blockStream.Length + LZMA_PROPERTIES_LENGTH; + Crc64Context cmpCrc64Context = new Crc64Context(); cmpCrc64Context.Init(); - cmpCrc64Context.Update(lzmaProperties); + + 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 = compressedBlockStream.Properties; + compressedBlockStream.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); @@ -2077,7 +2168,8 @@ namespace DiscImageChef.DiscImages Marshal.FreeHGlobal(structurePointer); imageStream.Write(structureBytes, 0, structureBytes.Length); structureBytes = null; - imageStream.Write(lzmaProperties, 0, lzmaProperties.Length); + if(currentBlockHeader.compression == CompressionType.Lzma) + imageStream.Write(lzmaProperties, 0, lzmaProperties.Length); imageStream.Write(blockStream.ToArray(), 0, (int)blockStream.Length); blockStream = null; currentBlockOffset = 0; @@ -2086,22 +2178,35 @@ namespace DiscImageChef.DiscImages // No block set if(blockStream == null) { - blockStream = new MemoryStream(); - compressedBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); - currentBlockHeader = new BlockHeader + currentBlockHeader = new BlockHeader { identifier = BlockType.DataBlock, type = DataType.UserData, compression = CompressionType.Lzma, sectorSize = (uint)data.Length }; - crc64 = new Crc64Context(); + + 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 compressedBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); + crc64 = new Crc64Context(); crc64.Init(); } ulong ddtEntry = (ulong)((imageStream.Position << shift) + currentBlockOffset); deduplicationTable.Add(hash, ddtEntry); - compressedBlockStream.Write(data, 0, data.Length); + if(currentBlockHeader.compression == CompressionType.Flac) + { + AudioBuffer audioBuffer = new AudioBuffer(AudioPCMConfig.RedBook, data, SAMPLES_PER_SECTOR); + flakeWriter.Write(audioBuffer); + } + else compressedBlockStream.Write(data, 0, data.Length); + SetDdtEntry(sectorAddress, ddtEntry); crc64.Update(data); currentBlockOffset++; @@ -2397,12 +2502,40 @@ namespace DiscImageChef.DiscImages { currentBlockHeader.length = currentBlockOffset * currentBlockHeader.sectorSize; currentBlockHeader.crc64 = BitConverter.ToUInt64(crc64.Final(), 0); - byte[] lzmaProperties = compressedBlockStream.Properties; - compressedBlockStream.Close(); - currentBlockHeader.cmpLength = (uint)blockStream.Length + LZMA_PROPERTIES_LENGTH; + Crc64Context cmpCrc64Context = new Crc64Context(); cmpCrc64Context.Init(); - cmpCrc64Context.Update(lzmaProperties); + + 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 = compressedBlockStream.Properties; + compressedBlockStream.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); @@ -2420,9 +2553,9 @@ namespace DiscImageChef.DiscImages Marshal.FreeHGlobal(structurePointer); imageStream.Write(structureBytes, 0, structureBytes.Length); structureBytes = null; - imageStream.Write(lzmaProperties, 0, lzmaProperties.Length); + if(currentBlockHeader.compression == CompressionType.Lzma) + imageStream.Write(lzmaProperties, 0, lzmaProperties.Length); imageStream.Write(blockStream.ToArray(), 0, (int)blockStream.Length); - blockStream = null; } IndexEntry idxEntry; @@ -3598,7 +3731,8 @@ namespace DiscImageChef.DiscImages enum CompressionType : ushort { None = 0, - Lzma = 1 + Lzma = 1, + Flac = 2 } enum DataType : ushort diff --git a/templates/dicformat.bt b/templates/dicformat.bt index 8d01bbdb..cce68248 100644 --- a/templates/dicformat.bt +++ b/templates/dicformat.bt @@ -535,7 +535,8 @@ enum MediaType enum CompressionType { None = 0, - Lzma = 1 + Lzma = 1, + Flac = 2 }; enum DataType @@ -751,7 +752,6 @@ typedef struct TrackEntry tracks[entries]; } TracksHeader; -DisplayFormatHex(); LittleEndian(); local int i; From e3d27298d534196f55a72fbd3b098d3ab46b3d0b Mon Sep 17 00:00:00 2001 From: Natalia Portillo Date: Fri, 26 Jan 2018 22:32:58 +0000 Subject: [PATCH 20/20] Add DiscImageChef format version 1. --- DiscImageChef.DiscImages/DiscImageChef.cs | 583 +++++++++++++++------- templates/dicformat.bt | 33 +- 2 files changed, 416 insertions(+), 200 deletions(-) diff --git a/DiscImageChef.DiscImages/DiscImageChef.cs b/DiscImageChef.DiscImages/DiscImageChef.cs index 68725e8f..035513e8 100644 --- a/DiscImageChef.DiscImages/DiscImageChef.cs +++ b/DiscImageChef.DiscImages/DiscImageChef.cs @@ -50,13 +50,13 @@ Apple GCR sector tags). Optical disks contain a track block that describes the tracks. - Streaming tapes contain a file block that describes the files and an optional partition block that describes the tape + TODO: Streaming tapes contain a file block that describes the files and an optional partition block that describes the tape partitions. - There are also blocks for image metadata, contents metadata and dump hardware information. + 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. This is not yet implemented. + 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. @@ -90,51 +90,93 @@ using VendorString = DiscImageChef.Decoders.SecureDigital.VendorString; namespace DiscImageChef.DiscImages { - // TODO: Work in progress public class DiscImageChef : IWritableImage { - const ulong DIC_MAGIC = 0x544D464444434944; - const byte DICF_VERSION = 0; - const uint MAX_CACHE_SIZE = 256 * 1024 * 1024; - const int LZMA_PROPERTIES_LENGTH = 5; - const int MAX_DDT_ENTRY_CACHE = 16000000; - + /// 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; - const int MAX_FLAKE_BLOCK = 4608; - const int MIN_FLAKE_BLOCK = 256; + /// 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; - Dictionary blockCache; + /// Cache of uncompressed blocks. + Dictionary blockCache; + /// Cache of block headers. Dictionary blockHeaderCache; - MemoryStream blockStream; - SHA256 checksumProvider; - LzmaStream compressedBlockStream; - Crc64Context crc64; - BlockHeader currentBlockHeader; - uint currentBlockOffset; - uint currentCacheSize; - Dictionary ddtEntryCache; - Dictionary deduplicationTable; - FlakeWriter flakeWriter; - - FlakeWriterSettings flakeWriterSettings; - GeometryBlock geometryBlock; - DicHeader header; - ImageInfo imageInfo; - Stream imageStream; - List index; - bool inMemoryDdt; - LzmaEncoderProperties lzmaEncoderProperties; + /// 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; - long outMemoryDdtPosition; - byte[] sectorPrefix; - byte[] sectorSubchannel; - byte[] sectorSuffix; - byte shift; - byte[] structureBytes; - IntPtr structurePointer; - Dictionary trackFlags; - Dictionary trackIsrcs; - ulong[] userDataDdt; + /// 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() { @@ -212,6 +254,7 @@ namespace DiscImageChef.DiscImages 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)]; @@ -226,6 +269,7 @@ namespace DiscImageChef.DiscImages 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++) { @@ -296,6 +340,7 @@ namespace DiscImageChef.DiscImages "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]; @@ -322,6 +367,7 @@ namespace DiscImageChef.DiscImages 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) @@ -332,6 +378,7 @@ namespace DiscImageChef.DiscImages 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: @@ -401,6 +448,7 @@ namespace DiscImageChef.DiscImages imageInfo.Sectors = ddtHeader.entries; shift = ddtHeader.shift; + // Check for DDT compression switch(ddtHeader.compression) { case CompressionType.Lzma: @@ -436,6 +484,7 @@ namespace DiscImageChef.DiscImages 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)]; @@ -456,6 +505,7 @@ namespace DiscImageChef.DiscImages } break; + // Metadata block case BlockType.MetadataBlock: MetadataBlock metadataBlock = new MetadataBlock(); structureBytes = new byte[Marshal.SizeOf(metadataBlock)]; @@ -626,6 +676,7 @@ namespace DiscImageChef.DiscImages } break; + // Optical disc tracks block case BlockType.TracksBlock: TracksHeader tracksHeader = new TracksHeader(); structureBytes = new byte[Marshal.SizeOf(tracksHeader)]; @@ -719,6 +770,7 @@ namespace DiscImageChef.DiscImages currentCacheSize = 0; if(!inMemoryDdt) ddtEntryCache = new Dictionary(); + // Initialize tracks, sessions and partitions if(imageInfo.XmlMediaType == XmlMediaType.OpticalDisc) { if(Tracks == null || Tracks.Count == 0) @@ -827,6 +879,7 @@ namespace DiscImageChef.DiscImages byte[] sector; + // Check if block is cached if(blockCache.TryGetValue(blockOffset, out byte[] block) && blockHeaderCache.TryGetValue(blockOffset, out BlockHeader blockHeader)) { @@ -835,6 +888,7 @@ namespace DiscImageChef.DiscImages return sector; } + // Read block header imageStream.Position = (long)blockOffset; blockHeader = new BlockHeader(); structureBytes = new byte[Marshal.SizeOf(blockHeader)]; @@ -844,6 +898,7 @@ namespace DiscImageChef.DiscImages blockHeader = (BlockHeader)Marshal.PtrToStructure(structurePointer, typeof(BlockHeader)); Marshal.FreeHGlobal(structurePointer); + // Decompress block switch(blockHeader.compression) { case CompressionType.None: @@ -879,6 +934,7 @@ namespace DiscImageChef.DiscImages ImageNotSupportedException($"Found unsupported compression algorithm {(ushort)blockHeader.compression}"); } + // Check if cache needs to be emptied if(currentCacheSize + blockHeader.length >= MAX_CACHE_SIZE) { currentCacheSize = 0; @@ -886,6 +942,7 @@ namespace DiscImageChef.DiscImages blockCache = new Dictionary(); } + // Add block to cache currentCacheSize += blockHeader.length; blockHeaderCache.Add(blockOffset, blockHeader); blockCache.Add(blockOffset, block); @@ -1070,6 +1127,7 @@ namespace DiscImageChef.DiscImages dataSource = sectorPrefix; break; } + // These could be implemented case SectorTagType.CdSectorEcc: case SectorTagType.CdSectorEccP: case SectorTagType.CdSectorEccQ: @@ -1243,8 +1301,10 @@ namespace DiscImageChef.DiscImages 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); @@ -1261,6 +1321,7 @@ namespace DiscImageChef.DiscImages } return sectors; + // Join prefix (sync, header) with user data case TrackType.CdMode2Formless: case TrackType.CdMode2Form1: case TrackType.CdMode2Form2: @@ -1283,6 +1344,7 @@ namespace DiscImageChef.DiscImages case XmlMediaType.BlockMedia: switch(imageInfo.MediaType) { + // Join user data with tags case MediaType.AppleFileWare: case MediaType.AppleProfile: case MediaType.AppleSonySS: @@ -1376,6 +1438,7 @@ namespace DiscImageChef.DiscImages 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); @@ -1413,6 +1476,7 @@ namespace DiscImageChef.DiscImages 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(); @@ -1452,6 +1516,7 @@ namespace DiscImageChef.DiscImages 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; @@ -1502,6 +1567,7 @@ namespace DiscImageChef.DiscImages vrIndex.Add(entry); } + // Read up to 1MiB at a time for verification const int VERIFY_SIZE = 1024 * 1024; foreach(IndexEntry entry in vrIndex) @@ -1691,12 +1757,14 @@ namespace DiscImageChef.DiscImages 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) @@ -1723,6 +1791,7 @@ namespace DiscImageChef.DiscImages return false; } + // Check if appending to an existing image if(imageStream.Length > Marshal.SizeOf(typeof(DicHeader))) { header = new DicHeader(); @@ -1772,6 +1841,7 @@ namespace DiscImageChef.DiscImages index = new List(); + // If there exists an index, we are appending, so read index if(header.indexOffset > 0) { imageStream.Position = (long)header.indexOffset; @@ -1977,11 +2047,15 @@ namespace DiscImageChef.DiscImages 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; @@ -2019,6 +2093,7 @@ namespace DiscImageChef.DiscImages DicConsole.DebugWriteLine("DiscImageChef format plugin", "In memory DDT?: {0}", inMemoryDdt); + // Initialize tables imageStream.Seek(0, SeekOrigin.End); mediaTags = new Dictionary(); checksumProvider = SHA256.Create(); @@ -2026,6 +2101,7 @@ namespace DiscImageChef.DiscImages trackIsrcs = new Dictionary(); trackFlags = new Dictionary(); + // Initialize compressors properties (all maxed) lzmaEncoderProperties = new LzmaEncoderProperties(true, (int)dictionary, 273); flakeWriterSettings = new FlakeWriterSettings { @@ -2049,6 +2125,7 @@ namespace DiscImageChef.DiscImages 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"; @@ -2099,6 +2176,7 @@ namespace DiscImageChef.DiscImages Track trk = new Track(); + // If optical disc check track if(imageInfo.XmlMediaType == XmlMediaType.OpticalDisc) { trk = Tracks.FirstOrDefault(t => sectorAddress >= t.TrackStartSector && @@ -2109,11 +2187,13 @@ namespace DiscImageChef.DiscImages } // Close current block first - if(blockStream != null && - (currentBlockHeader.sectorSize != data.Length || - currentBlockOffset == 1 << shift || - currentBlockHeader.compression == CompressionType.Flac && - trk.TrackType != TrackType.Audio)) + 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); @@ -2143,8 +2223,8 @@ namespace DiscImageChef.DiscImages } else { - lzmaProperties = compressedBlockStream.Properties; - compressedBlockStream.Close(); + lzmaProperties = lzmaBlockStream.Properties; + lzmaBlockStream.Close(); cmpCrc64Context.Update(lzmaProperties); } @@ -2191,10 +2271,9 @@ namespace DiscImageChef.DiscImages blockStream = new MemoryStream(); if(currentBlockHeader.compression == CompressionType.Flac) - flakeWriter = - new FlakeWriter("", blockStream, flakeWriterSettings) {DoSeekTable = false}; - else compressedBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); - crc64 = new Crc64Context(); + flakeWriter = new FlakeWriter("", blockStream, flakeWriterSettings) {DoSeekTable = false}; + else lzmaBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); + crc64 = new Crc64Context(); crc64.Init(); } @@ -2205,7 +2284,7 @@ namespace DiscImageChef.DiscImages AudioBuffer audioBuffer = new AudioBuffer(AudioPCMConfig.RedBook, data, SAMPLES_PER_SECTOR); flakeWriter.Write(audioBuffer); } - else compressedBlockStream.Write(data, 0, data.Length); + else lzmaBlockStream.Write(data, 0, data.Length); SetDdtEntry(sectorAddress, ddtEntry); crc64.Update(data); @@ -2271,6 +2350,7 @@ namespace DiscImageChef.DiscImages 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: @@ -2298,6 +2378,7 @@ namespace DiscImageChef.DiscImages case XmlMediaType.BlockMedia: switch(imageInfo.MediaType) { + // Split user data from Apple tags case MediaType.AppleFileWare: case MediaType.AppleProfile: case MediaType.AppleSonyDS: @@ -2528,8 +2609,8 @@ namespace DiscImageChef.DiscImages } else { - lzmaProperties = compressedBlockStream.Properties; - compressedBlockStream.Close(); + lzmaProperties = lzmaBlockStream.Properties; + lzmaBlockStream.Close(); cmpCrc64Context.Update(lzmaProperties); } @@ -2560,6 +2641,7 @@ namespace DiscImageChef.DiscImages IndexEntry idxEntry; + // Write media tag blocks foreach(KeyValuePair mediaTag in mediaTags) { DataType dataType = GetDataTypeForMediaTag(mediaTag.Key); @@ -2583,11 +2665,11 @@ namespace DiscImageChef.DiscImages crc64 = BitConverter.ToUInt64(tagCrc, 0) }; - blockStream = new MemoryStream(); - compressedBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); - compressedBlockStream.Write(mediaTag.Value, 0, mediaTag.Value.Length); - byte[] lzmaProperties = compressedBlockStream.Properties; - compressedBlockStream.Close(); + 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 @@ -2607,8 +2689,8 @@ namespace DiscImageChef.DiscImages tagBlock.compression = CompressionType.Lzma; } - compressedBlockStream = null; - blockStream = null; + lzmaBlockStream = null; + blockStream = null; structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(tagBlock)); structureBytes = new byte[Marshal.SizeOf(tagBlock)]; @@ -2625,6 +2707,7 @@ namespace DiscImageChef.DiscImages index.Add(idxEntry); } + // If we have set the geometry block, write it if(geometryBlock.identifier == BlockType.GeometryBlock) { idxEntry = new IndexEntry @@ -2649,6 +2732,7 @@ namespace DiscImageChef.DiscImages index.Add(idxEntry); } + // If the DDT is in-memory, write it to disk if(inMemoryDdt) { idxEntry = new IndexEntry @@ -2671,19 +2755,19 @@ namespace DiscImageChef.DiscImages length = (ulong)(userDataDdt.LongLength * sizeof(ulong)) }; - blockStream = new MemoryStream(); - compressedBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); - crc64 = new Crc64Context(); + 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); - compressedBlockStream.Write(ddtEntry, 0, ddtEntry.Length); + lzmaBlockStream.Write(ddtEntry, 0, ddtEntry.Length); } - byte[] lzmaProperties = compressedBlockStream.Properties; - compressedBlockStream.Close(); + byte[] lzmaProperties = lzmaBlockStream.Properties; + lzmaBlockStream.Close(); ddtHeader.cmpLength = (uint)blockStream.Length + LZMA_PROPERTIES_LENGTH; Crc64Context cmpCrc64Context = new Crc64Context(); cmpCrc64Context.Init(); @@ -2700,14 +2784,15 @@ namespace DiscImageChef.DiscImages structureBytes = null; imageStream.Write(lzmaProperties, 0, lzmaProperties.Length); imageStream.Write(blockStream.ToArray(), 0, (int)blockStream.Length); - blockStream = null; - compressedBlockStream = null; + 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: @@ -2733,18 +2818,18 @@ namespace DiscImageChef.DiscImages crc64 = BitConverter.ToUInt64(blockCrc, 0) }; - blockStream = new MemoryStream(); - compressedBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); - compressedBlockStream.Write(sectorPrefix, 0, sectorPrefix.Length); - byte[] lzmaProperties = compressedBlockStream.Properties; - compressedBlockStream.Close(); + 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; - compressedBlockStream = null; + lzmaBlockStream = null; structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(prefixBlock)); structureBytes = new byte[Marshal.SizeOf(prefixBlock)]; @@ -2781,18 +2866,18 @@ namespace DiscImageChef.DiscImages crc64 = BitConverter.ToUInt64(blockCrc, 0) }; - blockStream = new MemoryStream(); - compressedBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); - compressedBlockStream.Write(sectorSuffix, 0, sectorSuffix.Length); - lzmaProperties = compressedBlockStream.Properties; - compressedBlockStream.Close(); + 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; - compressedBlockStream = null; + lzmaBlockStream = null; structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(prefixBlock)); structureBytes = new byte[Marshal.SizeOf(prefixBlock)]; @@ -2833,18 +2918,18 @@ namespace DiscImageChef.DiscImages crc64 = BitConverter.ToUInt64(blockCrc, 0) }; - blockStream = new MemoryStream(); - compressedBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); - compressedBlockStream.Write(sectorSubchannel, 0, sectorSubchannel.Length); - byte[] lzmaProperties = compressedBlockStream.Properties; - compressedBlockStream.Close(); + 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; - compressedBlockStream = null; + lzmaBlockStream = null; structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(subchannelBlock)); structureBytes = new byte[Marshal.SizeOf(subchannelBlock)]; @@ -2885,6 +2970,7 @@ namespace DiscImageChef.DiscImages }); } + // If there are tracks build the tracks block if(trackEntries.Count > 0) { blockStream = new MemoryStream(); @@ -2976,18 +3062,18 @@ namespace DiscImageChef.DiscImages crc64 = BitConverter.ToUInt64(blockCrc, 0) }; - blockStream = new MemoryStream(); - compressedBlockStream = new LzmaStream(lzmaEncoderProperties, false, blockStream); - compressedBlockStream.Write(sectorSubchannel, 0, sectorSubchannel.Length); - byte[] lzmaProperties = compressedBlockStream.Properties; - compressedBlockStream.Close(); + 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; - compressedBlockStream = null; + lzmaBlockStream = null; structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(subchannelBlock)); structureBytes = new byte[Marshal.SizeOf(subchannelBlock)]; @@ -3008,6 +3094,7 @@ namespace DiscImageChef.DiscImages break; } + // Write metadata if present SetMetadataFromTags(); MetadataBlock metadataBlock = new MetadataBlock(); blockStream = new MemoryStream(); @@ -3141,6 +3228,7 @@ namespace DiscImageChef.DiscImages 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}", @@ -3170,6 +3258,7 @@ namespace DiscImageChef.DiscImages blockStream = new MemoryStream(); + // Write index to memory foreach(IndexEntry entry in index) { structurePointer = Marshal.AllocHGlobal(Marshal.SizeOf(entry)); @@ -3189,6 +3278,7 @@ namespace DiscImageChef.DiscImages 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); @@ -3381,8 +3471,12 @@ namespace DiscImageChef.DiscImages } } + /// + /// 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); @@ -3397,6 +3491,7 @@ namespace DiscImageChef.DiscImages 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); @@ -3411,6 +3506,7 @@ namespace DiscImageChef.DiscImages imageInfo.DriveSerialNumber = $"{decoded.ProductSerialNumber}"; } + // Search for SCSI INQUIRY if(mediaTags.TryGetValue(MediaTagType.SCSI_INQUIRY, out byte[] scsiInquiry)) { Inquiry.SCSIInquiry? nullableInquiry = Inquiry.Decode(scsiInquiry); @@ -3428,6 +3524,7 @@ namespace DiscImageChef.DiscImages } } + // Search for ATA or ATAPI IDENTIFY if(!mediaTags.TryGetValue(MediaTagType.ATA_IDENTIFY, out byte[] ataIdentify) && !mediaTags.TryGetValue(MediaTagType.ATAPI_IDENTIFY, out ataIdentify)) return; @@ -3456,6 +3553,7 @@ namespace DiscImageChef.DiscImages imageInfo.DriveSerialNumber = identify.SerialNumber; } + // Get the CICM XML media type from DIC media type static XmlMediaType GetXmlMediaType(MediaType type) { switch(type) @@ -3542,6 +3640,7 @@ namespace DiscImageChef.DiscImages } } + // Gets a DDT entry ulong GetDdtEntry(ulong sectorAddress) { if(inMemoryDdt) return userDataDdt[sectorAddress]; @@ -3562,6 +3661,7 @@ namespace DiscImageChef.DiscImages return entry; } + // Sets a DDT entry void SetDdtEntry(ulong sectorAddress, ulong pointer) { if(inMemoryDdt) @@ -3577,6 +3677,7 @@ namespace DiscImageChef.DiscImages imageStream.Position = oldPosition; } + // Converts between image data type and dic media tag type static MediaTagType GetMediaTagTypeForDataType(DataType type) { switch(type) @@ -3652,6 +3753,7 @@ namespace DiscImageChef.DiscImages } } + // Converts between dic media tag type and image data type static DataType GetDataTypeForMediaTag(MediaTagType tag) { switch(tag) @@ -3728,100 +3830,201 @@ namespace DiscImageChef.DiscImages } } + /// 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 { - 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, + /// 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, - 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 + /// 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 { - DataBlock = 0x484B4C42, - DeDuplicationTable = 0x48544444, - Index = 0x48584449, - GeometryBlock = 0x4D4F4547, - MetadataBlock = 0x5444545D, - TracksBlock = 0x534B5254 + /// 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 @@ -3845,13 +4048,9 @@ namespace DiscImageChef.DiscImages 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 - /// + /// 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 - /// + /// Windows filetime (100 nanoseconds since 1601/01/01 00:00:00 UTC) of image last written time public long lastWrittenTime; } @@ -3998,6 +4197,7 @@ namespace DiscImageChef.DiscImages public uint driveFirmwareRevisionLength; } + /// Contains list of optical disc tracks [StructLayout(LayoutKind.Sequential, Pack = 1)] struct TracksHeader { @@ -4009,18 +4209,27 @@ namespace DiscImageChef.DiscImages public ulong crc64; } + /// Optical disc track [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)] struct TrackEntry { - public byte sequence; + /// Track sequence + public byte sequence; + /// Track type public TrackType type; - public long start; - public long end; - public long pregap; - public byte session; + /// 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; - public byte flags; + /// Track flags + public byte flags; } } } \ No newline at end of file diff --git a/templates/dicformat.bt b/templates/dicformat.bt index cce68248..cf34e2f9 100644 --- a/templates/dicformat.bt +++ b/templates/dicformat.bt @@ -3,16 +3,16 @@ // // File: dicformat.bt // Authors: Natalia Portillo -// Version: 0.0 +// Version: 1.0 // Purpose: DiscImageChef format // Category: Misc // File Mask: *.dicf -// ID Bytes: 44 49 43 44 44 46 4D 54 // DICDDFMT +// ID Bytes: 44 49 43 4D 46 52 4D 54 // DICMFRMT // History: -// 0.0 2018-01-25 Natalia Portillo: Initial release +// 1.0 2018-01-26 Natalia Portillo: Initial release //------------------------------------------------ -#define DIC_MAGIC 0x544D464444434944 +#define DIC_MAGIC 0x544D52464D434944 enum MediaType { @@ -620,12 +620,19 @@ enum DataType enum BlockType { - DataBlock = 0x484B4C42, - DeDuplicationTable = 0x48544444, - Index = 0x48584449, - GeometryBlock = 0x4D4F4547, - MetadataBlock = 0x5444545D, - TracksBlock = 0x534B5254 + 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 @@ -767,16 +774,16 @@ for(i = 0; i < index.entries; i++) FSeek(index.items[i].offset); switch(index.items[i].blockType) { - case 0x484B4C42: + case 0x4B4C4244: BlockHeader dataBlock; break; - case 0x48544444: + case 0x2A544444: DdtHeader deduplicationTable; break; case 0x4D4F4547: GeometryBlock geometry; break; - case 0x5444545D: + case 0x4154454D: MetadataBlock metadata; if(metadata.creatorOffset > 0) {