private RawTOCEntry EmitRawTOCEntry(ATOCEntry entry) { BCD2 tno, ino; //this should actually be zero. im not sure if this is stored as BCD2 or not tno = BCD2.FromDecimal(entry.TrackNo); //these are special values.. I think, taken from this: //http://www.staff.uni-mainz.de/tacke/scsi/SCSI2-14.html //the CCD will contain Points as decimal values except for these specially converted decimal values which should stay as BCD. //Why couldn't they all be BCD? I don't know. I guess because BCD is inconvenient, but only A0 and friends have special meaning. It's confusing. ino = BCD2.FromDecimal(entry.Point); if (entry.Point == 0xA0) { ino.BCDValue = 0xA0; } else if (entry.Point == 0xA1) { ino.BCDValue = 0xA1; } else if (entry.Point == 0xA2) { ino.BCDValue = 0xA2; } // get ADR & Control from ADR_Control byte byte adrc = Convert.ToByte(entry.ADR_Control); var Control = adrc & 0x0F; var ADR = adrc >> 4; var q = new SubchannelQ { q_status = SubchannelQ.ComputeStatus(ADR, (EControlQ)(Control & 0xF)), q_tno = tno, q_index = ino, min = BCD2.FromDecimal(entry.AMin), sec = BCD2.FromDecimal(entry.ASec), frame = BCD2.FromDecimal(entry.AFrame), zero = (byte)entry.Zero, ap_min = BCD2.FromDecimal(entry.PMin), ap_sec = BCD2.FromDecimal(entry.PSec), ap_frame = BCD2.FromDecimal(entry.PFrame), q_crc = 0, //meaningless }; return(new RawTOCEntry { QData = q }); }
public AFile Parse(Stream stream) { EndianBitConverter bc = EndianBitConverter.CreateForLittleEndian(); EndianBitConverter bcBig = EndianBitConverter.CreateForBigEndian(); bool isDvd = false; AFile aFile = new AFile(); aFile.MDSPath = (stream as FileStream).Name; stream.Seek(0, SeekOrigin.Begin); // check whether the header in the mds file is long enough if (stream.Length < 88) { throw new MDSParseException("Malformed MDS format: The descriptor file does not appear to be long enough."); } // parse header aFile.Header = aFile.Header.Parse(stream); // check version to make sure this is only v1.x // currently NO support for version 2.x if (aFile.Header.Version[0] > 1) { throw new MDSParseException("MDS Parse Error: Only MDS version 1.x is supported!\nDetected version: " + aFile.Header.Version[0] + "." + aFile.Header.Version[1]); } // parse sessions Dictionary <int, ASession> aSessions = new Dictionary <int, ASession>(); stream.Seek(aFile.Header.SessionOffset, SeekOrigin.Begin); for (int se = 0; se < aFile.Header.SessionCount; se++) { byte[] sessionHeader = new byte[24]; stream.Read(sessionHeader, 0, 24); //sessionHeader.Reverse().ToArray(); ASession session = new ASession(); session.SessionStart = bc.ToInt32(sessionHeader.Take(4).ToArray()); session.SessionEnd = bc.ToInt32(sessionHeader.Skip(4).Take(4).ToArray()); session.SessionNumber = bc.ToInt16(sessionHeader.Skip(8).Take(2).ToArray()); session.AllBlocks = sessionHeader[10]; session.NonTrackBlocks = sessionHeader[11]; session.FirstTrack = bc.ToInt16(sessionHeader.Skip(12).Take(2).ToArray()); session.LastTrack = bc.ToInt16(sessionHeader.Skip(14).Take(2).ToArray()); session.TrackOffset = bc.ToInt32(sessionHeader.Skip(20).Take(4).ToArray()); //mdsf.Sessions.Add(session); aSessions.Add(session.SessionNumber, session); } long footerOffset = 0; // parse track blocks Dictionary <int, ATrack> aTracks = new Dictionary <int, ATrack>(); // iterate through each session block foreach (ASession session in aSessions.Values) { stream.Seek(session.TrackOffset, SeekOrigin.Begin); //Dictionary<int, ATrack> sessionToc = new Dictionary<int, ATrack>(); // iterate through every block specified in each session for (int bl = 0; bl < session.AllBlocks; bl++) { byte[] trackHeader; ATrack track = new ATrack(); trackHeader = new byte[80]; stream.Read(trackHeader, 0, 80); track.Mode = trackHeader[0]; track.SubMode = trackHeader[1]; track.ADR_Control = trackHeader[2]; track.TrackNo = trackHeader[3]; track.Point = trackHeader[4]; track.AMin = trackHeader[5]; track.ASec = trackHeader[6]; track.AFrame = trackHeader[7]; track.Zero = trackHeader[8]; track.PMin = trackHeader[9]; track.PSec = trackHeader[10]; track.PFrame = trackHeader[11]; track.ExtraOffset = bc.ToInt32(trackHeader.Skip(12).Take(4).ToArray()); track.SectorSize = bc.ToInt16(trackHeader.Skip(16).Take(2).ToArray()); track.PLBA = bc.ToInt32(trackHeader.Skip(36).Take(4).ToArray()); track.StartOffset = BitConverter.ToUInt64(trackHeader.Skip(40).Take(8).ToArray(), 0); track.Files = bc.ToInt32(trackHeader.Skip(48).Take(4).ToArray()); track.FooterOffset = bc.ToInt32(trackHeader.Skip(52).Take(4).ToArray()); if (track.Mode == 0x02) { isDvd = true; throw new MDSParseException("DVD Detected. Not currently supported!"); } // check for track extra block - this can probably be handled in a separate loop, // but I'll just store the current stream position then seek forward to the extra block for this track Int64 currPos = stream.Position; // Only CDs have extra blocks - for DVDs ExtraOffset = track length if (track.ExtraOffset > 0 && !isDvd) { byte[] extHeader = new byte[8]; stream.Seek(track.ExtraOffset, SeekOrigin.Begin); stream.Read(extHeader, 0, 8); track.ExtraBlock.Pregap = bc.ToInt32(extHeader.Take(4).ToArray()); track.ExtraBlock.Sectors = bc.ToInt32(extHeader.Skip(4).Take(4).ToArray()); stream.Seek(currPos, SeekOrigin.Begin); } else if (isDvd == true) { track.ExtraBlock.Sectors = track.ExtraOffset; } // read the footer/filename block for this track currPos = stream.Position; long numOfFilenames = track.Files; for (long fi = 1; fi <= numOfFilenames; fi++) { // skip leadin/out info tracks if (track.FooterOffset == 0) { continue; } byte[] foot = new byte[16]; stream.Seek(track.FooterOffset, SeekOrigin.Begin); stream.Read(foot, 0, 16); AFooter f = new AFooter(); f.FilenameOffset = bc.ToInt32(foot.Take(4).ToArray()); f.WideChar = bc.ToInt32(foot.Skip(4).Take(4).ToArray()); track.FooterBlocks.Add(f); track.FooterBlocks = track.FooterBlocks.Distinct().ToList(); // parse the filename string string fileName = "*.mdf"; if (f.FilenameOffset > 0) { // filename offset is present stream.Seek(f.FilenameOffset, SeekOrigin.Begin); byte[] fname; if (numOfFilenames == 1) { if (aFile.Header.DPMOffset == 0) { // filename is in the remaining space to EOF fname = new byte[stream.Length - stream.Position]; } else { // filename is in the remaining space to EOF + dpm offset fname = new byte[aFile.Header.DPMOffset - stream.Position]; } } else { // looks like each filename string is 6 bytes with a trailing \0 fname = new byte[6]; } // read the filename stream.Read(fname, 0, fname.Length); // if widechar is 1 filename is stored using 16-bit, otherwise 8-bit is used if (f.WideChar == 1) { fileName = Encoding.Unicode.GetString(fname).TrimEnd('\0'); } else { fileName = Encoding.Default.GetString(fname).TrimEnd('\0'); } } else { // assume an MDF file with the same name as the MDS } string dir = Path.GetDirectoryName(aFile.MDSPath); if (f.FilenameOffset == 0 || string.Compare(fileName, "*.mdf", StringComparison.InvariantCultureIgnoreCase) == 0) { fileName = dir + @"\" + Path.GetFileNameWithoutExtension(aFile.MDSPath) + ".mdf"; } else { fileName = dir + @"\" + fileName; } track.ImageFileNamePaths.Add(fileName); track.ImageFileNamePaths = track.ImageFileNamePaths.Distinct().ToList(); } stream.Position = currPos; aTracks.Add(track.Point, track); aFile.Tracks.Add(track); if (footerOffset == 0) { footerOffset = track.FooterOffset; } } } // build custom session object aFile.ParsedSession = new List <Session>(); foreach (var s in aSessions.Values) { Session session = new Session(); ATrack startTrack; ATrack endTrack; if (!aTracks.TryGetValue(s.FirstTrack, out startTrack)) { break; } if (!aTracks.TryGetValue(s.LastTrack, out endTrack)) { break; } session.StartSector = startTrack.PLBA; session.StartTrack = s.FirstTrack; session.SessionSequence = s.SessionNumber; session.EndSector = endTrack.PLBA + endTrack.ExtraBlock.Sectors - 1; session.EndTrack = s.LastTrack; aFile.ParsedSession.Add(session); } // now build the TOC object foreach (var se in aFile.ParsedSession) { // get the first and last tracks int sTrack = se.StartTrack; int eTrack = se.EndTrack; // get list of all tracks from aTracks for this session var tracks = (from a in aTracks.Values where a.TrackNo >= sTrack || a.TrackNo <= eTrack orderby a.TrackNo select a).ToList(); // create the TOC entries foreach (var t in tracks) { ATOCEntry toc = new ATOCEntry(t.Point); toc.ADR_Control = t.ADR_Control; toc.AFrame = t.AFrame; toc.AMin = t.AMin; toc.ASec = t.ASec; toc.EntryNum = t.TrackNo; toc.PFrame = t.PFrame; toc.PLBA = Convert.ToInt32(t.PLBA); toc.PMin = t.PMin; toc.Point = t.Point; toc.PSec = t.PSec; toc.SectorSize = t.SectorSize; toc.Zero = t.Zero; toc.TrackOffset = Convert.ToInt64(t.StartOffset); toc.Session = se.SessionSequence; toc.ImageFileNamePaths = t.ImageFileNamePaths; toc.ExtraBlock = t.ExtraBlock; toc.BlobIndex = t.BlobIndex; aFile.TOCEntries.Add(toc); } } return(aFile); }