disc subchannel Q calculation; make TOC a little more useful by adding TOCPoints which are easier to search than nested sessions, tracks, and indices; and change path browser to use a superior folder browser which lets you enter paths into a textbox. I refuse to click to navigate folders

This commit is contained in:
zeromus 2011-08-14 23:13:31 +00:00
parent 266d81f644
commit 019ad69459
15 changed files with 863 additions and 278 deletions

View File

@ -176,6 +176,11 @@ namespace BizHawk.Emulation.Consoles.TurboGrafx
// like this, they probably become available as the bits come off the disc.
// but lets get some basic functionality before we go crazy.
// Idunno, maybe they do come in a sector at a time.
//note to vecna: maybe not at the sector level, but at a level > 1 sample and <= 1 sector, samples come out in blocks
//due to the way they are jumbled up (seriously, like put into a blender) for error correction purposes.
//we may as well assume that the cd audio decoding magic works at the level of one sector, but it isnt one sample.
if (SectorsLeftToRead == 0)
{
DataReadInProgress = false;
@ -491,6 +496,10 @@ throw new Exception("requesting 0 sectors read.............................");
private void CommandReadSubcodeQ()
{
//TODO VECNA - i changed this for you but maybe i did it wrong
var sectorEntry = disc.ReadSectorEntry(CurrentReadingSector);
DataIn.Clear();
switch (pce.CDAudio.Mode)
@ -499,15 +508,19 @@ throw new Exception("requesting 0 sectors read.............................");
case CDAudio.CDAudioMode.Paused: DataIn.Enqueue(2); break;
case CDAudio.CDAudioMode.Stopped: DataIn.Enqueue(3); break;
}
DataIn.Enqueue(0); // unused?
DataIn.Enqueue((byte)pce.CDAudio.PlayingTrack); // track
DataIn.Enqueue(1); // index
DataIn.Enqueue(1); // M(rel)
DataIn.Enqueue(1); // S(rel)
DataIn.Enqueue(1); // F(rel)
DataIn.Enqueue(1); // M(abs)
DataIn.Enqueue(1); // S(abs)
DataIn.Enqueue(1); // F(abs)
DataIn.Enqueue(sectorEntry.q_status); // unused?
//DataIn.Enqueue((byte)pce.CDAudio.PlayingTrack); // track //vecna's
DataIn.Enqueue(sectorEntry.q_tno.BCDValue); // track //zero's
DataIn.Enqueue(sectorEntry.q_index.BCDValue); // index
DataIn.Enqueue(sectorEntry.q_min.BCDValue); // M(rel)
DataIn.Enqueue(sectorEntry.q_sec.BCDValue); // S(rel)
DataIn.Enqueue(sectorEntry.q_frame.BCDValue); // F(rel)
DataIn.Enqueue(sectorEntry.q_amin.BCDValue); // M(abs)
DataIn.Enqueue(sectorEntry.q_asec.BCDValue); // S(abs)
DataIn.Enqueue(sectorEntry.q_aframe.BCDValue); // F(abs)
SetPhase(BusPhase.DataIn);
}

View File

@ -1,33 +1,35 @@
namespace BizHawk
{
public static class CRC32
{
public static class CRC32
{
// Lookup table for speed.
private static uint[] CRC32Table;
static CRC32()
{
static CRC32()
{
CRC32Table = new uint[256];
for (uint i = 0; i < 256; ++i)
{
for (uint i = 0; i < 256; ++i)
{
uint crc = i;
for (int j = 8; j > 0; --j)
{
for (int j = 8; j > 0; --j)
{
if ((crc & 1) == 1)
crc = ((crc >> 1) ^ 0xEDB88320);
else
else
crc >>= 1;
}
CRC32Table[i] = crc;
}
}
public static int Calculate(byte[] data)
{
uint Result = 0xFFFFFFFF;
foreach (var b in data)
Result = (((Result) >> 8) ^ CRC32Table[b ^ ((Result) & 0xFF)]);
return (int)~Result;
public static int Calculate(byte[] data)
{
uint Result = 0xFFFFFFFF;
foreach (var b in data)
Result = (((Result) >> 8) ^ CRC32Table[b ^ ((Result) & 0xFF)]);
return (int) ~Result;
}
}
}

View File

@ -84,8 +84,8 @@ namespace BizHawk.DiscSystem
throw new DiscReferenceException(blobPath, ex);
}
blob_length_lba = (int) (blob.Length/blob_sectorsize);
blob_leftover = (int) (blob.Length - blob_length_lba*blob_sectorsize);
blob_length_lba = (int)(blob.Length / blob_sectorsize);
blob_leftover = (int)(blob.Length - blob_length_lba * blob_sectorsize);
cue_blob = blob;
}
else throw new DiscReferenceException(blobPath, new InvalidOperationException("unknown cue file type: " + cue_file.StrFileType));
@ -149,9 +149,9 @@ namespace BizHawk.DiscSystem
if (curr_track == 1)
{
if (cue_track.PreGap.LBA != 0)
throw new InvalidOperationException("not supported: cue files with track 1 pregaps");
//but now we add one anyway
cue_track.PreGap = new Cue.CueTimestamp(150);
throw new InvalidOperationException("not supported (yet): cue files with track 1 pregaps");
//but now we add one anyway, because every existing cue+bin seems to implicitly specify this
cue_track.PreGap = new Timestamp(150);
}
//check whether a pregap is requested.
@ -190,7 +190,7 @@ namespace BizHawk.DiscSystem
{
toc_index.lba = track_disc_pregap_lba - (cue_track.Indexes[1].Timestamp.LBA - cue_track.Indexes[0].Timestamp.LBA);
}
else toc_index.lba = Sectors.Count;
else toc_index.lba = Sectors.Count;
//calculate length of the index
//if it is the last index then we use our calculation from before, otherwise we check the next index
@ -202,7 +202,7 @@ namespace BizHawk.DiscSystem
//emit sectors
for (int lba = 0; lba < index_length_lba; lba++)
{
bool is_last_lba_in_index = (lba == index_length_lba-1);
bool is_last_lba_in_index = (lba == index_length_lba - 1);
bool is_last_lba_in_track = is_last_lba_in_index && is_last_index;
switch (cue_track.TrackType)
@ -220,7 +220,7 @@ namespace BizHawk.DiscSystem
Sector_Raw sector_raw = new Sector_Raw();
sector_raw.BaseSector = sector_rawblob;
//take care to handle final sectors that are too short.
if (is_last_lba_in_track && blob_leftover>0)
if (is_last_lba_in_track && blob_leftover > 0)
{
Sector_ZeroPad sector_zeropad = new Sector_ZeroPad();
sector_zeropad.BaseSector = sector_rawblob;
@ -328,7 +328,7 @@ namespace BizHawk.DiscSystem
public static int BINSectorSizeForTrackType(ETrackType type)
{
switch(type)
switch (type)
{
case ETrackType.Mode1_2352:
case ETrackType.Mode2_2352:
@ -371,52 +371,16 @@ namespace BizHawk.DiscSystem
{
public ETrackType TrackType;
public int TrackNum;
public CueTimestamp PreGap = new CueTimestamp();
public CueTimestamp PostGap = new CueTimestamp();
public Timestamp PreGap = new Timestamp();
public Timestamp PostGap = new Timestamp();
public Dictionary<int, CueTrackIndex> Indexes = new Dictionary<int, CueTrackIndex>();
}
public class CueTimestamp
{
/// <summary>
/// creates timestamp of 00:00:00
/// </summary>
public CueTimestamp()
{
Value = "00:00:00";
}
/// <summary>
/// creates a timestamp from a string in the form mm:ss:ff
/// </summary>
public CueTimestamp(string value) {
this.Value = value;
MIN = int.Parse(value.Substring(0, 2));
SEC = int.Parse(value.Substring(3, 2));
FRAC = int.Parse(value.Substring(6, 2));
LBA = MIN * 60 * 75 + SEC * 75 + FRAC;
}
public readonly string Value;
public readonly int MIN, SEC, FRAC, LBA;
/// <summary>
/// creates timestamp from supplied LBA
/// </summary>
public CueTimestamp(int LBA)
{
this.LBA = LBA;
MIN = LBA / (60*75);
SEC = (LBA / 75)%60;
FRAC = LBA % 75;
Value = string.Format("{0:D2}:{1:D2}:{2:D2}", MIN, SEC, FRAC);
}
}
public class CueTrackIndex
{
public CueTrackIndex(int num) { IndexNum = num; }
public int IndexNum;
public CueTimestamp Timestamp;
public Timestamp Timestamp;
public int ZeroLBA;
}
@ -517,11 +481,11 @@ namespace BizHawk.DiscSystem
throw new CueBrokenException("malformed index number");
if (clp.EOF) throw new CueBrokenException("invalid cue structure (missing index timestamp)");
string str_timestamp = clp.ReadToken();
if(indexnum <0 || indexnum>99) throw new CueBrokenException("`All index numbers must be between 0 and 99 inclusive.`");
if (indexnum < 0 || indexnum > 99) throw new CueBrokenException("`All index numbers must be between 0 and 99 inclusive.`");
if (indexnum != 1 && indexnum != last_index_num + 1) throw new CueBrokenException("`The first index must be 0 or 1 with all other indexes being sequential to the first one.`");
last_index_num = indexnum;
CueTrackIndex cti = new CueTrackIndex(indexnum);
cti.Timestamp = new CueTimestamp(str_timestamp);
cti.Timestamp = new Timestamp(str_timestamp);
cti.IndexNum = indexnum;
currTrack.Indexes[indexnum] = cti;
break;
@ -529,13 +493,13 @@ namespace BizHawk.DiscSystem
case "PREGAP":
if (track_has_pregap) throw new CueBrokenException("`Only one PREGAP command is allowed per track.`");
if (currTrack.Indexes.Count > 0) throw new CueBrokenException("`The PREGAP command must appear after a TRACK command, but before any INDEX commands.`");
currTrack.PreGap = new CueTimestamp(clp.ReadToken());
currTrack.PreGap = new Timestamp(clp.ReadToken());
track_has_pregap = true;
break;
case "POSTGAP":
if (track_has_postgap) throw new CueBrokenException("`Only one POSTGAP command is allowed per track.`");
track_has_postgap = true;
currTrack.PostGap = new CueTimestamp(clp.ReadToken());
currTrack.PostGap = new Timestamp(clp.ReadToken());
break;
case "CATALOG":
case "PERFORMER":

View File

@ -56,19 +56,55 @@ namespace BizHawk.DiscSystem
}
}
//TODO - rename these APIs to ReadSector
public partial class Disc
{
//main API to read a 2352-byte LBA from a disc.
//this starts at the beginning of the disc (at the lead-in)
//so add 150 to get to get an address in the user data area
/// <summary>
/// Main API to read a 2352-byte sector from a disc.
/// This starts at the beginning of the "userdata" area of the disc (track 1, index 0)
/// However, located here is a mandatory pregap of 2 seconds (or more?).
/// so you may need to add 150 depending on how your system addresses things to get to the "start" of the first track. (track 1, index 1)
/// </summary>
public void ReadLBA_2352(int lba, byte[] buffer, int offset)
{
Sectors[lba].Sector.Read(buffer, offset);
}
//main API to read a 2048-byte LBA from a disc.
//this starts at the beginning of the disc (at the lead-in)
//so add 150 to get to get an address in the user data area
/// <summary>
/// Returns a SectorEntry from which you can retrieve various interesting pieces of information about the sector.
/// The SectorEntry's interface is not likely to be stable, though, but it may be more convenient.
/// </summary>
public SectorEntry ReadSectorEntry(int lba)
{
return Sectors[lba];
}
/// <summary>
/// Reads the specified sector's subcode (96 bytes) deinterleaved into the provided buffer.
/// P is first 12 bytes, followed by 12 Q bytes, etc.
/// I'm not sure what format scsi commands generally return it in.
/// It could be this, or RAW (interleaved) which I could also supply when we need it
/// </summary>
public void ReadSector_Subcode_Deinterleaved(int lba, byte[] buffer, int offset)
{
Array.Clear(buffer, offset, 96);
Sectors[lba].Read_SubchannelQ(buffer, offset + 12);
}
/// <summary>
/// Reads the specified sector's subchannel Q (12 bytes) into the provided buffer
/// </summary>
public void ReadSector_Subchannel_Q(int lba, byte[] buffer, int offset)
{
Sectors[lba].Read_SubchannelQ(buffer, offset);
}
/// <summary>
/// Main API to read a 2048-byte sector from a disc.
/// This starts at the beginning of the "userdata" area of the disc (track 1, index 0)
/// However, located here is a mandatory pregap of 2 seconds (or more?).
/// so you may need to add 150 depending on how your system addresses things to get to the "start" of the first track. (track 1, index 1)
/// </summary>
public void ReadLBA_2048(int lba, byte[] buffer, int offset)
{
byte[] temp = new byte[2352];
@ -77,12 +113,13 @@ namespace BizHawk.DiscSystem
}
/// <summary>
/// main API to determine how many LBA sectors are available
/// Main API to determine how many sectors are available on the disc.
/// This counts from absolute sector 0 to the final sector available.
/// </summary>
public int LBACount { get { return Sectors.Count; } }
/// <summary>
/// indicates whether this disc took significant work to load from the disc (i.e. decoding of ECM or audio data)
/// indicates whether this disc took significant work to load from the hard drive (i.e. decoding of ECM or audio data)
/// In this case, the user may appreciate a prompt to export the disc so that it won't take so long next time.
/// </summary>
public bool WasSlowLoad { get; private set; }
@ -96,7 +133,7 @@ namespace BizHawk.DiscSystem
}
// converts LBA to minute:second:frame format.
//TODO - somewhat redundant with CueTimestamp, which is due for refactoring into something not cue-related
//TODO - somewhat redundant with Timestamp, which is due for refactoring into something not cue-related
public static void ConvertLBAtoMSF(int lba, out byte m, out byte s, out byte f)
{
m = (byte) (lba / 75 / 60);

View File

@ -262,6 +262,49 @@ namespace BizHawk.DiscSystem
{
public SectorEntry(ISector sec) { this.Sector = sec; }
public ISector Sector;
//todo - add some PARAMETER fields to this, so that the ISector can use them (so that each ISector doesnt have to be constructed also)
//also then, maybe this could be a struct
//q-subchannel stuff. can be returned directly, or built into the entire subcode sector if you want
/// <summary>
/// ADR and CONTROL
/// </summary>
public byte q_status;
/// <summary>
/// BCD indications of the current track number and index
/// </summary>
public BCD2 q_tno, q_index;
/// <summary>
/// track-relative timestamp
/// </summary>
public BCD2 q_min, q_sec, q_frame;
/// <summary>
/// absolute timestamp
/// </summary>
public BCD2 q_amin, q_asec, q_aframe;
public void Read_SubchannelQ(byte[] buffer, int offset)
{
buffer[offset + 0] = q_status;
buffer[offset + 1] = q_tno.BCDValue;
buffer[offset + 2] = q_index.BCDValue;
buffer[offset + 3] = q_min.BCDValue;
buffer[offset + 4] = q_sec.BCDValue;
buffer[offset + 5] = q_frame.BCDValue;
buffer[offset + 6] = 0;
buffer[offset + 7] = q_amin.BCDValue;
buffer[offset + 8] = q_asec.BCDValue;
buffer[offset + 9] = q_aframe.BCDValue;
ushort crc16 = CRC16_CCITT.Calculate(buffer, 0, 10);
//CRC is stored inverted and big endian
buffer[offset + 10] = (byte)(~(crc16 >> 8));
buffer[offset + 11] = (byte)(~(crc16));
}
}
public List<IBlob> Blobs = new List<IBlob>();
@ -324,7 +367,8 @@ namespace BizHawk.DiscSystem
ret.cue = string.Format("FILE \"{0}\" BINARY\n", bfd.name) + cue;
ret.bins.Add(bfd);
bfd.SectorSize = 2352;
//skip the lead-in!
//skip the mandatory track 1 pregap! cue+bin files do not contain it
for (int i = 150; i < TOC.length_lba; i++)
{
bfd.lbas.Add(i);
@ -345,7 +389,7 @@ namespace BizHawk.DiscSystem
ret.bins.Add(bfd);
int lba=0;
//skip leadin
//skip the mandatory track 1 pregap! cue+bin files do not contain it
if (i == 0) lba = 150;
for (; lba < track.length_lba; lba++)
@ -360,19 +404,19 @@ namespace BizHawk.DiscSystem
foreach (var index in track.Indexes)
{
int x = index.lba - track.Indexes[0].lba;
if (prefs.OmitRedundantIndex0 && index.num == 0 && index.lba == track.Indexes[1].lba)
{
//dont emit index 0 when it is the same as index 1, it confuses some cue parsers
}
else if (i==0 && index.num == 0)
{
//don't generate the first index, it is illogical
}
else
//if (prefs.OmitRedundantIndex0 && index.num == 0 && index.lba == track.Indexes[1].lba)
//{
// //dont emit index 0 when it is the same as index 1, it confuses some cue parsers
//}
//else if (i==0 && index.num == 0)
//{
// //don't generate the first index, it is illogical
//}
//else
{
//track 1 included the lead-in at the beginning of it. sneak past that.
if (i == 0) x -= 150;
sbCue.AppendFormat(" INDEX {0:D2} {1}\n", index.num, new Cue.CueTimestamp(x).Value);
//if (i == 0) x -= 150;
sbCue.AppendFormat(" INDEX {0:D2} {1}\n", index.num, new Timestamp(x).Value);
}
}
}
@ -398,6 +442,8 @@ namespace BizHawk.DiscSystem
{
var ret = new Disc();
ret.FromCuePathInternal(cuePath);
ret.TOC.GeneratePoints();
ret.PopulateQSubchannel();
return ret;
}
@ -405,8 +451,155 @@ namespace BizHawk.DiscSystem
{
var ret = new Disc();
ret.FromIsoPathInternal(isoPath);
ret.TOC.GeneratePoints();
ret.PopulateQSubchannel();
return ret;
}
/// <summary>
/// creates subchannel Q data track for this disc
/// </summary>
void PopulateQSubchannel()
{
int lba = 0;
int dpIndex = 0;
while (lba < Sectors.Count)
{
if (dpIndex < TOC.Points.Count - 1)
{
if (lba >= TOC.Points[dpIndex + 1].LBA)
{
dpIndex++;
}
}
var dp = TOC.Points[dpIndex];
var se = Sectors[lba];
int control = 0;
//choose a control byte depending on whether this is an audio or data track
if(dp.Track.TrackType == ETrackType.Audio)
control = (int)Q_Control.StereoNoPreEmph;
else control = (int)Q_Control.DataUninterrupted;
//we always use ADR=1 (mode-1 q block)
//this could be more sophisticated but it is almost useless for emulation (only useful for catalog/ISRC numbers)
int adr = 1;
se.q_status = (byte)(adr | (control << 4));
se.q_tno = BCD2.FromDecimal(dp.TrackNum);
se.q_index = BCD2.FromDecimal(dp.IndexNum);
int track_relative_lba = lba - dp.Track.Indexes[1].lba;
track_relative_lba = Math.Abs(track_relative_lba);
Timestamp track_relative_timestamp = new Timestamp(track_relative_lba);
se.q_min = BCD2.FromDecimal(track_relative_timestamp.MIN);
se.q_sec = BCD2.FromDecimal(track_relative_timestamp.SEC);
se.q_frame = BCD2.FromDecimal(track_relative_timestamp.FRAC);
Timestamp absolute_timestamp = new Timestamp(lba);
se.q_amin = BCD2.FromDecimal(absolute_timestamp.MIN);
se.q_asec = BCD2.FromDecimal(absolute_timestamp.SEC);
se.q_aframe = BCD2.FromDecimal(absolute_timestamp.FRAC);
lba++;
}
}
static byte IntToBCD(int n)
{
int ones;
int tens = Math.DivRem(n,10,out ones);
return (byte)((tens<<4)|ones);
}
private enum Q_Control
{
StereoNoPreEmph = 0,
StereoPreEmph = 1,
MonoNoPreemph = 8,
MonoPreEmph = 9,
DataUninterrupted = 4,
DataIncremental = 5,
CopyProhibitedMask = 0,
CopyPermittedMask = 2,
}
}
/// <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)
{
BCD2 ret = new BCD2();
ret.DecimalValue = d;
return ret;
}
static byte IntToBCD(int n)
{
int ones;
int tens = Math.DivRem(n, 10, out ones);
return (byte)((tens << 4) | ones);
}
}
public class Timestamp
{
/// <summary>
/// creates timestamp of 00:00:00
/// </summary>
public Timestamp()
{
Value = "00:00:00";
}
/// <summary>
/// creates a timestamp from a string in the form mm:ss:ff
/// </summary>
public Timestamp(string value)
{
this.Value = value;
MIN = int.Parse(value.Substring(0, 2));
SEC = int.Parse(value.Substring(3, 2));
FRAC = int.Parse(value.Substring(6, 2));
LBA = MIN * 60 * 75 + SEC * 75 + FRAC;
}
public readonly string Value;
public readonly int MIN, SEC, FRAC, LBA;
/// <summary>
/// creates timestamp from supplied LBA
/// </summary>
public Timestamp(int LBA)
{
this.LBA = LBA;
MIN = LBA / (60 * 75);
SEC = (LBA / 75) % 60;
FRAC = LBA % 75;
Value = string.Format("{0:D2}:{1:D2}:{2:D2}", MIN, SEC, FRAC);
}
}
public enum ETrackType
@ -435,6 +628,11 @@ namespace BizHawk.DiscSystem
/// </summary>
public bool ReallyDumpBin;
/// <summary>
/// dump a .sub.q along with bins. one day we'll want to dump the entire subcode but really Q is all thats important for debugging most things
/// </summary>
public bool DumpSubchannelQ;
/// <summary>
/// generate remarks and other annotations to help humans understand whats going on, but which will confuse many cue parsers
/// </summary>
@ -455,11 +653,13 @@ namespace BizHawk.DiscSystem
/// </summary>
public bool SingleSession;
/// <summary>
/// some cue parsers can't handle redundant Index 0 (equal to Index 1). Such as daemon tools. So, hide those indices.
/// Our canonical format craves explicitness so this is defaulted off.
/// </summary>
public bool OmitRedundantIndex0 = false;
//THIS IS WRONG-HEADED. track 1 index 0 must never equal index 1! apparently.
///// <summary>
///// some cue parsers can't handle redundant Index 0 (equal to Index 1). Such as daemon tools. So, hide those indices.
///// Our canonical format craves explicitness so this is defaulted off.
///// </summary>
//public bool OmitRedundantIndex0 = false;
/// <summary>
/// DO NOT CHANGE THIS! All sectors will be written with ECM data. It's a waste of space, but it is exact. (not completely supported yet)
@ -482,6 +682,8 @@ namespace BizHawk.DiscSystem
{
public string name;
public List<int> lbas = new List<int>();
//todo - do we really need this? i dont think so...
public List<bool> lba_zeros = new List<bool>();
public int SectorSize;
}
@ -507,8 +709,8 @@ namespace BizHawk.DiscSystem
string sha1 = Util.Hash_SHA1(dump, 0, dump.Length);
int pregap = track.Indexes[1].lba - track.Indexes[0].lba;
Cue.CueTimestamp pregap_ts = new Cue.CueTimestamp(pregap);
Cue.CueTimestamp len_ts = new Cue.CueTimestamp(track.length_lba);
Timestamp pregap_ts = new Timestamp(pregap);
Timestamp len_ts = new Timestamp(track.length_lba);
sb.AppendFormat("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\t{7}\t{8}\n",
i,
Cue.RedumpTypeStringForTrackType(track.TrackType),
@ -532,6 +734,7 @@ namespace BizHawk.DiscSystem
public void Dump(string directory, CueBinPrefs prefs, ProgressReport progress)
{
byte[] subQ_temp = new byte[12];
progress.TaskCount = 2;
progress.Message = "Generating Cue";
@ -545,37 +748,56 @@ namespace BizHawk.DiscSystem
progress.TaskCurrent = 1;
progress.ProgressEstimate = bins.Sum((bfd) => bfd.lbas.Count);
progress.ProgressCurrent = 0;
if(prefs.ReallyDumpBin)
foreach (var bfd in bins)
{
int sectorSize = bfd.SectorSize;
byte[] temp = new byte[2352];
byte[] empty = new byte[2352];
string trackBinFile = bfd.name;
string trackBinPath = Path.Combine(directory, trackBinFile);
using (FileStream fs = new FileStream(trackBinPath, FileMode.Create, FileAccess.Write, FileShare.None))
{
for(int i=0;i<bfd.lbas.Count;i++)
{
if (progress.CancelSignal) return;
if(!prefs.ReallyDumpBin) return;
progress.ProgressCurrent++;
int lba = bfd.lbas[i];
if (bfd.lba_zeros[i])
foreach (var bfd in bins)
{
int sectorSize = bfd.SectorSize;
byte[] temp = new byte[2352];
byte[] empty = new byte[2352];
string trackBinFile = bfd.name;
string trackBinPath = Path.Combine(directory, trackBinFile);
string subQPath = Path.ChangeExtension(trackBinPath, ".sub.q");
FileStream fsSubQ = null;
FileStream fs = new FileStream(trackBinPath, FileMode.Create, FileAccess.Write, FileShare.None);
try
{
if (prefs.DumpSubchannelQ)
fsSubQ = new FileStream(subQPath, FileMode.Create, FileAccess.Write, FileShare.None);
for (int i = 0; i < bfd.lbas.Count; i++)
{
if (progress.CancelSignal) return;
progress.ProgressCurrent++;
int lba = bfd.lbas[i];
if (bfd.lba_zeros[i])
{
fs.Write(empty, 0, sectorSize);
}
else
{
if (sectorSize == 2352)
disc.ReadLBA_2352(lba, temp, 0);
else if (sectorSize == 2048) disc.ReadLBA_2048(lba, temp, 0);
else throw new InvalidOperationException();
fs.Write(temp, 0, sectorSize);
//write subQ if necessary
if (fsSubQ != null)
{
fs.Write(empty, 0, sectorSize);
}
else
{
if (sectorSize == 2352)
disc.ReadLBA_2352(lba, temp, 0);
else if (sectorSize == 2048) disc.ReadLBA_2048(lba, temp, 0);
else throw new InvalidOperationException();
fs.Write(temp, 0, sectorSize);
disc.Sectors[lba].Read_SubchannelQ(subQ_temp, 0);
fsSubQ.Write(subQ_temp, 0, 12);
}
}
}
}
finally
{
fs.Dispose();
if (fsSubQ != null) fsSubQ.Dispose();
}
}
}
}
}

View File

@ -9,6 +9,88 @@ namespace BizHawk.DiscSystem
public class DiscTOC
{
/// <summary>
/// Sessions contained in the disc. Right now support for anything other than 1 session is totally not working
/// </summary>
public List<Session> Sessions = new List<Session>();
/// <summary>
/// this is an unfinished concept of "TOC Points" which is sometimes more convenient way for organizing the disc contents
/// </summary>
public List<TOCPoint> Points = new List<TOCPoint>();
/// <summary>
/// Todo - comment about what this actually means
/// </summary>
public int length_lba;
/// <summary>
/// todo - comment about what this actually means
/// </summary>
public Timestamp FriendlyLength { get { return new Timestamp(length_lba); } }
/// <summary>
/// seeks the point immediately before (or equal to) this LBA
/// </summary>
public TOCPoint SeekPoint(int lba)
{
for(int i=0;i<Points.Count;i++)
{
TOCPoint tp = Points[i];
if (tp.LBA > lba)
return Points[i - 1];
}
return Points[Points.Count - 1];
}
public long BinarySize
{
get { return length_lba * 2352; }
}
/// <summary>
///
/// </summary>
public class TOCPoint
{
public int Num;
public int LBA, TrackNum, IndexNum;
public Track Track;
}
/// <summary>
/// Generates the Points list from the current TOC
/// </summary>
public void GeneratePoints()
{
int num = 0;
Points.Clear();
foreach (var ses in Sessions)
{
foreach (var track in ses.Tracks)
foreach (var index in track.Indexes)
{
var tp = new TOCPoint();
tp.Num = num++;
tp.LBA = index.lba;
tp.TrackNum = track.num;
tp.IndexNum = index.num;
tp.Track = track;
Points.Add(tp);
}
var tpLeadout = new TOCPoint();
var lastTrack = ses.Tracks[ses.Tracks.Count - 1];
tpLeadout.Num = num++;
tpLeadout.LBA = lastTrack.Indexes[1].lba + lastTrack.length_lba;
tpLeadout.IndexNum = 0;
tpLeadout.TrackNum = 100;
tpLeadout.Track = null; //no leadout track.. now... or ever?
Points.Add(tpLeadout);
}
}
public class Session
{
public int num;
@ -16,7 +98,7 @@ namespace BizHawk.DiscSystem
//the length of the session (should be the sum of all track lengths)
public int length_lba;
public Cue.CueTimestamp FriendlyLength { get { return new Cue.CueTimestamp(length_lba); } }
public Timestamp FriendlyLength { get { return new Timestamp(length_lba); } }
}
public class Track
@ -31,7 +113,7 @@ namespace BizHawk.DiscSystem
/// the time before track 1 index 1 is the lead-in and isn't accounted for in any track...
/// </summary>
public int length_lba;
public Cue.CueTimestamp FriendlyLength { get { return new Cue.CueTimestamp(length_lba); } }
public Timestamp FriendlyLength { get { return new Timestamp(length_lba); } }
}
public class Index
@ -43,7 +125,7 @@ namespace BizHawk.DiscSystem
//HEY! This is commented out because it is a bad idea.
//The length of a section is almost useless, and if you want it, you are probably making an error.
//public int length_lba;
//public Cue.CueTimestamp FriendlyLength { get { return new Cue.CueTimestamp(length_lba); } }
//public Cue.Timestamp FriendlyLength { get { return new Cue.Timestamp(length_lba); } }
}
public string GenerateCUE_OneBin(CueBinPrefs prefs)
@ -53,7 +135,6 @@ namespace BizHawk.DiscSystem
//this generates a single-file cue!!!!!!! dont expect it to generate bin-per-track!
StringBuilder sb = new StringBuilder();
bool leadin = true;
foreach (var session in Sessions)
{
if (!prefs.SingleSession)
@ -66,6 +147,7 @@ namespace BizHawk.DiscSystem
foreach (var track in session.Tracks)
{
ETrackType trackType = track.TrackType;
//mutate track type according to our principle of canonicalization
if (trackType == ETrackType.Mode1_2048 && prefs.DumpECM)
trackType = ETrackType.Mode1_2352;
@ -74,26 +156,17 @@ namespace BizHawk.DiscSystem
else sb.AppendFormat(" TRACK {0:D2} {1}\n", track.num, Cue.TrackTypeStringForTrackType(trackType));
foreach (var index in track.Indexes)
{
if (prefs.OmitRedundantIndex0 && index.num == 0 && index.lba == track.Indexes[1].lba)
//cue+bin has an implicit 150 sector pregap which neither the cue nor the bin has any awareness of
//except for the baked-in sector addressing.
//but, if there is an extra-long pregap, we want to reflect it this way
int lba = index.lba - 150;
if (lba <= 0 && index.num == 0 && track.num == 1)
{
//dont emit index 0 when it is the same as index 1. it confuses daemon tools.
//(make this an option?)
}
else if (leadin)
{
//don't generate the first index, it is illogical
}
else
{
//subtract leadin. CUE format seems to always imply this exact amount.
//however, physical discs could possibly have a slightly longer lead-in.
//this could be done with a pregap on track 1 perhaps..
//would we handle it here correctly? i think so
int lba = index.lba - 150;
sb.AppendFormat(" INDEX {0:D2} {1}\n", index.num, new Cue.CueTimestamp(lba).Value);
sb.AppendFormat(" INDEX {0:D2} {1}\n", index.num, new Timestamp(lba).Value);
}
leadin = false;
}
}
}
@ -101,14 +174,6 @@ namespace BizHawk.DiscSystem
return sb.ToString();
}
public List<Session> Sessions = new List<Session>();
public int length_lba;
public Cue.CueTimestamp FriendlyLength { get { return new Cue.CueTimestamp(length_lba); } }
public long BinarySize
{
get { return length_lba*2352; }
}
public void AnalyzeLengthsFromIndexLengths()
{

View File

@ -9,111 +9,66 @@ using System.Collections.Generic;
namespace BizHawk.DiscSystem
{
public class SubcodeStream
//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
{
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); }
}
private static ushort[] table = new ushort[256];
long Position { get; set; }
int cached_addr = -1;
SubcodePacketDecoder cached_decoder = null;
byte[] cached_buffer = new byte[24];
public int ReadByte()
static CRC16_CCITT()
{
int subcode_addr = (int)Position;
int subcode_byte = subcode_addr & 1;
subcode_addr /= 2;
subcode_addr *= 24;
if (subcode_addr != cached_addr)
ushort value;
ushort temp;
for (ushort i = 0; i < 256; ++i)
{
cached_decoder.Reset();
source.Position = offset + subcode_addr;
if (source.Read(cached_buffer, 0, 24) != 24)
return -1;
cached_addr = subcode_addr;
value = 0;
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<length;i++)
{
byte b = data[offset + i];
int index = (b ^ ((Result >> 8) & 0xFF));
Result = (ushort)((Result << 8) ^ table[index]);
}
return Result;
}
}
public class SubcodeDataDecoder
{
public static void Unpack_Q(byte[] output, int out_ofs, byte[] input, int in_ofs)
{
for (int i = 0; i < 12; i++)
output[out_ofs + i] = 0;
for (int i = 0; i < 96; i++)
{
int bytenum = i >> 3;
int bitnum = i & 7;
bitnum = 7 - bitnum;
int bitval = (byte)((input[in_ofs + i] >> 6) & 1);
bitval <<= bitnum;
output[out_ofs + bytenum] |= (byte)bitval;
}
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;
}
}
}

View File

@ -51,6 +51,7 @@ namespace BizHawk.Emulation.Sound
if (track < 1 || track > Disc.TOC.Sessions[0].Tracks.Count)
return;
//note for vecna: you may find that the new "Point" and "SeekPoint" concept in the TOC is more useful than this kind of logic. just something to think about
StartLBA = Disc.TOC.Sessions[0].Tracks[track - 1].Indexes[1].lba;
EndLBA = StartLBA + Disc.TOC.Sessions[0].Tracks[track - 1].length_lba;
PlayingTrack = track;
@ -65,6 +66,7 @@ namespace BizHawk.Emulation.Sound
var tracks = Disc.TOC.Sessions[0].Tracks;
bool foundTrack = false;
//note for vecna: you may find that the new "Point" and "SeekPoint" concept in the TOC is more useful than this kind of logic. just something to think about
for (track = 0; track < tracks.Count; track++)
{
int trackStart = tracks[track].Indexes[0].lba;

View File

@ -271,7 +271,7 @@ namespace BizHawk.MultiClient
private void BrowseFolder(TextBox box, string Name, string System)
{
FolderBrowserDialog f = new FolderBrowserDialog();
FolderBrowserEx f = new FolderBrowserEx();
f.Description = "Set the directory for " + Name;
f.SelectedPath = PathManager.MakeAbsolutePath(box.Text, System);
DialogResult result = f.ShowDialog();

View File

@ -85,6 +85,9 @@
<Compile Include="7z\SevenZipExtractorAsynchronous.cs" />
<Compile Include="7z\SevenZipSfx.cs" />
<Compile Include="7z\StreamWrappers.cs" />
<Compile Include="FolderBrowserDialogEx.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="InputConfigBase.cs">
<SubType>Form</SubType>
</Compile>

View File

@ -0,0 +1,312 @@
using System.Runtime.InteropServices;
using System.Text;
using System;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;
using System.Security.Permissions;
namespace BizHawk
{
/// <summary>
/// Component wrapping access to the Browse For Folder common dialog box.
/// Call the ShowDialog() method to bring the dialog box up.
/// </summary>
public sealed class FolderBrowserEx : Component
{
private static readonly int MAX_PATH = 260;
// Root node of the tree view.
private FolderID startLocation = FolderID.Desktop;
// Browse info options.
private int publicOptions = (int) Win32API.Shell32.BffStyles.RestrictToFilesystem |
(int) Win32API.Shell32.BffStyles.RestrictToDomain;
private int privateOptions = (int)(Win32API.Shell32.BffStyles.NewDialogStyle | Win32API.Shell32.BffStyles.ShowTextBox);
// Description text to show.
public string Description = "Please select a folder below:";
// Folder chosen by the user.
private string directoryPath = String.Empty;
/// <summary>
/// Enum of CSIDLs identifying standard shell folders.
/// </summary>
public enum FolderID
{
Desktop = 0x0000,
Printers = 0x0004,
MyDocuments = 0x0005,
Favorites = 0x0006,
Recent = 0x0008,
SendTo = 0x0009,
StartMenu = 0x000b,
MyComputer = 0x0011,
NetworkNeighborhood = 0x0012,
Templates = 0x0015,
MyPictures = 0x0027,
NetAndDialUpConnections = 0x0031,
}
/// <summary>
/// Helper function that returns the IMalloc interface used by the shell.
/// </summary>
private static Win32API.IMalloc GetSHMalloc()
{
Win32API.IMalloc malloc;
Win32API.Shell32.SHGetMalloc(out malloc);
return malloc;
}
/// <summary>
/// Shows the folder browser dialog box.
/// </summary>
public DialogResult ShowDialog()
{
return ShowDialog(null);
}
private int callback(IntPtr hwnd, uint uMsg, IntPtr lParam, IntPtr lpData)
{
switch (uMsg)
{
case 1:
{
IntPtr str = Marshal.StringToHGlobalUni(SelectedPath);
Win32.SendMessage(hwnd, (0x400 + 103), 1, str.ToInt32());
Marshal.FreeHGlobal(str);
break;
}
}
return 0;
}
/// <summary>
/// Shows the folder browser dialog box with the specified owner window.
/// </summary>
public DialogResult ShowDialog(IWin32Window owner)
{
IntPtr pidlRoot = IntPtr.Zero;
// Get/find an owner HWND for this dialog.
IntPtr hWndOwner;
if (owner != null)
{
hWndOwner = owner.Handle;
}
else
{
hWndOwner = Win32API.GetActiveWindow();
}
// Get the IDL for the specific startLocation.
Win32API.Shell32.SHGetSpecialFolderLocation(hWndOwner, (int) startLocation, out pidlRoot);
if (pidlRoot == IntPtr.Zero)
{
return DialogResult.Cancel;
}
int mergedOptions = (int) publicOptions | (int) privateOptions;
if ((mergedOptions & (int) Win32API.Shell32.BffStyles.NewDialogStyle) != 0)
{
if (System.Threading.ApartmentState.MTA == Application.OleRequired())
mergedOptions = mergedOptions & (~(int) Win32API.Shell32.BffStyles.NewDialogStyle);
}
IntPtr pidlRet = IntPtr.Zero;
try
{
// Construct a BROWSEINFO.
Win32API.Shell32.BROWSEINFO bi = new Win32API.Shell32.BROWSEINFO();
IntPtr buffer = Marshal.AllocHGlobal(MAX_PATH);
bi.pidlRoot = pidlRoot;
bi.hwndOwner = hWndOwner;
bi.pszDisplayName = buffer;
bi.lpszTitle = Description;
bi.ulFlags = mergedOptions;
bi.lpfn = new Win32API.Shell32.BFFCALLBACK(callback);
// The rest of the fields are initialized to zero by the constructor.
// bi.lParam = IntPtr.Zero; bi.iImage = 0;
// Show the dialog.
pidlRet = Win32API.Shell32.SHBrowseForFolder(ref bi);
// Free the buffer you've allocated on the global heap.
Marshal.FreeHGlobal(buffer);
if (pidlRet == IntPtr.Zero)
{
// User clicked Cancel.
return DialogResult.Cancel;
}
// Then retrieve the path from the IDList.
StringBuilder sb = new StringBuilder(MAX_PATH);
if (0 == Win32API.Shell32.SHGetPathFromIDList(pidlRet, sb))
{
return DialogResult.Cancel;
}
// Convert to a string.
directoryPath = sb.ToString();
}
finally
{
Win32API.IMalloc malloc = GetSHMalloc();
malloc.Free(pidlRoot);
if (pidlRet != IntPtr.Zero)
{
malloc.Free(pidlRet);
}
}
return DialogResult.OK;
}
/// <summary>
/// Helper function used to set and reset bits in the publicOptions bitfield.
/// </summary>
private void SetOptionField(int mask, bool turnOn)
{
if (turnOn)
publicOptions |= mask;
else
publicOptions &= ~mask;
}
/// <summary>
/// Only return file system directories. If the user selects folders
/// that are not part of the file system, the OK button is unavailable.
/// </summary>
[Category("Navigation")]
[Description("Only return file system directories. If the user selects folders " +
"that are not part of the file system, the OK button is unavailable.")]
[DefaultValue(true)]
public bool OnlyFilesystem
{
get { return (publicOptions & (int) Win32API.Shell32.BffStyles.RestrictToFilesystem) != 0; }
set { SetOptionField((int) Win32API.Shell32.BffStyles.RestrictToFilesystem, value); }
}
/// <summary>
/// Location of the root folder from which to start browsing. Only the specified
/// folder and any folders beneath it in the namespace hierarchy appear
/// in the dialog box.
/// </summary>
[Category("Navigation")]
[Description("Location of the root folder from which to start browsing. Only the specified " +
"folder and any folders beneath it in the namespace hierarchy appear " +
"in the dialog box.")]
[DefaultValue(typeof (FolderID), "0")]
public FolderID StartLocation
{
get { return startLocation; }
set
{
new UIPermission(UIPermissionWindow.AllWindows).Demand();
startLocation = value;
}
}
public string SelectedPath;
}
internal class Win32API
{
// C# representation of the IMalloc interface.
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("00000002-0000-0000-C000-000000000046")]
public interface IMalloc
{
[PreserveSig]
IntPtr Alloc([In] int cb);
[PreserveSig]
IntPtr Realloc([In] IntPtr pv, [In] int cb);
[PreserveSig]
void Free([In] IntPtr pv);
[PreserveSig]
int GetSize([In] IntPtr pv);
[PreserveSig]
int DidAlloc(IntPtr pv);
[PreserveSig]
void HeapMinimize();
}
[DllImport("User32.DLL")]
public static extern IntPtr GetActiveWindow();
public class Shell32
{
// Styles used in the BROWSEINFO.ulFlags field.
[Flags]
public enum BffStyles
{
RestrictToFilesystem = 0x0001, // BIF_RETURNONLYFSDIRS
RestrictToDomain = 0x0002, // BIF_DONTGOBELOWDOMAIN
RestrictToSubfolders = 0x0008, // BIF_RETURNFSANCESTORS
ShowTextBox = 0x0010, // BIF_EDITBOX
ValidateSelection = 0x0020, // BIF_VALIDATE
NewDialogStyle = 0x0040, // BIF_NEWDIALOGSTYLE
BrowseForComputer = 0x1000, // BIF_BROWSEFORCOMPUTER
BrowseForPrinter = 0x2000, // BIF_BROWSEFORPRINTER
BrowseForEverything = 0x4000, // BIF_BROWSEINCLUDEFILES
}
// Delegate type used in BROWSEINFO.lpfn field.
public delegate int BFFCALLBACK(IntPtr hwnd, uint uMsg, IntPtr lParam, IntPtr lpData);
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct BROWSEINFO
{
public IntPtr hwndOwner;
public IntPtr pidlRoot;
public IntPtr pszDisplayName;
[MarshalAs(UnmanagedType.LPTStr)] public string lpszTitle;
public int ulFlags;
[MarshalAs(UnmanagedType.FunctionPtr)] public BFFCALLBACK lpfn;
public IntPtr lParam;
public int iImage;
}
[DllImport("Shell32.DLL")]
public static extern int SHGetMalloc(out IMalloc ppMalloc);
[DllImport("Shell32.DLL")]
public static extern int SHGetSpecialFolderLocation(
IntPtr hwndOwner, int nFolder, out IntPtr ppidl);
[DllImport("Shell32.DLL")]
public static extern int SHGetPathFromIDList(
IntPtr pidl, StringBuilder Path);
[DllImport("Shell32.DLL", CharSet = CharSet.Auto)]
public static extern IntPtr SHBrowseForFolder(ref BROWSEINFO bi);
}
}
}

View File

@ -244,11 +244,6 @@ namespace BizHawk
LVIS_STATEIMAGEMASK = 0xF000,
}
internal struct WindowsFunction {
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
}
#endregion
@ -425,7 +420,7 @@ namespace BizHawk
Marshal.StructureToPtr(stateItem, ptrItem, true);
// Send the message to the control window.
int result = WindowsFunction.SendMessage(
int result = Win32.SendMessage(
this.Handle,
(int)ListViewMessages.LVM_SETITEMSTATE,
index,
@ -444,7 +439,7 @@ namespace BizHawk
private void SetVirtualItemCount() {
int result;
result = WindowsFunction.SendMessage(
result = Win32.SendMessage(
this.Handle,
(int)ListViewMessages.LVM_SETITEMCOUNT,
itemCount,
@ -612,13 +607,13 @@ namespace BizHawk
lvhti.pt.x = x;
lvhti.pt.y = y;
Marshal.StructureToPtr(lvhti, ptrlvhti, true);
int z = WindowsFunction.SendMessage(this.Handle, (int)ListViewMessages.LVM_HITTEST, 0, ptrlvhti.ToInt32());
int z = Win32.SendMessage(this.Handle, (int)ListViewMessages.LVM_HITTEST, 0, ptrlvhti.ToInt32());
Marshal.PtrToStructure(ptrlvhti, lvhti);
return z;
}
public void ensureVisible(int index) {
WindowsFunction.SendMessage(Handle, (int)ListViewMessages.LVM_ENSUREVISIBLE, index, 1);
Win32.SendMessage(Handle, (int)ListViewMessages.LVM_ENSUREVISIBLE, index, 1);
}
public void ensureVisible() {

View File

@ -430,6 +430,10 @@ namespace BizHawk
[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int SendMessage(IntPtr hWnd, int msg, int wParam, int lParam);
}
}

View File

@ -86,21 +86,31 @@ namespace BizHawk
//Console.WriteLine(disc.ReadTOC().DebugPrint());
//disc.DumpBin_2352("d:\\test.2352");
////test reading the subcode data. unfortunately we don't have lead-in subcode so we have no TOC
//using (FileStream fs = File.OpenRead("c:\\bof4.sub"))
//test reading the subcode data. unfortunately we don't have lead-in subcode so we have no TOC
//using (FileStream fs = File.OpenRead(@"D:\programs\cdrdao\awakening.bin"))
//{
// Disc.SubcodeStream stream = new Disc.SubcodeStream(fs, 0);
// stream.Channel = 'q';
// using (FileStream fsOut = File.OpenWrite("c:\\bof4.sub.q"))
// using (FileStream fsOut = File.OpenWrite(@"D:\programs\cdrdao\awakening.sub.q"))
// //using (FileStream fsOut = File.OpenWrite(@"D:\programs\cdrdao\data.sub.q"))
// {
// for (; ; )
// int numSectors = (int)fs.Length / (2352 + 96);
// for (int i = 0; i < numSectors; i++)
// {
// int ret = stream.ReadByte();
// if (ret == -1) break;
// fsOut.WriteByte((byte)ret);
// fs.Position = i * (2352 + 96) + 2352;
// byte[] tempout = new byte[12];
// byte[] tempin = new byte[96];
// fs.Read(tempin, 0, 96);
// DiscSystem.SubcodeDataDecoder.Unpack_Q(tempout, 0, tempin, 0);
// fsOut.Write(tempout, 0, 12);
// }
// //for (; ; )
// //{
// // int ret = stream.ReadByte();
// // if (ret == -1) break;
// // fsOut.WriteByte((byte)ret);
// //}
// }
//}
//} return;
//DiscSystem.Disc disc = DiscSystem.Disc.FromCuePath(@"D:\discs\Bomberman_'94_Taikenban_(SCD)(JPN)_-_wav'd\Bomberman '94 Taikenban (SCD)(JPN)_hawked.cue");
//DiscSystem.Disc disc = DiscSystem.Disc.FromCuePath(@"D:\discs\Bomberman_'94_Taikenban_(SCD)(JPN)_-_wav'd\Bomberman '94 Taikenban (SCD)(JPN).cue");
@ -114,16 +124,17 @@ namespace BizHawk
//var cueBin = disc.DumpCueBin("Bomberman '94 Taikenban (SCD)(JPN)_hawked", prefs);
//cueBin.Dump(@"D:\discs\Bomberman_'94_Taikenban_(SCD)(JPN)_-_wav'd", prefs);
DiscSystem.Disc disc = DiscSystem.Disc.FromCuePath(@"D:\discs\Angels II - Holy Night (J)[Prototype]\Angels II - Holy Night (J)[Prototype].cue");
DiscSystem.Disc disc = DiscSystem.Disc.FromCuePath(@"D:\programs\cdrdao\eac-ripped\Awakening.cue");
var prefs = new DiscSystem.CueBinPrefs();
prefs.AnnotateCue = false;
prefs.OneBlobPerTrack = false;
prefs.ReallyDumpBin = true;
prefs.OmitRedundantIndex0 = true;
prefs.DumpSubchannelQ = true;
//prefs.OmitRedundantIndex0 = true;
prefs.SingleSession = true;
//var cueBin = disc.DumpCueBin("Bomberman '94 Taikenban (SCD)(JPN)_hawked_hawked", prefs);
var cueBin = disc.DumpCueBin("test", prefs);
cueBin.Dump(@"D:\discs\Angels II - Holy Night (J)[Prototype]", prefs);
cueBin.Dump(@"D:\programs\cdrdao\eac-ripped", prefs);
}
}

View File

@ -103,7 +103,7 @@ namespace BizHawk
prefs.AnnotateCue = checkCueProp_Annotations.Checked;
prefs.OneBlobPerTrack = checkCueProp_OneBlobPerTrack.Checked;
prefs.ReallyDumpBin = false;
prefs.OmitRedundantIndex0 = checkCueProp_OmitRedundantIndex0.Checked;
//prefs.OmitRedundantIndex0 = checkCueProp_OmitRedundantIndex0.Checked;
prefs.SingleSession = true;
return prefs;
}