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 CUE.CueTrackType TrackType = CUE.CueTrackType.Audio; if (ses.LeadoutTrack.IsData) { if (job.Disc.TOC.Session1Format == SessionFormat.Type20_CDXA || job.Disc.TOC.Session1Format == SessionFormat.Type10_CDI) { TrackType = CUE.CueTrackType.Mode2_2352; } else { TrackType = CUE.CueTrackType.Mode1_2352; } } CUE.SS_Gap ss_gap = new CUE.SS_Gap() { Policy = Policy, sq = sq, TrackType = TrackType, Pause = true //? }; ss_gap.Synth(job); }
/// <summary>appends the new entries to the provided list</summary> /// <exception cref="InvalidOperationException"><see cref="IN_Session1Format"/> is <see cref="SessionFormat.None"/> or a non-member</exception> 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 }); }
/// <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 }); }
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.Sector; 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 } }
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 } }
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 CUE.CueTrackType TrackType = CUE.CueTrackType.Audio; if (ses.LeadoutTrack.IsData) { if (job.Disc.TOC.Session1Format == SessionFormat.Type20_CDXA || job.Disc.TOC.Session1Format == SessionFormat.Type10_CDI) TrackType = CUE.CueTrackType.Mode2_2352; else TrackType = CUE.CueTrackType.Mode1_2352; } CUE.SS_Gap ss_gap = new CUE.SS_Gap() { Policy = Policy, sq = sq, TrackType = TrackType, Pause = true //? }; ss_gap.Synth(job); }