diff --git a/BizHawk.Client.DiscoHawk/DiscoHawk.cs b/BizHawk.Client.DiscoHawk/DiscoHawk.cs index f0964de8aa..e788189bb0 100644 --- a/BizHawk.Client.DiscoHawk/DiscoHawk.cs +++ b/BizHawk.Client.DiscoHawk/DiscoHawk.cs @@ -357,13 +357,13 @@ namespace BizHawk.Client.DiscoHawk var src_dsr = new DiscSectorReader(src_disc); var dst_dsr = new DiscSectorReader(dst_disc); - var src_toc = src_disc.TOCRaw; - var dst_toc = dst_disc.TOCRaw; + var src_toc = src_disc.TOC; + var dst_toc = dst_disc.TOC; var src_databuf = new byte[2448]; var dst_databuf = new byte[2448]; - Action sw_dump_toc_one = (item) => + Action sw_dump_toc_one = (item) => { if (!item.Exists) sw.Write("(---missing---)"); @@ -386,12 +386,12 @@ namespace BizHawk.Client.DiscoHawk } //verify TOC match - if (src_disc.TOCRaw.FirstRecordedTrackNumber != dst_disc.TOCRaw.FirstRecordedTrackNumber - || src_disc.TOCRaw.LastRecordedTrackNumber != dst_disc.TOCRaw.LastRecordedTrackNumber) + if (src_disc.TOC.FirstRecordedTrackNumber != dst_disc.TOC.FirstRecordedTrackNumber + || src_disc.TOC.LastRecordedTrackNumber != dst_disc.TOC.LastRecordedTrackNumber) { sw.WriteLine("Mismatch of RecordedTrackNumbers: {0}-{1} vs {2}-{3}", - src_disc.TOCRaw.FirstRecordedTrackNumber, src_disc.TOCRaw.LastRecordedTrackNumber, - dst_disc.TOCRaw.FirstRecordedTrackNumber, dst_disc.TOCRaw.LastRecordedTrackNumber + src_disc.TOC.FirstRecordedTrackNumber, src_disc.TOC.LastRecordedTrackNumber, + dst_disc.TOC.FirstRecordedTrackNumber, dst_disc.TOC.LastRecordedTrackNumber ); goto SKIPPO; } diff --git a/BizHawk.Emulation.Common/Sound/CDAudio.cs b/BizHawk.Emulation.Common/Sound/CDAudio.cs index aeac7fb0d2..8d9c2234ed 100644 --- a/BizHawk.Emulation.Common/Sound/CDAudio.cs +++ b/BizHawk.Emulation.Common/Sound/CDAudio.cs @@ -70,7 +70,7 @@ namespace BizHawk.Emulation.Common.Components public void PlayStartingAtLba(int lba) { - var track = Disc.Structure.SeekTrack(lba); + var track = Disc.Session1.SeekTrack(lba); if (track == null) return; PlayingTrack = track.Number; diff --git a/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/Yabause.cs b/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/Yabause.cs index c76e508fbb..322e440347 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/Yabause.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sega/Saturn/Yabause.cs @@ -421,7 +421,7 @@ namespace BizHawk.Emulation.Cores.Sega.Saturn rTOC[99] = (int)(rTOC[0] & 0xff000000 | 0x010000); rTOC[100] = (int)(rTOC[ntrk - 1] & 0xff000000 | (uint)(ntrk << 16)); - rTOC[101] = (int)(rTOC[ntrk - 1] & 0xff000000 | (uint)(CD.TOCRaw.LeadoutLBA.Sector)); //zero 03-jul-2014 - maybe off by 150 + rTOC[101] = (int)(rTOC[ntrk - 1] & 0xff000000 | (uint)(CD.TOC.LeadoutLBA.Sector)); //zero 03-jul-2014 - maybe off by 150 Marshal.Copy(rTOC, 0, dest, 102); diff --git a/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs b/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs index 64d919b237..0b13d41790 100644 --- a/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs +++ b/BizHawk.Emulation.Cores/Consoles/Sony/PSX/Octoshock.cs @@ -160,15 +160,15 @@ namespace BizHawk.Emulation.Cores.Sony.PSX int ShockDisc_ReadTOC(IntPtr opaque, OctoshockDll.ShockTOC* read_target, OctoshockDll.ShockTOCTrack* tracks101) { - read_target->disc_type = (byte)Disc.TOCRaw.Session1Format; - read_target->first_track = (byte)Disc.TOCRaw.FirstRecordedTrackNumber; //i _think_ thats what is meant here - read_target->last_track = (byte)Disc.TOCRaw.LastRecordedTrackNumber; //i _think_ thats what is meant here + read_target->disc_type = (byte)Disc.TOC.Session1Format; + read_target->first_track = (byte)Disc.TOC.FirstRecordedTrackNumber; //i _think_ thats what is meant here + read_target->last_track = (byte)Disc.TOC.LastRecordedTrackNumber; //i _think_ thats what is meant here tracks101[0].lba = tracks101[0].adr = tracks101[0].control = 0; for (int i = 1; i < 100; i++) { - var item = Disc.TOCRaw.TOCItems[i]; + var item = Disc.TOC.TOCItems[i]; tracks101[i].adr = (byte)(item.Exists ? 1 : 0); tracks101[i].lba = (uint)item.LBATimestamp.Sector; tracks101[i].control = (byte)item.Control; @@ -177,7 +177,7 @@ namespace BizHawk.Emulation.Cores.Sony.PSX ////the lead-out track is to be synthesized tracks101[read_target->last_track + 1].adr = 1; tracks101[read_target->last_track + 1].control = 0; - tracks101[read_target->last_track + 1].lba = (uint)Disc.TOCRaw.LeadoutLBA.Sector; + tracks101[read_target->last_track + 1].lba = (uint)Disc.TOC.LeadoutLBA.Sector; //element 100 is to be copied as the lead-out track tracks101[100] = tracks101[read_target->last_track + 1]; diff --git a/BizHawk.Emulation.DiscSystem/API/DiscStructure.cs b/BizHawk.Emulation.DiscSystem/API/DiscStructure.cs index ead82b203d..b1559654b5 100644 --- a/BizHawk.Emulation.DiscSystem/API/DiscStructure.cs +++ b/BizHawk.Emulation.DiscSystem/API/DiscStructure.cs @@ -18,126 +18,6 @@ namespace BizHawk.Emulation.DiscSystem /// public List Sessions = new List(); - - /// - /// Determines which track of session 1 is at the specified LBA. - /// Returns null if it's before track 1 - /// - public Track SeekTrack(int lba) - { - var ses = Sessions[1]; - - //take care with this loop bounds: - for (int i = 1; i <= ses.InformationTrackCount; i++) - { - var track = ses.Tracks[i]; - if (track.LBA > lba) - return (i==1)?null:ses.Tracks[i]; - } - return ses.Tracks[ses.Tracks.Count]; - } - - ///// - ///// Synthesizes the DiscStructure from RawTOCEntriesJob - ///// - //public class SynthesizeFromRawTOCEntriesJob - //{ - // public IEnumerable Entries; - // public DiscStructure Result; - - // public void Run() - // { - // Result = new DiscStructure(); - // var session = new Session(); - // Result.Sessions.Add(session); - - // //TODO - are these necessarily in order? - // foreach (var te in Entries) - // { - // int pt = te.QData.q_index.DecimalValue; - // int lba = te.QData.Timestamp.Sector; - // var bcd2 = new BCD2 { BCDValue = (byte)pt }; - // if (bcd2.DecimalValue > 99) //A0 A1 A2 leadout and crap - // continue; - // var track = new Track { LBA = lba, Number = pt }; - // session.Tracks.Add(track); - // } - // } - //} - - public class SynthesizeFromTOCRawJob - { - public Disc IN_Disc; - public DiscTOCRaw TOCRaw; - public DiscStructure Result; - - public void Run() - { - var dsr = new DiscSectorReader(IN_Disc); - dsr.Policy.DeterministicClearBuffer = false; - - Result = new DiscStructure(); - var session = new Session(); - Result.Sessions.Add(null); //placeholder session for reindexing - Result.Sessions.Add(session); - - session.Number = 1; - - if(TOCRaw.FirstRecordedTrackNumber != 1) - throw new InvalidOperationException("Unsupported: FirstRecordedTrackNumber != 1"); - - //add a lead-in track - session.Tracks.Add(new DiscStructure.Track() { - Number = 0, - Control = EControlQ.None, //TODO - not accurate (take from track 1?) - LBA = -150 //TODO - not accurate - }); - - int ntracks = TOCRaw.LastRecordedTrackNumber - TOCRaw.FirstRecordedTrackNumber + 1; - for(int i=0;i public Track LeadoutTrack { get { return Tracks[Tracks.Count - 1]; } } + + /// + /// Determines which track of the session is at the specified LBA. + /// Returns null if it's before track 1 + /// + public Track SeekTrack(int lba) + { + var ses = this; + + //take care with this loop bounds: + for (int i = 1; i <= ses.InformationTrackCount; i++) + { + var track = ses.Tracks[i]; + if (track.LBA > lba) + return (i == 1) ? null : ses.Tracks[i]; + } + return ses.Tracks[ses.Tracks.Count]; + } } /// diff --git a/BizHawk.Emulation.DiscSystem/API/DiscTOC.cs b/BizHawk.Emulation.DiscSystem/API/DiscTOC.cs new file mode 100644 index 0000000000..939eb02166 --- /dev/null +++ b/BizHawk.Emulation.DiscSystem/API/DiscTOC.cs @@ -0,0 +1,71 @@ +using System; +using System.Text; +using System.Collections.Generic; + +namespace BizHawk.Emulation.DiscSystem +{ + /// + /// Represents our best guess at what a disc drive firmware will receive by reading the TOC from the lead-in track, modeled after CCD contents and mednafen/PSX needs. + /// + public class DiscTOC + { + /// + /// The TOC specifies the first recorded track number, independently of whatever may actually be recorded + /// + public int FirstRecordedTrackNumber = -1; + + /// + /// The TOC specifies the last recorded track number, independently of whatever may actually be recorded + /// + public int LastRecordedTrackNumber = -1; + + /// + /// The TOC specifies the format of the session, so here it is. + /// + public SessionFormat Session1Format = SessionFormat.None; + + /// + /// Information about a single track in the TOC + /// + public struct TOCItem + { + /// + /// [IEC10149] "the control field used in the information track" + /// the raw TOC entries do have a control field which is supposed to match what's found in the track. + /// Determining whether a track contains audio or data is very important. + /// A track mode can't be safely determined from reading sectors from the actual track if it's an audio track (there's no sector header with a mode byte) + /// + public EControlQ Control; + + /// + /// Whether the Control indicates that this is data + /// + public bool IsData { get { return (Control & EControlQ.DATA) != 0; } } + + /// + /// The location of the track (Index 1) + /// + public Timestamp LBATimestamp; + + /// + /// Whether this entry exists (since the table is 101 entries long always) + /// + public bool Exists; + } + + /// + /// This is a convenient format for storing the TOC (taken from mednafen) + /// Index 0 is empty, so that track 1 is in index 1. + /// Index 100 is the Lead-out track + /// + public TOCItem[] TOCItems = new TOCItem[101]; + + /// + /// The timestamp of the leadout track. In other words, the end of the user area. + /// + public Timestamp LeadoutLBA { get { return TOCItems[100].LBATimestamp; } } + } + + + +} \ No newline at end of file diff --git a/BizHawk.Emulation.DiscSystem/API/TOCRaw.cs b/BizHawk.Emulation.DiscSystem/API/TOCRaw.cs deleted file mode 100644 index d6a5fb4290..0000000000 --- a/BizHawk.Emulation.DiscSystem/API/TOCRaw.cs +++ /dev/null @@ -1,157 +0,0 @@ -using System; -using System.Text; -using System.Collections.Generic; - -namespace BizHawk.Emulation.DiscSystem -{ - /// - /// Represents our best guess at what a disc drive firmware will receive by reading the TOC from the lead-in track, modeled after CCD contents and mednafen/PSX needs. - /// - public class DiscTOCRaw - { - /// - /// Synthesizes the TOC from a set of raw entries. - /// When a disc drive firmware reads the lead-in area, it builds this TOC from finding q-mode 1 sectors in the Q subchannel of the lead-in area. - /// Question: I guess it must ignore q-mode != 1? what else would it do with it? - /// - public class SynthesizeFromRawTOCEntriesJob - { - public IEnumerable Entries; - public List Log = new List(); - public DiscTOCRaw Result; - - public void Run() - { - SynthesizeFromRawTOCEntriesJob job = this; - DiscTOCRaw ret = new DiscTOCRaw(); - - //this is a dummy, for convenience in array indexing, so that track 1 is at array index 1 - ret.TOCItems[0].LBATimestamp = new Timestamp(0); //arguably could be -150, but let's not just yet - ret.TOCItems[0].Control = 0; - ret.TOCItems[0].Exists = false; - - //just in case this doesnt get set... - ret.FirstRecordedTrackNumber = 0; - ret.LastRecordedTrackNumber = 0; - - int maxFoundTrack = 0; - - foreach (var te in job.Entries) - { - var q = te.QData; - var point = q.q_index.DecimalValue; - - //see ECMD-394 page 5-14 for info about point = 0xA0, 0xA1, 0xA2 - - if (point == 0x00) - job.Log.Add("unexpected POINT=00 in lead-in Q-channel"); - else if (point == 255) - throw new InvalidOperationException("point == 255"); - else if (point <= 99) - { - maxFoundTrack = Math.Max(maxFoundTrack, point); - ret.TOCItems[point].LBATimestamp = new Timestamp(q.AP_Timestamp.Sector - 150); //RawTOCEntries contained an absolute time - ret.TOCItems[point].Control = q.CONTROL; - ret.TOCItems[point].Exists = true; - } - else if (point == 100) //0xA0 bcd - { - ret.FirstRecordedTrackNumber = q.ap_min.DecimalValue; - if (q.ap_frame.DecimalValue != 0) job.Log.Add("PFRAME should be 0 for POINT=0xA0"); - if (q.ap_sec.DecimalValue == 0x00) ret.Session1Format = DiscTOCRaw.SessionFormat.Type00_CDROM_CDDA; - else if (q.ap_sec.DecimalValue == 0x10) ret.Session1Format = DiscTOCRaw.SessionFormat.Type10_CDI; - else if (q.ap_sec.DecimalValue == 0x20) ret.Session1Format = DiscTOCRaw.SessionFormat.Type20_CDXA; - else job.Log.Add("Unrecognized session format: PSEC should be one of {0x00,0x10,0x20} for POINT=0xA0"); - } - else if (point == 101) //0xA1 bcd - { - ret.LastRecordedTrackNumber = q.ap_min.DecimalValue; - if (q.ap_sec.DecimalValue != 0) job.Log.Add("PSEC should be 0 for POINT=0xA1"); - if (q.ap_frame.DecimalValue != 0) job.Log.Add("PFRAME should be 0 for POINT=0xA1"); - } - else if (point == 102) //0xA2 bcd - { - ret.TOCItems[100].LBATimestamp = new Timestamp(q.AP_Timestamp.Sector - 150); //RawTOCEntries contained an absolute time - ret.TOCItems[100].Control = 0; //not clear what this should be - ret.TOCItems[100].Exists = true; - } - } - - //this is speculative: - //well, nothing to be done here.. - if (ret.FirstRecordedTrackNumber == -1) { } - if (ret.LastRecordedTrackNumber == -1) { ret.LastRecordedTrackNumber = maxFoundTrack; } - if (ret.Session1Format == SessionFormat.None) ret.Session1Format = SessionFormat.Type00_CDROM_CDDA; - - //if (!ret.LeadoutTimestamp.Valid) { - // //we're DOOMED. we cant know the length of the last track without this.... - //} - job.Result = ret; - } - } - - public enum SessionFormat - { - None = -1, - Type00_CDROM_CDDA = 0x00, - Type10_CDI = 0x10, - Type20_CDXA = 0x20 - } - - /// - /// The TOC specifies the first recorded track number, independently of whatever may actually be recorded - /// - public int FirstRecordedTrackNumber = -1; - - /// - /// The TOC specifies the last recorded track number, independently of whatever may actually be recorded - /// - public int LastRecordedTrackNumber = -1; - - /// - /// The TOC specifies the format of the session, so here it is. - /// - public SessionFormat Session1Format = SessionFormat.None; - - /// - /// Information about a single track in the TOC - /// - public struct TOCItem - { - /// - /// [IEC10149] "the control field used in the information track" - /// the raw TOC entries do have a control field which is supposed to match what's found in the track. - /// Determining whether a track contains audio or data is very important. - /// A track mode can't be safely determined from reading sectors from the actual track if it's an audio track (there's no sector header with a mode byte) - /// - public EControlQ Control; - - /// - /// Whether the Control indicates that this is data - /// - public bool IsData { get { return (Control & EControlQ.DATA) != 0; } } - - /// - /// The location of the track (Index 1) - /// - public Timestamp LBATimestamp; - - /// - /// Whether this entry exists (since the table is 101 entries long always) - /// - public bool Exists; - } - - /// - /// This is a convenient format for storing the TOC (taken from mednafen) - /// Index 0 is empty, so that track 1 is in index 1. - /// Index 100 is the Lead-out track - /// - public TOCItem[] TOCItems = new TOCItem[101]; - - /// - /// The timestamp of the leadout track. In other words, the end of the user area. - /// - public Timestamp LeadoutLBA { get { return TOCItems[100].LBATimestamp; } } - } -} \ No newline at end of file diff --git a/BizHawk.Emulation.DiscSystem/BizHawk.Emulation.DiscSystem.csproj b/BizHawk.Emulation.DiscSystem/BizHawk.Emulation.DiscSystem.csproj index f2f8adf30c..3a949c95ee 100644 --- a/BizHawk.Emulation.DiscSystem/BizHawk.Emulation.DiscSystem.csproj +++ b/BizHawk.Emulation.DiscSystem/BizHawk.Emulation.DiscSystem.csproj @@ -56,7 +56,7 @@ - + @@ -88,6 +88,9 @@ + + + @@ -104,8 +107,6 @@ - - diff --git a/BizHawk.Emulation.DiscSystem/CUE/CUE_Compile.cs b/BizHawk.Emulation.DiscSystem/CUE/CUE_Compile.cs index 811e471b0c..c6373f7446 100644 --- a/BizHawk.Emulation.DiscSystem/CUE/CUE_Compile.cs +++ b/BizHawk.Emulation.DiscSystem/CUE/CUE_Compile.cs @@ -81,7 +81,7 @@ namespace BizHawk.Emulation.DiscSystem internal class CompiledDiscInfo { public int FirstRecordedTrackNumber, LastRecordedTrackNumber; - public DiscTOCRaw.SessionFormat SessionFormat; + public SessionFormat SessionFormat; } internal class CompiledCueTrack @@ -182,13 +182,13 @@ namespace BizHawk.Emulation.DiscSystem { case CueFile.TrackType.Mode2_2336: case CueFile.TrackType.Mode2_2352: - OUT_CompiledDiscInfo.SessionFormat = DiscTOCRaw.SessionFormat.Type20_CDXA; + OUT_CompiledDiscInfo.SessionFormat = SessionFormat.Type20_CDXA; discinfo_session1Format_determined = true; break; case CueFile.TrackType.CDI_2336: case CueFile.TrackType.CDI_2352: - OUT_CompiledDiscInfo.SessionFormat = DiscTOCRaw.SessionFormat.Type10_CDI; + OUT_CompiledDiscInfo.SessionFormat = SessionFormat.Type10_CDI; discinfo_session1Format_determined = true; break; diff --git a/BizHawk.Emulation.DiscSystem/Disc.cs b/BizHawk.Emulation.DiscSystem/Disc.cs index 2b3883c689..e048aae907 100644 --- a/BizHawk.Emulation.DiscSystem/Disc.cs +++ b/BizHawk.Emulation.DiscSystem/Disc.cs @@ -17,89 +17,10 @@ using System.Collections.Generic; //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 -//(cdrdao only supports R-W) - -//here is a featureset list of windows cd burning programs (useful for cuesheet compatibility info) -//http://www.dcsoft.com/cue_mastering_progs.htm - -//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 -//http://digitalx.org/cue-sheet/examples/ - -//"qemu cdrom emulator" -//http://www.koders.com/c/fid7171440DEC7C18B932715D671DEE03743111A95A.aspx - -//less good -//http://www.cyberciti.biz/faq/getting-volume-information-from-cds-iso-images/ -//http://www.cims.nyu.edu/cgi-systems/man.cgi?section=7I&topic=cdio - -//some other docs -//http://www.emutalk.net/threads/54428-Reference-for-8-byte-sub-header-used-in-CDROM-XA references http://ccsun.nchu.edu.tw/~imtech/cou...act%20Disc.pdf which is pretty cool - -//ideas: -/* - * do some stuff asynchronously. for example, decoding mp3 sectors. - * 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) - * mp3 blobs should be hashed and dropped in %TEMP% as a wav decode -*/ - -//here is an MIT licensed C mp3 decoder -//http://core.fluendo.com/gstreamer/src/gst-fluendo-mp3/ - -/*information on saturn TOC and session data structures is on pdf page 58 of System Library User's Manual; - * as seen in yabause, there are 1000 u32s in this format: - * Ctrl[4bit] Adr[4bit] StartFrameAddressFAD[24bit] (nonexisting tracks are 0xFFFFFFFF) - * Followed by Fist Track Information, Last Track Information.. - * Ctrl[4bit] Adr[4bit] FirstTrackNumber/LastTrackNumber[8bit] and then some stuff I dont understand - * ..and Read Out Information: - * Ctrl[4bit] Adr[4bit] ReadOutStartFrameAddress[24bit] - * - * Also there is some stuff about FAD of sessions. - * This should be generated by the saturn core, but we need to make sure we pass down enough information to do it -*/ - -//2048 bytes packed into 2352: -//12 bytes sync(00 ff ff ff ff ff ff ff ff ff ff 00) -//3 bytes sector address (min+A0),sec,frac //does this correspond to ccd `point` field in the TOC entries? -//sector mode byte (0: silence; 1: 2048Byte mode (EDC,ECC,CIRC), 2: mode2 (could be 2336[vanilla mode2], 2048[xa mode2 form1], 2324[xa mode2 form2]) -//cue sheets may use mode1_2048 (and the error coding needs to be regenerated to get accurate raw data) or mode1_2352 (the entire sector is present) -//audio is a different mode, seems to be just 2352 bytes with no sync, header or error correction. i guess the CIRC error correction is still there - namespace BizHawk.Emulation.DiscSystem { - public partial class Disc : IDisposable { - /// - /// Free-form optional memos about the disc - /// - public Dictionary Memos = new Dictionary(); - - /// - /// The raw TOC entries found in the lead-in track. - /// These aren't very useful, but theyre one of the most lowest-level data structures from which other TOC-related stuff is derived - /// - public List RawTOCEntries = new List(); - - /// - /// The DiscTOCRaw corresponding to the RawTOCEntries. - /// TODO - rename to TOC - /// TODO - there's one of these for every session, so... having one here doesnt make sense - /// - public DiscTOCRaw TOCRaw; - /// /// The DiscStructure corresponding to the TOCRaw /// @@ -110,6 +31,41 @@ namespace BizHawk.Emulation.DiscSystem /// public DiscStructure.Session Session1 { get { return Structure.Sessions[1]; } } + /// + /// The DiscTOCRaw corresponding to the RawTOCEntries. + /// TODO - rename to TOC + /// TODO - there's one of these for every session, so... having one here doesnt make sense + /// + public DiscTOC TOC; + + /// + /// The raw TOC entries found in the lead-in track. + /// These aren't very useful, but theyre one of the most lowest-level data structures from which other TOC-related stuff is derived + /// + public List RawTOCEntries = new List(); + + /// + /// Free-form optional memos about the disc + /// + public Dictionary Memos = new Dictionary(); + + public void Dispose() + { + foreach (var res in DisposableResources) + { + res.Dispose(); + } + } + + /// + /// The DiscMountPolicy used to mount the disc. Consider this read-only. + /// NOT SURE WE NEED THIS + /// + //public DiscMountPolicy DiscMountPolicy; + + + //---------------------------------------------------------------------------- + /// /// Disposable resources (blobs, mostly) referenced by this disc /// @@ -125,72 +81,11 @@ namespace BizHawk.Emulation.DiscSystem /// internal SectorSynthParams SynthParams = new SectorSynthParams(); - /// - /// The DiscMountPolicy used to mount the disc. Consider this read-only. - /// NOT SURE WE NEED THIS - /// - //public DiscMountPolicy DiscMountPolicy; - - public Disc() + internal Disc() { } - public void Dispose() - { - foreach (var res in DisposableResources) - { - res.Dispose(); - } - } - - /// - /// generates lead-out sectors according to very crude approximations - /// TODO - this isnt being used right now - /// - public class SynthesizeLeadoutJob - { - public int Length; - public Disc Disc; - - public void Run() - { - //TODO: encode_mode2_form2_sector - - var leadoutTs = Disc.TOCRaw.LeadoutLBA; - var lastTrackTOCItem = Disc.TOCRaw.TOCItems[Disc.TOCRaw.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 - } - } - } + /// /// Automagically loads a disc, without any fine-tuned control at all diff --git a/BizHawk.Emulation.DiscSystem/DiscTypes.cs b/BizHawk.Emulation.DiscSystem/DiscTypes.cs index 9d502a58ad..7ba69358e1 100644 --- a/BizHawk.Emulation.DiscSystem/DiscTypes.cs +++ b/BizHawk.Emulation.DiscSystem/DiscTypes.cs @@ -19,4 +19,12 @@ namespace BizHawk.Emulation.DiscSystem { BizHawk, MednaDisc, LibMirage } + + public enum SessionFormat + { + None = -1, + Type00_CDROM_CDDA = 0x00, + Type10_CDI = 0x10, + Type20_CDXA = 0x20 + } } \ No newline at end of file diff --git a/BizHawk.Emulation.DiscSystem/Jobs/DiscMountJob.cs b/BizHawk.Emulation.DiscSystem/Jobs/DiscMountJob.cs index 9fddaf195f..b3ea5daf8f 100644 --- a/BizHawk.Emulation.DiscSystem/Jobs/DiscMountJob.cs +++ b/BizHawk.Emulation.DiscSystem/Jobs/DiscMountJob.cs @@ -93,11 +93,11 @@ namespace BizHawk.Emulation.DiscSystem { //generate toc and structure: //1. TOCRaw from RawTOCEntries - var tocSynth = new DiscTOCRaw.SynthesizeFromRawTOCEntriesJob() { Entries = OUT_Disc.RawTOCEntries }; + var tocSynth = new Synthesize_DiscTOC_From_RawTOCEntries_Job() { Entries = OUT_Disc.RawTOCEntries }; tocSynth.Run(); - OUT_Disc.TOCRaw = tocSynth.Result; + OUT_Disc.TOC = tocSynth.Result; //2. Structure frmo TOCRaw - var structureSynth = new DiscStructure.SynthesizeFromTOCRawJob() { IN_Disc = OUT_Disc, TOCRaw = OUT_Disc.TOCRaw }; + var structureSynth = new Synthesize_DiscStructure_From_DiscTOC_Job() { IN_Disc = OUT_Disc, TOCRaw = OUT_Disc.TOC }; structureSynth.Run(); OUT_Disc.Structure = structureSynth.Result; } diff --git a/BizHawk.Emulation.DiscSystem/Jobs/LoggedJob.cs b/BizHawk.Emulation.DiscSystem/Jobs/LoggedJob.cs index e9ed8e1c2a..2d4aa62862 100644 --- a/BizHawk.Emulation.DiscSystem/Jobs/LoggedJob.cs +++ b/BizHawk.Emulation.DiscSystem/Jobs/LoggedJob.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; namespace BizHawk.Emulation.DiscSystem { - /// /// Returns ERRORS for things which will can't be processed any further. Processing of this job will continue though to try to collect the maximum amount of feedback. /// Returns WARNINGS for things which will are irregular or erroneous but later jobs might be able to handle, or which can be worked around by configuration assumptions. diff --git a/BizHawk.Emulation.DiscSystem/Jobs/Synthesize_A0A1A2_Job.cs b/BizHawk.Emulation.DiscSystem/Jobs/Synthesize_A0A1A2_Job.cs index 275d81c5a5..e9fb7e20d2 100644 --- a/BizHawk.Emulation.DiscSystem/Jobs/Synthesize_A0A1A2_Job.cs +++ b/BizHawk.Emulation.DiscSystem/Jobs/Synthesize_A0A1A2_Job.cs @@ -32,7 +32,7 @@ namespace BizHawk.Emulation.DiscSystem /// /// The session format for this TOC /// - public DiscTOCRaw.SessionFormat IN_Session1Format; + public SessionFormat IN_Session1Format; /// /// Appends the new entries to the provided list @@ -53,9 +53,9 @@ namespace BizHawk.Emulation.DiscSystem switch(IN_Session1Format) { //TODO these probably shouldn't be decimal values - case DiscTOCRaw.SessionFormat.Type00_CDROM_CDDA: sq.ap_sec.DecimalValue = 0x00; break; - case DiscTOCRaw.SessionFormat.Type10_CDI: sq.ap_sec.DecimalValue = 0x10; break; - case DiscTOCRaw.SessionFormat.Type20_CDXA: sq.ap_sec.DecimalValue = 0x20; break; + 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; diff --git a/BizHawk.Emulation.DiscSystem/Jobs/Synthesize_DiscStructure_From_DiscTOC_Job.cs b/BizHawk.Emulation.DiscSystem/Jobs/Synthesize_DiscStructure_From_DiscTOC_Job.cs new file mode 100644 index 0000000000..13eb30cdce --- /dev/null +++ b/BizHawk.Emulation.DiscSystem/Jobs/Synthesize_DiscStructure_From_DiscTOC_Job.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; + +namespace BizHawk.Emulation.DiscSystem +{ + public class Synthesize_DiscStructure_From_DiscTOC_Job + { + public Disc IN_Disc; + public DiscTOC TOCRaw; + public DiscStructure Result; + + public void Run() + { + var dsr = new DiscSectorReader(IN_Disc); + dsr.Policy.DeterministicClearBuffer = false; + + Result = new DiscStructure(); + var session = new DiscStructure.Session(); + Result.Sessions.Add(null); //placeholder session for reindexing + Result.Sessions.Add(session); + + session.Number = 1; + + if (TOCRaw.FirstRecordedTrackNumber != 1) + throw new InvalidOperationException("Unsupported: FirstRecordedTrackNumber != 1"); + + //add a lead-in track + session.Tracks.Add(new DiscStructure.Track() + { + Number = 0, + Control = EControlQ.None, //TODO - not accurate (take from track 1?) + LBA = -150 //TODO - not accurate + }); + + int ntracks = TOCRaw.LastRecordedTrackNumber - TOCRaw.FirstRecordedTrackNumber + 1; + for (int i = 0; i < ntracks; i++) + { + var item = TOCRaw.TOCItems[i + 1]; + var track = new DiscStructure.Track() + { + Number = i + 1, + Control = item.Control, + LBA = item.LBATimestamp.Sector + }; + session.Tracks.Add(track); + + if (!item.IsData) + track.Mode = 0; + else + { + //determine the mode by a hardcoded heuristic: check mode of first sector + track.Mode = dsr.ReadLBA_Mode(track.LBA); + } + + //determine track length according to... how? It isn't clear. + //Let's not do this until it's needed. + //if (i == ntracks - 1) + // track.Length = TOCRaw.LeadoutLBA.Sector - track.LBA; + //else track.Length = (TOCRaw.TOCItems[i + 2].LBATimestamp.Sector - track.LBA); + } + + //add lead-out track + session.Tracks.Add(new DiscStructure.Track() + { + Number = 0xA0, //right? + Control = EControlQ.None, //TODO - not accurate (take from track 1?) + LBA = TOCRaw.LeadoutLBA.Sector + }); + + //link track list + for (int i = 0; i < session.Tracks.Count - 1; i++) + { + session.Tracks[i].NextTrack = session.Tracks[i + 1]; + } + + //other misc fields + session.InformationTrackCount = session.Tracks.Count - 2; + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.DiscSystem/Jobs/Synthesize_DiscTOC_From_RawTOCEntries_Job.cs b/BizHawk.Emulation.DiscSystem/Jobs/Synthesize_DiscTOC_From_RawTOCEntries_Job.cs new file mode 100644 index 0000000000..652baa8ff2 --- /dev/null +++ b/BizHawk.Emulation.DiscSystem/Jobs/Synthesize_DiscTOC_From_RawTOCEntries_Job.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; + +namespace BizHawk.Emulation.DiscSystem +{ + /// + /// Synthesizes the TOC from a set of raw entries. + /// When a disc drive firmware reads the lead-in area, it builds this TOC from finding q-mode 1 sectors in the Q subchannel of the lead-in area. + /// Question: I guess it must ignore q-mode != 1? what else would it do with it? + /// + public class Synthesize_DiscTOC_From_RawTOCEntries_Job + { + public IEnumerable Entries; + public List Log = new List(); + public DiscTOC Result; + + public void Run() + { + var job = this; + DiscTOC ret = new DiscTOC(); + + //this is a dummy, for convenience in array indexing, so that track 1 is at array index 1 + ret.TOCItems[0].LBATimestamp = new Timestamp(0); //arguably could be -150, but let's not just yet + ret.TOCItems[0].Control = 0; + ret.TOCItems[0].Exists = false; + + //just in case this doesnt get set... + ret.FirstRecordedTrackNumber = 0; + ret.LastRecordedTrackNumber = 0; + + int maxFoundTrack = 0; + + foreach (var te in job.Entries) + { + var q = te.QData; + var point = q.q_index.DecimalValue; + + //see ECMD-394 page 5-14 for info about point = 0xA0, 0xA1, 0xA2 + + if (point == 0x00) + job.Log.Add("unexpected POINT=00 in lead-in Q-channel"); + else if (point == 255) + throw new InvalidOperationException("point == 255"); + else if (point <= 99) + { + maxFoundTrack = Math.Max(maxFoundTrack, point); + ret.TOCItems[point].LBATimestamp = new Timestamp(q.AP_Timestamp.Sector - 150); //RawTOCEntries contained an absolute time + ret.TOCItems[point].Control = q.CONTROL; + ret.TOCItems[point].Exists = true; + } + else if (point == 100) //0xA0 bcd + { + ret.FirstRecordedTrackNumber = q.ap_min.DecimalValue; + if (q.ap_frame.DecimalValue != 0) job.Log.Add("PFRAME should be 0 for POINT=0xA0"); + if (q.ap_sec.DecimalValue == 0x00) ret.Session1Format = SessionFormat.Type00_CDROM_CDDA; + else if (q.ap_sec.DecimalValue == 0x10) ret.Session1Format = SessionFormat.Type10_CDI; + else if (q.ap_sec.DecimalValue == 0x20) ret.Session1Format = SessionFormat.Type20_CDXA; + else job.Log.Add("Unrecognized session format: PSEC should be one of {0x00,0x10,0x20} for POINT=0xA0"); + } + else if (point == 101) //0xA1 bcd + { + ret.LastRecordedTrackNumber = q.ap_min.DecimalValue; + if (q.ap_sec.DecimalValue != 0) job.Log.Add("PSEC should be 0 for POINT=0xA1"); + if (q.ap_frame.DecimalValue != 0) job.Log.Add("PFRAME should be 0 for POINT=0xA1"); + } + else if (point == 102) //0xA2 bcd + { + ret.TOCItems[100].LBATimestamp = new Timestamp(q.AP_Timestamp.Sector - 150); //RawTOCEntries contained an absolute time + ret.TOCItems[100].Control = 0; //not clear what this should be + ret.TOCItems[100].Exists = true; + } + } + + //this is speculative: + //well, nothing to be done here.. + if (ret.FirstRecordedTrackNumber == -1) { } + if (ret.LastRecordedTrackNumber == -1) { ret.LastRecordedTrackNumber = maxFoundTrack; } + if (ret.Session1Format == SessionFormat.None) ret.Session1Format = SessionFormat.Type00_CDROM_CDDA; + + //if (!ret.LeadoutTimestamp.Valid) { + // //we're DOOMED. we cant know the length of the last track without this.... + //} + job.Result = ret; + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.DiscSystem/Jobs/Synthesize_Leadout_Job.cs b/BizHawk.Emulation.DiscSystem/Jobs/Synthesize_Leadout_Job.cs new file mode 100644 index 0000000000..09fc0af1d5 --- /dev/null +++ b/BizHawk.Emulation.DiscSystem/Jobs/Synthesize_Leadout_Job.cs @@ -0,0 +1,60 @@ +using System; +using System.Linq; +using System.Text; +using System.IO; +using System.Collections.Generic; + +//TODO - generate correct Q subchannel CRC + +namespace BizHawk.Emulation.DiscSystem +{ + + /// + /// generates lead-out sectors according to very crude approximations + /// TODO - this isnt being used right now + /// + public class Synthesize_LeadoutJob + { + public int Length; + public Disc Disc; + + 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 + } + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.DiscSystem/docs/notes.txt b/BizHawk.Emulation.DiscSystem/docs/notes.txt index 0fa207e1ad..dc0c96227a 100644 --- a/BizHawk.Emulation.DiscSystem/docs/notes.txt +++ b/BizHawk.Emulation.DiscSystem/docs/notes.txt @@ -1,4 +1,20 @@ -//lets leave some notes here until we've rewritten every possible thing -- error recovery, ECM file format, ISO filesystem, etc. +//OLD ideas: +/* + * do some stuff asynchronously. for example, decoding mp3 sectors. + * 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) + * mp3 blobs should be hashed and dropped in %TEMP% as a wav decode +*/ + + +//2048 bytes packed into 2352: +//12 bytes sync(00 ff ff ff ff ff ff ff ff ff ff 00) +//3 bytes sector address (min+A0),sec,frac //does this correspond to ccd `point` field in the TOC entries? +//sector mode byte (0: silence; 1: 2048Byte mode (EDC,ECC,CIRC), 2: mode2 (could be 2336[vanilla mode2], 2048[xa mode2 form1], 2324[xa mode2 form2]) +//cue sheets may use mode1_2048 (and the error coding needs to be regenerated to get accurate raw data) or mode1_2352 (the entire sector is present) +//audio is a different mode, seems to be just 2352 bytes with no sync, header or error correction. i guess the CIRC error correction is still there + //we used this code //https://code.google.com/p/iso-parser/ @@ -36,3 +52,49 @@ //check this for some iso stuff but seems like it ripped off corlett's code //http://lioneditor.googlecode.com/svn/trunk/utils/isopatcherv05/src/ //http://code.ohloh.net/file?fid=185llKM04w3QCqwC2MdFgtUiQ94&cid=yPMRq_HKxUg&s=ecc_computeblock%28pSector%20%2B%200xC%2C%2052%2C%2043%2C%2086%2C%2088%2C%20pSector%20%2B%200x8C8%29&mp=1&ml=1&me=1&md=1&browser=Default#L106 + + + + +//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 +//(cdrdao only supports R-W) + +//here is a featureset list of windows cd burning programs (useful for cuesheet compatibility info) +//http://www.dcsoft.com/cue_mastering_progs.htm + +//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 +//http://digitalx.org/cue-sheet/examples/ + +//"qemu cdrom emulator" +//http://www.koders.com/c/fid7171440DEC7C18B932715D671DEE03743111A95A.aspx + +//less good +//http://www.cyberciti.biz/faq/getting-volume-information-from-cds-iso-images/ +//http://www.cims.nyu.edu/cgi-systems/man.cgi?section=7I&topic=cdio + +//some other docs +//http://www.emutalk.net/threads/54428-Reference-for-8-byte-sub-header-used-in-CDROM-XA references http://ccsun.nchu.edu.tw/~imtech/cou...act%20Disc.pdf which is pretty cool + +//here is an MIT licensed C mp3 decoder +//http://core.fluendo.com/gstreamer/src/gst-fluendo-mp3/ + +/*information on saturn TOC and session data structures is on pdf page 58 of System Library User's Manual; + * as seen in yabause, there are 1000 u32s in this format: + * Ctrl[4bit] Adr[4bit] StartFrameAddressFAD[24bit] (nonexisting tracks are 0xFFFFFFFF) + * Followed by Fist Track Information, Last Track Information.. + * Ctrl[4bit] Adr[4bit] FirstTrackNumber/LastTrackNumber[8bit] and then some stuff I dont understand + * ..and Read Out Information: + * Ctrl[4bit] Adr[4bit] ReadOutStartFrameAddress[24bit] + * + * Also there is some stuff about FAD of sessions. + * This should be generated by the saturn core, but we need to make sure we pass down enough information to do it +*/ diff --git a/BizHawk.Emulation.DiscSystem/docs/todo.txt b/BizHawk.Emulation.DiscSystem/docs/todo.txt deleted file mode 100644 index 62e27cf05f..0000000000 --- a/BizHawk.Emulation.DiscSystem/docs/todo.txt +++ /dev/null @@ -1 +0,0 @@ -. consider normalizing everything to use -150 for lead-in crappola ?