2011-05-08 09:07:46 +00:00
using System ;
2011-08-07 03:21:03 +00:00
using System.Linq ;
2011-05-08 09:07:46 +00:00
using System.Text ;
using System.IO ;
using System.Collections.Generic ;
2015-06-23 18:57:11 +00:00
//ARCHITECTURE NOTE:
2015-06-27 09:57:22 +00:00
//No provisions are made for caching synthesized data for later accelerated use.
//This is because, in the worst case that might result in synthesizing an entire disc in memory.
//Instead, users should be advised to `hawk` the disc first for most rapid access so that synthesis won't be necessary and speed will be maximized.
//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)
//https://books.google.com/books?id=caF_AAAAQBAJ&lpg=PA124&ots=OA9Ttj9CHZ&dq=disc%20TOC%20point%20A2&pg=PA124
2015-06-23 18:57:11 +00:00
//http://www.staff.uni-mainz.de/tacke/scsi/SCSI2-14.html
2011-05-08 09:07:46 +00:00
//http://www.pctechguide.com/iso-9660-data-format-for-cds-cd-roms-cd-rs-and-cd-rws
//http://linux.die.net/man/1/cue2toc
2011-08-07 03:21:03 +00:00
//http://cdemu.sourceforge.net/project.php#sf
2011-05-08 09:07:46 +00:00
//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)
2011-06-20 09:09:21 +00:00
//here is a featureset list of windows cd burning programs (useful for cuesheet compatibility info)
//http://www.dcsoft.com/cue_mastering_progs.htm
2011-05-08 09:07:46 +00:00
//good
//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
//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
2015-06-23 18:57:11 +00:00
//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
2011-05-08 09:07:46 +00:00
//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 )
* 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 [ 4 bit ] Adr [ 4 bit ] StartFrameAddressFAD [ 24 bit ] ( nonexisting tracks are 0xFFFFFFFF )
* Followed by Fist Track Information , Last Track Information . .
* Ctrl [ 4 bit ] Adr [ 4 bit ] FirstTrackNumber / LastTrackNumber [ 8 bit ] and then some stuff I dont understand
* . . and Read Out Information :
* Ctrl [ 4 bit ] Adr [ 4 bit ] ReadOutStartFrameAddress [ 24 bit ]
*
* 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?
2013-06-25 08:31:48 +00:00
//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])
2011-05-08 09:07:46 +00:00
//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
2013-11-03 23:45:44 +00:00
namespace BizHawk.Emulation.DiscSystem
2011-05-08 09:07:46 +00:00
{
2015-06-23 18:57:11 +00:00
2011-09-04 06:15:41 +00:00
public partial class Disc : IDisposable
2011-05-08 09:07:46 +00:00
{
2015-01-05 23:01:23 +00:00
/// <summary>
/// Free-form optional memos about the disc
/// </summary>
public Dictionary < string , object > Memos = new Dictionary < string , object > ( ) ;
2012-11-17 22:16:09 +00:00
/// <summary>
2014-12-04 05:40:10 +00:00
/// The raw TOC entries found in the lead-in track.
2015-06-23 18:57:11 +00:00
/// NOTE: it seems unlikey that we'll ever get these exactly.
/// The cd reader is supposed to read the multiple copies and pick the best-of-3 and turn them into a TOCRaw
/// So really this only needs to stick around so we can make the TOCRaw from it.
/// Not much of a different view, but.. different
2012-11-17 22:16:09 +00:00
/// </summary>
2014-12-04 05:40:10 +00:00
public List < RawTOCEntry > RawTOCEntries = new List < RawTOCEntry > ( ) ;
2011-05-08 09:07:46 +00:00
2013-06-25 08:31:48 +00:00
/// <summary>
2015-06-23 18:57:11 +00:00
/// The DiscTOCRaw corresponding to the RawTOCEntries.
/// Note: these should be retrieved differently, through a view accessor
2013-06-25 08:31:48 +00:00
/// </summary>
2014-12-04 05:40:10 +00:00
public DiscTOCRaw TOCRaw ;
2013-06-25 08:31:48 +00:00
/// <summary>
2014-12-04 05:40:10 +00:00
/// The DiscStructure corresponding the the TOCRaw
2013-06-25 08:31:48 +00:00
/// </summary>
2014-12-04 05:40:10 +00:00
public DiscStructure Structure ;
2013-06-25 08:31:48 +00:00
/// <summary>
2015-06-23 18:57:11 +00:00
/// Disposable resources (blobs, mostly) referenced by this disc
2013-06-25 08:31:48 +00:00
/// </summary>
2015-06-23 18:57:11 +00:00
internal List < IDisposable > DisposableResources = new List < IDisposable > ( ) ;
2011-05-08 09:07:46 +00:00
2013-06-25 08:31:48 +00:00
/// <summary>
2014-12-04 05:40:10 +00:00
/// The sectors on the disc
2013-06-25 08:31:48 +00:00
/// </summary>
2014-12-04 05:40:10 +00:00
public List < SectorEntry > Sectors = new List < SectorEntry > ( ) ;
2011-05-08 09:07:46 +00:00
2015-06-23 18:57:11 +00:00
internal SectorSynthParams SynthParams = new SectorSynthParams ( ) ;
2014-12-04 05:40:10 +00:00
public Disc ( )
2011-05-08 09:07:46 +00:00
{
}
2011-09-04 06:15:41 +00:00
public void Dispose ( )
{
2015-06-23 18:57:11 +00:00
foreach ( var res in DisposableResources )
2011-09-04 06:15:41 +00:00
{
2015-06-23 18:57:11 +00:00
res . Dispose ( ) ;
2011-09-04 06:15:41 +00:00
}
}
2011-05-08 09:07:46 +00:00
2015-06-23 18:57:11 +00:00
/// <summary>
/// generates lead-out sectors according to very crude approximations
/// </summary>
public class SynthesizeLeadoutJob
2011-06-20 09:09:21 +00:00
{
2015-06-23 18:57:11 +00:00
public int Length ;
public Disc Disc ;
public void Run ( )
2011-06-20 09:09:21 +00:00
{
2015-06-23 18:57:11 +00:00
//TODO: encode_mode2_form2_sector
var sz = new Sector_Zero ( ) ;
2011-06-20 09:09:21 +00:00
2015-06-23 18:57:11 +00:00
var leadoutTs = Disc . TOCRaw . LeadoutTimestamp ;
var lastTrackTOCItem = Disc . TOCRaw . TOCItems [ Disc . TOCRaw . LastRecordedTrackNumber ] ; //NOTE: in case LastRecordedTrackNumber is al ie, this will malfunction
2011-06-20 09:09:21 +00:00
2015-06-23 18:57:11 +00:00
//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 ;
2011-06-20 09:09:21 +00:00
2015-06-23 18:57:11 +00:00
//TODO - needs to be encoded as a certain mode (mode 2 form 2 for psx... i guess...)
2011-06-20 09:09:21 +00:00
2015-06-23 18:57:11 +00:00
for ( int i = 0 ; i < Length ; i + + )
{
var se = new SectorEntry ( sz ) ;
Disc . Sectors . Add ( se ) ;
SubchannelQ sq = new SubchannelQ ( ) ;
2011-05-08 09:07:46 +00:00
2015-06-23 18:57:11 +00:00
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 ) ;
2015-01-05 23:01:23 +00:00
2015-06-23 18:57:11 +00:00
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 ) ;
2015-01-05 23:01:23 +00:00
2015-06-23 18:57:11 +00:00
sq . q_tno . DecimalValue = 0xAA ; //special value for leadout
sq . q_index . DecimalValue = 1 ;
2015-01-05 23:01:23 +00:00
2015-06-23 18:57:11 +00:00
byte ADR = 1 ;
sq . SetStatus ( ADR , leadoutFlags ) ;
2011-05-08 09:07:46 +00:00
2015-06-23 18:57:11 +00:00
var subcode = new BufferedSubcodeSector ( ) ;
subcode . Synthesize_SubchannelQ ( ref sq , true ) ;
se . SubcodeSector = subcode ;
}
}
2014-12-04 05:40:10 +00:00
}
2011-09-04 06:15:41 +00:00
/// <summary>
2015-06-23 18:57:11 +00:00
/// Automagically loads a disc, without any fine-tuned control at all
2011-09-04 06:15:41 +00:00
/// </summary>
2015-06-23 18:57:11 +00:00
public static Disc LoadAutomagic ( string path )
2011-05-08 09:07:46 +00:00
{
2015-06-23 18:57:11 +00:00
var job = new DiscMountJob { IN_FromPath = path } ;
job . IN_DiscInterface = DiscInterface . MednaDisc ; //TEST
job . Run ( ) ;
return job . OUT_Disc ;
2011-05-08 09:07:46 +00:00
}
2011-08-14 23:13:31 +00:00
2015-06-23 18:57:11 +00:00
2014-12-10 19:39:19 +00:00
/// <summary>
/// Synthesizes a crudely estimated TOCRaw from the disc structure.
/// </summary>
public void Synthesize_TOCRawFromStructure ( )
{
TOCRaw = new DiscTOCRaw ( ) ;
TOCRaw . FirstRecordedTrackNumber = 1 ;
TOCRaw . LastRecordedTrackNumber = Structure . Sessions [ 0 ] . Tracks . Count ;
int lastEnd = 0 ;
for ( int i = 0 ; i < Structure . Sessions [ 0 ] . Tracks . Count ; i + + )
{
var track = Structure . Sessions [ 0 ] . Tracks [ i ] ;
TOCRaw . TOCItems [ i + 1 ] . Control = track . Control ;
TOCRaw . TOCItems [ i + 1 ] . Exists = true ;
//TOCRaw.TOCItems[i + 1].LBATimestamp = new Timestamp(track.Start_ABA - 150); //AUGH. see comment in Start_ABA
//TOCRaw.TOCItems[i + 1].LBATimestamp = new Timestamp(track.Indexes[1].LBA); //ZOUNDS!
2015-01-12 07:30:59 +00:00
//TOCRaw.TOCItems[i + 1].LBATimestamp = new Timestamp(track.Indexes[1].LBA + 150); //WHATEVER, I DONT KNOW. MAKES IT MATCH THE CCD, BUT THERES MORE PROBLEMS
TOCRaw . TOCItems [ i + 1 ] . LBATimestamp = new Timestamp ( track . Indexes [ 1 ] . LBA ) ; //WHAT?? WE NEED THIS AFTER ALL! ZOUNDS MEANS, THERE WAS JUST SOME OTHER BUG
2014-12-10 19:39:19 +00:00
lastEnd = track . LengthInSectors + track . Indexes [ 1 ] . LBA ;
}
}
2015-01-05 23:01:23 +00:00
/// <summary>
/// applies an SBI file to the disc
/// </summary>
2015-06-23 18:57:11 +00:00
public void ApplySBI ( SBI . SubQPatchData sbi , bool asMednafen )
2015-01-05 23:01:23 +00:00
{
//save this, it's small, and we'll want it for disc processing a/b checks
Memos [ "sbi" ] = sbi ;
int n = sbi . ABAs . Count ;
byte [ ] subcode = new byte [ 96 ] ;
int b = 0 ;
for ( int i = 0 ; i < n ; i + + )
{
int aba = sbi . ABAs [ i ] ;
var oldSubcode = this . Sectors [ aba ] . SubcodeSector ;
oldSubcode . ReadSubcodeDeinterleaved ( subcode , 0 ) ;
for ( int j = 0 ; j < 12 ; j + + )
{
short patch = sbi . subq [ b + + ] ;
if ( patch = = - 1 ) continue ;
else subcode [ 12 + j ] = ( byte ) patch ;
}
2015-06-23 18:57:11 +00:00
var bss = BufferedSubcodeSector . CloneFromBytesDeinterleaved ( subcode ) ;
Sectors [ aba ] . SubcodeSector = bss ;
//not fully sure what the basis is for this, but here we go
if ( asMednafen )
{
bss . Synthesize_SunchannelQ_Checksum ( ) ;
bss . SubcodeDeinterleaved [ 12 + 10 ] ^ = 0xFF ;
bss . SubcodeDeinterleaved [ 12 + 11 ] ^ = 0xFF ;
}
2015-01-05 23:01:23 +00:00
}
}
2011-08-14 23:13:31 +00:00
/// <summary>
2014-12-04 05:40:10 +00:00
/// Creates the subcode (really, just subchannel Q) for this disc from its current TOC.
/// Depends on the TOCPoints existing in the structure
2014-12-14 10:36:03 +00:00
/// TODO - do we need a fully 0xFF P-subchannel for PSX?
2011-08-14 23:13:31 +00:00
/// </summary>
2014-12-10 19:39:19 +00:00
void Synthesize_SubcodeFromStructure ( )
2011-08-14 23:13:31 +00:00
{
2011-08-15 10:43:36 +00:00
int aba = 0 ;
2011-08-14 23:13:31 +00:00
int dpIndex = 0 ;
2014-12-21 23:32:39 +00:00
//TODO - from mednafen (on PC-FX chip chan kick)
//If we're more than 2 seconds(150 sectors) from the real "start" of the track/INDEX 01, and the track is a data track,
//and the preceding track is an audio track, encode it as audio(by taking the SubQ control field from the preceding
2014-12-18 06:23:44 +00:00
//NOTE: discs may have subcode which is nonsense or possibly not recoverable from a sensible disc structure.
//but this function does what it says.
//SO: heres the main idea of how this works.
//we have the Structure.Points (whose name we dont like) which is a list of sectors where the tno/index changes.
//So for each sector, we see if we've advanced to the next point.
//TODO - check if this is synthesized correctly when producing a structure from a TOCRaw
2011-08-15 10:43:36 +00:00
while ( aba < Sectors . Count )
2011-08-14 23:13:31 +00:00
{
2014-12-04 05:40:10 +00:00
if ( dpIndex < Structure . Points . Count - 1 )
2011-08-14 23:13:31 +00:00
{
2014-12-21 23:32:39 +00:00
while ( aba > = Structure . Points [ dpIndex + 1 ] . ABA )
2011-08-14 23:13:31 +00:00
{
dpIndex + + ;
}
}
2014-12-04 05:40:10 +00:00
var dp = Structure . Points [ dpIndex ] ;
2011-08-14 23:13:31 +00:00
2014-12-21 23:32:39 +00:00
2011-08-15 10:43:36 +00:00
var se = Sectors [ aba ] ;
2011-08-14 23:13:31 +00:00
2015-01-12 00:17:15 +00:00
EControlQ control = dp . Control ;
2014-12-18 06:23:44 +00:00
bool pause = true ;
2014-12-21 23:32:39 +00:00
if ( dp . Num ! = 0 ) //TODO - shouldnt this be IndexNum?
2014-12-18 06:23:44 +00:00
pause = false ;
2015-06-23 18:57:11 +00:00
if ( ( dp . Control & EControlQ . DATA ) ! = 0 )
2014-12-18 06:23:44 +00:00
pause = false ;
2015-01-12 00:17:15 +00:00
int adr = dp . ADR ;
2014-12-04 05:40:10 +00:00
SubchannelQ sq = new SubchannelQ ( ) ;
sq . q_status = SubchannelQ . ComputeStatus ( adr , control ) ;
2015-06-23 18:57:11 +00:00
sq . q_tno = BCD2 . FromDecimal ( dp . TrackNum ) ;
sq . q_index = BCD2 . FromDecimal ( dp . IndexNum ) ;
2011-08-14 23:13:31 +00:00
2011-08-15 10:43:36 +00:00
int track_relative_aba = aba - dp . Track . Indexes [ 1 ] . aba ;
track_relative_aba = Math . Abs ( track_relative_aba ) ;
Timestamp track_relative_timestamp = new Timestamp ( track_relative_aba ) ;
2014-12-04 05:40:10 +00:00
sq . min = BCD2 . FromDecimal ( track_relative_timestamp . MIN ) ;
sq . sec = BCD2 . FromDecimal ( track_relative_timestamp . SEC ) ;
sq . frame = BCD2 . FromDecimal ( track_relative_timestamp . FRAC ) ;
sq . zero = 0 ;
2011-08-15 10:43:36 +00:00
Timestamp absolute_timestamp = new Timestamp ( aba ) ;
2014-12-04 05:40:10 +00:00
sq . ap_min = BCD2 . FromDecimal ( absolute_timestamp . MIN ) ;
sq . ap_sec = BCD2 . FromDecimal ( absolute_timestamp . SEC ) ;
sq . ap_frame = BCD2 . FromDecimal ( absolute_timestamp . FRAC ) ;
var bss = new BufferedSubcodeSector ( ) ;
bss . Synthesize_SubchannelQ ( ref sq , true ) ;
2014-12-18 06:23:44 +00:00
//TEST: need this for psx?
if ( pause ) bss . Synthesize_SubchannelP ( true ) ;
2014-12-04 05:40:10 +00:00
se . SubcodeSector = bss ;
2011-08-14 23:13:31 +00:00
2011-08-15 10:43:36 +00:00
aba + + ;
2011-08-14 23:13:31 +00:00
}
}
static byte IntToBCD ( int n )
{
int ones ;
int tens = Math . DivRem ( n , 10 , out ones ) ;
return ( byte ) ( ( tens < < 4 ) | ones ) ;
}
}
/// <summary>
/// encapsulates a 2 digit BCD number as used various places in the CD specs
/// </summary>
public struct BCD2
{
/// <summary>
/// The raw BCD value. you can't do math on this number! but you may be asked to supply it to a game program.
/// The largest number it can logically contain is 99
/// </summary>
public byte BCDValue ;
/// <summary>
/// The derived decimal value. you can do math on this! the largest number it can logically contain is 99.
/// </summary>
public int DecimalValue
{
get { return ( BCDValue & 0xF ) + ( ( BCDValue > > 4 ) & 0xF ) * 10 ; }
set { BCDValue = IntToBCD ( value ) ; }
}
/// <summary>
/// makes a BCD2 from a decimal number. don't supply a number > 99 or you might not like the results
/// </summary>
public static BCD2 FromDecimal ( int d )
{
2013-11-15 00:49:19 +00:00
return new BCD2 { DecimalValue = d } ;
2011-08-14 23:13:31 +00:00
}
2015-06-23 18:57:11 +00:00
public static BCD2 FromBCD ( byte b )
{
return new BCD2 { BCDValue = b } ;
}
2015-01-05 23:01:23 +00:00
public static int BCDToInt ( byte n )
{
var bcd = new BCD2 ( ) ;
bcd . BCDValue = n ;
return bcd . DecimalValue ;
}
2011-08-14 23:13:31 +00:00
2015-01-05 23:01:23 +00:00
public static byte IntToBCD ( int n )
2011-08-14 23:13:31 +00:00
{
int ones ;
int tens = Math . DivRem ( n , 10 , out ones ) ;
return ( byte ) ( ( tens < < 4 ) | ones ) ;
}
2015-06-23 18:57:11 +00:00
public override string ToString ( )
{
return BCDValue . ToString ( "X2" ) ;
}
2011-08-14 23:13:31 +00:00
}
2015-06-23 18:57:11 +00:00
/// <summary>
/// todo - rename to MSF? It can specify durations, so maybe it should be not suggestive of timestamp
/// TODO - can we maybe use BCD2 in here
/// </summary>
2014-12-04 05:40:10 +00:00
public struct Timestamp
2011-08-14 23:13:31 +00:00
{
/// <summary>
2015-06-23 18:57:11 +00:00
/// Checks if the string is a legit MSF. It's strict.
2011-08-14 23:13:31 +00:00
/// </summary>
2015-06-23 18:57:11 +00:00
public static bool IsMatch ( string str )
2011-08-14 23:13:31 +00:00
{
2015-06-23 18:57:11 +00:00
return new Timestamp ( str ) . Valid ;
2014-12-04 05:40:10 +00:00
}
/// <summary>
2015-06-23 18:57:11 +00:00
/// creates a timestamp from a string in the form mm:ss:ff
2014-12-04 05:40:10 +00:00
/// </summary>
2015-06-23 18:57:11 +00:00
public Timestamp ( string str )
2014-12-04 05:40:10 +00:00
{
2015-06-23 18:57:11 +00:00
if ( str . Length ! = 8 ) goto BOGUS ;
if ( str [ 0 ] < '0' | | str [ 0 ] > '9' ) goto BOGUS ;
if ( str [ 1 ] < '0' | | str [ 1 ] > '9' ) goto BOGUS ;
if ( str [ 2 ] ! = ':' ) goto BOGUS ;
if ( str [ 3 ] < '0' | | str [ 3 ] > '9' ) goto BOGUS ;
if ( str [ 4 ] < '0' | | str [ 4 ] > '9' ) goto BOGUS ;
if ( str [ 5 ] ! = ':' ) goto BOGUS ;
if ( str [ 6 ] < '0' | | str [ 6 ] > '9' ) goto BOGUS ;
if ( str [ 7 ] < '0' | | str [ 7 ] > '9' ) goto BOGUS ;
MIN = ( byte ) ( ( str [ 0 ] - '0' ) * 10 + ( str [ 1 ] - '0' ) ) ;
SEC = ( byte ) ( ( str [ 3 ] - '0' ) * 10 + ( str [ 4 ] - '0' ) ) ;
FRAC = ( byte ) ( ( str [ 6 ] - '0' ) * 10 + ( str [ 7 ] - '0' ) ) ;
Valid = true ;
return ;
BOGUS :
MIN = SEC = FRAC = 0 ;
Valid = false ;
return ;
2011-08-14 23:13:31 +00:00
}
/// <summary>
2015-06-23 18:57:11 +00:00
/// The string representation of the MSF
2011-08-14 23:13:31 +00:00
/// </summary>
2015-06-23 18:57:11 +00:00
public string Value
2011-08-14 23:13:31 +00:00
{
2015-06-23 18:57:11 +00:00
get
{
if ( ! Valid ) return "--:--:--" ;
return string . Format ( "{0:D2}:{1:D2}:{2:D2}" , MIN , SEC , FRAC ) ;
}
2011-08-14 23:13:31 +00:00
}
2011-05-08 09:07:46 +00:00
2015-06-23 18:57:11 +00:00
public readonly byte MIN , SEC , FRAC ;
public readonly bool Valid ;
2014-12-04 05:40:10 +00:00
/// <summary>
2015-06-23 18:57:11 +00:00
/// The fully multiplied out flat-address Sector number
2014-12-04 05:40:10 +00:00
/// </summary>
2015-06-23 18:57:11 +00:00
public int Sector { get { return MIN * 60 * 75 + SEC * 75 + FRAC ; } }
2014-12-04 05:40:10 +00:00
/// <summary>
2015-06-23 18:57:11 +00:00
/// creates timestamp from the supplied MSF
2014-12-04 05:40:10 +00:00
/// </summary>
2015-06-23 18:57:11 +00:00
public Timestamp ( int m , int s , int f )
{
MIN = ( byte ) m ;
SEC = ( byte ) s ;
FRAC = ( byte ) f ;
Valid = true ;
}
2011-08-14 23:13:31 +00:00
2011-08-06 10:43:05 +00:00
/// <summary>
2015-06-23 18:57:11 +00:00
/// creates timestamp from supplied SectorNumber
2011-08-06 10:43:05 +00:00
/// </summary>
2015-06-23 18:57:11 +00:00
public Timestamp ( int SectorNumber )
2011-06-20 09:09:21 +00:00
{
2015-06-23 18:57:11 +00:00
MIN = ( byte ) ( SectorNumber / ( 60 * 75 ) ) ;
SEC = ( byte ) ( ( SectorNumber / 75 ) % 60 ) ;
FRAC = ( byte ) ( SectorNumber % 75 ) ;
Valid = true ;
2011-06-20 09:09:21 +00:00
}
2015-06-23 18:57:11 +00:00
public override string ToString ( )
2011-06-20 09:09:21 +00:00
{
2015-06-23 18:57:11 +00:00
return Value ;
2011-08-07 03:21:03 +00:00
}
2015-06-23 18:57:11 +00:00
}
2011-08-07 03:21:03 +00:00
2015-06-23 18:57:11 +00:00
//not being used yet
class DiscPreferences
{
2011-08-14 23:13:31 +00:00
2011-06-20 09:09:21 +00:00
}
2011-05-08 09:07:46 +00:00
}