2014-12-04 05:40:10 +00:00
using System ;
2015-06-23 18:57:11 +00:00
//TODO - call on unmanaged code in mednadisc if available to do deinterleaving faster. be sure to benchmark it though..
2014-12-04 05:40:10 +00:00
//a decent little subcode reference
2011-05-14 21:56:54 +00:00
//http://www.jbum.com/cdg_revealed.html
2015-06-23 18:57:11 +00:00
//NOTES: the 'subchannel Q' stuff here has a lot to do with the q-Mode 1. q-Mode 2 is different,
//and q-Mode 1 technically is defined a little differently in the lead-in area, although the fields align so the datastructures can be reused
//Q subchannel basic structure: (quick ref: https://en.wikipedia.org/wiki/Compact_Disc_subcode)
//Byte 1: (aka `status`)
// q-Control: 4 bits (i.e. flags)
// q-Mode: 4 bits (aka ADR; WHY is this called ADR?)
//q-Data: other stuff depending on q-Mode and type of track
//q-CRC: CRC of preceding
2013-11-03 23:45:44 +00:00
namespace BizHawk.Emulation.DiscSystem
2011-05-14 21:56:54 +00:00
{
2014-12-04 05:40:10 +00:00
/// <summary>
2015-06-23 18:57:11 +00:00
/// Control bit flags for the Q Subchannel.
2014-12-04 05:40:10 +00:00
/// </summary>
[Flags]
public enum EControlQ
{
None = 0 ,
2015-06-23 18:57:11 +00:00
PRE = 1 , //Pre-emphasis enabled (audio tracks only)
DCP = 2 , //Digital copy permitted
DATA = 4 , //set for data tracks, clear for audio tracks
_4CH = 8 , //Four channel audio
}
2014-12-04 05:40:10 +00:00
/// <summary>
/// Why did I make this a struct? I thought there might be a shitton of these and I was trying to cut down on object creation churn during disc-loading.
/// But I ended up mostly just having a shitton of byte[] for each buffer (I could improve that later to possibly reference a blob on top of a memorystream)
/// So, I should probably change that.
/// </summary>
public struct SubchannelQ
{
/// <summary>
/// ADR and CONTROL
2015-06-27 09:57:22 +00:00
/// TODO - make BCD2? PROBABLY NOT. I DONT KNOW.
2014-12-04 05:40:10 +00:00
/// </summary>
public byte q_status ;
/// <summary>
2015-06-27 09:57:22 +00:00
/// normal track: BCD indication of the current track number
2014-12-04 05:40:10 +00:00
/// leadin track: should be 0
/// </summary>
2015-06-23 18:57:11 +00:00
public BCD2 q_tno ;
2014-12-04 05:40:10 +00:00
/// <summary>
2015-06-27 09:57:22 +00:00
/// normal track: BCD indication of the current index
2014-12-04 05:40:10 +00:00
/// leadin track: 'POINT' field used to ID the TOC entry #
/// </summary>
2015-06-23 18:57:11 +00:00
public BCD2 q_index ;
2014-12-04 05:40:10 +00:00
/// <summary>
/// These are the initial set of timestamps. Meaning varies:
/// check yellowbook 22.3.3 and 22.3.4
/// leadin track: unknown
2015-07-06 11:06:37 +00:00
/// user information track: relative timestamp
2015-06-23 18:57:11 +00:00
/// leadout: relative timestamp
2015-06-27 09:57:22 +00:00
/// TODO - why are these BCD2? having things in BCD2 is freaking annoying, I should only make them BCD2 when serializing into a subchannel Q buffer
2015-06-23 18:57:11 +00:00
/// EDIT - elsewhere I rambled "why not BCD2?". geh. need to make a final organized approach
2014-12-04 05:40:10 +00:00
/// </summary>
public BCD2 min , sec , frame ;
/// <summary>
/// This is supposed to be zero.. but CCD format stores it, so maybe it's useful for copy protection or something
/// </summary>
public byte zero ;
/// <summary>
/// These are the second set of timestamps. Meaning varies:
/// check yellowbook 22.3.3 and 22.3.4
2015-06-23 18:57:11 +00:00
/// leadin track q-mode 1: TOC entry, absolute MSF of track
2015-07-06 11:06:37 +00:00
/// user information track: absolute timestamp
2015-06-23 18:57:11 +00:00
/// leadout: absolute timestamp
2014-12-04 05:40:10 +00:00
/// </summary>
public BCD2 ap_min , ap_sec , ap_frame ;
/// <summary>
2015-07-06 11:06:37 +00:00
/// Don't assume this CRC is correct, in the case of some copy protections it is intended to be wrong.
/// Furthermore, it is meaningless (and in BizHawk, unpopulated) for a TOC Entry
/// (since an invalid CRC on a [theyre redundantly/duplicately stored] toc entry would cause it to get discarded in favor of another one with a correct CRC)
2014-12-04 05:40:10 +00:00
/// </summary>
public ushort q_crc ;
/// <summary>
/// Retrieves the initial set of timestamps (min,sec,frac) as a convenient Timestamp
/// </summary>
2015-07-01 10:26:10 +00:00
public Timestamp Timestamp
{
get { return new Timestamp ( min . DecimalValue , sec . DecimalValue , frame . DecimalValue ) ; }
set { min . DecimalValue = value . MIN ; sec . DecimalValue = value . SEC ; frame . DecimalValue = value . FRAC ; }
}
2014-12-04 05:40:10 +00:00
/// <summary>
2015-06-23 18:57:11 +00:00
/// Retrieves the second set of timestamps (ap_min, ap_sec, ap_frac) as a convenient Timestamp.
2015-07-15 02:42:31 +00:00
/// TODO - rename everything AP here, it's nonsense. (the P is)
2014-12-04 05:40:10 +00:00
/// </summary>
2015-06-23 18:57:11 +00:00
public Timestamp AP_Timestamp {
get { return new Timestamp ( ap_min . DecimalValue , ap_sec . DecimalValue , ap_frame . DecimalValue ) ; }
set { ap_min . DecimalValue = value . MIN ; ap_sec . DecimalValue = value . SEC ; ap_frame . DecimalValue = value . FRAC ; }
}
2014-12-04 05:40:10 +00:00
/// <summary>
2015-06-27 09:57:22 +00:00
/// sets the status byte from the provided adr/qmode and control values
2014-12-04 05:40:10 +00:00
/// </summary>
2015-06-27 09:57:22 +00:00
public void SetStatus ( byte adr_or_qmode , EControlQ control )
2014-12-04 05:40:10 +00:00
{
2015-06-27 09:57:22 +00:00
q_status = ComputeStatus ( adr_or_qmode , control ) ;
2014-12-04 05:40:10 +00:00
}
/// <summary>
2015-06-27 09:57:22 +00:00
/// computes a status byte from the provided adr/qmode and control values
2014-12-04 05:40:10 +00:00
/// </summary>
2015-06-27 09:57:22 +00:00
public static byte ComputeStatus ( int adr_or_qmode , EControlQ control )
2014-12-04 05:40:10 +00:00
{
2015-06-27 09:57:22 +00:00
return ( byte ) ( adr_or_qmode | ( ( ( int ) control ) < < 4 ) ) ;
2014-12-04 05:40:10 +00:00
}
/// <summary>
/// Retrives the ADR field of the q_status member (low 4 bits)
/// </summary>
public int ADR { get { return q_status & 0xF ; } }
/// <summary>
/// Retrieves the CONTROL field of the q_status member (high 4 bits)
/// </summary>
public EControlQ CONTROL { get { return ( EControlQ ) ( ( q_status > > 4 ) & 0xF ) ; } }
}
2015-07-08 04:17:38 +00:00
2011-05-14 21:56:54 +00:00
}