continue cue loading

This commit is contained in:
zeromus 2015-07-01 02:56:55 -05:00
parent 7deb5e8575
commit 70f97851b3
6 changed files with 292 additions and 268 deletions

View File

@ -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; } }

View File

@ -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>

View File

@ -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();

View File

@ -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;
// }
//}

View File

@ -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,

View File

@ -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)