using System;
//TODO - call on unmanaged code in mednadisc if available to do deinterleaving faster. be sure to benchmark it though..
//a decent little subcode reference
//http://www.jbum.com/cdg_revealed.html
//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
namespace BizHawk.Emulation.DiscSystem
{
///
/// Control bit flags for the Q Subchannel.
///
[Flags]
public enum EControlQ
{
None = 0,
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
}
///
/// 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.
///
public struct SubchannelQ
{
///
/// ADR and CONTROL
/// TODO - make BCD2? PROBABLY NOT. I DONT KNOW.
///
public byte q_status;
///
/// normal track: BCD indication of the current track number
/// leadin track: should be 0
///
public BCD2 q_tno;
///
/// normal track: BCD indication of the current index
/// leadin track: 'POINT' field used to ID the TOC entry #
///
public BCD2 q_index;
///
/// These are the initial set of timestamps. Meaning varies:
/// check yellowbook 22.3.3 and 22.3.4
/// leadin track: unknown
/// user information track: relative timestamp
/// leadout: relative timestamp
/// 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
/// EDIT - elsewhere I rambled "why not BCD2?". geh. need to make a final organized approach
///
public BCD2 min, sec, frame;
///
/// This is supposed to be zero.. but CCD format stores it, so maybe it's useful for copy protection or something
///
public byte zero;
///
/// These are the second set of timestamps. Meaning varies:
/// check yellowbook 22.3.3 and 22.3.4
/// leadin track q-mode 1: TOC entry, absolute MSF of track
/// user information track: absolute timestamp
/// leadout: absolute timestamp
///
public BCD2 ap_min, ap_sec, ap_frame;
///
/// 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)
///
public ushort q_crc;
///
/// Retrieves the initial set of timestamps (min,sec,frac) as a convenient Timestamp
///
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; }
}
///
/// Retrieves the second set of timestamps (ap_min, ap_sec, ap_frac) as a convenient Timestamp.
///
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; }
}
///
/// sets the status byte from the provided adr/qmode and control values
///
public void SetStatus(byte adr_or_qmode, EControlQ control)
{
q_status = ComputeStatus(adr_or_qmode, control);
}
///
/// computes a status byte from the provided adr/qmode and control values
///
public static byte ComputeStatus(int adr_or_qmode, EControlQ control)
{
return (byte)(adr_or_qmode | (((int)control) << 4));
}
///
/// Retrives the ADR field of the q_status member (low 4 bits)
///
public int ADR { get { return q_status & 0xF; } }
///
/// Retrieves the CONTROL field of the q_status member (high 4 bits)
///
public EControlQ CONTROL { get { return (EControlQ)((q_status >> 4) & 0xF); } }
}
//this has been checked against mednafen's and seems to match
//there are a few dozen different ways to do CRC16-CCITT
//this table is backwards or something. at any rate its tailored to the needs of the Q subchannel
internal static class CRC16_CCITT
{
private static readonly ushort[] table = new ushort[256];
static CRC16_CCITT()
{
for (ushort i = 0; i < 256; ++i)
{
ushort value = 0;
ushort temp = (ushort)(i << 8);
for (byte j = 0; j < 8; ++j)
{
if (((value ^ temp) & 0x8000) != 0)
value = (ushort)((value << 1) ^ 0x1021);
else
value <<= 1;
temp <<= 1;
}
table[i] = value;
}
}
public static ushort Calculate(byte[] data, int offset, int length)
{
ushort Result = 0;
for(int i=0;i> 8) & 0xFF));
Result = (ushort)((Result << 8) ^ table[index]);
}
return Result;
}
}
}