continue cue loading
This commit is contained in:
parent
7deb5e8575
commit
70f97851b3
|
@ -89,6 +89,8 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
/// <summary>
|
||||
/// Main API to determine how many LBAs are available on the disc.
|
||||
/// This counts from LBA 0 to the final sector available.
|
||||
/// THIS IS DUMB. Like everything else here.
|
||||
/// Fetch it from a toc or disc structure
|
||||
/// </summary>
|
||||
public int LBACount { get { return ABACount - 150; } }
|
||||
|
||||
|
|
|
@ -7,6 +7,43 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
{
|
||||
public partial class Disc : IDisposable
|
||||
{
|
||||
|
||||
internal sealed class Blob_ZeroPadAdapter : IBlob
|
||||
{
|
||||
IBlob srcBlob;
|
||||
long srcBlobLength;
|
||||
public Blob_ZeroPadAdapter(IBlob srcBlob, long srcBlobLength)
|
||||
{
|
||||
this.srcBlob = srcBlob;
|
||||
this.srcBlobLength = srcBlobLength;
|
||||
}
|
||||
|
||||
public int Read(long byte_pos, byte[] buffer, int offset, int count)
|
||||
{
|
||||
int todo = count;
|
||||
long end = byte_pos + todo;
|
||||
if (end > srcBlobLength)
|
||||
{
|
||||
long temp = (int)(srcBlobLength - end);
|
||||
if (temp > int.MaxValue)
|
||||
throw new InvalidOperationException();
|
||||
todo = (int)temp;
|
||||
|
||||
//zero-fill the unused part (just for safety's sake)
|
||||
Array.Clear(buffer, offset + todo, count - todo);
|
||||
}
|
||||
|
||||
srcBlob.Read(byte_pos, buffer, offset, todo);
|
||||
|
||||
//since it's zero padded, this never fails and always reads the requested amount
|
||||
return count;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For use with blobs which are prematurely ended: buffers what there is, and zero-pads the rest
|
||||
/// </summary>
|
||||
|
|
|
@ -22,6 +22,10 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
internal class CompiledCueIndex
|
||||
{
|
||||
public int Number;
|
||||
|
||||
/// <summary>
|
||||
/// this is annoying, it should just be an integer
|
||||
/// </summary>
|
||||
public Timestamp FileMSF;
|
||||
|
||||
public override string ToString()
|
||||
|
@ -84,6 +88,12 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
{
|
||||
public int BlobIndex;
|
||||
public int Number;
|
||||
|
||||
/// <summary>
|
||||
/// A track that's final in the file gets its length from the length of the file; other tracks lengths are determined from the succeeding track
|
||||
/// </summary>
|
||||
public bool IsFinalInFile;
|
||||
|
||||
public CompiledCDText CDTextData = new CompiledCDText();
|
||||
public Timestamp PregapLength, PostgapLength;
|
||||
public CueFile.TrackFlags Flags = CueFile.TrackFlags.None;
|
||||
|
@ -150,6 +160,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
CompiledCDText curr_cdtext;
|
||||
int curr_blobIndex = -1;
|
||||
CompiledCueTrack curr_track = null;
|
||||
CompiledCueFile curr_file = null;
|
||||
bool discinfo_session1Format_determined = false;
|
||||
|
||||
void UpdateDiscInfo(CueFile.Command.TRACK trackCommand)
|
||||
|
@ -179,8 +190,22 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
}
|
||||
}
|
||||
|
||||
void AddFile(CueFile.Command.FILE f)
|
||||
void CloseFile()
|
||||
{
|
||||
if (curr_track != null)
|
||||
{
|
||||
//flag this track as the final one in the file
|
||||
curr_track.IsFinalInFile = true;
|
||||
}
|
||||
|
||||
curr_file = null;
|
||||
}
|
||||
|
||||
void OpenFile(CueFile.Command.FILE f)
|
||||
{
|
||||
if (curr_file != null)
|
||||
CloseFile();
|
||||
|
||||
curr_blobIndex++;
|
||||
|
||||
var Resolver = IN_CueFormat.Resolver;
|
||||
|
@ -311,7 +336,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
var index0 = new CompiledCueIndex();
|
||||
var index1 = curr_track.Indexes[0];
|
||||
index0.Number = 0;
|
||||
index0.FileMSF = index1.FileMSF;
|
||||
index0.FileMSF = index1.FileMSF; //same MSF as index 1 will make it effectively nonexistent
|
||||
curr_track.Indexes.Insert(0, index0);
|
||||
}
|
||||
|
||||
|
@ -401,7 +426,8 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
|
||||
if (cmd is CueFile.Command.FILE)
|
||||
{
|
||||
AddFile(cmd as CueFile.Command.FILE);
|
||||
CloseFile();
|
||||
OpenFile(cmd as CueFile.Command.FILE);
|
||||
}
|
||||
|
||||
if (cmd is CueFile.Command.INDEX)
|
||||
|
@ -424,6 +450,9 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
|
||||
}
|
||||
|
||||
//it's a bit odd to close the file before closing the track, but...
|
||||
//we need to be sure to CloseFile first to make sure the track is marked as the final one in the file
|
||||
CloseFile();
|
||||
CloseTrack();
|
||||
|
||||
CreateTrack1Pregap();
|
||||
|
|
|
@ -49,177 +49,35 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
Normal, Pregap, Postgap
|
||||
}
|
||||
|
||||
//current blob file state
|
||||
int file_cfi_index = -1;
|
||||
IBlob file_blob = null;
|
||||
CueFile.Command.FILE file_currentCommand = null;
|
||||
long file_ofs = 0, file_len = 0;
|
||||
int file_msf = -1;
|
||||
|
||||
//current track, flags, and index state
|
||||
CueFile.Command.TRACK track_pendingCommand = null;
|
||||
CueFile.Command.TRACK track_currentCommand = null;
|
||||
CueFile.TrackFlags track_pendingFlags = CueFile.TrackFlags.None;
|
||||
CueFile.TrackFlags track_currentFlags = CueFile.TrackFlags.None;
|
||||
|
||||
//burn state.
|
||||
//TODO - separate burner into another class?
|
||||
BurnType burntype_current;
|
||||
Timestamp burn_pregap_timestamp;
|
||||
|
||||
|
||||
void BeginBurnPregap()
|
||||
class BlobInfo
|
||||
{
|
||||
//TODO?
|
||||
public IBlob Blob;
|
||||
public long Length;
|
||||
}
|
||||
|
||||
void BurnPregap(Timestamp length)
|
||||
//not sure if we need this...
|
||||
class TrackInfo
|
||||
{
|
||||
burntype_current = BurnType.Pregap;
|
||||
burn_pregap_timestamp = length;
|
||||
int length_lba = length.Sector;
|
||||
public int Length;
|
||||
|
||||
//TODO: read [IEC10149] 20, 20.1, & 20.2 to assign pre-gap and post-gap types correctly depending on track number and previous track
|
||||
//ALSO, if the last track is data, we need to make a post-gap
|
||||
//we can grab the previously generated sector in order to figure out how to encode new pregap sectors
|
||||
for(int i=0;i<length_lba;i++)
|
||||
BurnSector();
|
||||
public CompiledCueTrack CompiledCueTrack;
|
||||
}
|
||||
|
||||
void ProcessFile(CueFile.Command.FILE file)
|
||||
{
|
||||
////if we're currently in a file, finish it
|
||||
//if (file_currentCommand != null)
|
||||
// BurnToEOF();
|
||||
List<BlobInfo> BlobInfos;
|
||||
List<TrackInfo> TrackInfos = new List<TrackInfo>();
|
||||
|
||||
////open the new blob
|
||||
//file_currentCommand = file;
|
||||
//file_msf = 0;
|
||||
//var cfi = IN_CompileJob.OUT_FileInfos[++file_cfi_index];
|
||||
|
||||
////mount the file
|
||||
//if (cfi.Type == AnalyzeCueJob.CueFileType.BIN || cfi.Type == AnalyzeCueJob.CueFileType.Unknown)
|
||||
//{
|
||||
// //raw files:
|
||||
// var blob = new Disc.Blob_RawFile { PhysicalPath = cfi.FullPath };
|
||||
// OUT_Disc.DisposableResources.Add(file_blob = blob);
|
||||
// file_len = blob.Length;
|
||||
//}
|
||||
//else if (cfi.Type == AnalyzeCueJob.CueFileType.ECM)
|
||||
//{
|
||||
// var blob = new Disc.Blob_ECM();
|
||||
// OUT_Disc.DisposableResources.Add(file_blob = blob);
|
||||
// blob.Load(cfi.FullPath);
|
||||
// file_len = blob.Length;
|
||||
//}
|
||||
//else if (cfi.Type == AnalyzeCueJob.CueFileType.WAVE)
|
||||
//{
|
||||
// var blob = new Disc.Blob_WaveFile();
|
||||
// OUT_Disc.DisposableResources.Add(file_blob = blob);
|
||||
// blob.Load(cfi.FullPath);
|
||||
// file_len = blob.Length;
|
||||
//}
|
||||
//else if (cfi.Type == AnalyzeCueJob.CueFileType.DecodeAudio)
|
||||
//{
|
||||
// FFMpeg ffmpeg = new FFMpeg();
|
||||
// if (!ffmpeg.QueryServiceAvailable())
|
||||
// {
|
||||
// throw new DiscReferenceException(cfi.FullPath, "No decoding service was available (make sure ffmpeg.exe is available. even though this may be a wav, ffmpeg is used to load oddly formatted wave files. If you object to this, please send us a note and we'll see what we can do. It shouldn't be too hard.)");
|
||||
// }
|
||||
// AudioDecoder dec = new AudioDecoder();
|
||||
// byte[] buf = dec.AcquireWaveData(cfi.FullPath);
|
||||
// var blob = new Disc.Blob_WaveFile();
|
||||
// OUT_Disc.DisposableResources.Add(file_blob = blob);
|
||||
// blob.Load(new MemoryStream(buf));
|
||||
//}
|
||||
}
|
||||
|
||||
void BurnToEOF()
|
||||
{
|
||||
while (file_ofs < file_len)
|
||||
BurnSector();
|
||||
|
||||
//TODO - if a postgap was requested, do it now
|
||||
}
|
||||
|
||||
void ProcessIndex(CueFile.Command.INDEX index)
|
||||
{
|
||||
//burn sectors with the previous registers until we reach the current index MSF
|
||||
int index_file_msf = index.Timestamp.Sector;
|
||||
while (file_msf < index_file_msf)
|
||||
BurnSector();
|
||||
|
||||
//latch current track settings
|
||||
track_currentCommand = track_pendingCommand;
|
||||
track_currentFlags = track_pendingFlags;
|
||||
|
||||
//index 0 is annoying. we have to code its subchannels while knowing the index that comes next
|
||||
//this is the main reason for transforming the cue file into a CueGrid (any index 0 can easily reference the index 1 that comes after it)
|
||||
if (index.Number == 0)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
void EatBlobFileSector(int required, out IBlob blob, out long blobOffset)
|
||||
{
|
||||
blob = file_blob;
|
||||
blobOffset = file_ofs;
|
||||
if (file_ofs + required > file_len)
|
||||
{
|
||||
Warn("Zero-padding mis-sized cue blob file: " + Path.GetFileName(file_currentCommand.Path));
|
||||
blob = Disc.Blob_ZeroPadBuffer.MakeBufferFrom(file_blob,file_ofs,required);
|
||||
OUT_Disc.DisposableResources.Add(blob);
|
||||
blobOffset = 0;
|
||||
}
|
||||
file_ofs += required;
|
||||
}
|
||||
|
||||
void BurnSector_Normal()
|
||||
{
|
||||
SS_Base ss = null;
|
||||
switch (track_currentCommand.Type)
|
||||
{
|
||||
case CueFile.TrackType.Mode2_2352:
|
||||
ss = new SS_2352();
|
||||
EatBlobFileSector(2352, out ss.Blob, out ss.BlobOffset);
|
||||
break;
|
||||
case CueFile.TrackType.Audio:
|
||||
ss = new SS_2352();
|
||||
EatBlobFileSector(2352, out ss.Blob, out ss.BlobOffset);
|
||||
break;
|
||||
}
|
||||
|
||||
var se = new SectorEntry(null);
|
||||
se.SectorSynth = ss;
|
||||
OUT_Disc.Sectors.Add(se);
|
||||
}
|
||||
|
||||
void BurnSector_Pregap()
|
||||
{
|
||||
var se = new SectorEntry(null);
|
||||
se.SectorSynth = new SS_Mode1_2048(); //TODO - actually burn the right thing
|
||||
OUT_Disc.Sectors.Add(se);
|
||||
|
||||
burn_pregap_timestamp = new Timestamp(burn_pregap_timestamp.Sector - 1);
|
||||
}
|
||||
|
||||
void BurnSector()
|
||||
{
|
||||
switch (burntype_current)
|
||||
{
|
||||
case BurnType.Normal:
|
||||
BurnSector_Normal();
|
||||
break;
|
||||
case BurnType.Pregap:
|
||||
BurnSector_Pregap();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MountBlobs()
|
||||
{
|
||||
IBlob file_blob = null;
|
||||
long file_len = 0;
|
||||
|
||||
BlobInfos = new List<BlobInfo>();
|
||||
foreach (var ccf in IN_CompileJob.OUT_CompiledCueFiles)
|
||||
{
|
||||
var bi = new BlobInfo();
|
||||
BlobInfos.Add(bi);
|
||||
|
||||
switch (ccf.Type)
|
||||
{
|
||||
case CompiledCueFileType.BIN:
|
||||
|
@ -228,7 +86,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
//raw files:
|
||||
var blob = new Disc.Blob_RawFile { PhysicalPath = ccf.FullPath };
|
||||
OUT_Disc.DisposableResources.Add(file_blob = blob);
|
||||
file_len = blob.Length;
|
||||
bi.Length = blob.Length;
|
||||
break;
|
||||
}
|
||||
case CompiledCueFileType.ECM:
|
||||
|
@ -236,7 +94,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
var blob = new Disc.Blob_ECM();
|
||||
OUT_Disc.DisposableResources.Add(file_blob = blob);
|
||||
blob.Load(ccf.FullPath);
|
||||
file_len = blob.Length;
|
||||
bi.Length = blob.Length;
|
||||
break;
|
||||
}
|
||||
case CompiledCueFileType.WAVE:
|
||||
|
@ -244,7 +102,7 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
var blob = new Disc.Blob_WaveFile();
|
||||
OUT_Disc.DisposableResources.Add(file_blob = blob);
|
||||
blob.Load(ccf.FullPath);
|
||||
file_len = blob.Length;
|
||||
bi.Length = blob.Length;
|
||||
break;
|
||||
}
|
||||
case CompiledCueFileType.DecodeAudio:
|
||||
|
@ -259,35 +117,69 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
var blob = new Disc.Blob_WaveFile();
|
||||
OUT_Disc.DisposableResources.Add(file_blob = blob);
|
||||
blob.Load(new MemoryStream(buf));
|
||||
bi.Length = buf.Length;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
} //switch(file type)
|
||||
|
||||
//wrap all the blobs with zero padding
|
||||
bi.Blob = new Disc.Blob_ZeroPadAdapter(file_blob, bi.Length);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AnalyzeTracks()
|
||||
{
|
||||
var compiledTracks = IN_CompileJob.OUT_CompiledCueTracks;
|
||||
|
||||
for(int t=0;t<compiledTracks.Count;t++)
|
||||
{
|
||||
var cct = compiledTracks[t];
|
||||
|
||||
var ti = new TrackInfo() { CompiledCueTrack = cct };
|
||||
TrackInfos.Add(ti);
|
||||
|
||||
//OH NO! CANT DO THIS!
|
||||
//need to read sectors from file to reliably know its ending size.
|
||||
//could determine it from file mode.
|
||||
//do we really need this?
|
||||
//if (cct.IsFinalInFile)
|
||||
//{
|
||||
// //length is determined from length of file
|
||||
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void Run()
|
||||
{
|
||||
//params
|
||||
var compiled = IN_CompileJob;
|
||||
OUT_Disc = new Disc();
|
||||
|
||||
//generation state
|
||||
int curr_index;
|
||||
int curr_blobIndex = -1;
|
||||
int curr_blobMSF = -1;
|
||||
BlobInfo curr_blobInfo = null;
|
||||
long curr_blobOffset = -1;
|
||||
|
||||
//mount all input files
|
||||
MountBlobs();
|
||||
|
||||
//make a lookup from track number to CompiledCueTrack
|
||||
Dictionary<int, CompiledCueTrack> trackLookup = new Dictionary<int, CompiledCueTrack>();
|
||||
foreach (var cct in compiled.OUT_CompiledCueTracks)
|
||||
trackLookup[cct.Number] = cct;
|
||||
//unhappily, we cannot determine the length of all the tracks without knowing the length of the files
|
||||
//now that the files are mounted, we can figure the track lengths
|
||||
AnalyzeTracks();
|
||||
|
||||
//loop from track 1 to 99
|
||||
//(track 0 isnt handled yet, that's way distant work)
|
||||
for (int t = 1; t <= 99; t++)
|
||||
for (int t = 1; t < TrackInfos.Count; t++)
|
||||
{
|
||||
CompiledCueTrack cct;
|
||||
if (!trackLookup.TryGetValue(t, out cct))
|
||||
continue;
|
||||
TrackInfo ti = TrackInfos[t];
|
||||
CompiledCueTrack cct = ti.CompiledCueTrack;
|
||||
|
||||
//---------------------------------
|
||||
//generate track pregap
|
||||
|
@ -310,92 +202,129 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
//after this, pregap sectors are generated like a normal sector, but the subQ is specified as a pregap instead of a normal track
|
||||
//---------------------------------
|
||||
|
||||
//---------------------------------
|
||||
//WE ARE NOW AT INDEX 1
|
||||
//---------------------------------
|
||||
|
||||
//---------------------------------
|
||||
//generate the RawTOCEntry for this track
|
||||
SubchannelQ sq = new SubchannelQ();
|
||||
//absent some kind of policy for how to set it, this is a safe assumption:
|
||||
byte ADR = 1;
|
||||
sq.SetStatus(ADR, (EControlQ)(int)cct.Flags);
|
||||
sq.q_tno.BCDValue = 0; //kind of a little weird here.. the track number becomes the 'point' and put in the index instead. 0 is the track number here.
|
||||
sq.q_index = BCD2.FromDecimal(cct.Number);
|
||||
//not too sure about these yet
|
||||
sq.min = BCD2.FromDecimal(0);
|
||||
sq.sec = BCD2.FromDecimal(0);
|
||||
sq.frame = BCD2.FromDecimal(0);
|
||||
sq.AP_Timestamp = new Timestamp(OUT_Disc.Sectors.Count + 150); //its supposed to be an absolute timestamp
|
||||
OUT_Disc.RawTOCEntries.Add(new RawTOCEntry { QData = sq });
|
||||
}
|
||||
//generate sectors for this track.
|
||||
|
||||
//advance to the next file if needed
|
||||
if (curr_blobIndex != cct.BlobIndex)
|
||||
{
|
||||
curr_blobIndex = cct.BlobIndex;
|
||||
curr_blobOffset = 0;
|
||||
curr_blobMSF = 0;
|
||||
curr_blobInfo = BlobInfos[curr_blobIndex];
|
||||
}
|
||||
|
||||
//work until the next track is reached, or the end of the current file is reached, depending on the track type
|
||||
curr_index = 0;
|
||||
for (; ; )
|
||||
{
|
||||
bool trackDone = false;
|
||||
|
||||
//select the appropriate index by inspecting the next index and seeing if we've reached it
|
||||
for (; ; )
|
||||
{
|
||||
if (curr_index == cct.Indexes.Count - 1)
|
||||
break;
|
||||
if (curr_blobMSF >= cct.Indexes[curr_index + 1].FileMSF.Sector)
|
||||
{
|
||||
curr_index++;
|
||||
if (curr_index == 1)
|
||||
{
|
||||
//---- WE ARE NOW AT INDEX 1 -----
|
||||
//generate the RawTOCEntry for this track (make another method for this please)
|
||||
SubchannelQ toc_sq = new SubchannelQ();
|
||||
//absent some kind of policy for how to set it, this is a safe assumption:
|
||||
byte toc_ADR = 1;
|
||||
toc_sq.SetStatus(toc_ADR, (EControlQ)(int)cct.Flags);
|
||||
toc_sq.q_tno.BCDValue = 0; //kind of a little weird here.. the track number becomes the 'point' and put in the index instead. 0 is the track number here.
|
||||
toc_sq.q_index = BCD2.FromDecimal(cct.Number);
|
||||
//not too sure about these yet
|
||||
toc_sq.min = BCD2.FromDecimal(0);
|
||||
toc_sq.sec = BCD2.FromDecimal(0);
|
||||
toc_sq.frame = BCD2.FromDecimal(0);
|
||||
toc_sq.AP_Timestamp = new Timestamp(OUT_Disc.Sectors.Count); //its supposed to be an absolute timestamp
|
||||
OUT_Disc.RawTOCEntries.Add(new RawTOCEntry { QData = toc_sq });
|
||||
}
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
//generate a sector:
|
||||
ISector siface = null;
|
||||
SS_Base ss = null;
|
||||
switch (cct.TrackType)
|
||||
{
|
||||
case CueFile.TrackType.Mode2_2352:
|
||||
case CueFile.TrackType.Audio:
|
||||
ss = new SS_2352() { Blob = curr_blobInfo.Blob, BlobOffset = curr_blobOffset };
|
||||
curr_blobOffset += 2352;
|
||||
break;
|
||||
}
|
||||
|
||||
//make the subcode
|
||||
//TODO - according to policies, or better yet, defer this til it's needed (user delivers a policies object to disc reader apis)
|
||||
//at any rate, we'd paste this logic into there so let's go ahead and write it here
|
||||
var subcode = new BufferedSubcodeSector(); //(its lame that we have to use this; make a static method when we delete this class)
|
||||
SubchannelQ sq = new SubchannelQ();
|
||||
byte ADR = 1; //absent some kind of policy for how to set it, this is a safe assumption:
|
||||
sq.SetStatus(ADR, (EControlQ)(int)cct.Flags);
|
||||
sq.q_tno = BCD2.FromDecimal(cct.Number);
|
||||
sq.q_index = BCD2.FromDecimal(curr_index);
|
||||
int LBA = OUT_Disc.Sectors.Count;
|
||||
sq.ap_min = BCD2.FromDecimal(new Timestamp(LBA).MIN);
|
||||
sq.ap_sec = BCD2.FromDecimal(new Timestamp(LBA).SEC);
|
||||
sq.ap_frame = BCD2.FromDecimal(new Timestamp(LBA).FRAC);
|
||||
int track_relative_msf = curr_blobMSF - cct.Indexes[1].FileMSF.Sector;
|
||||
sq.min = BCD2.FromDecimal(new Timestamp(track_relative_msf).MIN);
|
||||
sq.sec = BCD2.FromDecimal(new Timestamp(track_relative_msf).SEC);
|
||||
sq.frame = BCD2.FromDecimal(new Timestamp(track_relative_msf).FRAC);
|
||||
//finally we're done: synthesize subchannel
|
||||
subcode.Synthesize_SubchannelQ(ref sq, true);
|
||||
|
||||
//make the SectorEntry (some temporary bullshit here)
|
||||
var se = new SectorEntry(null);
|
||||
se.SectorSynth = ss;
|
||||
ss.sq = sq;
|
||||
OUT_Disc.Sectors.Add(se);
|
||||
curr_blobMSF++;
|
||||
|
||||
if (cct.IsFinalInFile)
|
||||
{
|
||||
//sometimes, break when the file is exhausted
|
||||
if (curr_blobOffset >= curr_blobInfo.Length)
|
||||
trackDone = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//other times, break when the track is done
|
||||
//(this check is safe because it's not the final track overall if it's not the final track in a file)
|
||||
if (curr_blobMSF >= TrackInfos[t + 1].CompiledCueTrack.Indexes[0].FileMSF.Sector)
|
||||
trackDone = true;
|
||||
}
|
||||
|
||||
if (trackDone)
|
||||
break;
|
||||
}
|
||||
|
||||
//TODO - POSTGAP
|
||||
|
||||
} //end track loop
|
||||
|
||||
|
||||
|
||||
////now for the magic. Process commands in order
|
||||
//for (int i = 0; i < cue.Commands.Count; i++)
|
||||
//{
|
||||
// var cmd = cue.Commands[i];
|
||||
//add RawTOCEntries A0 A1 A2 to round out the TOC
|
||||
var TOCMiscInfo = new Synthesize_A0A1A2_Job {
|
||||
IN_FirstRecordedTrackNumber = IN_CompileJob.OUT_CompiledDiscInfo.FirstRecordedTrackNumber,
|
||||
IN_LastRecordedTrackNumber = IN_CompileJob.OUT_CompiledDiscInfo.LastRecordedTrackNumber,
|
||||
IN_Session1Format = IN_CompileJob.OUT_CompiledDiscInfo.SessionFormat,
|
||||
IN_LeadoutTimestamp = new Timestamp(OUT_Disc.Sectors.Count) //do we need a +150?
|
||||
};
|
||||
TOCMiscInfo.Run(OUT_Disc.RawTOCEntries);
|
||||
|
||||
// //these commands get dealt with globally. nothing to be done here
|
||||
// if (cmd is CueFile.Command.CATALOG || cmd is CueFile.Command.CDTEXTFILE) continue;
|
||||
|
||||
// //nothing to be done for comments
|
||||
// if (cmd is CueFile.Command.REM) continue;
|
||||
// if (cmd is CueFile.Command.COMMENT) continue;
|
||||
|
||||
// //handle cdtext and ISRC state updates, theyre kind of like little registers
|
||||
// if (cmd is CueFile.Command.PERFORMER)
|
||||
// cdtext_performer = (cmd as CueFile.Command.PERFORMER).Value;
|
||||
// if (cmd is CueFile.Command.SONGWRITER)
|
||||
// cdtext_songwriter = (cmd as CueFile.Command.SONGWRITER).Value;
|
||||
// if (cmd is CueFile.Command.TITLE)
|
||||
// cdtext_title = (cmd as CueFile.Command.TITLE).Value;
|
||||
// if (cmd is CueFile.Command.ISRC)
|
||||
// isrc = (cmd as CueFile.Command.ISRC).Value;
|
||||
|
||||
// //flags are also a kind of a register. but the flags value is reset by the track command
|
||||
// if (cmd is CueFile.Command.FLAGS)
|
||||
// {
|
||||
// track_pendingFlags = (cmd as CueFile.Command.FLAGS).Flags;
|
||||
// }
|
||||
|
||||
// if (cmd is CueFile.Command.TRACK)
|
||||
// {
|
||||
// var track = cmd as CueFile.Command.TRACK;
|
||||
|
||||
// //register the track for further processing when an GENERATION command appears
|
||||
// track_pendingCommand = track;
|
||||
// track_pendingFlags = CueFile.TrackFlags.None;
|
||||
|
||||
// }
|
||||
|
||||
// if (cmd is CueFile.Command.FILE)
|
||||
// {
|
||||
// ProcessFile(cmd as CueFile.Command.FILE);
|
||||
// }
|
||||
|
||||
// if (cmd is CueFile.Command.INDEX)
|
||||
// {
|
||||
// ProcessIndex(cmd as CueFile.Command.INDEX);
|
||||
// }
|
||||
//}
|
||||
|
||||
//BurnToEOF();
|
||||
|
||||
////add RawTOCEntries A0 A1 A2 to round out the TOC
|
||||
//var TOCMiscInfo = new Synthesize_A0A1A2_Job {
|
||||
// IN_FirstRecordedTrackNumber = sloshy_firstRecordedTrackNumber,
|
||||
// IN_LastRecordedTrackNumber = sloshy_lastRecordedTrackNumber,
|
||||
// IN_Session1Format = sloshy_session1Format,
|
||||
// IN_LeadoutTimestamp = new Timestamp(OUT_Disc.Sectors.Count)
|
||||
//};
|
||||
//TOCMiscInfo.Run(OUT_Disc.RawTOCEntries);
|
||||
|
||||
////generate the TOCRaw from the RawTocEntries
|
||||
//var tocSynth = new DiscTOCRaw.SynthesizeFromRawTOCEntriesJob() { Entries = OUT_Disc.RawTOCEntries };
|
||||
//tocSynth.Run();
|
||||
//OUT_Disc.TOCRaw = tocSynth.Result;
|
||||
//generate the TOCRaw from the RawTocEntries
|
||||
var tocSynth = new DiscTOCRaw.SynthesizeFromRawTOCEntriesJob() { Entries = OUT_Disc.RawTOCEntries };
|
||||
tocSynth.Run();
|
||||
OUT_Disc.TOCRaw = tocSynth.Result;
|
||||
|
||||
////generate lead-out track with some canned number of sectors
|
||||
////TODO - move this somewhere else and make it controllable depending on which console is loading up the disc
|
||||
|
@ -411,4 +340,30 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
} //Run()
|
||||
} //class LoadCueJob
|
||||
} //partial class CUE_Format2
|
||||
} //namespace BizHawk.Emulation.DiscSystem
|
||||
} //namespace BizHawk.Emulation.DiscSystem
|
||||
|
||||
|
||||
|
||||
|
||||
//TODO:
|
||||
//if (index_num == 0 || type == SectorWriteType.Pregap)
|
||||
//{
|
||||
// //PREGAP:
|
||||
// //things are negative here.
|
||||
// if (track_relative_msf > 0) throw new InvalidOperationException("Perplexing internal error with non-negative pregap MSF");
|
||||
// track_relative_msf = -track_relative_msf;
|
||||
|
||||
// //now for something special.
|
||||
// //yellow-book says:
|
||||
// //pre-gap for "first part of a digital data track not containing user data and encoded as a pause"
|
||||
// //first interval: at least 75 sectors coded as preceding track
|
||||
// //second interval: at least 150 sectors coded as user data track.
|
||||
// //so... we ASSUME the 150 sector pregap is more important. so if thats all there is, theres no 75 sector pregap like the old track
|
||||
// //if theres a longer pregap, then we generate weird old track pregap to contain the rest.
|
||||
// if (track_relative_msf > 150)
|
||||
// {
|
||||
// //only if we're burning a data track now
|
||||
// if((track_flags & CueFile.TrackFlags.DATA)!=0)
|
||||
// sq.q_status = priorSubchannelQ.q_status;
|
||||
// }
|
||||
//}
|
|
@ -29,15 +29,14 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
|
||||
/// <summary>
|
||||
/// Represents the contents of a cue file
|
||||
/// This is relatively unstructured because in a lot (maybe every) way a cue file is meant to be processed one command at a time
|
||||
/// </summary>
|
||||
public class CueFile
|
||||
{
|
||||
// (here are all the commands we can encounter)
|
||||
|
||||
//TODO - record line number origin of command?
|
||||
// (here are all the commands we can encounter)
|
||||
public static class Command
|
||||
{
|
||||
//TODO - record line number origin of command? Kind of nice but inessential
|
||||
public class CATALOG { public string Value; public override string ToString() { return string.Format("CATALOG: {0}", Value); } }
|
||||
public class CDTEXTFILE { public string Path; public override string ToString() { return string.Format("CDTEXTFILE: {0}", Path); } }
|
||||
public class FILE { public string Path; public FileType Type; public override string ToString() { return string.Format("FILE ({0}): {1}", Type, Path); } }
|
||||
|
@ -88,9 +87,11 @@ namespace BizHawk.Emulation.DiscSystem
|
|||
//All audio files (WAVE, AIFF, and MP3) must be in 44.1KHz 16-bit stereo format.
|
||||
//BUT NOTE: MP3 can be VBR and the length can't be known without decoding the whole thing.
|
||||
//But, some ideas:
|
||||
//1. we could operate ffmpeg differently to retrieve the length, which maybe it can do without having to
|
||||
//1. we could operate ffmpeg differently to retrieve the length, which maybe it can do without having to decode the entire thing
|
||||
//2. we could retrieve it from an ID3 if present.
|
||||
//3. as a last resort, since MP3 is the annoying case usually, we could include my c# mp3 parser and sum the length
|
||||
//3. as a last resort, since MP3 is the annoying case usually, we could include my c# mp3 parser and sum the length (test the performance, this might be reasonably fast on par with ECM parsing)
|
||||
//NOTE: once deciding the length, we would have to stick with it! samples would have to be discarded or inserted to make the track work out
|
||||
//but we COULD effectively achieve stream-loading mp3 discs, with enough work.
|
||||
public enum FileType
|
||||
{
|
||||
Unspecified,
|
||||
|
|
|
@ -11,14 +11,17 @@ using System.Collections.Generic;
|
|||
//This will result in a completely flattened CCD where everything comes right off the hard drive
|
||||
//Our choice here might be an unwise decision for disc ID and miscellaneous purposes but it's best for gaming and stream-converting (hawking and hashing)
|
||||
|
||||
//TODO: in principle, we could mount audio to decode only on an as-needed basis
|
||||
//this might result in hiccups during emulation, though, so it should be an option.
|
||||
//This would imply either decode-length processing (scan file without decoding) or decoding and discarding the data.
|
||||
//We should probably have some richer policy specifications for this kind of thing, but it's not a high priority. Main workflow is still discohawking.
|
||||
//Alternate policies would probably be associated with copious warnings (examples: ? ? ?)
|
||||
|
||||
|
||||
//https://books.google.com/books?id=caF_AAAAQBAJ&lpg=PA124&ots=OA9Ttj9CHZ&dq=disc%20TOC%20point%20A2&pg=PA124
|
||||
|
||||
|
||||
//http://www.staff.uni-mainz.de/tacke/scsi/SCSI2-14.html
|
||||
|
||||
//http://www.pctechguide.com/iso-9660-data-format-for-cds-cd-roms-cd-rs-and-cd-rws
|
||||
//http://linux.die.net/man/1/cue2toc
|
||||
|
||||
//http://cdemu.sourceforge.net/project.php#sf
|
||||
|
||||
//apparently cdrdao is the ultimate linux tool for doing this stuff but it doesnt support DAO96 (or other DAO modes) that would be necessary to extract P-Q subchannels
|
||||
|
@ -27,13 +30,11 @@ using System.Collections.Generic;
|
|||
//here is a featureset list of windows cd burning programs (useful for cuesheet compatibility info)
|
||||
//http://www.dcsoft.com/cue_mastering_progs.htm
|
||||
|
||||
//good
|
||||
//good links
|
||||
//http://linux-sxs.org/bedtime/cdapi.html
|
||||
//http://en.wikipedia.org/wiki/Track_%28CD%29
|
||||
//http://docs.google.com/viewer?a=v&q=cache:imNKye05zIEJ:www.13thmonkey.org/documentation/SCSI/mmc-r10a.pdf+q+subchannel+TOC+format&hl=en&gl=us&pid=bl&srcid=ADGEEShtYqlluBX2lgxTL3pVsXwk6lKMIqSmyuUCX4RJ3DntaNq5vI2pCvtkyze-fumj7vvrmap6g1kOg5uAVC0IxwU_MRhC5FB0c_PQ2BlZQXDD7P3GeNaAjDeomelKaIODrhwOoFNb&sig=AHIEtbRXljAcFjeBn3rMb6tauHWjSNMYrw
|
||||
//r:\consoles\~docs\yellowbook
|
||||
//http://digitalx.org/cue-sheet/examples/
|
||||
//
|
||||
|
||||
//"qemu cdrom emulator"
|
||||
//http://www.koders.com/c/fid7171440DEC7C18B932715D671DEE03743111A95A.aspx
|
||||
|
@ -48,7 +49,6 @@ using System.Collections.Generic;
|
|||
//ideas:
|
||||
/*
|
||||
* do some stuff asynchronously. for example, decoding mp3 sectors.
|
||||
* keep a list of 'blobs' (giant bins or decoded wavs likely) which can reference the disk
|
||||
* keep a list of sectors and the blob/offset from which they pull -- also whether the sector is available
|
||||
* if it is not available and something requests it then it will have to block while that sector gets generated
|
||||
* perhaps the blobs know how to resolve themselves and the requested sector can be immediately resolved (priority boost)
|
||||
|
|
Loading…
Reference in New Issue