mirror of
https://github.com/aaru-dps/RedBookPlayer.git
synced 2025-12-16 19:24:41 +00:00
Redefine subchannel data for later (nw)
This commit is contained in:
@@ -1332,55 +1332,6 @@ namespace RedBookPlayer.Models.Hardware
|
|||||||
|
|
||||||
#region Helpers
|
#region Helpers
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reformat raw subchannel data for a single sector
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="subchannelData">Raw subchannel data to format</param>
|
|
||||||
/// <returns>Dictionary mapping subchannel to formatted data</returns>
|
|
||||||
public Dictionary<char, byte[]> ConvertSingleSubchannel(byte[] subchannelData)
|
|
||||||
{
|
|
||||||
if(subchannelData == null || subchannelData.Length != 96)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
// Create the output dictionary for the formatted data
|
|
||||||
Dictionary<char, byte[]> formattedData = new Dictionary<char, byte[]>
|
|
||||||
{
|
|
||||||
['P'] = new byte[12],
|
|
||||||
['Q'] = new byte[12],
|
|
||||||
['R'] = new byte[12],
|
|
||||||
['S'] = new byte[12],
|
|
||||||
['T'] = new byte[12],
|
|
||||||
['U'] = new byte[12],
|
|
||||||
['V'] = new byte[12],
|
|
||||||
['W'] = new byte[12],
|
|
||||||
};
|
|
||||||
|
|
||||||
// Loop through all bytes in the subchannel data and populate
|
|
||||||
int index = -1;
|
|
||||||
for(int i = 0; i < 96; i++)
|
|
||||||
{
|
|
||||||
// Get the modulo value of the current byte
|
|
||||||
int modValue = i % 8;
|
|
||||||
if(modValue == 0)
|
|
||||||
index++;
|
|
||||||
|
|
||||||
// Retrieve the next byte
|
|
||||||
byte b = subchannelData[i];
|
|
||||||
|
|
||||||
// Set the respective bit in the new byte data
|
|
||||||
formattedData['P'][index] |= (byte)(HasBitSet(b, 7) ? 1 << (7 - modValue) : 0);
|
|
||||||
formattedData['Q'][index] |= (byte)(HasBitSet(b, 6) ? 1 << (7 - modValue) : 0);
|
|
||||||
formattedData['R'][index] |= (byte)(HasBitSet(b, 5) ? 1 << (7 - modValue) : 0);
|
|
||||||
formattedData['S'][index] |= (byte)(HasBitSet(b, 4) ? 1 << (7 - modValue) : 0);
|
|
||||||
formattedData['T'][index] |= (byte)(HasBitSet(b, 3) ? 1 << (7 - modValue) : 0);
|
|
||||||
formattedData['U'][index] |= (byte)(HasBitSet(b, 2) ? 1 << (7 - modValue) : 0);
|
|
||||||
formattedData['V'][index] |= (byte)(HasBitSet(b, 1) ? 1 << (7 - modValue) : 0);
|
|
||||||
formattedData['W'][index] |= (byte)(HasBitSet(b, 0) ? 1 << (7 - modValue) : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return formattedData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reformat raw subchannel data for multiple sectors
|
/// Reformat raw subchannel data for multiple sectors
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1395,14 +1346,14 @@ namespace RedBookPlayer.Models.Hardware
|
|||||||
int modValue = subchannelData.Length / 96;
|
int modValue = subchannelData.Length / 96;
|
||||||
Dictionary<char, byte[]> formattedData = new Dictionary<char, byte[]>
|
Dictionary<char, byte[]> formattedData = new Dictionary<char, byte[]>
|
||||||
{
|
{
|
||||||
['P'] = new byte[12 * modValue],
|
['P'] = new byte[8 * modValue],
|
||||||
['Q'] = new byte[12 * modValue],
|
['Q'] = new byte[8 * modValue],
|
||||||
['R'] = new byte[12 * modValue],
|
['R'] = new byte[8 * modValue],
|
||||||
['S'] = new byte[12 * modValue],
|
['S'] = new byte[8 * modValue],
|
||||||
['T'] = new byte[12 * modValue],
|
['T'] = new byte[8 * modValue],
|
||||||
['U'] = new byte[12 * modValue],
|
['U'] = new byte[8 * modValue],
|
||||||
['V'] = new byte[12 * modValue],
|
['V'] = new byte[8 * modValue],
|
||||||
['W'] = new byte[12 * modValue],
|
['W'] = new byte[8 * modValue],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Read in 96-byte chunks
|
// Read in 96-byte chunks
|
||||||
@@ -1410,29 +1361,22 @@ namespace RedBookPlayer.Models.Hardware
|
|||||||
{
|
{
|
||||||
byte[] buffer = new byte[96];
|
byte[] buffer = new byte[96];
|
||||||
Array.Copy(subchannelData, i * 96, buffer, 0, 96);
|
Array.Copy(subchannelData, i * 96, buffer, 0, 96);
|
||||||
Dictionary<char, byte[]> singleData = ConvertSingleSubchannel(buffer);
|
var singleSubchannel = new SubchannelData(buffer);
|
||||||
|
Dictionary<char, byte[]> singleData = singleSubchannel.ConvertData();
|
||||||
|
|
||||||
Array.Copy(singleData['P'], 0, formattedData['P'], 12 * i, 12);
|
Array.Copy(singleData['P'], 0, formattedData['P'], 8 * i, 8);
|
||||||
Array.Copy(singleData['Q'], 0, formattedData['Q'], 12 * i, 12);
|
Array.Copy(singleData['Q'], 0, formattedData['Q'], 8 * i, 8);
|
||||||
Array.Copy(singleData['R'], 0, formattedData['R'], 12 * i, 12);
|
Array.Copy(singleData['R'], 0, formattedData['R'], 8 * i, 8);
|
||||||
Array.Copy(singleData['S'], 0, formattedData['S'], 12 * i, 12);
|
Array.Copy(singleData['S'], 0, formattedData['S'], 8 * i, 8);
|
||||||
Array.Copy(singleData['T'], 0, formattedData['T'], 12 * i, 12);
|
Array.Copy(singleData['T'], 0, formattedData['T'], 8 * i, 8);
|
||||||
Array.Copy(singleData['U'], 0, formattedData['U'], 12 * i, 12);
|
Array.Copy(singleData['U'], 0, formattedData['U'], 8 * i, 8);
|
||||||
Array.Copy(singleData['V'], 0, formattedData['V'], 12 * i, 12);
|
Array.Copy(singleData['V'], 0, formattedData['V'], 8 * i, 8);
|
||||||
Array.Copy(singleData['W'], 0, formattedData['W'], 12 * i, 12);
|
Array.Copy(singleData['W'], 0, formattedData['W'], 8 * i, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
return formattedData;
|
return formattedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Check if a bit is set in a byte
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value">Byte value to check</param>
|
|
||||||
/// <param name="bitIndex">Index of the bit to check</param>
|
|
||||||
/// <returns>True if the bit was set, false otherwise</returns>
|
|
||||||
private bool HasBitSet(byte value, int bitIndex) => (value & (1 << bitIndex)) != 0;
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
69
RedBookPlayer.Models/Hardware/SubchannelData.cs
Normal file
69
RedBookPlayer.Models/Hardware/SubchannelData.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace RedBookPlayer.Models.Hardware
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents subchannel data for a single sector
|
||||||
|
/// </summary>
|
||||||
|
/// <see cref="https://jbum.com/cdg_revealed.html"/>
|
||||||
|
internal class SubchannelData
|
||||||
|
{
|
||||||
|
public SubchannelPacket[] Packets { get; private set; } = new SubchannelPacket[4];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new subchannel data from a byte array
|
||||||
|
/// </summary>
|
||||||
|
public SubchannelData(byte[] bytes)
|
||||||
|
{
|
||||||
|
if(bytes == null || bytes.Length != 96)
|
||||||
|
return;
|
||||||
|
|
||||||
|
byte[] buffer = new byte[24];
|
||||||
|
for(int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
Array.Copy(bytes, 24 * i, buffer, 0, 24);
|
||||||
|
Packets[i] = new SubchannelPacket(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert the packet data into separate named subchannels
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<char, byte[]> ConvertData()
|
||||||
|
{
|
||||||
|
if(this.Packets == null || this.Packets.Length != 4)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Prepare the output formatted data
|
||||||
|
Dictionary<char, byte[]> formattedData = new Dictionary<char, byte[]>
|
||||||
|
{
|
||||||
|
['P'] = new byte[8],
|
||||||
|
['Q'] = new byte[8],
|
||||||
|
['R'] = new byte[8],
|
||||||
|
['S'] = new byte[8],
|
||||||
|
['T'] = new byte[8],
|
||||||
|
['U'] = new byte[8],
|
||||||
|
['V'] = new byte[8],
|
||||||
|
['W'] = new byte[8],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Loop through all subchannel packets
|
||||||
|
for(int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
Dictionary<char, byte[]> singleData = Packets[i].ConvertData();
|
||||||
|
|
||||||
|
Array.Copy(singleData['P'], 0, formattedData['P'], 2 * i, 2);
|
||||||
|
Array.Copy(singleData['Q'], 0, formattedData['Q'], 2 * i, 2);
|
||||||
|
Array.Copy(singleData['R'], 0, formattedData['R'], 2 * i, 2);
|
||||||
|
Array.Copy(singleData['S'], 0, formattedData['S'], 2 * i, 2);
|
||||||
|
Array.Copy(singleData['T'], 0, formattedData['T'], 2 * i, 2);
|
||||||
|
Array.Copy(singleData['U'], 0, formattedData['U'], 2 * i, 2);
|
||||||
|
Array.Copy(singleData['V'], 0, formattedData['V'], 2 * i, 2);
|
||||||
|
Array.Copy(singleData['W'], 0, formattedData['W'], 2 * i, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return formattedData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
89
RedBookPlayer.Models/Hardware/SubchannelPacket.cs
Normal file
89
RedBookPlayer.Models/Hardware/SubchannelPacket.cs
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace RedBookPlayer.Models.Hardware
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a single packet of subcode data
|
||||||
|
/// </summary>
|
||||||
|
/// <see cref="https://jbum.com/cdg_revealed.html"/>
|
||||||
|
internal class SubchannelPacket
|
||||||
|
{
|
||||||
|
public byte Command { get; private set; }
|
||||||
|
public byte Instruction { get; private set; }
|
||||||
|
public byte[] ParityQ { get; private set; } = new byte[2];
|
||||||
|
public byte[] Data { get; private set; } = new byte[16];
|
||||||
|
public byte[] ParityP { get; private set; } = new byte[4];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new subchannel packet from a byte array
|
||||||
|
/// </summary>
|
||||||
|
public SubchannelPacket(byte[] bytes)
|
||||||
|
{
|
||||||
|
if(bytes == null || bytes.Length != 24)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.Command = bytes[0];
|
||||||
|
this.Instruction = bytes[1];
|
||||||
|
|
||||||
|
Array.Copy(bytes, 2, this.ParityQ, 0, 2);
|
||||||
|
Array.Copy(bytes, 4, this.Data, 0, 16);
|
||||||
|
Array.Copy(bytes, 20, this.ParityP, 0, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Convert the data into separate named subchannels
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<char, byte[]> ConvertData()
|
||||||
|
{
|
||||||
|
if(this.Data == null || this.Data.Length != 16)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Create the output dictionary for the formatted data
|
||||||
|
Dictionary<char, byte[]> formattedData = new Dictionary<char, byte[]>
|
||||||
|
{
|
||||||
|
['P'] = new byte[2],
|
||||||
|
['Q'] = new byte[2],
|
||||||
|
['R'] = new byte[2],
|
||||||
|
['S'] = new byte[2],
|
||||||
|
['T'] = new byte[2],
|
||||||
|
['U'] = new byte[2],
|
||||||
|
['V'] = new byte[2],
|
||||||
|
['W'] = new byte[2],
|
||||||
|
};
|
||||||
|
|
||||||
|
// Loop through all bytes in the subchannel data and populate
|
||||||
|
int index = -1;
|
||||||
|
for(int i = 0; i < 16; i++)
|
||||||
|
{
|
||||||
|
// Get the modulo value of the current byte
|
||||||
|
int modValue = i % 8;
|
||||||
|
if(modValue == 0)
|
||||||
|
index++;
|
||||||
|
|
||||||
|
// Retrieve the next byte
|
||||||
|
byte b = this.Data[i];
|
||||||
|
|
||||||
|
// Set the respective bit in the new byte data
|
||||||
|
formattedData['P'][index] |= (byte)(HasBitSet(b, 7) ? 1 << (7 - modValue) : 0);
|
||||||
|
formattedData['Q'][index] |= (byte)(HasBitSet(b, 6) ? 1 << (7 - modValue) : 0);
|
||||||
|
formattedData['R'][index] |= (byte)(HasBitSet(b, 5) ? 1 << (7 - modValue) : 0);
|
||||||
|
formattedData['S'][index] |= (byte)(HasBitSet(b, 4) ? 1 << (7 - modValue) : 0);
|
||||||
|
formattedData['T'][index] |= (byte)(HasBitSet(b, 3) ? 1 << (7 - modValue) : 0);
|
||||||
|
formattedData['U'][index] |= (byte)(HasBitSet(b, 2) ? 1 << (7 - modValue) : 0);
|
||||||
|
formattedData['V'][index] |= (byte)(HasBitSet(b, 1) ? 1 << (7 - modValue) : 0);
|
||||||
|
formattedData['W'][index] |= (byte)(HasBitSet(b, 0) ? 1 << (7 - modValue) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return formattedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check if a bit is set in a byte
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value">Byte value to check</param>
|
||||||
|
/// <param name="bitIndex">Index of the bit to check</param>
|
||||||
|
/// <returns>True if the bit was set, false otherwise</returns>
|
||||||
|
private bool HasBitSet(byte value, int bitIndex) => (value & (1 << bitIndex)) != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user