Exemple #1
0
        /// <exception cref="MDSParseException">path reference no longer points to file</exception>
        Dictionary <int, IBlob> MountBlobs(AFile mdsf, Disc disc)
        {
            Dictionary <int, IBlob> BlobIndex = new Dictionary <int, IBlob>();

            int count = 0;

            foreach (var track in mdsf.Tracks)
            {
                foreach (var file in track.ImageFileNamePaths.Distinct())
                {
                    if (!File.Exists(file))
                    {
                        throw new MDSParseException($"Malformed MDS format: nonexistent image file: {file}");
                    }

                    IBlob mdfBlob = null;
                    long  mdfLen  = -1;

                    //mount the file
                    if (mdfBlob == null)
                    {
                        var mdfFile = new Disc.Blob_RawFile()
                        {
                            PhysicalPath = file
                        };
                        mdfLen  = mdfFile.Length;
                        mdfBlob = mdfFile;
                    }

                    bool dupe = false;
                    foreach (var re in disc.DisposableResources)
                    {
                        if (re.ToString() == mdfBlob.ToString())
                        {
                            dupe = true;
                        }
                    }

                    if (!dupe)
                    {
                        // wrap in zeropadadapter
                        disc.DisposableResources.Add(mdfBlob);
                        BlobIndex[count++] = mdfBlob;
                    }
                }
            }

            return(BlobIndex);
        }
Exemple #2
0
        /// <exception cref="CCDParseException">file <paramref name="ccdPath"/> not found, nonexistent IMG file, nonexistent SUB file, IMG or SUB file not multiple of <c>2352 B</c>, or IMG and SUB files differ in length</exception>
        public Disc LoadCCDToDisc(string ccdPath, DiscMountPolicy IN_DiscMountPolicy)
        {
            var loadResults = LoadCCDPath(ccdPath);

            if (!loadResults.Valid)
            {
                throw loadResults.FailureException;
            }

            Disc disc = new Disc();

            IBlob imgBlob = null, subBlob = null;
            long  imgLen = -1, subLen;

            //mount the IMG file
            //first check for a .ecm in place of the img
            var imgPath = loadResults.ImgPath;

            if (!File.Exists(imgPath))
            {
                var ecmPath = Path.ChangeExtension(imgPath, ".img.ecm");
                if (File.Exists(ecmPath))
                {
                    if (Disc.Blob_ECM.IsECM(ecmPath))
                    {
                        var ecm = new Disc.Blob_ECM();
                        ecm.Load(ecmPath);
                        imgBlob = ecm;
                        imgLen  = ecm.Length;
                    }
                }
            }
            if (imgBlob == null)
            {
                if (!File.Exists(loadResults.ImgPath))
                {
                    throw new CCDParseException("Malformed CCD format: nonexistent IMG file!");
                }
                var imgFile = new Disc.Blob_RawFile()
                {
                    PhysicalPath = loadResults.ImgPath
                };
                imgLen  = imgFile.Length;
                imgBlob = imgFile;
            }
            disc.DisposableResources.Add(imgBlob);

            //mount the SUB file
            if (!File.Exists(loadResults.SubPath))
            {
                throw new CCDParseException("Malformed CCD format: nonexistent SUB file!");
            }
            var subFile = new Disc.Blob_RawFile()
            {
                PhysicalPath = loadResults.SubPath
            };

            subBlob = subFile;
            disc.DisposableResources.Add(subBlob);
            subLen = subFile.Length;

            //quick integrity check of file sizes
            if (imgLen % 2352 != 0)
            {
                throw new CCDParseException("Malformed CCD format: IMG file length not multiple of 2352");
            }
            int NumImgSectors = (int)(imgLen / 2352);

            if (subLen != NumImgSectors * 96)
            {
                throw new CCDParseException("Malformed CCD format: SUB file length not matching IMG");
            }

            var ccdf = loadResults.ParsedCCDFile;

            //the only instance of a sector synthesizer we'll need
            SS_CCD synth = new SS_CCD();

            //generate DiscTOCRaw items from the ones specified in the CCD file
            //TODO - range validate these (too many truncations to byte)
            disc.RawTOCEntries = new List <RawTOCEntry>();
            foreach (var entry in ccdf.TOCEntries)
            {
                BCD2 tno, ino;

                //this should actually be zero. im not sure if this is stored as BCD2 or not
                tno = BCD2.FromDecimal(entry.TrackNo);

                //these are special values.. I think, taken from this:
                //http://www.staff.uni-mainz.de/tacke/scsi/SCSI2-14.html
                //the CCD will contain Points as decimal values except for these specially converted decimal values which should stay as BCD.
                //Why couldn't they all be BCD? I don't know. I guess because BCD is inconvenient, but only A0 and friends have special meaning. It's confusing.
                ino = BCD2.FromDecimal(entry.Point);
                if (entry.Point == 0xA0)
                {
                    ino.BCDValue = 0xA0;
                }
                else if (entry.Point == 0xA1)
                {
                    ino.BCDValue = 0xA1;
                }
                else if (entry.Point == 0xA2)
                {
                    ino.BCDValue = 0xA2;
                }

                var q = new SubchannelQ
                {
                    q_status = SubchannelQ.ComputeStatus(entry.ADR, (EControlQ)(entry.Control & 0xF)),
                    q_tno    = tno,
                    q_index  = ino,
                    min      = BCD2.FromDecimal(entry.AMin),
                    sec      = BCD2.FromDecimal(entry.ASec),
                    frame    = BCD2.FromDecimal(entry.AFrame),
                    zero     = (byte)entry.Zero,
                    ap_min   = BCD2.FromDecimal(entry.PMin),
                    ap_sec   = BCD2.FromDecimal(entry.PSec),
                    ap_frame = BCD2.FromDecimal(entry.PFrame),
                    q_crc    = 0,                  //meaningless
                };

                disc.RawTOCEntries.Add(new RawTOCEntry {
                    QData = q
                });
            }

            //analyze the RAWTocEntries to figure out what type of track track 1 is
            var tocSynth = new Synthesize_DiscTOC_From_RawTOCEntries_Job()
            {
                Entries = disc.RawTOCEntries
            };

            tocSynth.Run();

            //Add sectors for the mandatory track 1 pregap, which isn't stored in the CCD file
            //We reuse some CUE code for this.
            //If we load other formats later we might should abstract this even further (to a synthesizer job)
            //It can't really be abstracted from cue files though due to the necessity of merging this with other track 1 pregaps
            CUE.CueTrackType pregapTrackType = CUE.CueTrackType.Audio;
            if (tocSynth.Result.TOCItems[1].IsData)
            {
                if (tocSynth.Result.Session1Format == SessionFormat.Type20_CDXA)
                {
                    pregapTrackType = CUE.CueTrackType.Mode2_2352;
                }
                else if (tocSynth.Result.Session1Format == SessionFormat.Type10_CDI)
                {
                    pregapTrackType = CUE.CueTrackType.CDI_2352;
                }
                else if (tocSynth.Result.Session1Format == SessionFormat.Type00_CDROM_CDDA)
                {
                    pregapTrackType = CUE.CueTrackType.Mode1_2352;
                }
            }
            for (int i = 0; i < 150; i++)
            {
                var ss_gap = new CUE.SS_Gap()
                {
                    Policy    = IN_DiscMountPolicy,
                    TrackType = pregapTrackType
                };
                disc._Sectors.Add(ss_gap);

                int qRelMSF = i - 150;

                //tweak relMSF due to ambiguity/contradiction in yellowbook docs
                if (!IN_DiscMountPolicy.CUE_PregapContradictionModeA)
                {
                    qRelMSF++;
                }

                //setup subQ
                byte ADR = 1;                 //absent some kind of policy for how to set it, this is a safe assumption:
                ss_gap.sq.SetStatus(ADR, tocSynth.Result.TOCItems[1].Control);
                ss_gap.sq.q_tno        = BCD2.FromDecimal(1);
                ss_gap.sq.q_index      = BCD2.FromDecimal(0);
                ss_gap.sq.AP_Timestamp = i;
                ss_gap.sq.Timestamp    = qRelMSF;

                //setup subP
                ss_gap.Pause = true;
            }

            //build the sectors:
            //set up as many sectors as we have img/sub for, even if the TOC doesnt reference them
            //(the TOC is unreliable, and the Track records are redundant)
            for (int i = 0; i < NumImgSectors; i++)
            {
                disc._Sectors.Add(synth);
            }

            return(disc);
        }
Exemple #3
0
        /// <exception cref="MDSParseException">no file found at <paramref name="mdsPath"/> or BLOB error</exception>
        public Disc LoadMDSToDisc(string mdsPath, DiscMountPolicy IN_DiscMountPolicy)
        {
            var loadResults = LoadMDSPath(mdsPath);

            if (!loadResults.Valid)
            {
                throw loadResults.FailureException;
            }

            Disc disc = new Disc();

            // load all blobs
            Dictionary <int, IBlob> BlobIndex = MountBlobs(loadResults.ParsedMDSFile, disc);

            var mdsf = loadResults.ParsedMDSFile;

            //generate DiscTOCRaw items from the ones specified in the MDS file
            disc.RawTOCEntries = new List <RawTOCEntry>();
            foreach (var entry in mdsf.TOCEntries)
            {
                disc.RawTOCEntries.Add(EmitRawTOCEntry(entry));
            }

            //analyze the RAWTocEntries to figure out what type of track track 1 is
            var tocSynth = new Synthesize_DiscTOC_From_RawTOCEntries_Job()
            {
                Entries = disc.RawTOCEntries
            };

            tocSynth.Run();

            // now build the sectors
            int currBlobIndex = 0;

            foreach (var session in mdsf.ParsedSession)
            {
                for (int i = session.StartTrack; i <= session.EndTrack; i++)
                {
                    int relMSF = -1;

                    var track = mdsf.TOCEntries.Where(t => t.Point == i).FirstOrDefault();
                    if (track == null)
                    {
                        break;
                    }

                    // ignore the info entries
                    if (track.Point == 0xA0 ||
                        track.Point == 0xA1 ||
                        track.Point == 0xA2)
                    {
                        continue;
                    }

                    // get the blob(s) for this track
                    // its probably a safe assumption that there will be only one blob per track,
                    // but i'm still not 100% sure on this
                    var tr = (from a in mdsf.TOCEntries
                              where a.Point == i
                              select a).FirstOrDefault();

                    if (tr == null)
                    {
                        throw new MDSParseException("BLOB Error!");
                    }

                    List <string> blobstrings = new List <string>();
                    foreach (var t in tr.ImageFileNamePaths)
                    {
                        if (!blobstrings.Contains(t))
                        {
                            blobstrings.Add(t);
                        }
                    }

                    var tBlobs = (from a in tr.ImageFileNamePaths
                                  select a).ToList();

                    if (tBlobs.Count < 1)
                    {
                        throw new MDSParseException("BLOB Error!");
                    }

                    // is the currBlob valid for this track, or do we need to increment?
                    string bString = tBlobs.First();

                    IBlob mdfBlob = null;

                    // check for track pregap and create if neccessary
                    // this is specified in the track extras block
                    if (track.ExtraBlock.Pregap > 0)
                    {
                        CUE.CueTrackType pregapTrackType = CUE.CueTrackType.Audio;
                        if (tocSynth.Result.TOCItems[1].IsData)
                        {
                            if (tocSynth.Result.Session1Format == SessionFormat.Type20_CDXA)
                            {
                                pregapTrackType = CUE.CueTrackType.Mode2_2352;
                            }
                            else if (tocSynth.Result.Session1Format == SessionFormat.Type10_CDI)
                            {
                                pregapTrackType = CUE.CueTrackType.CDI_2352;
                            }
                            else if (tocSynth.Result.Session1Format == SessionFormat.Type00_CDROM_CDDA)
                            {
                                pregapTrackType = CUE.CueTrackType.Mode1_2352;
                            }
                        }
                        for (int pre = 0; pre < track.ExtraBlock.Pregap; pre++)
                        {
                            relMSF++;

                            var ss_gap = new CUE.SS_Gap()
                            {
                                Policy    = IN_DiscMountPolicy,
                                TrackType = pregapTrackType
                            };
                            disc._Sectors.Add(ss_gap);

                            int qRelMSF = pre - Convert.ToInt32(track.ExtraBlock.Pregap);

                            //tweak relMSF due to ambiguity/contradiction in yellowbook docs
                            if (!IN_DiscMountPolicy.CUE_PregapContradictionModeA)
                            {
                                qRelMSF++;
                            }

                            //setup subQ
                            byte ADR = 1;                             //absent some kind of policy for how to set it, this is a safe assumption:
                            ss_gap.sq.SetStatus(ADR, tocSynth.Result.TOCItems[1].Control);
                            ss_gap.sq.q_tno        = BCD2.FromDecimal(1);
                            ss_gap.sq.q_index      = BCD2.FromDecimal(0);
                            ss_gap.sq.AP_Timestamp = pre;
                            ss_gap.sq.Timestamp    = qRelMSF;

                            //setup subP
                            ss_gap.Pause = true;
                        }
                        // pregap processing completed
                    }



                    // create track sectors
                    long currBlobOffset = track.TrackOffset;
                    for (long sector = session.StartSector; sector <= session.EndSector; sector++)
                    {
                        CUE.SS_Base sBase = null;

                        // get the current blob from the BlobIndex
                        Disc.Blob_RawFile currBlob = BlobIndex[currBlobIndex] as Disc.Blob_RawFile;
                        long currBlobLength        = currBlob.Length;
                        long currBlobPosition      = sector;
                        if (currBlobPosition == currBlobLength)
                        {
                            currBlobIndex++;
                        }
                        mdfBlob = disc.DisposableResources[currBlobIndex] as Disc.Blob_RawFile;

                        //int userSector = 2048;
                        switch (track.SectorSize)
                        {
                        case 2448:
                            sBase = new CUE.SS_2352()
                            {
                                Policy = IN_DiscMountPolicy
                            };
                            //userSector = 2352;
                            break;

                        case 2048:
                        default:
                            sBase = new CUE.SS_Mode1_2048()
                            {
                                Policy = IN_DiscMountPolicy
                            };
                            //userSector = 2048;
                            break;

                            //throw new Exception($"Not supported: Sector Size {track.SectorSize}");
                        }

                        // configure blob
                        sBase.Blob       = mdfBlob;
                        sBase.BlobOffset = currBlobOffset;

                        currBlobOffset += track.SectorSize;                         // userSector;

                        // add subchannel data
                        relMSF++;
                        BCD2 tno, ino;

                        //this should actually be zero. im not sure if this is stored as BCD2 or not
                        tno = BCD2.FromDecimal(track.TrackNo);

                        //these are special values.. I think, taken from this:
                        //http://www.staff.uni-mainz.de/tacke/scsi/SCSI2-14.html
                        //the CCD will contain Points as decimal values except for these specially converted decimal values which should stay as BCD.
                        //Why couldn't they all be BCD? I don't know. I guess because BCD is inconvenient, but only A0 and friends have special meaning. It's confusing.
                        ino = BCD2.FromDecimal(track.Point);
                        if (track.Point == 0xA0)
                        {
                            ino.BCDValue = 0xA0;
                        }
                        else if (track.Point == 0xA1)
                        {
                            ino.BCDValue = 0xA1;
                        }
                        else if (track.Point == 0xA2)
                        {
                            ino.BCDValue = 0xA2;
                        }

                        // get ADR & Control from ADR_Control byte
                        byte adrc    = Convert.ToByte(track.ADR_Control);
                        var  Control = adrc & 0x0F;
                        var  ADR     = adrc >> 4;

                        var q = new SubchannelQ
                        {
                            q_status     = SubchannelQ.ComputeStatus(ADR, (EControlQ)(Control & 0xF)),
                            q_tno        = BCD2.FromDecimal(track.Point),
                            q_index      = ino,
                            AP_Timestamp = disc._Sectors.Count,
                            Timestamp    = relMSF - Convert.ToInt32(track.ExtraBlock.Pregap)
                        };

                        sBase.sq = q;

                        disc._Sectors.Add(sBase);
                    }
                }
            }

            return(disc);
        }
Exemple #4
0
        /// <summary>
        /// Loads a CCD at the specified path to a Disc object
        /// </summary>
        public Disc LoadCCDToDisc(string ccdPath)
        {
            var loadResults = LoadCCDPath(ccdPath);

            if (!loadResults.Valid)
            {
                throw loadResults.FailureException;
            }

            Disc disc = new Disc();

            var ccdf    = loadResults.ParsedCCDFile;
            var imgBlob = new Disc.Blob_RawFile()
            {
                PhysicalPath = loadResults.ImgPath
            };
            var subBlob = new Disc.Blob_RawFile()
            {
                PhysicalPath = loadResults.SubPath
            };

            disc.Blobs.Add(imgBlob);
            disc.Blobs.Add(subBlob);

            //generate DiscTOCRaw items from the ones specified in the CCD file
            //TODO - range validate these (too many truncations to byte)
            disc.RawTOCEntries = new List <RawTOCEntry>();
            BufferedSubcodeSector bss = new BufferedSubcodeSector();

            foreach (var entry in ccdf.TOCEntries)
            {
                var q = new SubchannelQ
                {
                    q_status = SubchannelQ.ComputeStatus(entry.ADR, (EControlQ)(entry.Control & 0xF)),
                    q_tno    = (byte)entry.TrackNo,
                    q_index  = (byte)entry.Point,
                    min      = BCD2.FromDecimal(entry.AMin),
                    sec      = BCD2.FromDecimal(entry.ASec),
                    frame    = BCD2.FromDecimal(entry.AFrame),
                    zero     = (byte)entry.Zero,
                    ap_min   = BCD2.FromDecimal(entry.PMin),
                    ap_sec   = BCD2.FromDecimal(entry.PSec),
                    ap_frame = BCD2.FromDecimal(entry.PFrame),
                };

                //CRC cant be calculated til we've got all the fields setup
                q.q_crc = bss.Synthesize_SubchannelQ(ref q, true);

                disc.RawTOCEntries.Add(new RawTOCEntry {
                    QData = q
                });
            }

            //generate the toc from the entries
            var tocSynth = new DiscTOCRaw.SynthesizeFromRawTOCEntriesJob()
            {
                Entries = disc.RawTOCEntries
            };

            tocSynth.Run();
            disc.TOCRaw = tocSynth.Result;

            //synthesize DiscStructure
            var structureSynth = new DiscStructure.SynthesizeFromDiscTOCRawJob()
            {
                TOCRaw = disc.TOCRaw
            };

            structureSynth.Run();
            disc.Structure = structureSynth.Result;

            //I *think* implicitly there is an index 0.. at.. i dunno, 0 maybe, for track 1
            {
                var dsi0 = new DiscStructure.Index();
                dsi0.LBA    = 0;
                dsi0.Number = 0;
                disc.Structure.Sessions[0].Tracks[0].Indexes.Add(dsi0);
            }

            //now, how to get the track types for the DiscStructure?
            //1. the CCD tells us (somehow the reader has judged)
            //2. scan it out of the Q subchannel
            //lets choose1.
            //TODO - better consider how to handle the situation where we have havent received all the [TRACK] items we need
            foreach (var st in disc.Structure.Sessions[0].Tracks)
            {
                var ccdt = ccdf.TracksByNumber[st.Number];
                switch (ccdt.Mode)
                {
                case 0:
                    st.TrackType = ETrackType.Audio;                             //for CCD, this means audio, apparently.
                    break;

                case 1:
                    st.TrackType = ETrackType.Mode1_2352;
                    break;

                case 2:
                    st.TrackType = ETrackType.Mode2_2352;
                    break;

                default:
                    throw new InvalidOperationException("Unsupported CCD mode");
                }

                //add indexes for this track
                foreach (var ccdi in ccdt.Indexes)
                {
                    var dsi = new DiscStructure.Index();
                    //if (ccdi.Key == 0) continue;
                    dsi.LBA    = ccdi.Value;
                    dsi.Number = ccdi.Key;
                    st.Indexes.Add(dsi);
                }
            }

            //add sectors for the lead-in, which isn't stored in the CCD file, I think
            //TODO - synthesize lead-in sectors from TOC, if the lead-in isn't available.
            //need a test case for that though.
            var leadin_sector_zero  = new Sector_Zero();
            var leadin_subcode_zero = new ZeroSubcodeSector();

            for (int i = 0; i < 150; i++)
            {
                var se = new SectorEntry(leadin_sector_zero);
                disc.Sectors.Add(se);
                se.SubcodeSector = leadin_subcode_zero;
            }

            //build the sectors:
            //set up as many sectors as we have img/sub for, even if the TOC doesnt reference them (TOC is unreliable, although the tracks should have covered it all)
            for (int i = 0; i < loadResults.NumImgSectors; i++)
            {
                var isec = new Sector_RawBlob();
                isec.Offset = ((long)i) * 2352;
                isec.Blob   = imgBlob;

                var se = new SectorEntry(isec);
                disc.Sectors.Add(se);

                var scsec = new BlobSubcodeSectorPreDeinterleaved();
                scsec.Offset     = ((long)i) * 96;
                scsec.Blob       = subBlob;
                se.SubcodeSector = scsec;
            }

            return(disc);
        }