Beispiel #1
0
        public void Synth(SectorSynthJob job)
        {
            //be lazy, just generate the whole sector unconditionally
            //this is mostly based on mednafen's approach, which was probably finely tailored for PSX
            //heres the comments on the subject:
            //  I'm not trusting that the "control" field for the TOC leadout entry will always be set properly, so | the control fields for the last track entry
            //  and the leadout entry together before extracting the D2 bit.  Audio track->data leadout is fairly benign though maybe noisy(especially if we ever implement
            //  data scrambling properly), but data track->audio leadout could break things in an insidious manner for the more accurate drive emulation code).

            var ses          = job.Disc.Structure.Sessions[SessionNumber];
            int lba_relative = job.LBA - ses.LeadoutTrack.LBA;

            //data is zero

            int ts  = lba_relative;
            int ats = job.LBA;

            const int ADR     = 0x1; // Q channel data encodes position
            EControlQ control = ses.LeadoutTrack.Control;

            //ehhh? CDI?
            //if(toc.tracks[toc.last_track].valid)
            // control |= toc.tracks[toc.last_track].control & 0x4;
            //else if(toc.disc_type == DISC_TYPE_CD_I)
            // control |= 0x4;
            control |= (EControlQ)(((int)ses.LastInformationTrack.Control) & 4);

            SubchannelQ sq = new SubchannelQ();

            sq.SetStatus(ADR, control);
            sq.q_tno.BCDValue   = 0xAA;
            sq.q_index.BCDValue = 0x01;
            sq.Timestamp        = ts;
            sq.AP_Timestamp     = ats;
            sq.zero             = 0;

            //finally, rely on a gap sector to do the heavy lifting to synthesize this
            DiscFormats.CUE.CueTrackType TrackType = DiscFormats.CUE.CueTrackType.Audio;
            if (ses.LeadoutTrack.IsData)
            {
                if (job.Disc.TOC.Session1Format == SessionFormat.Type20_CDXA || job.Disc.TOC.Session1Format == SessionFormat.Type10_CDI)
                {
                    TrackType = DiscFormats.CUE.CueTrackType.Mode2_2352;
                }
                else
                {
                    TrackType = DiscFormats.CUE.CueTrackType.Mode1_2352;
                }
            }

            DiscFormats.CUE.SS_Gap ss_gap = new DiscFormats.CUE.SS_Gap()
            {
                Policy    = Policy,
                sq        = sq,
                TrackType = TrackType,
                Pause     = true //?
            };

            ss_gap.Synth(job);
        }
Beispiel #2
0
        /// <summary>
        /// Appends the new entries to the provided list
        /// </summary>
        public void Run(List <RawTOCEntry> entries)
        {
            //NOTE: entries are inserted at the beginning due to observations of CCD indicating they might need to be that way
            //Since I'm being asked to synthesize them here, I guess I can put them in whatever order I want, can't I?

            SubchannelQ sq = new SubchannelQ();

            //ADR (q-Mode) is necessarily 0x01 for a RawTOCEntry
            const int kADR            = 1;
            const int kUnknownControl = 0;

            sq.SetStatus(kADR, (EControlQ)kUnknownControl);

            //first recorded track number:
            sq.q_index.BCDValue    = 0xA0;
            sq.ap_min.DecimalValue = IN_FirstRecordedTrackNumber;
            switch (IN_Session1Format)
            {
            //TODO these probably shouldn't be decimal values
            case SessionFormat.Type00_CDROM_CDDA: sq.ap_sec.DecimalValue = 0x00; break;

            case SessionFormat.Type10_CDI: sq.ap_sec.DecimalValue = 0x10; break;

            case SessionFormat.Type20_CDXA: sq.ap_sec.DecimalValue = 0x20; break;

            default: throw new InvalidOperationException("Invalid Session1Format");
            }
            sq.ap_frame.DecimalValue = 0;

            entries.Insert(0, new RawTOCEntry {
                QData = sq
            });

            //last recorded track number:
            sq.q_index.BCDValue      = 0xA1;
            sq.ap_min.DecimalValue   = IN_LastRecordedTrackNumber;
            sq.ap_sec.DecimalValue   = 0;
            sq.ap_frame.DecimalValue = 0;

            entries.Insert(1, new RawTOCEntry {
                QData = sq
            });

            //leadout:
            sq.q_index.BCDValue = 0xA2;
            sq.AP_Timestamp     = IN_LeadoutTimestamp;

            entries.Insert(2, new RawTOCEntry {
                QData = sq
            });
        }
Beispiel #3
0
        /// <summary>
        /// Serializes the provided SubchannelQ structure into a buffer
        /// Returns the crc, calculated or otherwise.
        /// </summary>
        public static ushort SubQ_Serialize(byte[] buf12, int offset, ref SubchannelQ sq)
        {
            buf12[offset + 0] = sq.q_status;
            buf12[offset + 1] = sq.q_tno.BCDValue;
            buf12[offset + 2] = sq.q_index.BCDValue;
            buf12[offset + 3] = sq.min.BCDValue;
            buf12[offset + 4] = sq.sec.BCDValue;
            buf12[offset + 5] = sq.frame.BCDValue;
            buf12[offset + 6] = sq.zero;
            buf12[offset + 7] = sq.ap_min.BCDValue;
            buf12[offset + 8] = sq.ap_sec.BCDValue;
            buf12[offset + 9] = sq.ap_frame.BCDValue;

            return(SubQ_SynthChecksum(buf12, offset));
        }
Beispiel #4
0
        void EmitRawTOCEntry(CompiledCueTrack cct)
        {
            SubchannelQ toc_sq = new SubchannelQ();
            //absent some kind of policy for how to set it, this is a safe assumption:
            byte toc_ADR = 1;

            toc_sq.SetStatus(toc_ADR, (EControlQ)(int)cct.Flags);
            toc_sq.q_tno.BCDValue = 0;             //kind of a little weird here.. the track number becomes the 'point' and put in the index instead. 0 is the track number here.
            toc_sq.q_index        = BCD2.FromDecimal(cct.Number);
            //not too sure about these yet
            toc_sq.min          = BCD2.FromDecimal(0);
            toc_sq.sec          = BCD2.FromDecimal(0);
            toc_sq.frame        = BCD2.FromDecimal(0);
            toc_sq.AP_Timestamp = OUT_Disc._Sectors.Count;
            OUT_Disc.RawTOCEntries.Add(new RawTOCEntry {
                QData = toc_sq
            });
        }
        public void ReadLBA_SubchannelQ(int offset, ref SubchannelQ sq)
        {
            sq.q_status       = buffer[offset + 0];
            sq.q_tno          = buffer[offset + 1];
            sq.q_index        = buffer[offset + 2];
            sq.min.BCDValue   = buffer[offset + 3];
            sq.sec.BCDValue   = buffer[offset + 4];
            sq.frame.BCDValue = buffer[offset + 5];
            //nothing in byte[6]
            sq.ap_min.BCDValue   = buffer[offset + 7];
            sq.ap_sec.BCDValue   = buffer[offset + 8];
            sq.ap_frame.BCDValue = buffer[offset + 9];

            //CRC is stored inverted and big endian.. so... do the opposite
            byte hibyte = (byte)(~buffer[offset + 10]);
            byte lobyte = (byte)(~buffer[offset + 11]);

            sq.q_crc = (ushort)((hibyte << 8) | lobyte);
        }
        public void Run()
        {
            //TODO: encode_mode2_form2_sector

            var leadoutTs        = Disc.TOC.LeadoutLBA;
            var lastTrackTOCItem = Disc.TOC.TOCItems[Disc.TOC.LastRecordedTrackNumber];             //NOTE: in case LastRecordedTrackNumber is al ie, this will malfunction

            //leadout flags.. let's set them the same as the last track.
            //THIS IS NOT EXACTLY THE SAME WAY MEDNAFEN DOES IT
            EControlQ leadoutFlags = lastTrackTOCItem.Control;

            //TODO - needs to be encoded as a certain mode (mode 2 form 2 for psx... i guess...)

            for (int i = 0; i < Length; i++)
            {
                //var se = new SectorEntry(sz);
                //Disc.Sectors.Add(se);
                SubchannelQ sq = new SubchannelQ();

                int track_relative_msf = i;
                sq.min   = BCD2.FromDecimal(new Timestamp(track_relative_msf).MIN);
                sq.sec   = BCD2.FromDecimal(new Timestamp(track_relative_msf).SEC);
                sq.frame = BCD2.FromDecimal(new Timestamp(track_relative_msf).FRAC);

                int absolute_msf = i + leadoutTs;
                sq.ap_min   = BCD2.FromDecimal(new Timestamp(absolute_msf + 150).MIN);
                sq.ap_sec   = BCD2.FromDecimal(new Timestamp(absolute_msf + 150).SEC);
                sq.ap_frame = BCD2.FromDecimal(new Timestamp(absolute_msf + 150).FRAC);

                sq.q_tno.DecimalValue   = 0xAA;               //special value for leadout
                sq.q_index.DecimalValue = 1;

                byte ADR = 1;
                sq.SetStatus(ADR, leadoutFlags);

                //TODO - actually stash the subQ
            }
        }
    static bool NewTest(string path)
    {
        bool ret = false;

        Disc disc;

        if (Path.GetExtension(path).ToLower() == ".cue")
        {
            disc = Disc.FromCuePath(path, new CueBinPrefs());
        }
        else
        {
            disc = Disc.FromCCDPath(path);
        }
        IntPtr mednadisc = mednadisc_LoadCD(path);

        //TODO - test leadout a bit, or determine length some superior way
        //TODO - check length against mednadisc

        int nSectors    = (int)(disc.Structure.BinarySize / 2352) - 150;
        var subbuf      = new byte[96];
        var discbuf     = new byte[2352 + 96];
        var monkeybuf   = new byte[2352 + 96];
        var disc_qbuf   = new byte[96];
        var monkey_qbuf = new byte[96];

        for (int i = 0; i < nSectors; i++)
        {
            mednadisc_ReadSector(mednadisc, i, monkeybuf);
            disc.ReadLBA_2352(i, discbuf, 0);
            disc.ReadLBA_SectorEntry(i).SubcodeSector.ReadSubcodeDeinterleaved(subbuf, 0);
            SubcodeUtils.Interleave(subbuf, 0, discbuf, 2352);
            //remove P
            for (int q = 2352; q < 2352 + 96; q++)
            {
                discbuf[q]   &= 0x7F;
                monkeybuf[q] &= 0x7F;
            }
            for (int q = 0; q < 2352 + 96; q++)
            {
                if (discbuf[q] != monkeybuf[q])
                {
                    Console.WriteLine("MISMATCH: " + Path.GetFileName(path));

                    //decode Q subchannels for manual investigation
                    SubcodeUtils.Deinterleave(discbuf, 2352, disc_qbuf, 0);
                    var         asr    = new QuickSubcodeReader(disc_qbuf);
                    SubchannelQ disc_q = new SubchannelQ();
                    asr.ReadLBA_SubchannelQ(12, ref disc_q);

                    SubcodeUtils.Deinterleave(monkeybuf, 2352, monkey_qbuf, 0);
                    asr = new QuickSubcodeReader(monkey_qbuf);
                    SubchannelQ monkey_q = new SubchannelQ();
                    asr.ReadLBA_SubchannelQ(12, ref monkey_q);

                    goto END;
                }
            }
        }

        ret = true;

END:
        disc.Dispose();
        mednadisc_CloseCD(mednadisc);

        return(ret);
    }
Beispiel #8
0
 void EmitRawTOCEntry(CompiledCueTrack cct)
 {
     SubchannelQ toc_sq = new SubchannelQ();
     //absent some kind of policy for how to set it, this is a safe assumption:
     byte toc_ADR = 1;
     toc_sq.SetStatus(toc_ADR, (EControlQ)(int)cct.Flags);
     toc_sq.q_tno.BCDValue = 0; //kind of a little weird here.. the track number becomes the 'point' and put in the index instead. 0 is the track number here.
     toc_sq.q_index = BCD2.FromDecimal(cct.Number);
     //not too sure about these yet
     toc_sq.min = BCD2.FromDecimal(0);
     toc_sq.sec = BCD2.FromDecimal(0);
     toc_sq.frame = BCD2.FromDecimal(0);
     toc_sq.AP_Timestamp = new Timestamp(OUT_Disc.Sectors.Count);
     OUT_Disc.RawTOCEntries.Add(new RawTOCEntry { QData = toc_sq });
 }
Beispiel #9
0
        private void RunMednaDisc()
        {
            var disc = new Disc();

            OUT_Disc = disc;

            //create a MednaDisc and give it to the disc for ownership
            var md = new MednaDisc(IN_FromPath);

            disc.DisposableResources.Add(md);

            //"length of disc" for BizHawk's purposes (NOT a robust concept!) is determined by beginning of leadout track
            var m_leadoutTrack = md.TOCTracks[100];
            int nSectors       = (int)m_leadoutTrack.lba;

            //make synth param memos
            disc.SynthParams.MednaDisc = md;

            //this is the sole sector synthesizer we'll need
            var synth = new SS_MednaDisc();

            OUT_Disc.SynthProvider = new SimpleSectorSynthProvider {
                SS = synth
            };

            //ADR (q-Mode) is necessarily 0x01 for a RawTOCEntry
            const int kADR            = 1;
            const int kUnknownControl = 0;

            //mednafen delivers us what is essentially but not exactly (or completely) a TOCRaw.
            //we need to synth RawTOCEntries from this and then turn it into a proper TOCRaw
            //when coming from mednafen, there are 101 entries.
            //entry[0] is placeholder junk, not to be used
            //entry[100] is the leadout track (A0)
            //A1 and A2 are in the form of FirstRecordedTrackNumber and LastRecordedTrackNumber
            for (int i = 1; i < 101; i++)
            {
                var m_te = md.TOCTracks[i];

                //don't add invalid (absent) items
                if (!m_te.Valid)
                {
                    continue;
                }

                var m_ts = new Timestamp((int)m_te.lba + 150);                 //these are supposed to be absolute timestamps

                var q = new SubchannelQ
                {
                    q_status = SubchannelQ.ComputeStatus(kADR, (EControlQ)m_te.control),
                    q_tno    = BCD2.FromDecimal(0), //unknown with mednadisc
                    q_index  = BCD2.FromDecimal(i),
                    min      = BCD2.FromDecimal(0), //unknown with mednadisc
                    sec      = BCD2.FromDecimal(0), //unknown with mednadisc
                    frame    = BCD2.FromDecimal(0), //unknown with mednadisc
                    zero     = 0,                   //unknown with mednadisc
                    ap_min   = BCD2.FromDecimal(m_ts.MIN),
                    ap_sec   = BCD2.FromDecimal(m_ts.SEC),
                    ap_frame = BCD2.FromDecimal(m_ts.FRAC),
                    q_crc    = 0                  //meaningless
                };

                //a special fixup: mednafen's entry 100 is the lead-out track, so change it into the A2 raw toc entry
                if (i == 100)
                {
                    q.q_index.BCDValue = 0xA2;
                }

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

            // synth A0 and A1 entries (indicating first and last recorded tracks and also session type)
            var qA0 = new SubchannelQ
            {
                q_status = SubchannelQ.ComputeStatus(kADR, kUnknownControl),
                q_tno    = BCD2.FromDecimal(0), //unknown with mednadisc
                q_index  = BCD2.FromBCD(0xA0),
                min      = BCD2.FromDecimal(0), //unknown with mednadisc
                sec      = BCD2.FromDecimal(0), //unknown with mednadisc
                frame    = BCD2.FromDecimal(0), //unknown with mednadisc
                zero     = 0,                   //unknown with mednadisc
                ap_min   = BCD2.FromDecimal(md.TOC.first_track),
                ap_sec   = BCD2.FromDecimal(md.TOC.disc_type),
                ap_frame = BCD2.FromDecimal(0),
                q_crc    = 0,              //meaningless
            };

            disc.RawTOCEntries.Add(new RawTOCEntry {
                QData = qA0
            });
            var qA1 = new SubchannelQ
            {
                q_status = SubchannelQ.ComputeStatus(kADR, kUnknownControl),
                q_tno    = BCD2.FromDecimal(0), //unknown with mednadisc
                q_index  = BCD2.FromBCD(0xA1),
                min      = BCD2.FromDecimal(0), //unknown with mednadisc
                sec      = BCD2.FromDecimal(0), //unknown with mednadisc
                frame    = BCD2.FromDecimal(0), //unknown with mednadisc
                zero     = 0,                   //unknown with mednadisc
                ap_min   = BCD2.FromDecimal(md.TOC.last_track),
                ap_sec   = BCD2.FromDecimal(0),
                ap_frame = BCD2.FromDecimal(0),
                q_crc    = 0,              //meaningless
            };

            disc.RawTOCEntries.Add(new RawTOCEntry {
                QData = qA1
            });
        }
Beispiel #10
0
        /// <summary>
        /// Loads a CCD at the specified path to a Disc object
        /// </summary>
        public Disc LoadCCDToDisc(string ccdPath, DiscMountPolicy IN_DiscMountPolicy)
        {
            var loadResults = LoadCCDPath(ccdPath);

            if (!loadResults.Valid)
            {
                return(null);
            }
            //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);
        }