Exemple #1
0
		public void Run()
		{
			//in params
			var cue = IN_CueFile;

			//output state
			OUT_GlobalCDText = new CompiledCDText();
			OUT_CompiledDiscInfo = new CompiledDiscInfo();
			OUT_CompiledCueFiles = new List<CompiledCueFile>();
			OUT_CompiledCueTracks = new List<CompiledCueTrack>();

			//add a track 0, for addressing convenience. 
			//note: for future work, track 0 may need emulation (accessible by very negative LBA--the TOC is stored there)
			var track0 = new CompiledCueTrack() {
				Number = 0,
			};
			OUT_CompiledCueTracks.Add(track0); 

			//global cd text will acquire the cdtext commands set before track commands
			curr_cdtext  = OUT_GlobalCDText;

			for (int i = 0; i < cue.Commands.Count; i++)
			{
				var cmd = cue.Commands[i];

				//these commands get dealt with globally. nothing to be done here
				//(but in the future we need to accumulate them into the compile pass output)
				if (cmd is CUE_File.Command.CATALOG || cmd is CUE_File.Command.CDTEXTFILE) continue;

				//nothing to be done for comments
				if (cmd is CUE_File.Command.REM) continue;
				if (cmd is CUE_File.Command.COMMENT) continue;

				//CD-text and related
				if (cmd is CUE_File.Command.PERFORMER) curr_cdtext.Performer = (cmd as CUE_File.Command.PERFORMER).Value;
				if (cmd is CUE_File.Command.SONGWRITER) curr_cdtext.Songwriter = (cmd as CUE_File.Command.SONGWRITER).Value;
				if (cmd is CUE_File.Command.TITLE) curr_cdtext.Title = (cmd as CUE_File.Command.TITLE).Value;
				if (cmd is CUE_File.Command.ISRC) curr_cdtext.ISRC = (cmd as CUE_File.Command.ISRC).Value;

				//flags can only be set when a track command is running
				if (cmd is CUE_File.Command.FLAGS)
				{
					if (curr_track == null)
						Warn("Ignoring invalid flag commands outside of a track command");
					else
						//take care to |= it here, so the data flag doesn't get cleared
						curr_track.Flags |= (cmd as CUE_File.Command.FLAGS).Flags;
				}

				if (cmd is CUE_File.Command.TRACK)
				{
					CloseTrack();
					OpenTrack(cmd as CUE_File.Command.TRACK);
				}

				if (cmd is CUE_File.Command.FILE)
				{
					CloseFile();
					OpenFile(cmd as CUE_File.Command.FILE);
				}

				if (cmd is CUE_File.Command.INDEX)
				{
					//todo - validate no postgap specified
					AddIndex(cmd as CUE_File.Command.INDEX);
				}

				if (cmd is CUE_File.Command.PREGAP)
				{
					//validate track open
					//validate no indexes
					curr_track.PregapLength = (cmd as CUE_File.Command.PREGAP).Length;
				}

				if (cmd is CUE_File.Command.POSTGAP)
				{
					curr_track.PostgapLength = (cmd as CUE_File.Command.POSTGAP).Length;
				}

			}

			//it's a bit odd to close the file before closing the track, but...
			//we need to be sure to CloseFile first to make sure the track is marked as the final one in the file
			CloseFile();
			CloseTrack();

			CreateTrack1Pregap();
			FinalAnalysis();

			FinishLog();
			
		} //Run()
Exemple #2
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 });
 }
Exemple #3
0
		void OpenTrack(CUE_File.Command.TRACK trackCommand)
		{
			curr_track = new CompiledCueTrack();

			//spill cdtext data into this track
			curr_cdtext = curr_track.CDTextData;
		
			curr_track.BlobIndex = curr_blobIndex;
			curr_track.Number = trackCommand.Number;
			curr_track.TrackType = trackCommand.Type;

			//default flags
			if (curr_track.TrackType != CueTrackType.Audio)
				curr_track.Flags = CueTrackFlags.DATA;

			if (!curr_fileHasTrack)
			{
				curr_fileHasTrack = curr_track.IsFirstInFile = true;
			}

			UpdateDiscInfo(trackCommand);
		}
Exemple #4
0
		void CloseTrack()
		{
			if (curr_track == null)
				return;
				
			//normalize: if an index 0 is missing, add it here
			if (curr_track.Indexes[0].Number != 0)
			{
				var index0 = new CompiledCueIndex();
				var index1 = curr_track.Indexes[0];
				index0.Number = 0;
				index0.FileMSF = index1.FileMSF; //same MSF as index 1 will make it effectively nonexistent

				//well now, if it's the first in the file, an implicit index will take its value from 00:00:00 in the file
				//this is the kind of thing I sought to solve originally by 'interpreting' the file, but it seems easy enough to handle this way
				//my carlin.cue tests this but test cases shouldnt be hard to find
				if (curr_track.IsFirstInFile)
					index0.FileMSF = new Timestamp(0);

				curr_track.Indexes.Insert(0, index0);
			}

			OUT_CompiledCueTracks.Add(curr_track);
			curr_track = null;
		}
Exemple #5
0
		void OpenTrack(CUE_File.Command.TRACK trackCommand)
		{
			//assert that a file is open
			if(curr_file == null)
			{
				Error("Track command encountered with no active file");
				throw new DiscJobAbortException();
			}

			curr_track = new CompiledCueTrack();

			//spill cdtext data into this track
			curr_cdtext = curr_track.CDTextData;
		
			curr_track.BlobIndex = curr_blobIndex;
			curr_track.Number = trackCommand.Number;
			curr_track.TrackType = trackCommand.Type;

			//default flags
			if (curr_track.TrackType != CueTrackType.Audio)
				curr_track.Flags = CueTrackFlags.DATA;

			if (!curr_fileHasTrack)
			{
				curr_fileHasTrack = curr_track.IsFirstInFile = true;
			}

			UpdateDiscInfo(trackCommand);
		}
Exemple #6
0
        public override void Run()
        {
            //params
            var compiled = IN_CompileJob;
            var context  = compiled.IN_CueContext;

            OUT_Disc = new Disc();

            //generation state
            int      curr_index;
            int      curr_blobIndex  = -1;
            int      curr_blobMSF    = -1;
            BlobInfo curr_blobInfo   = null;
            long     curr_blobOffset = -1;

            //mount all input files
            MountBlobs();

            //unhappily, we cannot determine the length of all the tracks without knowing the length of the files
            //now that the files are mounted, we can figure the track lengths
            AnalyzeTracks();

            //loop from track 1 to 99
            //(track 0 isnt handled yet, that's way distant work)
            for (int t = 1; t < TrackInfos.Count; t++)
            {
                TrackInfo        ti  = TrackInfos[t];
                CompiledCueTrack cct = ti.CompiledCueTrack;

                //---------------------------------
                //setup track pregap processing
                //per "Example 05" on digitalx.org, pregap can come from index specification and pregap command
                int specifiedPregapLength = cct.PregapLength.Sector;
                int impliedPregapLength   = cct.Indexes[1].FileMSF.Sector - cct.Indexes[0].FileMSF.Sector;
                int totalPregapLength     = specifiedPregapLength + impliedPregapLength;

                //from now on we'll track relative timestamp and increment it continually
                int relMSF = -totalPregapLength;

                //read more at policies declaration
                //if (!context.DiscMountPolicy.CUE_PauseContradictionModeA)
                //  relMSF += 1;
                //---------------------------------


                //---------------------------------
                //generate sectors for this track.

                //advance to the next file if needed
                if (curr_blobIndex != cct.BlobIndex)
                {
                    curr_blobIndex  = cct.BlobIndex;
                    curr_blobOffset = 0;
                    curr_blobMSF    = 0;
                    curr_blobInfo   = BlobInfos[curr_blobIndex];
                }

                //work until the next track is reached, or the end of the current file is reached, depending on the track type
                curr_index = 0;
                for (; ;)
                {
                    bool trackDone   = false;
                    bool generateGap = false;

                    if (specifiedPregapLength > 0)
                    {
                        //if burning through a specified pregap, count it down
                        generateGap = true;
                        specifiedPregapLength--;
                    }
                    else
                    {
                        //if burning through the file, select the appropriate index by inspecting the next index and seeing if we've reached it
                        for (; ;)
                        {
                            if (curr_index == cct.Indexes.Count - 1)
                            {
                                break;
                            }
                            if (curr_blobMSF >= cct.Indexes[curr_index + 1].FileMSF.Sector)
                            {
                                curr_index++;
                                if (curr_index == 1)
                                {
                                    //WE ARE NOW AT INDEX 1: generate the RawTOCEntry for this track
                                    EmitRawTOCEntry(cct);
                                }
                            }
                            else
                            {
                                break;
                            }
                        }
                    }

                    //select the track type for the subQ
                    //it's obviously the same as the main track type usually, but during a pregap it can be different
                    TrackInfo qTrack  = ti;
                    int       qRelMSF = relMSF;
                    if (curr_index == 0)
                    {
                        //tweak relMSF due to ambiguity/contradiction in yellowbook docs
                        if (!context.DiscMountPolicy.CUE_PregapContradictionModeA)
                        {
                            qRelMSF++;
                        }

                        //[IEC10149] says there's two "intervals" of a pregap.
                        //mednafen's pseudocode interpretation of this:
                        //if this is a data track and the previous track was not data, the last 150 sectors of the pregap match this track and the earlier sectors (at least 75) math the previous track
                        //I agree, so let's do it that way
                        if (t != 1 && cct.TrackType != CueTrackType.Audio && TrackInfos[t - 1].CompiledCueTrack.TrackType == CueTrackType.Audio)
                        {
                            if (relMSF < -150)
                            {
                                qTrack = TrackInfos[t - 1];
                            }
                        }
                    }

                    //generate the right kind of sector synth for this track
                    SS_Base ss = null;
                    if (generateGap)
                    {
                        ss = new SS_Gap {
                            TrackType = qTrack.CompiledCueTrack.TrackType
                        };
                    }
                    else
                    {
                        int sectorSize = int.MaxValue;
                        switch (qTrack.CompiledCueTrack.TrackType)
                        {
                        case CueTrackType.Audio:
                        case CueTrackType.CDI_2352:
                        case CueTrackType.Mode1_2352:
                        case CueTrackType.Mode2_2352:
                            ss         = new SS_2352();
                            sectorSize = 2352;
                            break;

                        case CueTrackType.Mode1_2048:
                            ss         = new SS_Mode1_2048();
                            sectorSize = 2048;
                            break;

                        default:
                        case CueTrackType.Mode2_2336:
                            throw new InvalidOperationException($"Not supported: {cct.TrackType}");
                        }

                        ss.Blob          = curr_blobInfo.Blob;
                        ss.BlobOffset    = curr_blobOffset;
                        curr_blobOffset += sectorSize;
                        curr_blobMSF++;
                    }

                    ss.Policy = context.DiscMountPolicy;

                    //setup subQ
                    byte ADR = 1;                     //absent some kind of policy for how to set it, this is a safe assumption:
                    ss.sq.SetStatus(ADR, (EControlQ)(int)qTrack.CompiledCueTrack.Flags);
                    ss.sq.q_tno        = BCD2.FromDecimal(cct.Number);
                    ss.sq.q_index      = BCD2.FromDecimal(curr_index);
                    ss.sq.AP_Timestamp = OUT_Disc._Sectors.Count;
                    ss.sq.Timestamp    = qRelMSF;

                    //setup subP
                    if (curr_index == 0)
                    {
                        ss.Pause = true;
                    }

                    OUT_Disc._Sectors.Add(ss);
                    relMSF++;

                    if (cct.IsFinalInFile)
                    {
                        //sometimes, break when the file is exhausted
                        if (curr_blobOffset >= curr_blobInfo.Length)
                        {
                            trackDone = true;
                        }
                    }
                    else
                    {
                        //other times, break when the track is done
                        //(this check is safe because it's not the final track overall if it's not the final track in a file)
                        if (curr_blobMSF >= TrackInfos[t + 1].CompiledCueTrack.Indexes[0].FileMSF.Sector)
                        {
                            trackDone = true;
                        }
                    }

                    if (trackDone)
                    {
                        break;
                    }
                }

                //---------------------------------
                //gen postgap sectors
                int specifiedPostgapLength = cct.PostgapLength.Sector;
                for (int s = 0; s < specifiedPostgapLength; s++)
                {
                    var ss = new SS_Gap
                    {
                        TrackType = cct.TrackType                          // TODO - old track type in some < -150 cases?
                    };

                    //-subq-
                    byte ADR = 1;
                    ss.sq.SetStatus(ADR, (EControlQ)(int)cct.Flags);
                    ss.sq.q_tno        = BCD2.FromDecimal(cct.Number);
                    ss.sq.q_index      = BCD2.FromDecimal(curr_index);
                    ss.sq.AP_Timestamp = OUT_Disc._Sectors.Count;
                    ss.sq.Timestamp    = relMSF;

                    //-subP-
                    //always paused--is this good enough?
                    ss.Pause = true;

                    OUT_Disc._Sectors.Add(ss);
                    relMSF++;
                }
            }             //end track loop


            //add RawTOCEntries A0 A1 A2 to round out the TOC
            var TOCMiscInfo = new Synthesize_A0A1A2_Job {
                IN_FirstRecordedTrackNumber = IN_CompileJob.OUT_CompiledDiscInfo.FirstRecordedTrackNumber,
                IN_LastRecordedTrackNumber  = IN_CompileJob.OUT_CompiledDiscInfo.LastRecordedTrackNumber,
                IN_Session1Format           = IN_CompileJob.OUT_CompiledDiscInfo.SessionFormat,
                IN_LeadoutTimestamp         = OUT_Disc._Sectors.Count
            };

            TOCMiscInfo.Run(OUT_Disc.RawTOCEntries);

            //TODO - generate leadout, or delegates at least

            //blech, old crap, maybe
            //OUT_Disc.Structure.Synthesize_TOCPointsFromSessions();

            //FinishLog();
        } //Run()
Exemple #7
0
        public override void Run()
        {
            //in params
            var cue = IN_CueFile;

            //output state
            OUT_GlobalCDText      = new CompiledCDText();
            OUT_CompiledDiscInfo  = new CompiledDiscInfo();
            OUT_CompiledCueFiles  = new List <CompiledCueFile>();
            OUT_CompiledCueTracks = new List <CompiledCueTrack>();

            //add a track 0, for addressing convenience.
            //note: for future work, track 0 may need emulation (accessible by very negative LBA--the TOC is stored there)
            var track0 = new CompiledCueTrack()
            {
                Number = 0,
            };

            OUT_CompiledCueTracks.Add(track0);

            //global cd text will acquire the cdtext commands set before track commands
            curr_cdtext = OUT_GlobalCDText;

            foreach (var cmd in cue.Commands)
            {
                switch (cmd)
                {
                case CUE_File.Command.CATALOG:
                case CUE_File.Command.CDTEXTFILE:
                    // these commands get dealt with globally. nothing to be done here
                    // (but in the future we need to accumulate them into the compile pass output)
                    continue;

                case CUE_File.Command.REM:
                case CUE_File.Command.COMMENT:
                    // nothing to be done for comments
                    continue;

                case CUE_File.Command.PERFORMER performerCmd:
                    curr_cdtext.Performer = performerCmd.Value;
                    break;

                case CUE_File.Command.SONGWRITER songwriterCmd:
                    curr_cdtext.Songwriter = songwriterCmd.Value;
                    break;

                case CUE_File.Command.TITLE titleCmd:
                    curr_cdtext.Title = titleCmd.Value;
                    break;

                case CUE_File.Command.ISRC isrcCmd:
                    curr_cdtext.ISRC = isrcCmd.Value;
                    break;

                case CUE_File.Command.FLAGS flagsCmd:
                    // flags can only be set when a track command is running
                    if (curr_track == null)
                    {
                        Warn("Ignoring invalid flag commands outside of a track command");
                    }
                    else
                    {
                        curr_track.Flags |= flagsCmd.Flags;                      // take care to |= it here, so the data flag doesn't get cleared
                    }
                    break;

                case CUE_File.Command.TRACK trackCmd:
                    CloseTrack();
                    OpenTrack(trackCmd);
                    break;

                case CUE_File.Command.FILE fileCmd:
                    CloseFile();
                    OpenFile(fileCmd);
                    break;

                case CUE_File.Command.INDEX indexCmd:
                    //TODO validate no postgap specified
                    AddIndex(indexCmd);
                    break;

                case CUE_File.Command.PREGAP pregapCmd:
                    //TODO validate track open
                    //TODO validate no indexes
                    curr_track.PregapLength = pregapCmd.Length;
                    break;

                case CUE_File.Command.POSTGAP postgapCmd:
                    curr_track.PostgapLength = postgapCmd.Length;
                    break;
                }
            }

            //it's a bit odd to close the file before closing the track, but...
            //we need to be sure to CloseFile first to make sure the track is marked as the final one in the file
            CloseFile();
            CloseTrack();

            CreateTrack1Pregap();
            FinalAnalysis();

            FinishLog();
        } //Run()