diff --git a/SabreTools.Core/Enums.cs b/SabreTools.Core/Enums.cs index 3359082c..8182d33a 100644 --- a/SabreTools.Core/Enums.cs +++ b/SabreTools.Core/Enums.cs @@ -1008,1280 +1008,6 @@ namespace SabreTools.Core #region FileTypes - /// - /// List of known blocks types - /// - /// - public enum AaruBlockType : uint - { - /// Block containing data - DataBlock = 0x4B4C4244, - /// Block containing a deduplication table - DeDuplicationTable = 0x2A544444, - /// Block containing the index - Index = 0x58444E49, - /// Block containing the index - Index2 = 0x32584449, - /// Block containing logical geometry - GeometryBlock = 0x4D4F4547, - /// Block containing metadata - MetadataBlock = 0x4154454D, - /// Block containing optical disc tracks - TracksBlock = 0x534B5254, - /// Block containing CICM XML metadata - CicmBlock = 0x4D434943, - /// 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, - /// Block containing an array of hardware used to create the image - DumpHardwareBlock = 0x2A504D44, - /// Block containing list of files for a tape image - TapeFileBlock = 0x454C4654, - /// Block containing list of partitions for a tape image - TapePartitionBlock = 0x54425054, - /// Block containing list of indexes for Compact Disc tracks - CompactDiscIndexesBlock = 0x58494443 - } - - /// - public enum AaruChecksumAlgorithm : byte - { - Invalid = 0, Md5 = 1, Sha1 = 2, - Sha256 = 3, SpamSum = 4 - } - - /// - /// List of known data types - /// - /// - public enum AaruDataType : ushort - { - /// No data - NoData = 0, - /// User data - UserData = 1, - /// CompactDisc partial Table of Contents - CompactDiscPartialToc = 2, - /// CompactDisc session information - CompactDiscSessionInfo = 3, - /// CompactDisc Table of Contents - CompactDiscToc = 4, - /// CompactDisc Power Management Area - CompactDiscPma = 5, - /// CompactDisc Absolute Time In Pregroove - CompactDiscAtip = 6, - /// CompactDisc Lead-in's CD-Text - CompactDiscLeadInCdText = 7, - /// DVD Physical Format Information - DvdPfi = 8, - /// DVD Lead-in's Copyright Management Information - DvdLeadInCmi = 9, - /// DVD Disc Key - DvdDiscKey = 10, - /// DVD Burst Cutting Area - DvdBca = 11, - /// DVD DMI - DvdDmi = 12, - /// DVD Media Identifier - DvdMediaIdentifier = 13, - /// DVD Media Key Block - DvdMediaKeyBlock = 14, - /// DVD-RAM Disc Definition Structure - DvdRamDds = 15, - /// DVD-RAM Medium Status - DvdRamMediumStatus = 16, - /// DVD-RAM Spare Area Information - DvdRamSpareArea = 17, - /// DVD-R RMD - DvdRRmd = 18, - /// DVD-R Pre-recorded Information - DvdRPrerecordedInfo = 19, - /// DVD-R Media Identifier - DvdRMediaIdentifier = 20, - /// DVD-R Physical Format Information - DvdRPfi = 21, - /// DVD ADress In Pregroove - DvdAdip = 22, - /// HD DVD Copy Protection Information - HdDvdCpi = 23, - /// HD DVD Medium Status - HdDvdMediumStatus = 24, - /// DVD DL Layer Capacity - DvdDlLayerCapacity = 25, - /// DVD DL Middle Zone Address - DvdDlMiddleZoneAddress = 26, - /// DVD DL Jump Interval Size - DvdDlJumpIntervalSize = 27, - /// DVD DL Manual Layer Jump LBA - DvdDlManualLayerJumpLba = 28, - /// Bluray Disc Information - BlurayDi = 29, - /// Bluray Burst Cutting Area - BlurayBca = 30, - /// Bluray Disc Definition Structure - BlurayDds = 31, - /// Bluray Cartridge Status - BlurayCartridgeStatus = 32, - /// Bluray Spare Area Information - BluraySpareArea = 33, - /// AACS Volume Identifier - AacsVolumeIdentifier = 34, - /// AACS Serial Number - AacsSerialNumber = 35, - /// AACS Media Identifier - AacsMediaIdentifier = 36, - /// AACS Media Key Block - AacsMediaKeyBlock = 37, - /// AACS Data Keys - AacsDataKeys = 38, - /// AACS LBA Extents - AacsLbaExtents = 39, - /// CPRM Media Key Block - CprmMediaKeyBlock = 40, - /// Recognized Layers - HybridRecognizedLayers = 41, - /// MMC Write Protection - ScsiMmcWriteProtection = 42, - /// MMC Disc Information - ScsiMmcDiscInformation = 43, - /// MMC Track Resources Information - ScsiMmcTrackResourcesInformation = 44, - /// MMC POW Resources Information - ScsiMmcPowResourcesInformation = 45, - /// SCSI INQUIRY RESPONSE - ScsiInquiry = 46, - /// SCSI MODE PAGE 2Ah - ScsiModePage2A = 47, - /// ATA IDENTIFY response - AtaIdentify = 48, - /// ATAPI IDENTIFY response - AtapiIdentify = 49, - /// PCMCIA CIS - PcmciaCis = 50, - /// SecureDigital CID - SecureDigitalCid = 51, - /// SecureDigital CSD - SecureDigitalCsd = 52, - /// SecureDigital SCR - SecureDigitalScr = 53, - /// SecureDigital OCR - SecureDigitalOcr = 54, - /// MultiMediaCard CID - MultiMediaCardCid = 55, - /// MultiMediaCard CSD - MultiMediaCardCsd = 56, - /// MultiMediaCard OCR - MultiMediaCardOcr = 57, - /// MultiMediaCard Extended CSD - MultiMediaCardExtendedCsd = 58, - /// Xbox Security Sector - XboxSecuritySector = 59, - /// Floppy Lead-out - FloppyLeadOut = 60, - /// Dvd Disc Control Block - DvdDiscControlBlock = 61, - /// CompactDisc First track pregap - CompactDiscFirstTrackPregap = 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, - /// CompactDisc Media Catalogue Number (as in Lead-in), 13 bytes, ASCII - CompactDiscMediaCatalogueNumber = 75, - /// CompactDisc sector prefix (sync, header), only incorrect stored - CdSectorPrefixCorrected = 76, - /// CompactDisc sector suffix (edc, ecc p, ecc q), only incorrect stored - CdSectorSuffixCorrected = 77, - /// CompactDisc MODE 2 subheader - CompactDiscMode2Subheader = 78, - /// CompactDisc Lead-in - CompactDiscLeadIn = 79 - } - - /// - /// Internal media format for AaruFormat - /// - /// - public enum AaruMediaType : uint - { - #region Generics, types 0 to 9 - /// Unknown disk type - 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, - /// USB flash drives - UnknownTape = 4, - #endregion Generics, types 0 to 9 - - #region Somewhat standard Compact Disc formats, types 10 to 39 - /// Any unknown or standard violating CD - 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, - /// - /// CD-i Ready, contains a track before the first TOC track, in mode 2, and all TOC tracks are Audio. Subchannel - /// marks track as audio pause. - /// - CDIREADY = 34, FMTOWNS = 35, - #endregion Somewhat standard Compact Disc formats, types 10 to 39 - - #region Standard DVD formats, types 40 to 50 - /// DVD-ROM (applies to DVD Video and DVD Audio) - DVDROM = 40, - /// DVD-R - DVDR = 41, - /// DVD-RW - DVDRW = 42, - /// DVD+R - DVDPR = 43, - /// DVD+RW - DVDPRW = 44, - /// DVD+RW DL - DVDPRWDL = 45, - /// DVD-R DL - DVDRDL = 46, - /// DVD+R DL - DVDPRDL = 47, - /// DVD-RAM - DVDRAM = 48, - /// DVD-RW DL - DVDRWDL = 49, - /// DVD-Download - DVDDownload = 50, - #endregion Standard DVD formats, types 40 to 50 - - #region Standard HD-DVD formats, types 51 to 59 - /// HD DVD-ROM (applies to HD DVD Video) - HDDVDROM = 51, - /// HD DVD-RAM - HDDVDRAM = 52, - /// HD DVD-R - HDDVDR = 53, - /// HD DVD-RW - HDDVDRW = 54, - /// HD DVD-R DL - HDDVDRDL = 55, - /// HD DVD-RW DL - HDDVDRWDL = 56, - #endregion Standard HD-DVD formats, types 51 to 59 - - #region Standard Blu-ray formats, types 60 to 69 - /// BD-ROM (and BD Video) - BDROM = 60, - /// BD-R - BDR = 61, - /// BD-RE - BDRE = 62, - /// BD-R XL - BDRXL = 63, - /// BD-RE XL - BDREXL = 64, - #endregion Standard Blu-ray formats, types 60 to 69 - - #region Rare or uncommon optical standards, types 70 to 79 - /// Enhanced Versatile Disc - EVD = 70, - /// Forward Versatile Disc - FVD = 71, - /// Holographic Versatile Disc - HVD = 72, - /// China Blue High Definition - CBHD = 73, - /// High Definition Versatile Multilayer Disc - HDVMD = 74, - /// Versatile Compact Disc High Density - VCDHD = 75, - /// Stacked Volumetric Optical Disc - SVOD = 76, - /// Five Dimensional disc - FDDVD = 77, - /// China Video Disc - CVD = 78, - #endregion Rare or uncommon optical standards, types 70 to 79 - - #region LaserDisc based, types 80 to 89 - /// Pioneer LaserDisc - LD = 80, - /// Pioneer LaserDisc data - LDROM = 81, LDROM2 = 82, LVROM = 83, MegaLD = 84, - #endregion LaserDisc based, types 80 to 89 - - #region MiniDisc based, types 90 to 99 - /// Sony Hi-MD - HiMD = 90, - /// Sony MiniDisc - MD = 91, - /// Sony MD-Data - MDData = 92, - /// Sony MD-Data2 - MDData2 = 93, - /// Sony MiniDisc, 60 minutes, formatted with Hi-MD format - MD60 = 94, - /// Sony MiniDisc, 74 minutes, formatted with Hi-MD format - MD74 = 95, - /// Sony MiniDisc, 80 minutes, formatted with Hi-MD format - MD80 = 96, - #endregion MiniDisc based, types 90 to 99 - - #region Plasmon UDO, types 100 to 109 - /// 5.25", Phase-Change, 1834348 sectors, 8192 bytes/sector, Ultra Density Optical, ECMA-350, ISO 17345 - UDO = 100, - /// 5.25", Phase-Change, 3669724 sectors, 8192 bytes/sector, Ultra Density Optical 2, ECMA-380, ISO 11976 - UDO2 = 101, - /// 5.25", Write-Once, 3668759 sectors, 8192 bytes/sector, Ultra Density Optical 2, ECMA-380, ISO 11976 - UDO2_WORM = 102, - #endregion Plasmon UDO, types 100 to 109 - - #region Sony game media, types 110 to 129 - PlayStationMemoryCard = 110, PlayStationMemoryCard2 = 111, - /// Sony PlayStation game CD - PS1CD = 112, - /// Sony PlayStation 2 game CD - PS2CD = 113, - /// Sony PlayStation 2 game DVD - PS2DVD = 114, - /// Sony PlayStation 3 game DVD - PS3DVD = 115, - /// Sony PlayStation 3 game Blu-ray - PS3BD = 116, - /// Sony PlayStation 4 game Blu-ray - PS4BD = 117, - /// Sony PlayStation Portable Universal Media Disc (ECMA-365) - UMD = 118, PlayStationVitaGameCard = 119, - #endregion Sony game media, types 110 to 129 - - #region Microsoft game media, types 130 to 149 - /// Microsoft X-box Game Disc - XGD = 130, - /// Microsoft X-box 360 Game Disc - XGD2 = 131, - /// Microsoft X-box 360 Game Disc - XGD3 = 132, - /// Microsoft X-box One Game Disc - XGD4 = 133, - #endregion Microsoft game media, types 130 to 149 - - #region Sega game media, types 150 to 169 - /// Sega MegaCD - MEGACD = 150, - /// Sega Saturn disc - SATURNCD = 151, - /// Sega/Yamaha Gigabyte Disc - GDROM = 152, - /// Sega/Yamaha recordable Gigabyte Disc - GDR = 153, SegaCard = 154, MilCD = 155, - #endregion Sega game media, types 150 to 169 - - #region Other game media, types 170 to 179 - /// PC-Engine / TurboGrafx cartridge - HuCard = 170, - /// PC-Engine / TurboGrafx CD - SuperCDROM2 = 171, - /// Atari Jaguar CD - JaguarCD = 172, - /// 3DO CD - ThreeDO = 173, - /// NEC PC-FX - PCFX = 174, - /// NEO-GEO CD - NeoGeoCD = 175, - /// Commodore CDTV - CDTV = 176, - /// Amiga CD32 - CD32 = 177, - /// Nuon (DVD based videogame console) - Nuon = 178, - /// Bandai Playdia - Playdia = 179, - #endregion Other game media, types 170 to 179 - - #region Apple standard floppy format, types 180 to 189 - /// 5.25", SS, DD, 35 tracks, 13 spt, 256 bytes/sector, GCR - Apple32SS = 180, - /// 5.25", DS, DD, 35 tracks, 13 spt, 256 bytes/sector, GCR - Apple32DS = 181, - /// 5.25", SS, DD, 35 tracks, 16 spt, 256 bytes/sector, GCR - Apple33SS = 182, - /// 5.25", DS, DD, 35 tracks, 16 spt, 256 bytes/sector, GCR - Apple33DS = 183, - /// 3.5", SS, DD, 80 tracks, 8 to 12 spt, 512 bytes/sector, GCR - AppleSonySS = 184, - /// 3.5", DS, DD, 80 tracks, 8 to 12 spt, 512 bytes/sector, GCR - AppleSonyDS = 185, - /// 5.25", DS, ?D, ?? tracks, ?? spt, 512 bytes/sector, GCR, opposite side heads, aka Twiggy - AppleFileWare = 186, - #endregion Apple standard floppy format - - #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 = 190, - /// 5.25", SS, DD, 40 tracks, 9 spt, 512 bytes/sector, MFM - DOS_525_SS_DD_9 = 191, - /// 5.25", DS, DD, 40 tracks, 8 spt, 512 bytes/sector, MFM - DOS_525_DS_DD_8 = 192, - /// 5.25", DS, DD, 40 tracks, 9 spt, 512 bytes/sector, MFM - DOS_525_DS_DD_9 = 193, - /// 5.25", DS, HD, 80 tracks, 15 spt, 512 bytes/sector, MFM - DOS_525_HD = 194, - /// 3.5", SS, DD, 80 tracks, 8 spt, 512 bytes/sector, MFM - DOS_35_SS_DD_8 = 195, - /// 3.5", SS, DD, 80 tracks, 9 spt, 512 bytes/sector, MFM - DOS_35_SS_DD_9 = 196, - /// 3.5", DS, DD, 80 tracks, 8 spt, 512 bytes/sector, MFM - DOS_35_DS_DD_8 = 197, - /// 3.5", DS, DD, 80 tracks, 9 spt, 512 bytes/sector, MFM - DOS_35_DS_DD_9 = 198, - /// 3.5", DS, HD, 80 tracks, 18 spt, 512 bytes/sector, MFM - DOS_35_HD = 199, - /// 3.5", DS, ED, 80 tracks, 36 spt, 512 bytes/sector, MFM - DOS_35_ED = 200, - /// 3.5", DS, HD, 80 tracks, 21 spt, 512 bytes/sector, MFM - DMF = 201, - /// 3.5", DS, HD, 82 tracks, 21 spt, 512 bytes/sector, MFM - 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 = 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 = 204, - #endregion IBM/Microsoft PC standard floppy formats, types 190 to 209 - - #region IBM standard floppy formats, types 210 to 219 - /// 8", SS, SD, 32 tracks, 8 spt, 319 bytes/sector, FM - IBM23FD = 210, - /// 8", SS, SD, 73 tracks, 26 spt, 128 bytes/sector, FM - IBM33FD_128 = 211, - /// 8", SS, SD, 74 tracks, 15 spt, 256 bytes/sector, FM, track 0 = 26 sectors, 128 bytes/sector - IBM33FD_256 = 212, - /// 8", SS, SD, 74 tracks, 8 spt, 512 bytes/sector, FM, track 0 = 26 sectors, 128 bytes/sector - IBM33FD_512 = 213, - /// 8", DS, SD, 74 tracks, 26 spt, 128 bytes/sector, FM, track 0 = 26 sectors, 128 bytes/sector - IBM43FD_128 = 214, - /// 8", DS, SD, 74 tracks, 26 spt, 256 bytes/sector, FM, track 0 = 26 sectors, 128 bytes/sector - 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 = 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 = 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 = 218, - #endregion IBM standard floppy formats, types 210 to 219 - - #region DEC standard floppy formats, types 220 to 229 - /// 8", SS, DD, 77 tracks, 26 spt, 128 bytes/sector, FM - RX01 = 220, - /// 8", SS, DD, 77 tracks, 26 spt, 256 bytes/sector, FM/MFM - RX02 = 221, - /// 8", DS, DD, 77 tracks, 26 spt, 256 bytes/sector, FM/MFM - RX03 = 222, - /// 5.25", SS, DD, 80 tracks, 10 spt, 512 bytes/sector, MFM - RX50 = 223, - #endregion DEC standard floppy formats, types 220 to 229 - - #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 = 230, - /// 5,25", SS, SD, 80 tracks, 10 spt, 256 bytes/sector, FM - ACORN_525_SS_SD_80 = 231, - /// 5,25", SS, DD, 40 tracks, 16 spt, 256 bytes/sector, MFM - ACORN_525_SS_DD_40 = 232, - /// 5,25", SS, DD, 80 tracks, 16 spt, 256 bytes/sector, MFM - ACORN_525_SS_DD_80 = 233, - /// 5,25", DS, DD, 80 tracks, 16 spt, 256 bytes/sector, MFM - ACORN_525_DS_DD = 234, - /// 3,5", DS, DD, 80 tracks, 5 spt, 1024 bytes/sector, MFM - ACORN_35_DS_DD = 235, - /// 3,5", DS, HD, 80 tracks, 10 spt, 1024 bytes/sector, MFM - ACORN_35_DS_HD = 236, - #endregion Acorn standard floppy formats, types 230 to 239 - - #region Atari standard floppy formats, types 240 to 249 - /// 5,25", SS, SD, 40 tracks, 18 spt, 128 bytes/sector, FM - ATARI_525_SD = 240, - /// 5,25", SS, ED, 40 tracks, 26 spt, 128 bytes/sector, MFM - ATARI_525_ED = 241, - /// 5,25", SS, DD, 40 tracks, 18 spt, 256 bytes/sector, MFM - ATARI_525_DD = 242, - /// 3,5", SS, DD, 80 tracks, 10 spt, 512 bytes/sector, MFM - ATARI_35_SS_DD = 243, - /// 3,5", DS, DD, 80 tracks, 10 spt, 512 bytes/sector, MFM - ATARI_35_DS_DD = 244, - /// 3,5", SS, DD, 80 tracks, 11 spt, 512 bytes/sector, MFM - ATARI_35_SS_DD_11 = 245, - /// 3,5", DS, DD, 80 tracks, 11 spt, 512 bytes/sector, MFM - ATARI_35_DS_DD_11 = 246, - #endregion Atari standard floppy formats, types 240 to 249 - - #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 = 250, - /// 3,5", DS, DD, 80 tracks, 11 spt, 512 bytes/sector, MFM (Amiga) - CBM_AMIGA_35_DD = 251, - /// 3,5", DS, HD, 80 tracks, 22 spt, 512 bytes/sector, MFM (Amiga) - CBM_AMIGA_35_HD = 252, - /// 5,25", SS, DD, 35 tracks, GCR - CBM_1540 = 253, - /// 5,25", SS, DD, 40 tracks, GCR - CBM_1540_Ext = 254, - /// 5,25", DS, DD, 35 tracks, GCR - CBM_1571 = 255, - #endregion Commodore standard floppy formats, types 250 to 259 - - #region NEC/SHARP standard floppy formats, types 260 to 269 - /// 8", DS, SD, 77 tracks, 26 spt, 128 bytes/sector, FM - NEC_8_SD = 260, - /// 8", DS, DD, 77 tracks, 26 spt, 256 bytes/sector, MFM - NEC_8_DD = 261, - /// 5.25", SS, SD, 80 tracks, 16 spt, 256 bytes/sector, FM - NEC_525_SS = 262, - /// 5.25", DS, SD, 80 tracks, 16 spt, 256 bytes/sector, MFM - NEC_525_DS = 263, - /// 5,25", DS, HD, 77 tracks, 8 spt, 1024 bytes/sector, MFM - NEC_525_HD = 264, - /// 3,5", DS, HD, 77 tracks, 8 spt, 1024 bytes/sector, MFM, aka mode 3 - NEC_35_HD_8 = 265, - /// 3,5", DS, HD, 80 tracks, 15 spt, 512 bytes/sector, MFM - NEC_35_HD_15 = 266, - /// 3,5", DS, TD, 240 tracks, 38 spt, 512 bytes/sector, MFM - NEC_35_TD = 267, - /// 5,25", DS, HD, 77 tracks, 8 spt, 1024 bytes/sector, MFM - SHARP_525 = NEC_525_HD, - /// 3,5", DS, HD, 80 tracks, 9 spt, 1024 bytes/sector, MFM - SHARP_525_9 = 268, - /// 3,5", DS, HD, 77 tracks, 8 spt, 1024 bytes/sector, MFM - SHARP_35 = NEC_35_HD_8, - /// 3,5", DS, HD, 80 tracks, 9 spt, 1024 bytes/sector, MFM - SHARP_35_9 = 269, - #endregion NEC/SHARP standard floppy formats, types 260 to 269 - - #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 = 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 = 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 = 272, - /// 3,5", DS, DD, 80 tracks, 9 spt, 512 bytes/sector, MFM - ECMA_100 = DOS_35_DS_DD_9, - /// 3,5", DS, HD, 80 tracks, 18 spt, 512 bytes/sector, MFM - ECMA_125 = DOS_35_HD, - /// 3,5", DS, ED, 80 tracks, 36 spt, 512 bytes/sector, MFM - ECMA_147 = DOS_35_ED, - /// 8", SS, SD, 77 tracks, 26 spt, 128 bytes/sector, FM - ECMA_54 = 273, - /// 8", DS, SD, 77 tracks, 26 spt, 128 bytes/sector, FM - 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 = 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 = 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 = 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 = 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 = 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 = 280, - /// 5,25", DS, DD, 80 tracks, 9 spt, 512 bytes/sector, FM - ECMA_78_2 = 281, - #endregion ECMA floppy standards, types 270 to 289 - - #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 = 290, - /// 5,25", DS, HD, 82 tracks, 17 spt, 512 bytes/sector, MFM - FDFORMAT_525_HD = 291, - /// 3,5", DS, DD, 82 tracks, 10 spt, 512 bytes/sector, MFM - FDFORMAT_35_DD = 292, - /// 3,5", DS, HD, 82 tracks, 21 spt, 512 bytes/sector, MFM - FDFORMAT_35_HD = 293, - #endregion Non-standard PC formats (FDFORMAT, 2M, etc), types 290 to 308 - - #region Apricot ACT standard floppy formats, type 309 - /// 3.5", DS, DD, 70 tracks, 9 spt, 512 bytes/sector, MFM - Apricot_35 = 309, - #endregion Apricot ACT standard floppy formats, type 309 - - #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, 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, types 340 to 359 - /// Obsolete type for 8"x11" Bernoulli Box disk - [Obsolete] - Bernoulli = 340, - /// Obsolete type for 5⅓" Bernoulli Box II disks - [Obsolete] - Bernoulli2 = 341, Ditto = 342, DittoMax = 343, Jaz = 344, - Jaz2 = 345, PocketZip = 346, REV120 = 347, - REV35 = 348, REV70 = 349, ZIP100 = 350, - ZIP250 = 351, ZIP750 = 352, - /// 5⅓" Bernoulli Box II disk with 35Mb capacity - Bernoulli35 = 353, - /// 5⅓" Bernoulli Box II disk with 44Mb capacity - Bernoulli44 = 354, - /// 5⅓" Bernoulli Box II disk with 65Mb capacity - Bernoulli65 = 355, - /// 5⅓" Bernoulli Box II disk with 90Mb capacity - Bernoulli90 = 356, - /// 5⅓" Bernoulli Box II disk with 105Mb capacity - Bernoulli105 = 357, - /// 5⅓" Bernoulli Box II disk with 150Mb capacity - Bernoulli150 = 358, - /// 5⅓" Bernoulli Box II disk with 230Mb capacity - Bernoulli230 = 359, - #endregion Iomega, types 340 to 359 - - #region Audio or video media, types 360 to 369 - CompactCassette = 360, Data8 = 361, MiniDV = 362, - /// D/CAS-25: Digital data on Compact Cassette form factor, special magnetic media, 9-track - Dcas25 = 363, - /// D/CAS-85: Digital data on Compact Cassette form factor, special magnetic media, 17-track - Dcas85 = 364, - /// D/CAS-103: Digital data on Compact Cassette form factor, special magnetic media, 21-track - Dcas103 = 365, - #endregion Audio media, types 360 to 369 - - #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, 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, 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, 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, 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, types 430 to 449 - /// SyQuest 135Mb cartridge for use in EZ135 and EZFlyer drives - EZ135 = 430, - /// SyQuest EZFlyer 230Mb cartridge for use in EZFlyer drive - EZ230 = 431, - /// SyQuest 4.7Gb for use in Quest drive - Quest = 432, - /// SyQuest SparQ 1Gb cartridge - SparQ = 433, - /// SyQuest 5Mb cartridge for SQ306RD drive - SQ100 = 434, - /// SyQuest 10Mb cartridge for SQ312RD drive - SQ200 = 435, - /// SyQuest 15Mb cartridge for SQ319RD drive - SQ300 = 436, - /// SyQuest 105Mb cartridge for SQ3105 and SQ3270 drives - SQ310 = 437, - /// SyQuest 270Mb cartridge for SQ3270 drive - SQ327 = 438, - /// SyQuest 44Mb cartridge for SQ555, SQ5110 and SQ5200C/SQ200 drives - SQ400 = 439, - /// SyQuest 88Mb cartridge for SQ5110 and SQ5200C/SQ200 drives - SQ800 = 440, - /// SyQuest 1.5Gb cartridge for SyJet drive - [Obsolete] - SQ1500 = 441, - /// SyQuest 200Mb cartridge for use in SQ5200C drive - SQ2000 = 442, - /// SyQuest 1.5Gb cartridge for SyJet drive - SyJet = 443, - #endregion SyQuest, types 430 to 449 - - #region Nintendo, types 450 to 469 - FamicomGamePak = 450, GameBoyAdvanceGamePak = 451, GameBoyGamePak = 452, - /// Nintendo GameCube Optical Disc - 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 = 463, - /// Nintendo Wii U Optical Disc - WUOD = 464, SwitchGameCard = 465, - #endregion Nintendo, types 450 to 469 - - #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, 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, types 510 to 519 - MemoryStick = 510, MemoryStickDuo = 511, MemoryStickMicro = 512, - MemoryStickPro = 513, MemoryStickProDuo = 514, - #endregion MemoryStick, types 510 to 519 - - #region SecureDigital, types 520 to 529 - microSD = 520, miniSD = 521, SecureDigital = 522, - #endregion SecureDigital, types 520 to 529 - - #region MultiMediaCard, types 530 to 539 - MMC = 530, MMCmicro = 531, RSMMC = 532, - MMCplus = 533, MMCmobile = 534, - #endregion MultiMediaCard, types 530 to 539 - - #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, 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, 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, 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, types 620 to 629 - VXA1 = 620, VXA2 = 621, VXA3 = 622, - #endregion VXA, types 620 to 629 - - #region Magneto-optical, types 630 to 659 - /// 5,25", M.O., WORM, 650Mb, 318750 sectors, 1024 bytes/sector, ECMA-153, ISO 11560 - ECMA_153 = 630, - /// 5,25", M.O., WORM, 600Mb, 581250 sectors, 512 bytes/sector, ECMA-153, ISO 11560 - ECMA_153_512 = 631, - /// 3,5", M.O., RW, 128Mb, 248826 sectors, 512 bytes/sector, ECMA-154, ISO 10090 - ECMA_154 = 632, - /// 5,25", M.O., RW/WORM, 1Gb, 904995 sectors, 512 bytes/sector, ECMA-183, ISO 13481 - ECMA_183_512 = 633, - /// 5,25", M.O., RW/WORM, 1Gb, 498526 sectors, 1024 bytes/sector, ECMA-183, ISO 13481 - ECMA_183 = 634, - /// 5,25", M.O., RW/WORM, 1.2Gb, 1165600 sectors, 512 bytes/sector, ECMA-184, ISO 13549 - ECMA_184_512 = 635, - /// 5,25", M.O., RW/WORM, 1.3Gb, 639200 sectors, 1024 bytes/sector, ECMA-184, ISO 13549 - ECMA_184 = 636, - /// 300mm, M.O., WORM, ??? sectors, 1024 bytes/sector, ECMA-189, ISO 13614 - ECMA_189 = 637, - /// 300mm, M.O., WORM, ??? sectors, 1024 bytes/sector, ECMA-190, ISO 13403 - ECMA_190 = 638, - /// 5,25", M.O., RW/WORM, 936921 or 948770 sectors, 1024 bytes/sector, ECMA-195, ISO 13842 - ECMA_195 = 639, - /// 5,25", M.O., RW/WORM, 1644581 or 1647371 sectors, 512 bytes/sector, ECMA-195, ISO 13842 - ECMA_195_512 = 640, - /// 3,5", M.O., 446325 sectors, 512 bytes/sector, ECMA-201, ISO 13963 - ECMA_201 = 641, - /// 3,5", M.O., 429975 sectors, 512 bytes/sector, embossed, ISO 13963 - ECMA_201_ROM = 642, - /// 3,5", M.O., 371371 sectors, 1024 bytes/sector, ECMA-223 - ECMA_223 = 643, - /// 3,5", M.O., 694929 sectors, 512 bytes/sector, ECMA-223 - ECMA_223_512 = 644, - /// 5,25", M.O., 1244621 sectors, 1024 bytes/sector, ECMA-238, ISO 15486 - ECMA_238 = 645, - /// 3,5", M.O., 310352, 320332 or 321100 sectors, 2048 bytes/sector, ECMA-239, ISO 15498 - ECMA_239 = 646, - /// 356mm, M.O., 14476734 sectors, 1024 bytes/sector, ECMA-260, ISO 15898 - ECMA_260 = 647, - /// 356mm, M.O., 24445990 sectors, 1024 bytes/sector, ECMA-260, ISO 15898 - ECMA_260_Double = 648, - /// 5,25", M.O., 1128134 sectors, 2048 bytes/sector, ECMA-280, ISO 18093 - ECMA_280 = 649, - /// 300mm, M.O., 7355716 sectors, 2048 bytes/sector, ECMA-317, ISO 20162 - ECMA_317 = 650, - /// 5,25", M.O., 1095840 sectors, 4096 bytes/sector, ECMA-322, ISO 22092 - ECMA_322 = 651, - /// 5,25", M.O., 2043664 sectors, 2048 bytes/sector, ECMA-322, ISO 22092 - ECMA_322_2k = 652, - /// 3,5", M.O., 605846 sectors, 2048 bytes/sector, Cherry Book, GigaMo, ECMA-351, ISO 17346 - GigaMo = 653, - /// 3,5", M.O., 1063146 sectors, 2048 bytes/sector, Cherry Book 2, GigaMo 2, ECMA-353, ISO 22533 - GigaMo2 = 654, - #endregion Magneto-optical, types 630 to 659 - - #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 = 662, HiFD = 663, QuickDisk = 664, UHD144 = 665, - VideoFloppy = 666, Wafer = 667, ZXMicrodrive = 668, - #endregion Other floppy standards, types 660 to 669 - - #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 specific media, types 690 to 699 - AppleProfile = 690, AppleWidget = 691, AppleHD20 = 692, - PriamDataTower = 693, Pippin = 694, - #endregion Apple specific media, types 690 to 699 - - #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 = 700, - /// - /// 546 cylinders, 14 tracks/cylinder, 31 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, - /// 121325568 bytes - /// - RA80 = 701, - /// - /// 1248 cylinders, 14 tracks/cylinder, 51 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, - /// 456228864 bytes - /// - RA81 = 702, - /// - /// 302 cylinders, 4 tracks/cylinder, 42 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, 25976832 - /// bytes - /// - RC25 = 703, - /// - /// 615 cylinders, 4 tracks/cylinder, 17 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, 21411840 - /// bytes - /// - RD31 = 704, - /// - /// 820 cylinders, 6 tracks/cylinder, 17 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, 42823680 - /// bytes - /// - RD32 = 705, - /// - /// 306 cylinders, 4 tracks/cylinder, 17 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, 10653696 - /// bytes - /// - RD51 = 706, - /// - /// 480 cylinders, 7 tracks/cylinder, 18 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, 30965760 - /// bytes - /// - RD52 = 707, - /// - /// 1024 cylinders, 7 tracks/cylinder, 18 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, - /// 75497472 bytes - /// - RD53 = 708, - /// - /// 1225 cylinders, 8 tracks/cylinder, 18 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, - /// 159936000 bytes - /// - RD54 = 709, - /// - /// 411 cylinders, 3 tracks/cylinder, 22 sectors/track, 256 words/sector, 16 bits/word, 512 bytes/sector, 13888512 - /// bytes - /// - RK06 = 710, - /// - /// 411 cylinders, 3 tracks/cylinder, 20 sectors/track, 256 words/sector, 18 bits/word, 576 bytes/sector, 14204160 - /// bytes - /// - RK06_18 = 711, - /// - /// 815 cylinders, 3 tracks/cylinder, 22 sectors/track, 256 words/sector, 16 bits/word, 512 bytes/sector, 27540480 - /// bytes - /// - RK07 = 712, - /// - /// 815 cylinders, 3 tracks/cylinder, 20 sectors/track, 256 words/sector, 18 bits/word, 576 bytes/sector, 28166400 - /// bytes - /// - RK07_18 = 713, - /// - /// 823 cylinders, 5 tracks/cylinder, 32 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, 67420160 - /// bytes - /// - RM02 = 714, - /// - /// 823 cylinders, 5 tracks/cylinder, 32 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, 67420160 - /// bytes - /// - RM03 = 715, - /// - /// 823 cylinders, 19 tracks/cylinder, 32 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, - /// 256196608 bytes - /// - RM05 = 716, - /// - /// 203 cylinders, 10 tracks/cylinder, 22 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, - /// 22865920 bytes - /// - RP02 = 717, - /// - /// 203 cylinders, 10 tracks/cylinder, 20 sectors/track, 128 words/sector, 36 bits/word, 576 bytes/sector, - /// 23385600 bytes - /// - RP02_18 = 718, - /// - /// 400 cylinders, 10 tracks/cylinder, 22 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, - /// 45056000 bytes - /// - RP03 = 719, - /// - /// 400 cylinders, 10 tracks/cylinder, 20 sectors/track, 128 words/sector, 36 bits/word, 576 bytes/sector, - /// 46080000 bytes - /// - RP03_18 = 720, - /// - /// 411 cylinders, 19 tracks/cylinder, 22 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, - /// 87960576 bytes - /// - RP04 = 721, - /// - /// 411 cylinders, 19 tracks/cylinder, 20 sectors/track, 128 words/sector, 36 bits/word, 576 bytes/sector, - /// 89959680 bytes - /// - RP04_18 = 722, - /// - /// 411 cylinders, 19 tracks/cylinder, 22 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, - /// 87960576 bytes - /// - RP05 = 723, - /// - /// 411 cylinders, 19 tracks/cylinder, 20 sectors/track, 128 words/sector, 36 bits/word, 576 bytes/sector, - /// 89959680 bytes - /// - RP05_18 = 724, - /// - /// 815 cylinders, 19 tracks/cylinder, 22 sectors/track, 128 words/sector, 32 bits/word, 512 bytes/sector, - /// 174423040 bytes - /// - RP06 = 725, - /// - /// 815 cylinders, 19 tracks/cylinder, 20 sectors/track, 128 words/sector, 36 bits/word, 576 bytes/sector, - /// 178387200 bytes - /// - RP06_18 = 726, - #endregion DEC hard disks, types 700 to 729 - - #region Imation, types 730 to 739 - LS120 = 730, LS240 = 731, FD32MB = 732, - RDX = 733, - /// Imation 320Gb RDX - RDX320 = 734, - #endregion Imation, types 730 to 739 - - #region VideoNow, types 740 to 749 - VideoNow = 740, VideoNowColor = 741, VideoNowXp = 742, - #endregion - - #region Iomega, types 750 to 759 - /// 8"x11" Bernoulli Box disk with 10Mb capacity - Bernoulli10 = 750, - /// 8"x11" Bernoulli Box disk with 20Mb capacity - Bernoulli20 = 751, - /// 5⅓" Bernoulli Box II disk with 20Mb capacity - BernoulliBox2_20 = 752, - #endregion Iomega, types 750 to 759 - - #region Kodak, types 760 to 769 - KodakVerbatim3 = 760, KodakVerbatim6 = 761, KodakVerbatim12 = 762, - #endregion Kodak, types 760 to 769 - - #region Sony and Panasonic Blu-ray derived, types 770 to 799 - /// Professional Disc for video, single layer, rewritable, 23Gb - ProfessionalDisc = 770, - /// Professional Disc for video, dual layer, rewritable, 50Gb - ProfessionalDiscDual = 771, - /// Professional Disc for video, triple layer, rewritable, 100Gb - ProfessionalDiscTriple = 772, - /// Professional Disc for video, quad layer, write once, 128Gb - ProfessionalDiscQuad = 773, - /// Professional Disc for DATA, single layer, rewritable, 23Gb - PDD = 774, - /// Professional Disc for DATA, single layer, write once, 23Gb - PDD_WORM = 775, - /// Archival Disc, 1st gen., 300Gb - ArchivalDisc = 776, - /// Archival Disc, 2nd gen., 500Gb - ArchivalDisc2 = 777, - /// Archival Disc, 3rd gen., 1Tb - ArchivalDisc3 = 778, - /// Optical Disc archive, 1st gen., write once, 300Gb - ODC300R = 779, - /// Optical Disc archive, 1st gen., rewritable, 300Gb - ODC300RE = 780, - /// Optical Disc archive, 2nd gen., write once, 600Gb - ODC600R = 781, - /// Optical Disc archive, 2nd gen., rewritable, 600Gb - ODC600RE = 782, - /// Optical Disc archive, 3rd gen., rewritable, 1200Gb - ODC1200RE = 783, - /// Optical Disc archive, 3rd gen., write once, 1500Gb - ODC1500R = 784, - /// Optical Disc archive, 4th gen., write once, 3300Gb - ODC3300R = 785, - /// Optical Disc archive, 5th gen., write once, 5500Gb - ODC5500R = 786 - #endregion Sony and Panasonic Blu-ray derived, types 770 to 799 - } - - /// - /// Compression being used in CHD - /// - public enum CHDCompression : uint - { - CHDCOMPRESSION_NONE = 0, - CHDCOMPRESSION_ZLIB = 1, - CHDCOMPRESSION_ZLIB_PLUS = 2, - CHDCOMPRESSION_AV = 3, - } - - /// - /// Availible CHD codec formats - /// - public enum CHD_CODEC : uint - { - NONE = 0, - - #region General Codecs - - ZLIB = 0x7a6c6962, // zlib - LZMA = 0x6c7a6d61, // lzma - HUFFMAN = 0x68756666, // huff - FLAC = 0x666c6163, // flac - - #endregion - - #region General Codecs with CD Frontend - - CD_ZLIB = 0x63647a6c, // cdzl - CD_LZMA = 0x63646c7a, // cdlz - CD_FLAC = 0x6364666c, // cdfl - - #endregion - - #region A/V Codecs - - AVHUFF = 0x61766875, // avhu - - #endregion - - #region Pseudo-Codecs Returned by hunk_info - - SELF = 1, // copy of another hunk - PARENT = 2, // copy of a parent's hunk - MINI = 3, // legacy "mini" 8-byte repeat - - #endregion - } - - /// - /// Compression method based on flag - /// - public enum CompressionMethod : ushort - { - Stored = 0, - Shrunk = 1, - ReducedCompressionFactor1 = 2, - ReducedCompressionFactor2 = 3, - ReducedCompressionFactor3 = 4, - ReducedCompressionFactor4 = 5, - Imploded = 6, - Tokenizing = 7, - Deflated = 8, - Delfate64 = 9, - PKWAREDataCompressionLibrary = 10, - Type11 = 11, // Reserved and unused (SHOULD NOT BE USED) - BZIP2 = 12, - Type13 = 13, // Reserved and unused (SHOULD NOT BE USED) - LZMA = 14, - Type15 = 15, // Reserved and unused (SHOULD NOT BE USED) - Type16 = 16, // Reserved and unused (SHOULD NOT BE USED) - Type17 = 17, // Reserved and unused (SHOULD NOT BE USED) - IBMTERSE = 18, - IBMLZ77 = 19, - WavPak = 97, - PPMdVersionIRev1 = 98, - } - - /// - /// Type of file that is being looked at - /// - public enum FileType - { - // Singleton - None = 0, - AaruFormat, - CHD, - - // Can contain children - Folder, - SevenZipArchive, - GZipArchive, - LRZipArchive, - LZ4Archive, - RarArchive, - TapeArchive, - XZArchive, - ZipArchive, - ZPAQArchive, - ZstdArchive, - } - /// /// Output format for rebuilt files /// diff --git a/SabreTools.Core/Globals.cs b/SabreTools.Core/Globals.cs index 59c212cb..9206765a 100644 --- a/SabreTools.Core/Globals.cs +++ b/SabreTools.Core/Globals.cs @@ -12,11 +12,6 @@ namespace SabreTools.Core { #region Public accessors - /// - /// Command line arguments passed in to the parent program - /// - public static string CommandLineArgs => string.Join(" ", Environment.GetCommandLineArgs()); - /// /// Directory path for the current executable /// @@ -43,6 +38,7 @@ namespace SabreTools.Core /// /// Temporary directory location /// + /// TODO: Find a way to get rid of this as a global variable and put it in DatFile public static string TempDir { get; set; } = Path.GetTempPath(); #endregion diff --git a/SabreTools.DatFiles/DatFile.DFD.cs b/SabreTools.DatFiles/DatFile.DFD.cs new file mode 100644 index 00000000..734945a0 --- /dev/null +++ b/SabreTools.DatFiles/DatFile.DFD.cs @@ -0,0 +1,421 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +using SabreTools.Core; +using SabreTools.DatItems; +using SabreTools.FileTypes; +using SabreTools.IO; + +// This file represents all methods related to creating a DatFile +// from a set of files and directories +namespace SabreTools.DatFiles +{ + // TODO: See if any of the methods can be broken up a bit more neatly + public abstract partial class DatFile + { + /// + /// Create a new Dat from a directory + /// + /// Base folder to be used in creating the DAT + /// TreatAsFiles representing CHD and Archive scanning + /// Type of files that should be skipped + /// True if blank items should be created for empty folders, false otherwise + /// Hashes to include in the information + public bool PopulateFromDir( + string basePath, + TreatAsFile asFiles = 0x00, + SkipFileType skipFileType = SkipFileType.None, + bool addBlanks = false, + Hash hashes = Hash.Standard) + { + // Clean the temp directory path + Globals.TempDir = DirectoryExtensions.Ensure(Globals.TempDir, temp: true); + + // Set the progress variables + long totalSize = 0; + long currentSize = 0; + + // Process the input + if (Directory.Exists(basePath)) + { + logger.Verbose($"Folder found: {basePath}"); + + // Get a list of all files to process + List files = Directory.EnumerateFiles(basePath, "*", SearchOption.AllDirectories).ToList(); + + // Loop through and add the file sizes + Parallel.ForEach(files, Globals.ParallelOptions, item => + { + Interlocked.Add(ref totalSize, new FileInfo(item).Length); + }); + + // Process the files in the main folder or any subfolder + logger.User(totalSize, currentSize); + foreach (string item in files) + { + CheckFileForHashes(item, basePath, asFiles, skipFileType, addBlanks, hashes); + currentSize += new FileInfo(item).Length; + logger.User(totalSize, currentSize, item); + } + + // Now find all folders that are empty, if we are supposed to + if (addBlanks) + ProcessDirectoryBlanks(basePath); + } + else if (File.Exists(basePath)) + { + logger.Verbose($"File found: {basePath}"); + + totalSize = new FileInfo(basePath).Length; + logger.User(totalSize, currentSize); + + string parentPath = Path.GetDirectoryName(Path.GetDirectoryName(basePath)); + CheckFileForHashes(basePath, parentPath, asFiles, skipFileType, addBlanks, hashes); + logger.User(totalSize, totalSize, basePath); + } + + // Now that we're done, delete the temp folder (if it's not the default) + logger.User("Cleaning temp folder"); + if (Globals.TempDir != Path.GetTempPath()) + { + if (Directory.Exists(Globals.TempDir)) + Directory.Delete(Globals.TempDir, true); + } + + return true; + } + + /// + /// Check a given file for hashes, based on current settings + /// + /// Filename of the item to be checked + /// Base folder to be used in creating the DAT + /// TreatAsFiles representing CHD and Archive scanning + /// Type of files that should be skipped + /// True if blank items should be created for empty folders, false otherwise + /// Hashes to include in the information + private void CheckFileForHashes(string item, string basePath, TreatAsFile asFiles, SkipFileType skipFileType, bool addBlanks, Hash hashes) + { + // If we're in depot mode, process it separately + if (CheckDepotFile(item)) + return; + + // Initialize possible archive variables + BaseArchive archive = BaseArchive.Create(item); + + // Process archives according to flags + if (archive != null) + { + // Set the archive flags + archive.AvailableHashes = hashes; + + // Skip if we're treating archives as files and skipping files + if (asFiles.HasFlag(TreatAsFile.Archive) && skipFileType == SkipFileType.File) + { + return; + } + + // Skip if we're skipping archives + else if (skipFileType == SkipFileType.Archive) + { + return; + } + + // Process as archive if we're not treating archives as files + else if (!asFiles.HasFlag(TreatAsFile.Archive)) + { + var extracted = archive.GetChildren(); + + // If we have internal items to process, do so + if (extracted != null) + ProcessArchive(item, basePath, extracted); + + // Now find all folders that are empty, if we are supposed to + if (addBlanks) + ProcessArchiveBlanks(item, basePath, archive); + } + + // Process as file if we're treating archives as files + else + { + ProcessFile(item, basePath, hashes, asFiles); + } + } + + // Process non-archives according to flags + else + { + // Skip if we're skipping files + if (skipFileType == SkipFileType.File) + return; + + // Process as file + else + ProcessFile(item, basePath, hashes, asFiles); + } + } + + /// + /// Check an item as if it's supposed to be in a depot + /// + /// Filename of the item to be checked + /// True if we checked a depot file, false otherwise + private bool CheckDepotFile(string item) + { + // If we're not in Depot mode, return false + if (Header.OutputDepot?.IsActive != true) + return false; + + // Check the file as if it were in a depot + GZipArchive gzarc = new GZipArchive(item); + BaseFile baseFile = gzarc.GetTorrentGZFileInfo(); + + // If the rom is valid, add it + if (baseFile != null && baseFile.Filename != null) + { + // Add the list if it doesn't exist already + Rom rom = new Rom(baseFile); + Items.Add(rom.GetKey(Field.DatItem_CRC), rom); + logger.Verbose($"File added: {Path.GetFileNameWithoutExtension(item)}"); + } + else + { + logger.Verbose($"File not added: {Path.GetFileNameWithoutExtension(item)}"); + return true; + } + + return true; + } + + /// + /// Process a single file as an archive + /// + /// File to be added + /// Path the represents the parent directory + /// List of BaseFiles representing the internal files + private void ProcessArchive(string item, string basePath, List extracted) + { + // Get the parent path for all items + string parent = (Path.GetDirectoryName(Path.GetFullPath(item)) + Path.DirectorySeparatorChar).Remove(0, basePath.Length) + Path.GetFileNameWithoutExtension(item); + + // First take care of the found items + Parallel.ForEach(extracted, Globals.ParallelOptions, baseFile => + { + DatItem datItem = DatItem.Create(baseFile); + ProcessFileHelper(item, datItem, basePath, parent); + }); + } + + /// + /// Process blank folders in an archive + /// + /// File containing the blanks + /// Path the represents the parent directory + /// BaseArchive to get blanks from + private void ProcessArchiveBlanks(string item, string basePath, BaseArchive archive) + { + List empties = new List(); + + // Get the parent path for all items + string parent = (Path.GetDirectoryName(Path.GetFullPath(item)) + Path.DirectorySeparatorChar).Remove(0, basePath.Length) + Path.GetFileNameWithoutExtension(item); + + // Now get all blank folders from the archive + if (archive != null) + empties = archive.GetEmptyFolders(); + + // Add add all of the found empties to the DAT + Parallel.ForEach(empties, Globals.ParallelOptions, empty => + { + Rom emptyRom = new Rom(Path.Combine(empty, "_"), item); + ProcessFileHelper(item, emptyRom, basePath, parent); + }); + } + + /// + /// Process blank folders in a directory + /// + /// Path the represents the parent directory + private void ProcessDirectoryBlanks(string basePath) + { + // If we're in depot mode, we don't process blanks + if (Header.OutputDepot?.IsActive == true) + return; + + List empties = DirectoryExtensions.ListEmpty(basePath); + Parallel.ForEach(empties, Globals.ParallelOptions, dir => + { + // Get the full path for the directory + string fulldir = Path.GetFullPath(dir); + + // Set the temporary variables + string gamename = string.Empty; + string romname = string.Empty; + + // If we have a SuperDAT, we want anything that's not the base path as the game, and the file as the rom + if (Header.Type == "SuperDAT") + { + gamename = fulldir.Remove(0, basePath.Length + 1); + romname = "_"; + } + + // Otherwise, we want just the top level folder as the game, and the file as everything else + else + { + gamename = fulldir.Remove(0, basePath.Length + 1).Split(Path.DirectorySeparatorChar)[0]; + romname = Path.Combine(fulldir.Remove(0, basePath.Length + 1 + gamename.Length), "_"); + } + + // Sanitize the names + gamename = gamename.Trim(Path.DirectorySeparatorChar); + romname = romname.Trim(Path.DirectorySeparatorChar); + + logger.Verbose($"Adding blank empty folder: {gamename}"); + Items["null"].Add(new Rom(romname, gamename)); + }); + } + + /// + /// Process a single file as a file + /// + /// File to be added + /// Path the represents the parent directory + /// Hashes to include in the information + /// TreatAsFiles representing CHD and Archive scanning + private void ProcessFile(string item, string basePath, Hash hashes, TreatAsFile asFiles) + { + logger.Verbose($"'{Path.GetFileName(item)}' treated like a file"); + BaseFile baseFile = BaseFile.GetInfo(item, header: Header.HeaderSkipper, hashes: hashes, asFiles: asFiles); + DatItem datItem = DatItem.Create(baseFile); + ProcessFileHelper(item, datItem, basePath, string.Empty); + } + + /// + /// Process a single file as a file (with found Rom data) + /// + /// File to be added + /// Rom data to be used to write to file + /// Path the represents the parent directory + /// Parent game to be used + private void ProcessFileHelper(string item, DatItem datItem, string basepath, string parent) + { + // If we didn't get an accepted parsed type somehow, cancel out + List parsed = new List { ItemType.Disk, ItemType.Media, ItemType.Rom }; + if (!parsed.Contains(datItem.ItemType)) + return; + + try + { + // If the basepath doesn't end with a directory separator, add it + if (!basepath.EndsWith(Path.DirectorySeparatorChar.ToString())) + basepath += Path.DirectorySeparatorChar.ToString(); + + // Make sure we have the full item path + item = Path.GetFullPath(item); + + // Process the item to sanitize names based on input + SetDatItemInfo(datItem, item, parent, basepath); + + // Add the file information to the DAT + string key = datItem.GetKey(Field.DatItem_CRC); + Items.Add(key, datItem); + + logger.Verbose($"File added: {datItem.GetName() ?? string.Empty}"); + } + catch (IOException ex) + { + logger.Error(ex); + return; + } + } + + /// + /// Set proper Game and Rom names from user inputs + /// + /// DatItem representing the input file + /// Item name to use + /// Parent name to use + /// Base path to use + private void SetDatItemInfo(DatItem datItem, string item, string parent, string basepath) + { + // Get the data to be added as game and item names + string machineName, itemName; + + // If the parent is blank, then we have a non-archive file + if (string.IsNullOrWhiteSpace(parent)) + { + // If we have a SuperDAT, we want anything that's not the base path as the game, and the file as the rom + if (Header.Type == "SuperDAT") + { + machineName = Path.GetDirectoryName(item.Remove(0, basepath.Length)); + itemName = Path.GetFileName(item); + } + + // Otherwise, we want just the top level folder as the game, and the file as everything else + else + { + machineName = item.Remove(0, basepath.Length).Split(Path.DirectorySeparatorChar)[0]; + itemName = item.Remove(0, (Path.Combine(basepath, machineName).Length)); + } + } + + // Otherwise, we assume that we have an archive + else + { + // If we have a SuperDAT, we want the archive name as the game, and the file as everything else (?) + if (Header.Type == "SuperDAT") + { + machineName = parent; + itemName = datItem.GetName(); + } + + // Otherwise, we want the archive name as the game, and the file as everything else + else + { + machineName = parent; + itemName = datItem.GetName(); + } + } + + // Sanitize the names + machineName = machineName.Trim(Path.DirectorySeparatorChar); + itemName = itemName?.Trim(Path.DirectorySeparatorChar) ?? string.Empty; + + if (!string.IsNullOrWhiteSpace(machineName) && string.IsNullOrWhiteSpace(itemName)) + { + itemName = machineName; + machineName = "Default"; + } + + // Update machine information + datItem.Machine.Name = machineName; + datItem.Machine.Description = machineName; + + // If we have a Disk, then the ".chd" extension needs to be removed + if (datItem.ItemType == ItemType.Disk && itemName.EndsWith(".chd")) + { + itemName = itemName.Substring(0, itemName.Length - 4); + } + + // If we have a Media, then the extension needs to be removed + else if (datItem.ItemType == ItemType.Media) + { + if (itemName.EndsWith(".dicf")) + itemName = itemName.Substring(0, itemName.Length - 5); + else if (itemName.EndsWith(".aaru")) + itemName = itemName.Substring(0, itemName.Length - 5); + else if (itemName.EndsWith(".aaruformat")) + itemName = itemName.Substring(0, itemName.Length - 11); + else if (itemName.EndsWith(".aaruf")) + itemName = itemName.Substring(0, itemName.Length - 6); + else if (itemName.EndsWith(".aif")) + itemName = itemName.Substring(0, itemName.Length - 4); + } + + // Set the item name back + datItem.SetFields(new Dictionary { [Field.DatItem_Name] = itemName }); + } + } +} \ No newline at end of file diff --git a/SabreTools.DatFiles/DatFile.Filtering.cs b/SabreTools.DatFiles/DatFile.Filtering.cs new file mode 100644 index 00000000..26832339 --- /dev/null +++ b/SabreTools.DatFiles/DatFile.Filtering.cs @@ -0,0 +1,1108 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using System.Threading.Tasks; + +using SabreTools.Core; +using SabreTools.DatItems; +using SabreTools.Filtering; +using SabreTools.IO; + +// This file represents all methods related to the Filtering namespace +namespace SabreTools.DatFiles +{ + public abstract partial class DatFile + { + /// + /// Apply cleaning methods to the DatFile + /// + /// Cleaner to use + /// True if the error that is thrown should be thrown back to the caller, false otherwise + /// True if cleaning was successful, false on error + public bool ApplyCleaning(Cleaner cleaner, bool throwOnError = false) + { + try + { + // Perform item-level cleaning + CleanDatItems(cleaner); + + // Bucket and dedupe according to the flag + if (cleaner?.DedupeRoms == DedupeType.Full) + Items.BucketBy(Field.DatItem_CRC, cleaner.DedupeRoms); + else if (cleaner?.DedupeRoms == DedupeType.Game) + Items.BucketBy(Field.Machine_Name, cleaner.DedupeRoms); + + // Process description to machine name + if (cleaner?.DescriptionAsName == true) + MachineDescriptionToName(); + + // If we are removing scene dates, do that now + if (cleaner?.SceneDateStrip == true) + StripSceneDatesFromItems(); + + // Run the one rom per game logic, if required + if (cleaner?.OneGamePerRegion == true) + OneGamePerRegion(cleaner.RegionList); + + // Run the one rom per game logic, if required + if (cleaner?.OneRomPerGame == true) + OneRomPerGame(); + + // If we are removing fields, do that now + if (cleaner.ExcludeFields != null && cleaner.ExcludeFields.Any()) + RemoveFieldsFromItems(cleaner.ExcludeFields); + + // Remove all marked items + Items.ClearMarked(); + + // We remove any blanks, if we aren't supposed to have any + if (cleaner?.KeepEmptyGames == false) + Items.ClearEmpty(); + } + catch (Exception ex) + { + logger.Error(ex); + if (throwOnError) throw ex; + return false; + } + + return true; + } + + /// + /// Apply a set of Extra INIs on the DatFile + /// + /// ExtrasIni to use + /// True if the error that is thrown should be thrown back to the caller, false otherwise + /// True if the extras were applied, false on error + public bool ApplyExtras(ExtraIni extras, bool throwOnError = false) + { + try + { + // Bucket by game first + Items.BucketBy(Field.Machine_Name, DedupeType.None); + + // Create a new set of mappings based on the items + var map = new Dictionary>(); + + // Loop through each of the extras + foreach (ExtraIniItem item in extras.Items) + { + foreach (var mapping in item.Mappings) + { + string key = mapping.Key; + List machineNames = mapping.Value; + + // Loop through the machines and add the new mappings + foreach (string machine in machineNames) + { + if (!map.ContainsKey(machine)) + map[machine] = new Dictionary(); + + map[machine][item.Field] = key; + } + } + } + + // Now apply the new set of mappings + foreach (string key in map.Keys) + { + // If the key doesn't exist, continue + if (!Items.ContainsKey(key)) + continue; + + List datItems = Items[key]; + var mappings = map[key]; + + foreach (var datItem in datItems) + { + datItem.SetFields(mappings); + } + } + } + catch (Exception ex) + { + logger.Error(ex); + if (throwOnError) throw ex; + return false; + } + + return true; + } + + /// + /// Apply a Filter on the DatFile + /// + /// Filter to use + /// True if entire machines are considered, false otherwise (default) + /// True if the error that is thrown should be thrown back to the caller, false otherwise + /// True if the DatFile was filtered, false on error + public bool ApplyFilter(Filter filter, bool perMachine = false, bool throwOnError = false) + { + // If we have a null filter, return false + if (filter == null) + return false; + + // If we're filtering per machine, bucket by machine first + if (perMachine) + Items.BucketBy(Field.Machine_Name, DedupeType.None); + + try + { + // Loop over every key in the dictionary + List keys = Items.Keys.ToList(); + foreach (string key in keys) + { + // For every item in the current key + bool machinePass = true; + List items = Items[key]; + foreach (DatItem item in items) + { + // If we have a null item, we can't pass it + if (item == null) + continue; + + // If the item is already filtered out, we skip + if (item.Remove) + continue; + + // If the rom doesn't pass the filter, mark for removal + if (!item.PassesFilter(filter)) + { + item.Remove = true; + + // If we're in machine mode, set and break + if (perMachine) + { + machinePass = false; + break; + } + } + } + + // If we didn't pass and we're in machine mode, set all items as remove + if (perMachine && !machinePass) + { + foreach (DatItem item in items) + { + item.Remove = true; + } + } + + // Assign back for caution + Items[key] = items; + } + } + catch (Exception ex) + { + logger.Error(ex); + if (throwOnError) throw ex; + return false; + } + + return true; + } + + /// + /// Apply splitting on the DatFile + /// + /// Split type to try + /// True if DatFile tags override splitting, false otherwise + /// True if the error that is thrown should be thrown back to the caller, false otherwise + /// True if the DatFile was split, false on error + public bool ApplySplitting(MergingFlag splitType, bool useTags, bool throwOnError = false) + { + try + { + // If we are using tags from the DAT, set the proper input for split type unless overridden + if (useTags && splitType == MergingFlag.None) + splitType = Header.ForceMerging; + + // Run internal splitting + switch (splitType) + { + case MergingFlag.None: + // No-op + break; + case MergingFlag.Device: + CreateDeviceNonMergedSets(DedupeType.None); + break; + case MergingFlag.Full: + CreateFullyNonMergedSets(DedupeType.None); + break; + case MergingFlag.NonMerged: + CreateNonMergedSets(DedupeType.None); + break; + case MergingFlag.Merged: + CreateMergedSets(DedupeType.None); + break; + case MergingFlag.Split: + CreateSplitSets(DedupeType.None); + break; + } + } + catch (Exception ex) + { + logger.Error(ex); + if (throwOnError) throw ex; + return false; + } + + return true; + } + + /// + /// Apply SuperDAT naming logic to a merged DatFile + /// + /// List of inputs to use for renaming + public void ApplySuperDAT(List inputs) + { + List keys = Items.Keys.ToList(); + Parallel.ForEach(keys, Globals.ParallelOptions, key => + { + List items = Items[key].ToList(); + List newItems = new List(); + foreach (DatItem item in items) + { + DatItem newItem = item; + string filename = inputs[newItem.Source.Index].CurrentPath; + string rootpath = inputs[newItem.Source.Index].ParentPath; + + if (!string.IsNullOrWhiteSpace(rootpath)) + rootpath += Path.DirectorySeparatorChar.ToString(); + + filename = filename.Remove(0, rootpath.Length); + newItem.Machine.Name = Path.GetDirectoryName(filename) + Path.DirectorySeparatorChar + + Path.GetFileNameWithoutExtension(filename) + Path.DirectorySeparatorChar + + newItem.Machine.Name; + + newItems.Add(newItem); + } + + Items.Remove(key); + Items.AddRange(key, newItems); + }); + } + + /// + /// Clean individual items based on the current filter + /// + /// Cleaner to use + public void CleanDatItems(Cleaner cleaner) + { + List keys = Items.Keys.ToList(); + foreach (string key in keys) + { + // For every item in the current key + List items = Items[key]; + foreach (DatItem item in items) + { + // If we have a null item, we can't clean it it + if (item == null) + continue; + + // Run cleaning per item + item.Clean(cleaner); + } + + // Assign back for caution + Items[key] = items; + } + } + + /// + /// Use game descriptions as names in the DAT, updating cloneof/romof/sampleof + /// + /// True if the error that is thrown should be thrown back to the caller, false otherwise + public void MachineDescriptionToName(bool throwOnError = false) + { + try + { + // First we want to get a mapping for all games to description + ConcurrentDictionary mapping = new ConcurrentDictionary(); + Parallel.ForEach(Items.Keys, Globals.ParallelOptions, key => + { + List items = Items[key]; + foreach (DatItem item in items) + { + // If the key mapping doesn't exist, add it + mapping.TryAdd(item.Machine.Name, item.Machine.Description.Replace('/', '_').Replace("\"", "''").Replace(":", " -")); + } + }); + + // Now we loop through every item and update accordingly + Parallel.ForEach(Items.Keys, Globals.ParallelOptions, key => + { + List items = Items[key]; + List newItems = new List(); + foreach (DatItem item in items) + { + // Update machine name + if (!string.IsNullOrWhiteSpace(item.Machine.Name) && mapping.ContainsKey(item.Machine.Name)) + item.Machine.Name = mapping[item.Machine.Name]; + + // Update cloneof + if (!string.IsNullOrWhiteSpace(item.Machine.CloneOf) && mapping.ContainsKey(item.Machine.CloneOf)) + item.Machine.CloneOf = mapping[item.Machine.CloneOf]; + + // Update romof + if (!string.IsNullOrWhiteSpace(item.Machine.RomOf) && mapping.ContainsKey(item.Machine.RomOf)) + item.Machine.RomOf = mapping[item.Machine.RomOf]; + + // Update sampleof + if (!string.IsNullOrWhiteSpace(item.Machine.SampleOf) && mapping.ContainsKey(item.Machine.SampleOf)) + item.Machine.SampleOf = mapping[item.Machine.SampleOf]; + + // Add the new item to the output list + newItems.Add(item); + } + + // Replace the old list of roms with the new one + Items.Remove(key); + Items.AddRange(key, newItems); + }); + } + catch (Exception ex) + { + logger.Warning(ex.ToString()); + if (throwOnError) throw ex; + } + } + + /// + /// Filter a DAT using 1G1R logic given an ordered set of regions + /// + /// Ordered list of regions to use + /// + /// In the most technical sense, the way that the region list is being used does not + /// confine its values to be just regions. Since it's essentially acting like a + /// specialized version of the machine name filter, anything that is usually encapsulated + /// in parenthesis would be matched on, including disc numbers, languages, editions, + /// and anything else commonly used. Please note that, unlike other existing 1G1R + /// solutions, this does not have the ability to contain custom mappings of parent + /// to clone sets based on name, nor does it have the ability to match on the + /// Release DatItem type. + /// + public void OneGamePerRegion(List regions) + { + // If we have null region list, make it empty + if (regions == null) + regions = new List(); + + // For sake of ease, the first thing we want to do is bucket by game + Items.BucketBy(Field.Machine_Name, DedupeType.None, norename: true); + + // Then we want to get a mapping of all machines to parents + Dictionary> parents = new Dictionary>(); + foreach (string key in Items.Keys) + { + DatItem item = Items[key][0]; + + // Match on CloneOf first + if (!string.IsNullOrEmpty(item.Machine.CloneOf)) + { + if (!parents.ContainsKey(item.Machine.CloneOf.ToLowerInvariant())) + parents.Add(item.Machine.CloneOf.ToLowerInvariant(), new List()); + + parents[item.Machine.CloneOf.ToLowerInvariant()].Add(item.Machine.Name.ToLowerInvariant()); + } + + // Then by RomOf + else if (!string.IsNullOrEmpty(item.Machine.RomOf)) + { + if (!parents.ContainsKey(item.Machine.RomOf.ToLowerInvariant())) + parents.Add(item.Machine.RomOf.ToLowerInvariant(), new List()); + + parents[item.Machine.RomOf.ToLowerInvariant()].Add(item.Machine.Name.ToLowerInvariant()); + } + + // Otherwise, treat it as a parent + else + { + if (!parents.ContainsKey(item.Machine.Name.ToLowerInvariant())) + parents.Add(item.Machine.Name.ToLowerInvariant(), new List()); + + parents[item.Machine.Name.ToLowerInvariant()].Add(item.Machine.Name.ToLowerInvariant()); + } + } + + // Once we have the full list of mappings, filter out games to keep + foreach (string key in parents.Keys) + { + // Find the first machine that matches the regions in order, if possible + string machine = default; + foreach (string region in regions) + { + machine = parents[key].FirstOrDefault(m => Regex.IsMatch(m, @"\(.*" + region + @".*\)", RegexOptions.IgnoreCase)); + if (machine != default) + break; + } + + // If we didn't get a match, use the parent + if (machine == default) + machine = key; + + // Remove the key from the list + parents[key].Remove(machine); + + // Remove the rest of the items from this key + parents[key].ForEach(k => Items.Remove(k)); + } + + // Finally, strip out the parent tags + RemoveTagsFromChild(); + } + + /// + /// Ensure that all roms are in their own game (or at least try to ensure) + /// + public void OneRomPerGame() + { + // Because this introduces subfolders, we need to set the SuperDAT type + Header.Type = "SuperDAT"; + + // For each rom, we want to update the game to be "/" + Parallel.ForEach(Items.Keys, Globals.ParallelOptions, key => + { + List items = Items[key]; + for (int i = 0; i < items.Count; i++) + { + items[i].SetOneRomPerGame(); + } + }); + } + + /// + /// Remove fields as per the header + /// + /// List of fields to use + public void RemoveFieldsFromItems(List fields) + { + // If we have null field list, make it empty + if (fields == null) + fields = new List(); + + // Output the logging statement + logger.User("Removing filtered fields"); + + // Now process all of the roms + Parallel.ForEach(Items.Keys, Globals.ParallelOptions, key => + { + List items = Items[key]; + for (int j = 0; j < items.Count; j++) + { + items[j].RemoveFields(fields); + } + + Items.Remove(key); + Items.AddRange(key, items); + }); + } + + /// + /// Strip the dates from the beginning of scene-style set names + /// + public void StripSceneDatesFromItems() + { + // Output the logging statement + logger.User("Stripping scene-style dates"); + + // Set the regex pattern to use + string pattern = @"([0-9]{2}\.[0-9]{2}\.[0-9]{2}-)(.*?-.*?)"; + + // Now process all of the roms + Parallel.ForEach(Items.Keys, Globals.ParallelOptions, key => + { + List items = Items[key]; + for (int j = 0; j < items.Count; j++) + { + DatItem item = items[j]; + if (Regex.IsMatch(item.Machine.Name, pattern)) + item.Machine.Name = Regex.Replace(item.Machine.Name, pattern, "$2"); + + if (Regex.IsMatch(item.Machine.Description, pattern)) + item.Machine.Description = Regex.Replace(item.Machine.Description, pattern, "$2"); + + items[j] = item; + } + + Items.Remove(key); + Items.AddRange(key, items); + }); + } + + // TODO: Should any of these create a new DatFile in the process? + // The reason this comes up is that doing any of the splits or merges + // is an inherently destructive process. Making it output a new DatFile + // might make it easier to deal with multiple internal steps. On the other + // hand, this will increase memory usage significantly and would force the + // existing paths to behave entirely differently + #region Internal Splitting/Merging + + /// + /// Use cdevice_ref tags to get full non-merged sets and remove parenting tags + /// + /// Dedupe type to be used + private void CreateDeviceNonMergedSets(DedupeType mergeroms) + { + logger.User("Creating device non-merged sets from the DAT"); + + // For sake of ease, the first thing we want to do is bucket by game + Items.BucketBy(Field.Machine_Name, mergeroms, norename: true); + + // Now we want to loop through all of the games and set the correct information + while (AddRomsFromDevices(false, false)) ; + while (AddRomsFromDevices(true, false)) ; + + // Then, remove the romof and cloneof tags so it's not picked up by the manager + RemoveTagsFromChild(); + } + + /// + /// Use cloneof tags to create non-merged sets and remove the tags plus using the device_ref tags to get full sets + /// + /// Dedupe type to be used + private void CreateFullyNonMergedSets(DedupeType mergeroms) + { + logger.User("Creating fully non-merged sets from the DAT"); + + // For sake of ease, the first thing we want to do is bucket by game + Items.BucketBy(Field.Machine_Name, mergeroms, norename: true); + + // Now we want to loop through all of the games and set the correct information + while (AddRomsFromDevices(true, true)) ; + AddRomsFromDevices(false, true); + AddRomsFromParent(); + + // Now that we have looped through the cloneof tags, we loop through the romof tags + AddRomsFromBios(); + + // Then, remove the romof and cloneof tags so it's not picked up by the manager + RemoveTagsFromChild(); + } + + /// + /// Use cloneof tags to create merged sets and remove the tags + /// + /// Dedupe type to be used + private void CreateMergedSets(DedupeType mergeroms) + { + logger.User("Creating merged sets from the DAT"); + + // For sake of ease, the first thing we want to do is bucket by game + Items.BucketBy(Field.Machine_Name, mergeroms, norename: true); + + // Now we want to loop through all of the games and set the correct information + AddRomsFromChildren(); + + // Now that we have looped through the cloneof tags, we loop through the romof tags + RemoveBiosRomsFromChild(false); + RemoveBiosRomsFromChild(true); + + // Finally, remove the romof and cloneof tags so it's not picked up by the manager + RemoveTagsFromChild(); + } + + /// + /// Use cloneof tags to create non-merged sets and remove the tags + /// + /// Dedupe type to be used + private void CreateNonMergedSets(DedupeType mergeroms) + { + logger.User("Creating non-merged sets from the DAT"); + + // For sake of ease, the first thing we want to do is bucket by game + Items.BucketBy(Field.Machine_Name, mergeroms, norename: true); + + // Now we want to loop through all of the games and set the correct information + AddRomsFromParent(); + + // Now that we have looped through the cloneof tags, we loop through the romof tags + RemoveBiosRomsFromChild(false); + RemoveBiosRomsFromChild(true); + + // Finally, remove the romof and cloneof tags so it's not picked up by the manager + RemoveTagsFromChild(); + } + + /// + /// Use cloneof and romof tags to create split sets and remove the tags + /// + /// Dedupe type to be used + private void CreateSplitSets(DedupeType mergeroms) + { + logger.User("Creating split sets from the DAT"); + + // For sake of ease, the first thing we want to do is bucket by game + Items.BucketBy(Field.Machine_Name, mergeroms, norename: true); + + // Now we want to loop through all of the games and set the correct information + RemoveRomsFromChild(); + + // Now that we have looped through the cloneof tags, we loop through the romof tags + RemoveBiosRomsFromChild(false); + RemoveBiosRomsFromChild(true); + + // Finally, remove the romof and cloneof tags so it's not picked up by the manager + RemoveTagsFromChild(); + } + + /// + /// Use romof tags to add roms to the children + /// + private void AddRomsFromBios() + { + List games = Items.Keys.OrderBy(g => g).ToList(); + foreach (string game in games) + { + // If the game has no items in it, we want to continue + if (Items[game].Count == 0) + continue; + + // Determine if the game has a parent or not + string parent = null; + if (!string.IsNullOrWhiteSpace(Items[game][0].Machine.RomOf)) + parent = Items[game][0].Machine.RomOf; + + // If the parent doesnt exist, we want to continue + if (string.IsNullOrWhiteSpace(parent)) + continue; + + // If the parent doesn't have any items, we want to continue + if (Items[parent].Count == 0) + continue; + + // If the parent exists and has items, we copy the items from the parent to the current game + DatItem copyFrom = Items[game][0]; + List parentItems = Items[parent]; + foreach (DatItem item in parentItems) + { + DatItem datItem = (DatItem)item.Clone(); + datItem.CopyMachineInformation(copyFrom); + if (Items[game].Where(i => i.GetName() == datItem.GetName()).Count() == 0 && !Items[game].Contains(datItem)) + Items.Add(game, datItem); + } + } + } + + /// + /// Use device_ref and optionally slotoption tags to add roms to the children + /// + /// True if only child device sets are touched, false for non-device sets (default) + /// True if slotoptions tags are used as well, false otherwise + private bool AddRomsFromDevices(bool dev = false, bool useSlotOptions = false) + { + bool foundnew = false; + List machines = Items.Keys.OrderBy(g => g).ToList(); + foreach (string machine in machines) + { + // If the machine doesn't have items, we continue + if (Items[machine] == null || Items[machine].Count == 0) + continue; + + // If the machine (is/is not) a device, we want to continue + if (dev ^ (Items[machine][0].Machine.MachineType.HasFlag(MachineType.Device))) + continue; + + // Get all device reference names from the current machine + List deviceReferences = Items[machine] + .Where(i => i.ItemType == ItemType.DeviceReference) + .Select(i => i as DeviceReference) + .Select(dr => dr.Name) + .Distinct() + .ToList(); + + // Get all slot option names from the current machine + List slotOptions = Items[machine] + .Where(i => i.ItemType == ItemType.Slot) + .Select(i => i as Slot) + .Where(s => s.SlotOptionsSpecified) + .SelectMany(s => s.SlotOptions) + .Select(so => so.DeviceName) + .Distinct() + .ToList(); + + // If we're checking device references + if (deviceReferences.Any()) + { + // Loop through all names and check the corresponding machines + List newDeviceReferences = new List(); + foreach (string deviceReference in deviceReferences) + { + // If the machine doesn't exist then we continue + if (Items[deviceReference] == null || Items[deviceReference].Count == 0) + continue; + + // Add to the list of new device reference names + List devItems = Items[deviceReference]; + newDeviceReferences.AddRange(devItems + .Where(i => i.ItemType == ItemType.DeviceReference) + .Select(i => (i as DeviceReference).Name)); + + // Set new machine information and add to the current machine + DatItem copyFrom = Items[machine][0]; + foreach (DatItem item in devItems) + { + // If the parent machine doesn't already contain this item, add it + if (!Items[machine].Any(i => i.ItemType == item.ItemType && i.GetName() == item.GetName())) + { + // Set that we found new items + foundnew = true; + + // Clone the item and then add it + DatItem datItem = (DatItem)item.Clone(); + datItem.CopyMachineInformation(copyFrom); + Items.Add(machine, datItem); + } + } + } + + // Now that every device reference is accounted for, add the new list of device references, if they don't already exist + foreach (string deviceReference in newDeviceReferences.Distinct()) + { + if (!deviceReferences.Contains(deviceReference)) + Items[machine].Add(new DeviceReference() { Name = deviceReference }); + } + } + + // If we're checking slotoptions + if (useSlotOptions && slotOptions.Any()) + { + // Loop through all names and check the corresponding machines + List newSlotOptions = new List(); + foreach (string slotOption in slotOptions) + { + // If the machine doesn't exist then we continue + if (Items[slotOption] == null || Items[slotOption].Count == 0) + continue; + + // Add to the list of new slot option names + List slotItems = Items[slotOption]; + newSlotOptions.AddRange(slotItems + .Where(i => i.ItemType == ItemType.Slot) + .Where(s => (s as Slot).SlotOptionsSpecified) + .SelectMany(s => (s as Slot).SlotOptions) + .Select(o => o.DeviceName)); + + // Set new machine information and add to the current machine + DatItem copyFrom = Items[machine][0]; + foreach (DatItem item in slotItems) + { + // If the parent machine doesn't already contain this item, add it + if (!Items[machine].Any(i => i.ItemType == item.ItemType && i.GetName() == item.GetName())) + { + // Set that we found new items + foundnew = true; + + // Clone the item and then add it + DatItem datItem = (DatItem)item.Clone(); + datItem.CopyMachineInformation(copyFrom); + Items.Add(machine, datItem); + } + } + } + + // Now that every device is accounted for, add the new list of slot options, if they don't already exist + foreach (string slotOption in newSlotOptions.Distinct()) + { + if (!slotOptions.Contains(slotOption)) + Items[machine].Add(new Slot() { SlotOptions = new List { new SlotOption { DeviceName = slotOption } } }); + } + } + } + + return foundnew; + } + + /// + /// Use cloneof tags to add roms to the children, setting the new romof tag in the process + /// + private void AddRomsFromParent() + { + List games = Items.Keys.OrderBy(g => g).ToList(); + foreach (string game in games) + { + // If the game has no items in it, we want to continue + if (Items[game].Count == 0) + continue; + + // Determine if the game has a parent or not + string parent = null; + if (!string.IsNullOrWhiteSpace(Items[game][0].Machine.CloneOf)) + parent = Items[game][0].Machine.CloneOf; + + // If the parent doesnt exist, we want to continue + if (string.IsNullOrWhiteSpace(parent)) + continue; + + // If the parent doesn't have any items, we want to continue + if (Items[parent].Count == 0) + continue; + + // If the parent exists and has items, we copy the items from the parent to the current game + DatItem copyFrom = Items[game][0]; + List parentItems = Items[parent]; + foreach (DatItem item in parentItems) + { + DatItem datItem = (DatItem)item.Clone(); + datItem.CopyMachineInformation(copyFrom); + if (Items[game].Where(i => i.GetName()?.ToLowerInvariant() == datItem.GetName()?.ToLowerInvariant()).Count() == 0 + && !Items[game].Contains(datItem)) + { + Items.Add(game, datItem); + } + } + + // Now we want to get the parent romof tag and put it in each of the items + List items = Items[game]; + string romof = Items[parent][0].Machine.RomOf; + foreach (DatItem item in items) + { + item.Machine.RomOf = romof; + } + } + } + + /// + /// Use cloneof tags to add roms to the parents, removing the child sets in the process + /// + /// True to add DatItems to subfolder of parent (not including Disk), false otherwise + private void AddRomsFromChildren(bool subfolder = true) + { + List games = Items.Keys.OrderBy(g => g).ToList(); + foreach (string game in games) + { + // If the game has no items in it, we want to continue + if (Items[game].Count == 0) + continue; + + // Determine if the game has a parent or not + string parent = null; + if (!string.IsNullOrWhiteSpace(Items[game][0].Machine.CloneOf)) + parent = Items[game][0].Machine.CloneOf; + + // If there is no parent, then we continue + if (string.IsNullOrWhiteSpace(parent)) + continue; + + // Otherwise, move the items from the current game to a subfolder of the parent game + DatItem copyFrom; + if (Items[parent].Count == 0) + { + copyFrom = new Rom(); + copyFrom.Machine.Name = parent; + copyFrom.Machine.Description = parent; + } + else + { + copyFrom = Items[parent][0]; + } + + List items = Items[game]; + foreach (DatItem item in items) + { + // Special disk handling + if (item.ItemType == ItemType.Disk) + { + Disk disk = item as Disk; + + // If the merge tag exists and the parent already contains it, skip + if (disk.MergeTag != null && Items[parent].Where(i => i.ItemType == ItemType.Disk).Select(i => (i as Disk).Name).Contains(disk.MergeTag)) + { + continue; + } + + // If the merge tag exists but the parent doesn't contain it, add to parent + else if (disk.MergeTag != null && !Items[parent].Where(i => i.ItemType == ItemType.Disk).Select(i => (i as Disk).Name).Contains(disk.MergeTag)) + { + disk.CopyMachineInformation(copyFrom); + Items.Add(parent, disk); + } + + // If there is no merge tag, add to parent + else if (disk.MergeTag == null) + { + disk.CopyMachineInformation(copyFrom); + Items.Add(parent, disk); + } + } + + // Special rom handling + else if (item.ItemType == ItemType.Rom) + { + Rom rom = item as Rom; + + // If the merge tag exists and the parent already contains it, skip + if (rom.MergeTag != null && Items[parent].Where(i => i.ItemType == ItemType.Rom).Select(i => (i as Rom).Name).Contains(rom.MergeTag)) + { + continue; + } + + // If the merge tag exists but the parent doesn't contain it, add to subfolder of parent + else if (rom.MergeTag != null && !Items[parent].Where(i => i.ItemType == ItemType.Rom).Select(i => (i as Rom).Name).Contains(rom.MergeTag)) + { + if (subfolder) + rom.Name = $"{rom.Machine.Name}\\{rom.Name}"; + + rom.CopyMachineInformation(copyFrom); + Items.Add(parent, rom); + } + + // If the parent doesn't already contain this item, add to subfolder of parent + else if (!Items[parent].Contains(item)) + { + if (subfolder) + rom.Name = $"{item.Machine.Name}\\{rom.Name}"; + + rom.CopyMachineInformation(copyFrom); + Items.Add(parent, rom); + } + } + + // All other that would be missing to subfolder of parent + else if (!Items[parent].Contains(item)) + { + if (subfolder) + item.SetFields(new Dictionary { [Field.DatItem_Name] = $"{item.Machine.Name}\\{item.GetName()}" }); + + item.CopyMachineInformation(copyFrom); + Items.Add(parent, item); + } + } + + // Then, remove the old game so it's not picked up by the writer + Items.Remove(game); + } + } + + /// + /// Remove all BIOS and device sets + /// + private void RemoveBiosAndDeviceSets() + { + List games = Items.Keys.OrderBy(g => g).ToList(); + foreach (string game in games) + { + if (Items[game].Count > 0 + && (Items[game][0].Machine.MachineType.HasFlag(MachineType.Bios) + || Items[game][0].Machine.MachineType.HasFlag(MachineType.Device))) + { + Items.Remove(game); + } + } + } + + /// + /// Use romof tags to remove bios roms from children + /// + /// True if only child Bios sets are touched, false for non-bios sets (default) + private void RemoveBiosRomsFromChild(bool bios = false) + { + // Loop through the romof tags + List games = Items.Keys.OrderBy(g => g).ToList(); + foreach (string game in games) + { + // If the game has no items in it, we want to continue + if (Items[game].Count == 0) + continue; + + // If the game (is/is not) a bios, we want to continue + if (bios ^ Items[game][0].Machine.MachineType.HasFlag(MachineType.Bios)) + continue; + + // Determine if the game has a parent or not + string parent = null; + if (!string.IsNullOrWhiteSpace(Items[game][0].Machine.RomOf)) + parent = Items[game][0].Machine.RomOf; + + // If the parent doesnt exist, we want to continue + if (string.IsNullOrWhiteSpace(parent)) + continue; + + // If the parent doesn't have any items, we want to continue + if (Items[parent].Count == 0) + continue; + + // If the parent exists and has items, we remove the items that are in the parent from the current game + List parentItems = Items[parent]; + foreach (DatItem item in parentItems) + { + DatItem datItem = (DatItem)item.Clone(); + while (Items[game].Contains(datItem)) + { + Items.Remove(game, datItem); + } + } + } + } + + /// + /// Use cloneof tags to remove roms from the children + /// + private void RemoveRomsFromChild() + { + List games = Items.Keys.OrderBy(g => g).ToList(); + foreach (string game in games) + { + // If the game has no items in it, we want to continue + if (Items[game].Count == 0) + continue; + + // Determine if the game has a parent or not + string parent = null; + if (!string.IsNullOrWhiteSpace(Items[game][0].Machine.CloneOf)) + parent = Items[game][0].Machine.CloneOf; + + // If the parent doesnt exist, we want to continue + if (string.IsNullOrWhiteSpace(parent)) + continue; + + // If the parent doesn't have any items, we want to continue + if (Items[parent].Count == 0) + continue; + + // If the parent exists and has items, we remove the parent items from the current game + List parentItems = Items[parent]; + foreach (DatItem item in parentItems) + { + DatItem datItem = (DatItem)item.Clone(); + while (Items[game].Contains(datItem)) + { + Items.Remove(game, datItem); + } + } + + // Now we want to get the parent romof tag and put it in each of the remaining items + List items = Items[game]; + string romof = Items[parent][0].Machine.RomOf; + foreach (DatItem item in items) + { + item.Machine.RomOf = romof; + } + } + } + + /// + /// Remove all romof and cloneof tags from all games + /// + private void RemoveTagsFromChild() + { + List games = Items.Keys.OrderBy(g => g).ToList(); + foreach (string game in games) + { + List items = Items[game]; + foreach (DatItem item in items) + { + item.Machine.CloneOf = null; + item.Machine.RomOf = null; + item.Machine.SampleOf = null; + } + } + } + + #endregion + } +} \ No newline at end of file diff --git a/SabreTools.DatFiles/DatFile.Parsing.cs b/SabreTools.DatFiles/DatFile.Parsing.cs new file mode 100644 index 00000000..50582586 --- /dev/null +++ b/SabreTools.DatFiles/DatFile.Parsing.cs @@ -0,0 +1,316 @@ +using System; +using System.IO; +using System.Text.RegularExpressions; + +using SabreTools.Core; +using SabreTools.DatItems; +using SabreTools.IO; + +// This file represents all methods related to parsing from a file +namespace SabreTools.DatFiles +{ + public abstract partial class DatFile + { + /// + /// Create a DatFile and parse a file into it + /// + /// Name of the file to be parsed + /// True if the error that is thrown should be thrown back to the caller, false otherwise + public static DatFile CreateAndParse(string filename, bool throwOnError = false) + { + DatFile datFile = Create(); + datFile.Parse(new ParentablePath(filename), throwOnError: throwOnError); + return datFile; + } + + /// + /// Parse a DAT and return all found games and roms within + /// + /// Name of the file to be parsed + /// Index ID for the DAT + /// True if full pathnames are to be kept, false otherwise (default) + /// True if original extension should be kept, false otherwise (default) + /// True if quotes are assumed in supported types (default), false otherwise + /// True if the error that is thrown should be thrown back to the caller, false otherwise + public void Parse( + string filename, + int indexId = 0, + bool keep = false, + bool keepext = false, + bool quotes = true, + bool throwOnError = false) + { + ParentablePath path = new ParentablePath(filename.Trim('"')); + Parse(path, indexId, keep, keepext, quotes, throwOnError); + } + + /// + /// Parse a DAT and return all found games and roms within + /// + /// Name of the file to be parsed + /// Index ID for the DAT + /// True if full pathnames are to be kept, false otherwise (default) + /// True if original extension should be kept, false otherwise (default) + /// True if quotes are assumed in supported types (default), false otherwise + /// True if the error that is thrown should be thrown back to the caller, false otherwise + public void Parse( + ParentablePath input, + int indexId = 0, + bool keep = false, + bool keepext = false, + bool quotes = true, + bool throwOnError = true) + { + // Get the current path from the filename + string currentPath = input.CurrentPath; + + // Check the file extension first as a safeguard + if (!PathExtensions.HasValidDatExtension(currentPath)) + return; + + // If the output filename isn't set already, get the internal filename + Header.FileName = (string.IsNullOrWhiteSpace(Header.FileName) ? (keepext ? Path.GetFileName(currentPath) : Path.GetFileNameWithoutExtension(currentPath)) : Header.FileName); + + // If the output type isn't set already, get the internal output type + DatFormat currentPathFormat = GetDatFormat(currentPath); + Header.DatFormat = (Header.DatFormat == 0 ? currentPathFormat : Header.DatFormat); + Items.SetBucketedBy(Field.DatItem_CRC); // Setting this because it can reduce issues later + + // Now parse the correct type of DAT + try + { + Create(currentPathFormat, this, quotes)?.ParseFile(currentPath, indexId, keep, throwOnError); + } + catch (Exception ex) + { + logger.Error(ex, $"Error with file '{currentPath}'"); + if (throwOnError) throw ex; + } + } + + /// + /// Get what type of DAT the input file is + /// + /// Name of the file to be parsed + /// The DatFormat corresponding to the DAT + protected DatFormat GetDatFormat(string filename) + { + // Limit the output formats based on extension + if (!PathExtensions.HasValidDatExtension(filename)) + return 0; + + // Get the extension from the filename + string ext = PathExtensions.GetNormalizedExtension(filename); + + // Check if file exists + if (!File.Exists(filename)) + return 0; + + // Some formats should only require the extension to know + switch (ext) + { + case "csv": + return DatFormat.CSV; + case "json": + return DatFormat.SabreJSON; + case "md5": + return DatFormat.RedumpMD5; +#if NET_FRAMEWORK + case "ripemd160": + return DatFormat.RedumpRIPEMD160; +#endif + case "sfv": + return DatFormat.RedumpSFV; + case "sha1": + return DatFormat.RedumpSHA1; + case "sha256": + return DatFormat.RedumpSHA256; + case "sha384": + return DatFormat.RedumpSHA384; + case "sha512": + return DatFormat.RedumpSHA512; + case "spamsum": + return DatFormat.RedumpSpamSum; + case "ssv": + return DatFormat.SSV; + case "tsv": + return DatFormat.TSV; + } + + // For everything else, we need to read it + // Get the first two non-whitespace, non-comment lines to check, if possible + string first = string.Empty, second = string.Empty; + + try + { + using (StreamReader sr = File.OpenText(filename)) + { + first = sr.ReadLine().ToLowerInvariant(); + while ((string.IsNullOrWhiteSpace(first) || first.StartsWith("