120 lines
2.9 KiB
C#
120 lines
2.9 KiB
C#
![]() |
using System;
|
|||
|
using System.Text;
|
|||
|
using System.Text.RegularExpressions;
|
|||
|
using System.IO;
|
|||
|
using System.Collections.Generic;
|
|||
|
|
|||
|
//a decent little subcode reference
|
|||
|
//http://www.jbum.com/cdg_revealed.html
|
|||
|
|
|||
|
namespace BizHawk.Disc
|
|||
|
{
|
|||
|
public class SubcodeStream
|
|||
|
{
|
|||
|
Stream source;
|
|||
|
long offset;
|
|||
|
public SubcodeStream(Stream source, long offset)
|
|||
|
{
|
|||
|
this.source = source;
|
|||
|
this.offset = offset;
|
|||
|
cached_decoder = new SubcodePacketDecoder(cached_buffer, 0);
|
|||
|
}
|
|||
|
int channel = 0;
|
|||
|
public char Channel
|
|||
|
{
|
|||
|
get { return (char)((7 - channel) + 'p'); }
|
|||
|
set { channel = SubcodePacketDecoder.NormalizeChannel(value); }
|
|||
|
}
|
|||
|
|
|||
|
long Position { get; set; }
|
|||
|
|
|||
|
int cached_addr = -1;
|
|||
|
SubcodePacketDecoder cached_decoder = null;
|
|||
|
byte[] cached_buffer = new byte[24];
|
|||
|
public int ReadByte()
|
|||
|
{
|
|||
|
int subcode_addr = (int)Position;
|
|||
|
int subcode_byte = subcode_addr & 1;
|
|||
|
subcode_addr /= 2;
|
|||
|
subcode_addr *= 24;
|
|||
|
if (subcode_addr != cached_addr)
|
|||
|
{
|
|||
|
cached_decoder.Reset();
|
|||
|
source.Position = offset + subcode_addr;
|
|||
|
if (source.Read(cached_buffer, 0, 24) != 24)
|
|||
|
return -1;
|
|||
|
cached_addr = subcode_addr;
|
|||
|
}
|
|||
|
Position = Position + 1;
|
|||
|
ushort val = cached_decoder.ReadChannel(channel);
|
|||
|
val >>= (8 * subcode_byte);
|
|||
|
val &= 0xFF;
|
|||
|
return (int)val;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
class SubcodePacketDecoder
|
|||
|
{
|
|||
|
internal static int NormalizeChannel(char channel)
|
|||
|
{
|
|||
|
int channum;
|
|||
|
if (channel >= 'P' && channel <= 'W') channum = channel - 'P';
|
|||
|
else if (channel >= 'p' && channel <= 'w') channum = (channel - 'p');
|
|||
|
else throw new InvalidOperationException("invalid channel specified");
|
|||
|
channum = 7 - channum;
|
|||
|
return channum;
|
|||
|
}
|
|||
|
|
|||
|
public void Reset()
|
|||
|
{
|
|||
|
cached = false;
|
|||
|
}
|
|||
|
byte[] buffer;
|
|||
|
int offset;
|
|||
|
public SubcodePacketDecoder(byte[] buffer, int offset)
|
|||
|
{
|
|||
|
this.buffer = buffer;
|
|||
|
this.offset = offset;
|
|||
|
}
|
|||
|
byte command { get { return buffer[offset + 0]; } set { buffer[offset + 0] = value; } }
|
|||
|
byte instruction { get { return buffer[offset + 1]; } set { buffer[offset + 1] = value; } }
|
|||
|
|
|||
|
public int parityQ_offset { get { return offset + 2; } }
|
|||
|
public int data_offset { get { return offset + 4; } }
|
|||
|
public int parityP_offset { get { return offset + 20; } }
|
|||
|
|
|||
|
public byte ReadData(int index)
|
|||
|
{
|
|||
|
return buffer[data_offset + index];
|
|||
|
}
|
|||
|
|
|||
|
public ushort ReadChannel(char channel)
|
|||
|
{
|
|||
|
return ReadChannel(NormalizeChannel(channel));
|
|||
|
}
|
|||
|
|
|||
|
bool cached;
|
|||
|
ushort[] decoded_channels = new ushort[8];
|
|||
|
public ushort ReadChannel(int channum)
|
|||
|
{
|
|||
|
if (!cached)
|
|||
|
{
|
|||
|
decoded_channels = new ushort[8];
|
|||
|
for (int i = 0; i < 8; i++)
|
|||
|
decoded_channels[i] = DecodeChannel(i);
|
|||
|
}
|
|||
|
return decoded_channels[channum];
|
|||
|
}
|
|||
|
|
|||
|
ushort DecodeChannel(int channum)
|
|||
|
{
|
|||
|
int ret = 0;
|
|||
|
for (int i = 0; i < 16; i++)
|
|||
|
{
|
|||
|
ret |= ((ReadData(i) >> channum) & 1) << i;
|
|||
|
}
|
|||
|
return (ushort)ret;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|