2011-05-08 09:07:46 +00:00
using System ;
using System.Text ;
using System.Collections.Generic ;
2013-11-03 23:45:44 +00:00
namespace BizHawk.Emulation.DiscSystem
2011-05-08 09:07:46 +00:00
{
2014-12-04 05:40:10 +00:00
/// <summary>
/// The TOC contains information directly describing the structure of the disc, as well as some more logically structured information derived from that.
/// Additionally, this class contains methods to synchronize between them.
/// This is a bit weird.. Perhaps the different views of the data should be seaprated.
///
/// One final caveat - I think the TOC should be independent for independent sessions.. the concept of multi-sessioning is so thoroughly ill-supported here, it will require radical renovation ever to support.
///
/// Sessions -> Tracks : These are the highest-level data structures of the disc. We should probably rename this to something like DiscStructure, and save DiscTOC for the actual TOC.
/// TOCPoint : These are the basic logical unit of data stored in the TOC. They're basically bookmarks for tracks.
/// TOCEntry : These are the basic unit of data in the rawest view of the TOC. They're stored in the lead-in Q subchannel, and there are multiple redundant copies, and they store several different types of information.
/// </summary>
2011-05-08 09:07:46 +00:00
public class DiscTOC
{
2011-08-14 23:13:31 +00:00
/// <summary>
2014-12-04 05:40:10 +00:00
/// Right now support for anything other than 1 session is totally not working
2011-08-14 23:13:31 +00:00
/// </summary>
public List < Session > Sessions = new List < Session > ( ) ;
/// <summary>
2014-12-04 05:40:10 +00:00
/// List of Points described by the TOC
2011-08-14 23:13:31 +00:00
/// </summary>
public List < TOCPoint > Points = new List < TOCPoint > ( ) ;
2014-12-04 05:40:10 +00:00
2011-08-14 23:13:31 +00:00
/// <summary>
/// Todo - comment about what this actually means
2011-08-15 10:43:36 +00:00
/// TODO - this is redundant with Sectors.Count
2011-08-14 23:13:31 +00:00
/// </summary>
2011-08-15 10:43:36 +00:00
public int length_aba ;
2011-08-14 23:13:31 +00:00
/// <summary>
/// todo - comment about what this actually means
/// </summary>
2011-08-15 10:43:36 +00:00
public Timestamp FriendlyLength { get { return new Timestamp ( length_aba ) ; } }
/// <summary>
/// todo - comment about what this actually means
/// </summary>
public long BinarySize
{
get { return length_aba * 2352 ; }
}
2011-08-14 23:13:31 +00:00
/// <summary>
/// seeks the point immediately before (or equal to) this LBA
/// </summary>
public TOCPoint SeekPoint ( int lba )
{
2011-08-15 10:43:36 +00:00
int aba = lba + 150 ;
2011-08-14 23:13:31 +00:00
for ( int i = 0 ; i < Points . Count ; i + + )
{
TOCPoint tp = Points [ i ] ;
2011-08-15 10:43:36 +00:00
if ( tp . ABA > aba )
2011-08-14 23:13:31 +00:00
return Points [ i - 1 ] ;
}
return Points [ Points . Count - 1 ] ;
}
/// <summary>
///
/// </summary>
public class TOCPoint
{
public int Num ;
2011-08-15 10:43:36 +00:00
public int ABA , TrackNum , IndexNum ;
2011-08-14 23:13:31 +00:00
public Track Track ;
2011-08-16 01:49:48 +00:00
public int LBA
{
get { return ABA - 150 ; }
}
2011-08-14 23:13:31 +00:00
}
/// <summary>
2014-12-04 05:40:10 +00:00
/// Generates the Points list from the current logical TOC
2011-08-14 23:13:31 +00:00
/// </summary>
2014-12-04 05:40:10 +00:00
public void SynthesizeTOCPointsFromSessions ( )
2011-08-14 23:13:31 +00:00
{
int num = 0 ;
Points . Clear ( ) ;
foreach ( var ses in Sessions )
{
foreach ( var track in ses . Tracks )
foreach ( var index in track . Indexes )
{
2013-11-04 03:12:50 +00:00
var tp = new TOCPoint
{
Num = num + + ,
ABA = index . aba ,
TrackNum = track . num ,
IndexNum = index . num ,
Track = track
} ;
2011-08-14 23:13:31 +00:00
Points . Add ( tp ) ;
}
var tpLeadout = new TOCPoint ( ) ;
var lastTrack = ses . Tracks [ ses . Tracks . Count - 1 ] ;
tpLeadout . Num = num + + ;
2011-08-15 10:43:36 +00:00
tpLeadout . ABA = lastTrack . Indexes [ 1 ] . aba + lastTrack . length_aba ;
2011-08-14 23:13:31 +00:00
tpLeadout . IndexNum = 0 ;
tpLeadout . TrackNum = 100 ;
tpLeadout . Track = null ; //no leadout track.. now... or ever?
Points . Add ( tpLeadout ) ;
}
}
2011-05-08 09:07:46 +00:00
public class Session
{
public int num ;
public List < Track > Tracks = new List < Track > ( ) ;
2011-08-02 08:26:33 +00:00
//the length of the session (should be the sum of all track lengths)
2011-08-15 10:43:36 +00:00
public int length_aba ;
public Timestamp FriendlyLength { get { return new Timestamp ( length_aba ) ; } }
2011-05-08 09:07:46 +00:00
}
public class Track
{
2011-06-20 09:09:21 +00:00
public ETrackType TrackType ;
2011-05-08 09:07:46 +00:00
public int num ;
public List < Index > Indexes = new List < Index > ( ) ;
2011-08-09 06:14:36 +00:00
/// <summary>
/// a track logically starts at index 1.
/// so this is the length from this index 1 to the next index 1 (or the end of the disc)
/// the time before track 1 index 1 is the lead-in and isn't accounted for in any track...
/// </summary>
2011-08-15 10:43:36 +00:00
public int length_aba ;
public Timestamp FriendlyLength { get { return new Timestamp ( length_aba ) ; } }
2011-05-08 09:07:46 +00:00
}
public class Index
{
public int num ;
2011-08-15 10:43:36 +00:00
public int aba ;
public int LBA
{
get { return aba - 150 ; }
}
2011-05-08 09:07:46 +00:00
//the length of the section
2011-08-09 06:14:36 +00:00
//HEY! This is commented out because it is a bad idea.
//The length of a section is almost useless, and if you want it, you are probably making an error.
2011-06-20 09:09:21 +00:00
//public int length_lba;
2011-08-14 23:13:31 +00:00
//public Cue.Timestamp FriendlyLength { get { return new Cue.Timestamp(length_lba); } }
2011-05-08 09:07:46 +00:00
}
2011-08-06 22:32:52 +00:00
public string GenerateCUE_OneBin ( CueBinPrefs prefs )
2011-05-08 09:07:46 +00:00
{
2011-08-07 03:21:03 +00:00
if ( prefs . OneBlobPerTrack ) throw new InvalidOperationException ( "OneBinPerTrack passed to GenerateCUE_OneBin" ) ;
2011-08-06 22:32:52 +00:00
2011-08-06 21:40:52 +00:00
//this generates a single-file cue!!!!!!! dont expect it to generate bin-per-track!
2011-05-08 09:07:46 +00:00
StringBuilder sb = new StringBuilder ( ) ;
2011-08-06 21:40:52 +00:00
2011-05-08 09:07:46 +00:00
foreach ( var session in Sessions )
{
2011-06-20 09:09:21 +00:00
if ( ! prefs . SingleSession )
{
//dont want to screw around with sessions for now
2011-08-15 10:43:36 +00:00
if ( prefs . AnnotateCue ) sb . AppendFormat ( "SESSION {0:D2} (length={1})\n" , session . num , session . length_aba ) ;
2011-06-20 09:09:21 +00:00
else sb . AppendFormat ( "SESSION {0:D2}\n" , session . num ) ;
}
2011-08-06 21:40:52 +00:00
2011-05-08 09:07:46 +00:00
foreach ( var track in session . Tracks )
{
2011-08-06 10:43:05 +00:00
ETrackType trackType = track . TrackType ;
2011-08-14 23:13:31 +00:00
2011-08-06 22:32:52 +00:00
//mutate track type according to our principle of canonicalization
2011-08-06 10:43:05 +00:00
if ( trackType = = ETrackType . Mode1_2048 & & prefs . DumpECM )
trackType = ETrackType . Mode1_2352 ;
2011-08-15 10:43:36 +00:00
if ( prefs . AnnotateCue ) sb . AppendFormat ( " TRACK {0:D2} {1} (length={2})\n" , track . num , Cue . TrackTypeStringForTrackType ( trackType ) , track . length_aba ) ;
2011-08-06 10:43:05 +00:00
else sb . AppendFormat ( " TRACK {0:D2} {1}\n" , track . num , Cue . TrackTypeStringForTrackType ( trackType ) ) ;
2011-05-08 09:07:46 +00:00
foreach ( var index in track . Indexes )
{
2011-08-14 23:13:31 +00:00
//cue+bin has an implicit 150 sector pregap which neither the cue nor the bin has any awareness of
//except for the baked-in sector addressing.
//but, if there is an extra-long pregap, we want to reflect it this way
2011-08-15 10:43:36 +00:00
int lba = index . aba - 150 ;
2011-08-14 23:13:31 +00:00
if ( lba < = 0 & & index . num = = 0 & & track . num = = 1 )
2011-08-06 22:01:53 +00:00
{
2011-08-06 21:40:52 +00:00
}
2011-08-28 06:31:31 +00:00
//dont emit index 0 when it is the same as index 1, it is illegal for some reason
else if ( index . num = = 0 & & index . aba = = track . Indexes [ 1 ] . aba )
{
//dont emit index 0 when it is the same as index 1, it confuses some cue parsers
}
2011-08-06 21:40:52 +00:00
else
{
2011-08-14 23:13:31 +00:00
sb . AppendFormat ( " INDEX {0:D2} {1}\n" , index . num , new Timestamp ( lba ) . Value ) ;
2011-08-06 21:40:52 +00:00
}
2011-05-08 09:07:46 +00:00
}
}
}
return sb . ToString ( ) ;
}
public void AnalyzeLengthsFromIndexLengths ( )
{
2011-06-20 09:09:21 +00:00
//this is a little more complex than it looks, because the length of a thing is not determined by summing it
//but rather by the difference in lbas between start and end
2011-08-15 10:43:36 +00:00
length_aba = 0 ;
2011-05-08 09:07:46 +00:00
foreach ( var session in Sessions )
{
2011-06-20 09:09:21 +00:00
var firstTrack = session . Tracks [ 0 ] ;
var lastTrack = session . Tracks [ session . Tracks . Count - 1 ] ;
2011-08-15 10:43:36 +00:00
session . length_aba = lastTrack . Indexes [ 0 ] . aba + lastTrack . length_aba - firstTrack . Indexes [ 0 ] . aba ;
length_aba + = session . length_aba ;
2011-05-08 09:07:46 +00:00
}
}
}
}