From 70f97851b3c726d28e0c2cdfb4693543a755da08 Mon Sep 17 00:00:00 2001 From: zeromus Date: Wed, 1 Jul 2015 02:56:55 -0500 Subject: [PATCH] continue cue loading --- BizHawk.Emulation.DiscSystem/API/Disc.API.cs | 2 + .../Blobs/Blob_ZeroPadAdapter.cs | 37 ++ .../CUE/CUE_Compile.cs | 35 +- BizHawk.Emulation.DiscSystem/CUE/CUE_Load.cs | 459 ++++++++---------- BizHawk.Emulation.DiscSystem/CUE/CUE_Parse.cs | 11 +- BizHawk.Emulation.DiscSystem/Disc.cs | 16 +- 6 files changed, 292 insertions(+), 268 deletions(-) diff --git a/BizHawk.Emulation.DiscSystem/API/Disc.API.cs b/BizHawk.Emulation.DiscSystem/API/Disc.API.cs index a7895df665..64f912d977 100644 --- a/BizHawk.Emulation.DiscSystem/API/Disc.API.cs +++ b/BizHawk.Emulation.DiscSystem/API/Disc.API.cs @@ -89,6 +89,8 @@ namespace BizHawk.Emulation.DiscSystem /// /// Main API to determine how many LBAs are available on the disc. /// This counts from LBA 0 to the final sector available. + /// THIS IS DUMB. Like everything else here. + /// Fetch it from a toc or disc structure /// public int LBACount { get { return ABACount - 150; } } diff --git a/BizHawk.Emulation.DiscSystem/Blobs/Blob_ZeroPadAdapter.cs b/BizHawk.Emulation.DiscSystem/Blobs/Blob_ZeroPadAdapter.cs index ca2cd1c362..ca3cd043c5 100644 --- a/BizHawk.Emulation.DiscSystem/Blobs/Blob_ZeroPadAdapter.cs +++ b/BizHawk.Emulation.DiscSystem/Blobs/Blob_ZeroPadAdapter.cs @@ -7,6 +7,43 @@ namespace BizHawk.Emulation.DiscSystem { public partial class Disc : IDisposable { + + internal sealed class Blob_ZeroPadAdapter : IBlob + { + IBlob srcBlob; + long srcBlobLength; + public Blob_ZeroPadAdapter(IBlob srcBlob, long srcBlobLength) + { + this.srcBlob = srcBlob; + this.srcBlobLength = srcBlobLength; + } + + public int Read(long byte_pos, byte[] buffer, int offset, int count) + { + int todo = count; + long end = byte_pos + todo; + if (end > srcBlobLength) + { + long temp = (int)(srcBlobLength - end); + if (temp > int.MaxValue) + throw new InvalidOperationException(); + todo = (int)temp; + + //zero-fill the unused part (just for safety's sake) + Array.Clear(buffer, offset + todo, count - todo); + } + + srcBlob.Read(byte_pos, buffer, offset, todo); + + //since it's zero padded, this never fails and always reads the requested amount + return count; + } + + public void Dispose() + { + } + } + /// /// For use with blobs which are prematurely ended: buffers what there is, and zero-pads the rest /// diff --git a/BizHawk.Emulation.DiscSystem/CUE/CUE_Compile.cs b/BizHawk.Emulation.DiscSystem/CUE/CUE_Compile.cs index 72676bc31f..d34a513cf8 100644 --- a/BizHawk.Emulation.DiscSystem/CUE/CUE_Compile.cs +++ b/BizHawk.Emulation.DiscSystem/CUE/CUE_Compile.cs @@ -22,6 +22,10 @@ namespace BizHawk.Emulation.DiscSystem internal class CompiledCueIndex { public int Number; + + /// + /// this is annoying, it should just be an integer + /// public Timestamp FileMSF; public override string ToString() @@ -84,6 +88,12 @@ namespace BizHawk.Emulation.DiscSystem { public int BlobIndex; public int Number; + + /// + /// A track that's final in the file gets its length from the length of the file; other tracks lengths are determined from the succeeding track + /// + public bool IsFinalInFile; + public CompiledCDText CDTextData = new CompiledCDText(); public Timestamp PregapLength, PostgapLength; public CueFile.TrackFlags Flags = CueFile.TrackFlags.None; @@ -150,6 +160,7 @@ namespace BizHawk.Emulation.DiscSystem CompiledCDText curr_cdtext; int curr_blobIndex = -1; CompiledCueTrack curr_track = null; + CompiledCueFile curr_file = null; bool discinfo_session1Format_determined = false; void UpdateDiscInfo(CueFile.Command.TRACK trackCommand) @@ -179,8 +190,22 @@ namespace BizHawk.Emulation.DiscSystem } } - void AddFile(CueFile.Command.FILE f) + void CloseFile() { + if (curr_track != null) + { + //flag this track as the final one in the file + curr_track.IsFinalInFile = true; + } + + curr_file = null; + } + + void OpenFile(CueFile.Command.FILE f) + { + if (curr_file != null) + CloseFile(); + curr_blobIndex++; var Resolver = IN_CueFormat.Resolver; @@ -311,7 +336,7 @@ namespace BizHawk.Emulation.DiscSystem var index0 = new CompiledCueIndex(); var index1 = curr_track.Indexes[0]; index0.Number = 0; - index0.FileMSF = index1.FileMSF; + index0.FileMSF = index1.FileMSF; //same MSF as index 1 will make it effectively nonexistent curr_track.Indexes.Insert(0, index0); } @@ -401,7 +426,8 @@ namespace BizHawk.Emulation.DiscSystem if (cmd is CueFile.Command.FILE) { - AddFile(cmd as CueFile.Command.FILE); + CloseFile(); + OpenFile(cmd as CueFile.Command.FILE); } if (cmd is CueFile.Command.INDEX) @@ -424,6 +450,9 @@ namespace BizHawk.Emulation.DiscSystem } + //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(); diff --git a/BizHawk.Emulation.DiscSystem/CUE/CUE_Load.cs b/BizHawk.Emulation.DiscSystem/CUE/CUE_Load.cs index 8575354178..3f17469f5c 100644 --- a/BizHawk.Emulation.DiscSystem/CUE/CUE_Load.cs +++ b/BizHawk.Emulation.DiscSystem/CUE/CUE_Load.cs @@ -49,177 +49,35 @@ namespace BizHawk.Emulation.DiscSystem Normal, Pregap, Postgap } - //current blob file state - int file_cfi_index = -1; - IBlob file_blob = null; - CueFile.Command.FILE file_currentCommand = null; - long file_ofs = 0, file_len = 0; - int file_msf = -1; - - //current track, flags, and index state - CueFile.Command.TRACK track_pendingCommand = null; - CueFile.Command.TRACK track_currentCommand = null; - CueFile.TrackFlags track_pendingFlags = CueFile.TrackFlags.None; - CueFile.TrackFlags track_currentFlags = CueFile.TrackFlags.None; - - //burn state. - //TODO - separate burner into another class? - BurnType burntype_current; - Timestamp burn_pregap_timestamp; - - - void BeginBurnPregap() + class BlobInfo { - //TODO? + public IBlob Blob; + public long Length; } - void BurnPregap(Timestamp length) + //not sure if we need this... + class TrackInfo { - burntype_current = BurnType.Pregap; - burn_pregap_timestamp = length; - int length_lba = length.Sector; + public int Length; - //TODO: read [IEC10149] 20, 20.1, & 20.2 to assign pre-gap and post-gap types correctly depending on track number and previous track - //ALSO, if the last track is data, we need to make a post-gap - //we can grab the previously generated sector in order to figure out how to encode new pregap sectors - for(int i=0;i BlobInfos; + List TrackInfos = new List(); - ////open the new blob - //file_currentCommand = file; - //file_msf = 0; - //var cfi = IN_CompileJob.OUT_FileInfos[++file_cfi_index]; - - ////mount the file - //if (cfi.Type == AnalyzeCueJob.CueFileType.BIN || cfi.Type == AnalyzeCueJob.CueFileType.Unknown) - //{ - // //raw files: - // var blob = new Disc.Blob_RawFile { PhysicalPath = cfi.FullPath }; - // OUT_Disc.DisposableResources.Add(file_blob = blob); - // file_len = blob.Length; - //} - //else if (cfi.Type == AnalyzeCueJob.CueFileType.ECM) - //{ - // var blob = new Disc.Blob_ECM(); - // OUT_Disc.DisposableResources.Add(file_blob = blob); - // blob.Load(cfi.FullPath); - // file_len = blob.Length; - //} - //else if (cfi.Type == AnalyzeCueJob.CueFileType.WAVE) - //{ - // var blob = new Disc.Blob_WaveFile(); - // OUT_Disc.DisposableResources.Add(file_blob = blob); - // blob.Load(cfi.FullPath); - // file_len = blob.Length; - //} - //else if (cfi.Type == AnalyzeCueJob.CueFileType.DecodeAudio) - //{ - // FFMpeg ffmpeg = new FFMpeg(); - // if (!ffmpeg.QueryServiceAvailable()) - // { - // throw new DiscReferenceException(cfi.FullPath, "No decoding service was available (make sure ffmpeg.exe is available. even though this may be a wav, ffmpeg is used to load oddly formatted wave files. If you object to this, please send us a note and we'll see what we can do. It shouldn't be too hard.)"); - // } - // AudioDecoder dec = new AudioDecoder(); - // byte[] buf = dec.AcquireWaveData(cfi.FullPath); - // var blob = new Disc.Blob_WaveFile(); - // OUT_Disc.DisposableResources.Add(file_blob = blob); - // blob.Load(new MemoryStream(buf)); - //} - } - - void BurnToEOF() - { - while (file_ofs < file_len) - BurnSector(); - - //TODO - if a postgap was requested, do it now - } - - void ProcessIndex(CueFile.Command.INDEX index) - { - //burn sectors with the previous registers until we reach the current index MSF - int index_file_msf = index.Timestamp.Sector; - while (file_msf < index_file_msf) - BurnSector(); - - //latch current track settings - track_currentCommand = track_pendingCommand; - track_currentFlags = track_pendingFlags; - - //index 0 is annoying. we have to code its subchannels while knowing the index that comes next - //this is the main reason for transforming the cue file into a CueGrid (any index 0 can easily reference the index 1 that comes after it) - if (index.Number == 0) - { - } - } - - void EatBlobFileSector(int required, out IBlob blob, out long blobOffset) - { - blob = file_blob; - blobOffset = file_ofs; - if (file_ofs + required > file_len) - { - Warn("Zero-padding mis-sized cue blob file: " + Path.GetFileName(file_currentCommand.Path)); - blob = Disc.Blob_ZeroPadBuffer.MakeBufferFrom(file_blob,file_ofs,required); - OUT_Disc.DisposableResources.Add(blob); - blobOffset = 0; - } - file_ofs += required; - } - - void BurnSector_Normal() - { - SS_Base ss = null; - switch (track_currentCommand.Type) - { - case CueFile.TrackType.Mode2_2352: - ss = new SS_2352(); - EatBlobFileSector(2352, out ss.Blob, out ss.BlobOffset); - break; - case CueFile.TrackType.Audio: - ss = new SS_2352(); - EatBlobFileSector(2352, out ss.Blob, out ss.BlobOffset); - break; - } - - var se = new SectorEntry(null); - se.SectorSynth = ss; - OUT_Disc.Sectors.Add(se); - } - - void BurnSector_Pregap() - { - var se = new SectorEntry(null); - se.SectorSynth = new SS_Mode1_2048(); //TODO - actually burn the right thing - OUT_Disc.Sectors.Add(se); - - burn_pregap_timestamp = new Timestamp(burn_pregap_timestamp.Sector - 1); - } - - void BurnSector() - { - switch (burntype_current) - { - case BurnType.Normal: - BurnSector_Normal(); - break; - case BurnType.Pregap: - BurnSector_Pregap(); - break; - } - } void MountBlobs() { + IBlob file_blob = null; + long file_len = 0; + + BlobInfos = new List(); foreach (var ccf in IN_CompileJob.OUT_CompiledCueFiles) { + var bi = new BlobInfo(); + BlobInfos.Add(bi); + switch (ccf.Type) { case CompiledCueFileType.BIN: @@ -228,7 +86,7 @@ namespace BizHawk.Emulation.DiscSystem //raw files: var blob = new Disc.Blob_RawFile { PhysicalPath = ccf.FullPath }; OUT_Disc.DisposableResources.Add(file_blob = blob); - file_len = blob.Length; + bi.Length = blob.Length; break; } case CompiledCueFileType.ECM: @@ -236,7 +94,7 @@ namespace BizHawk.Emulation.DiscSystem var blob = new Disc.Blob_ECM(); OUT_Disc.DisposableResources.Add(file_blob = blob); blob.Load(ccf.FullPath); - file_len = blob.Length; + bi.Length = blob.Length; break; } case CompiledCueFileType.WAVE: @@ -244,7 +102,7 @@ namespace BizHawk.Emulation.DiscSystem var blob = new Disc.Blob_WaveFile(); OUT_Disc.DisposableResources.Add(file_blob = blob); blob.Load(ccf.FullPath); - file_len = blob.Length; + bi.Length = blob.Length; break; } case CompiledCueFileType.DecodeAudio: @@ -259,35 +117,69 @@ namespace BizHawk.Emulation.DiscSystem var blob = new Disc.Blob_WaveFile(); OUT_Disc.DisposableResources.Add(file_blob = blob); blob.Load(new MemoryStream(buf)); + bi.Length = buf.Length; break; } default: throw new InvalidOperationException(); - } + } //switch(file type) + + //wrap all the blobs with zero padding + bi.Blob = new Disc.Blob_ZeroPadAdapter(file_blob, bi.Length); } } + + void AnalyzeTracks() + { + var compiledTracks = IN_CompileJob.OUT_CompiledCueTracks; + + for(int t=0;t trackLookup = new Dictionary(); - foreach (var cct in compiled.OUT_CompiledCueTracks) - trackLookup[cct.Number] = cct; + //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 <= 99; t++) + for (int t = 1; t < TrackInfos.Count; t++) { - CompiledCueTrack cct; - if (!trackLookup.TryGetValue(t, out cct)) - continue; + TrackInfo ti = TrackInfos[t]; + CompiledCueTrack cct = ti.CompiledCueTrack; //--------------------------------- //generate track pregap @@ -310,92 +202,129 @@ namespace BizHawk.Emulation.DiscSystem //after this, pregap sectors are generated like a normal sector, but the subQ is specified as a pregap instead of a normal track //--------------------------------- - //--------------------------------- - //WE ARE NOW AT INDEX 1 - //--------------------------------- //--------------------------------- - //generate the RawTOCEntry for this track - SubchannelQ sq = new SubchannelQ(); - //absent some kind of policy for how to set it, this is a safe assumption: - byte ADR = 1; - sq.SetStatus(ADR, (EControlQ)(int)cct.Flags); - 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. - sq.q_index = BCD2.FromDecimal(cct.Number); - //not too sure about these yet - sq.min = BCD2.FromDecimal(0); - sq.sec = BCD2.FromDecimal(0); - sq.frame = BCD2.FromDecimal(0); - sq.AP_Timestamp = new Timestamp(OUT_Disc.Sectors.Count + 150); //its supposed to be an absolute timestamp - OUT_Disc.RawTOCEntries.Add(new RawTOCEntry { QData = sq }); - } + //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; + + //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 (make another method for this please) + 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); //its supposed to be an absolute timestamp + OUT_Disc.RawTOCEntries.Add(new RawTOCEntry { QData = toc_sq }); + } + } + else break; + } + + //generate a sector: + ISector siface = null; + SS_Base ss = null; + switch (cct.TrackType) + { + case CueFile.TrackType.Mode2_2352: + case CueFile.TrackType.Audio: + ss = new SS_2352() { Blob = curr_blobInfo.Blob, BlobOffset = curr_blobOffset }; + curr_blobOffset += 2352; + break; + } + + //make the subcode + //TODO - according to policies, or better yet, defer this til it's needed (user delivers a policies object to disc reader apis) + //at any rate, we'd paste this logic into there so let's go ahead and write it here + var subcode = new BufferedSubcodeSector(); //(its lame that we have to use this; make a static method when we delete this class) + SubchannelQ sq = new SubchannelQ(); + byte ADR = 1; //absent some kind of policy for how to set it, this is a safe assumption: + sq.SetStatus(ADR, (EControlQ)(int)cct.Flags); + sq.q_tno = BCD2.FromDecimal(cct.Number); + sq.q_index = BCD2.FromDecimal(curr_index); + int LBA = OUT_Disc.Sectors.Count; + sq.ap_min = BCD2.FromDecimal(new Timestamp(LBA).MIN); + sq.ap_sec = BCD2.FromDecimal(new Timestamp(LBA).SEC); + sq.ap_frame = BCD2.FromDecimal(new Timestamp(LBA).FRAC); + int track_relative_msf = curr_blobMSF - cct.Indexes[1].FileMSF.Sector; + 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); + //finally we're done: synthesize subchannel + subcode.Synthesize_SubchannelQ(ref sq, true); + + //make the SectorEntry (some temporary bullshit here) + var se = new SectorEntry(null); + se.SectorSynth = ss; + ss.sq = sq; + OUT_Disc.Sectors.Add(se); + curr_blobMSF++; + + 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; + } + + //TODO - POSTGAP + + } //end track loop - - ////now for the magic. Process commands in order - //for (int i = 0; i < cue.Commands.Count; i++) - //{ - // var cmd = cue.Commands[i]; + //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 = new Timestamp(OUT_Disc.Sectors.Count) //do we need a +150? + }; + TOCMiscInfo.Run(OUT_Disc.RawTOCEntries); - // //these commands get dealt with globally. nothing to be done here - // if (cmd is CueFile.Command.CATALOG || cmd is CueFile.Command.CDTEXTFILE) continue; - - // //nothing to be done for comments - // if (cmd is CueFile.Command.REM) continue; - // if (cmd is CueFile.Command.COMMENT) continue; - - // //handle cdtext and ISRC state updates, theyre kind of like little registers - // if (cmd is CueFile.Command.PERFORMER) - // cdtext_performer = (cmd as CueFile.Command.PERFORMER).Value; - // if (cmd is CueFile.Command.SONGWRITER) - // cdtext_songwriter = (cmd as CueFile.Command.SONGWRITER).Value; - // if (cmd is CueFile.Command.TITLE) - // cdtext_title = (cmd as CueFile.Command.TITLE).Value; - // if (cmd is CueFile.Command.ISRC) - // isrc = (cmd as CueFile.Command.ISRC).Value; - - // //flags are also a kind of a register. but the flags value is reset by the track command - // if (cmd is CueFile.Command.FLAGS) - // { - // track_pendingFlags = (cmd as CueFile.Command.FLAGS).Flags; - // } - - // if (cmd is CueFile.Command.TRACK) - // { - // var track = cmd as CueFile.Command.TRACK; - - // //register the track for further processing when an GENERATION command appears - // track_pendingCommand = track; - // track_pendingFlags = CueFile.TrackFlags.None; - - // } - - // if (cmd is CueFile.Command.FILE) - // { - // ProcessFile(cmd as CueFile.Command.FILE); - // } - - // if (cmd is CueFile.Command.INDEX) - // { - // ProcessIndex(cmd as CueFile.Command.INDEX); - // } - //} - - //BurnToEOF(); - - ////add RawTOCEntries A0 A1 A2 to round out the TOC - //var TOCMiscInfo = new Synthesize_A0A1A2_Job { - // IN_FirstRecordedTrackNumber = sloshy_firstRecordedTrackNumber, - // IN_LastRecordedTrackNumber = sloshy_lastRecordedTrackNumber, - // IN_Session1Format = sloshy_session1Format, - // IN_LeadoutTimestamp = new Timestamp(OUT_Disc.Sectors.Count) - //}; - //TOCMiscInfo.Run(OUT_Disc.RawTOCEntries); - - ////generate the TOCRaw from the RawTocEntries - //var tocSynth = new DiscTOCRaw.SynthesizeFromRawTOCEntriesJob() { Entries = OUT_Disc.RawTOCEntries }; - //tocSynth.Run(); - //OUT_Disc.TOCRaw = tocSynth.Result; + //generate the TOCRaw from the RawTocEntries + var tocSynth = new DiscTOCRaw.SynthesizeFromRawTOCEntriesJob() { Entries = OUT_Disc.RawTOCEntries }; + tocSynth.Run(); + OUT_Disc.TOCRaw = tocSynth.Result; ////generate lead-out track with some canned number of sectors ////TODO - move this somewhere else and make it controllable depending on which console is loading up the disc @@ -411,4 +340,30 @@ namespace BizHawk.Emulation.DiscSystem } //Run() } //class LoadCueJob } //partial class CUE_Format2 -} //namespace BizHawk.Emulation.DiscSystem \ No newline at end of file +} //namespace BizHawk.Emulation.DiscSystem + + + + +//TODO: + //if (index_num == 0 || type == SectorWriteType.Pregap) + //{ + // //PREGAP: + // //things are negative here. + // if (track_relative_msf > 0) throw new InvalidOperationException("Perplexing internal error with non-negative pregap MSF"); + // track_relative_msf = -track_relative_msf; + + // //now for something special. + // //yellow-book says: + // //pre-gap for "first part of a digital data track not containing user data and encoded as a pause" + // //first interval: at least 75 sectors coded as preceding track + // //second interval: at least 150 sectors coded as user data track. + // //so... we ASSUME the 150 sector pregap is more important. so if thats all there is, theres no 75 sector pregap like the old track + // //if theres a longer pregap, then we generate weird old track pregap to contain the rest. + // if (track_relative_msf > 150) + // { + // //only if we're burning a data track now + // if((track_flags & CueFile.TrackFlags.DATA)!=0) + // sq.q_status = priorSubchannelQ.q_status; + // } + //} \ No newline at end of file diff --git a/BizHawk.Emulation.DiscSystem/CUE/CUE_Parse.cs b/BizHawk.Emulation.DiscSystem/CUE/CUE_Parse.cs index 33ced561c9..1228f737ef 100644 --- a/BizHawk.Emulation.DiscSystem/CUE/CUE_Parse.cs +++ b/BizHawk.Emulation.DiscSystem/CUE/CUE_Parse.cs @@ -29,15 +29,14 @@ namespace BizHawk.Emulation.DiscSystem /// /// Represents the contents of a cue file - /// This is relatively unstructured because in a lot (maybe every) way a cue file is meant to be processed one command at a time /// public class CueFile { - // (here are all the commands we can encounter) - //TODO - record line number origin of command? + // (here are all the commands we can encounter) public static class Command { + //TODO - record line number origin of command? Kind of nice but inessential public class CATALOG { public string Value; public override string ToString() { return string.Format("CATALOG: {0}", Value); } } public class CDTEXTFILE { public string Path; public override string ToString() { return string.Format("CDTEXTFILE: {0}", Path); } } public class FILE { public string Path; public FileType Type; public override string ToString() { return string.Format("FILE ({0}): {1}", Type, Path); } } @@ -88,9 +87,11 @@ namespace BizHawk.Emulation.DiscSystem //All audio files (WAVE, AIFF, and MP3) must be in 44.1KHz 16-bit stereo format. //BUT NOTE: MP3 can be VBR and the length can't be known without decoding the whole thing. //But, some ideas: - //1. we could operate ffmpeg differently to retrieve the length, which maybe it can do without having to + //1. we could operate ffmpeg differently to retrieve the length, which maybe it can do without having to decode the entire thing //2. we could retrieve it from an ID3 if present. - //3. as a last resort, since MP3 is the annoying case usually, we could include my c# mp3 parser and sum the length + //3. as a last resort, since MP3 is the annoying case usually, we could include my c# mp3 parser and sum the length (test the performance, this might be reasonably fast on par with ECM parsing) + //NOTE: once deciding the length, we would have to stick with it! samples would have to be discarded or inserted to make the track work out + //but we COULD effectively achieve stream-loading mp3 discs, with enough work. public enum FileType { Unspecified, diff --git a/BizHawk.Emulation.DiscSystem/Disc.cs b/BizHawk.Emulation.DiscSystem/Disc.cs index 839498e847..dd9a2a2100 100644 --- a/BizHawk.Emulation.DiscSystem/Disc.cs +++ b/BizHawk.Emulation.DiscSystem/Disc.cs @@ -11,14 +11,17 @@ using System.Collections.Generic; //This will result in a completely flattened CCD where everything comes right off the hard drive //Our choice here might be an unwise decision for disc ID and miscellaneous purposes but it's best for gaming and stream-converting (hawking and hashing) +//TODO: in principle, we could mount audio to decode only on an as-needed basis +//this might result in hiccups during emulation, though, so it should be an option. +//This would imply either decode-length processing (scan file without decoding) or decoding and discarding the data. +//We should probably have some richer policy specifications for this kind of thing, but it's not a high priority. Main workflow is still discohawking. +//Alternate policies would probably be associated with copious warnings (examples: ? ? ?) + + //https://books.google.com/books?id=caF_AAAAQBAJ&lpg=PA124&ots=OA9Ttj9CHZ&dq=disc%20TOC%20point%20A2&pg=PA124 - - //http://www.staff.uni-mainz.de/tacke/scsi/SCSI2-14.html - //http://www.pctechguide.com/iso-9660-data-format-for-cds-cd-roms-cd-rs-and-cd-rws //http://linux.die.net/man/1/cue2toc - //http://cdemu.sourceforge.net/project.php#sf //apparently cdrdao is the ultimate linux tool for doing this stuff but it doesnt support DAO96 (or other DAO modes) that would be necessary to extract P-Q subchannels @@ -27,13 +30,11 @@ using System.Collections.Generic; //here is a featureset list of windows cd burning programs (useful for cuesheet compatibility info) //http://www.dcsoft.com/cue_mastering_progs.htm -//good +//good links //http://linux-sxs.org/bedtime/cdapi.html //http://en.wikipedia.org/wiki/Track_%28CD%29 //http://docs.google.com/viewer?a=v&q=cache:imNKye05zIEJ:www.13thmonkey.org/documentation/SCSI/mmc-r10a.pdf+q+subchannel+TOC+format&hl=en&gl=us&pid=bl&srcid=ADGEEShtYqlluBX2lgxTL3pVsXwk6lKMIqSmyuUCX4RJ3DntaNq5vI2pCvtkyze-fumj7vvrmap6g1kOg5uAVC0IxwU_MRhC5FB0c_PQ2BlZQXDD7P3GeNaAjDeomelKaIODrhwOoFNb&sig=AHIEtbRXljAcFjeBn3rMb6tauHWjSNMYrw -//r:\consoles\~docs\yellowbook //http://digitalx.org/cue-sheet/examples/ -// //"qemu cdrom emulator" //http://www.koders.com/c/fid7171440DEC7C18B932715D671DEE03743111A95A.aspx @@ -48,7 +49,6 @@ using System.Collections.Generic; //ideas: /* * do some stuff asynchronously. for example, decoding mp3 sectors. - * keep a list of 'blobs' (giant bins or decoded wavs likely) which can reference the disk * keep a list of sectors and the blob/offset from which they pull -- also whether the sector is available * if it is not available and something requests it then it will have to block while that sector gets generated * perhaps the blobs know how to resolve themselves and the requested sector can be immediately resolved (priority boost)