change analyze stage to be a compile stage, which digests the commands as well as analyzing the files and gives an area for extra validation separate from the loading stage
This commit is contained in:
parent
8b0593dcb2
commit
3c26d48a59
|
@ -1,313 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.IO;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace BizHawk.Emulation.DiscSystem
|
|
||||||
{
|
|
||||||
partial class CUE_Format2
|
|
||||||
{
|
|
||||||
public class AnalyzeCueJob : LoggedJob
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// input: the CueFile to analyze
|
|
||||||
/// </summary>
|
|
||||||
public CueFile IN_CueFile;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An integer between 0 and 10 indicating how costly it will be to load this disc completely.
|
|
||||||
/// Activites like decoding non-seekable media will increase the load time.
|
|
||||||
/// 0 - Requires no noticeable time
|
|
||||||
/// 1 - Requires minimal processing (indexing ECM)
|
|
||||||
/// 10 - Requires ages, decoding audio data, etc.
|
|
||||||
/// </summary>
|
|
||||||
public int OUT_LoadTime;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Analyzed-information about a file member of a cue
|
|
||||||
/// </summary>
|
|
||||||
public class CueFileInfo
|
|
||||||
{
|
|
||||||
public string FullPath;
|
|
||||||
public CueFileType Type;
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
return string.Format("{0}: {1}", Type, Path.GetFileName(FullPath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// What type of file we're looking at.. each one would require a different ingestion handler
|
|
||||||
/// </summary>
|
|
||||||
public enum CueFileType
|
|
||||||
{
|
|
||||||
Unknown,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// a raw BIN that can be mounted directly
|
|
||||||
/// </summary>
|
|
||||||
BIN,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// a raw WAV that can be mounted directly
|
|
||||||
/// </summary>
|
|
||||||
WAVE,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// an ECM file that can be mounted directly (once the index is generated)
|
|
||||||
/// </summary>
|
|
||||||
ECM,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An encoded audio file which can be seeked on the fly, therefore roughly mounted on the fly
|
|
||||||
/// THIS ISN'T SUPPORTED YET
|
|
||||||
/// </summary>
|
|
||||||
SeekAudio,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An encoded audio file which can't be seeked on the fly. It must be decoded to a temp buffer, or pre-discohawked
|
|
||||||
/// </summary>
|
|
||||||
DecodeAudio,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// For each file referenced by the cue file, info about it
|
|
||||||
/// </summary>
|
|
||||||
public List<CueFileInfo> OUT_FileInfos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CueFileResolver
|
|
||||||
{
|
|
||||||
public bool caseSensitive = false;
|
|
||||||
public bool IsHardcodedResolve { get; private set; }
|
|
||||||
string baseDir;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieving the FullName from a FileInfo can be slow (and probably other operations), so this will cache all the needed values
|
|
||||||
/// TODO - could we treat it like an actual cache and only fill the FullName if it's null?
|
|
||||||
/// </summary>
|
|
||||||
struct MyFileInfo
|
|
||||||
{
|
|
||||||
public string FullName;
|
|
||||||
public FileInfo FileInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
DirectoryInfo diBasedir;
|
|
||||||
MyFileInfo[] fisBaseDir;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// sets the base directory and caches the list of files in the directory
|
|
||||||
/// </summary>
|
|
||||||
public void SetBaseDirectory(string baseDir)
|
|
||||||
{
|
|
||||||
this.baseDir = baseDir;
|
|
||||||
diBasedir = new DirectoryInfo(baseDir);
|
|
||||||
//list all files, so we dont scan repeatedly.
|
|
||||||
fisBaseDir = MyFileInfosFromFileInfos(diBasedir.GetFiles());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// TODO - doesnt seem like we're using this...
|
|
||||||
/// </summary>
|
|
||||||
public void SetHardcodeResolve(IDictionary<string, string> hardcodes)
|
|
||||||
{
|
|
||||||
IsHardcodedResolve = true;
|
|
||||||
fisBaseDir = new MyFileInfo[hardcodes.Count];
|
|
||||||
int i = 0;
|
|
||||||
foreach (var kvp in hardcodes)
|
|
||||||
{
|
|
||||||
fisBaseDir[i++] = new MyFileInfo { FullName = kvp.Key, FileInfo = new FileInfo(kvp.Value) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MyFileInfo[] MyFileInfosFromFileInfos(FileInfo[] fis)
|
|
||||||
{
|
|
||||||
var myfis = new MyFileInfo[fis.Length];
|
|
||||||
for (int i = 0; i < fis.Length; i++)
|
|
||||||
{
|
|
||||||
myfis[i].FileInfo = fis[i];
|
|
||||||
myfis[i].FullName = fis[i].FullName;
|
|
||||||
}
|
|
||||||
return myfis;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Performs cue-intelligent logic to acquire a file requested by the cue.
|
|
||||||
/// Returns the resulting full path(s).
|
|
||||||
/// If there are multiple options, it returns them all
|
|
||||||
/// </summary>
|
|
||||||
public List<string> Resolve(string path)
|
|
||||||
{
|
|
||||||
string targetFile = Path.GetFileName(path);
|
|
||||||
string targetFragment = Path.GetFileNameWithoutExtension(path);
|
|
||||||
|
|
||||||
DirectoryInfo di = null;
|
|
||||||
MyFileInfo[] fileInfos;
|
|
||||||
if (!string.IsNullOrEmpty(Path.GetDirectoryName(path)))
|
|
||||||
{
|
|
||||||
di = new FileInfo(path).Directory;
|
|
||||||
//fileInfos = di.GetFiles(Path.GetFileNameWithoutExtension(path)); //does this work?
|
|
||||||
fileInfos = MyFileInfosFromFileInfos(di.GetFiles()); //we (probably) have to enumerate all the files to do a search anyway, so might as well do this
|
|
||||||
//TODO - dont do the search until a resolve fails
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
di = diBasedir;
|
|
||||||
fileInfos = fisBaseDir;
|
|
||||||
}
|
|
||||||
|
|
||||||
var results = new List<FileInfo>();
|
|
||||||
foreach (var fi in fileInfos)
|
|
||||||
{
|
|
||||||
var ext = Path.GetExtension(fi.FullName).ToLowerInvariant();
|
|
||||||
|
|
||||||
//some choices are always bad: (we're looking for things like .bin and .wav)
|
|
||||||
//it's a little unclear whether we should go for a whitelist or a blacklist here.
|
|
||||||
//there's similar numbers of cases either way.
|
|
||||||
//perhaps we could code both (and prefer choices from the whitelist)
|
|
||||||
if (ext == ".cue" || ext == ".sbi" || ext == ".ccd" || ext == ".sub")
|
|
||||||
continue;
|
|
||||||
|
|
||||||
|
|
||||||
string fragment = Path.GetFileNameWithoutExtension(fi.FullName);
|
|
||||||
//match files with differing extensions
|
|
||||||
int cmp = string.Compare(fragment, targetFragment, !caseSensitive);
|
|
||||||
if (cmp != 0)
|
|
||||||
//match files with another extension added on (likely to be mygame.bin.ecm)
|
|
||||||
cmp = string.Compare(fragment, targetFile, !caseSensitive);
|
|
||||||
if (cmp == 0)
|
|
||||||
results.Add(fi.FileInfo);
|
|
||||||
|
|
||||||
}
|
|
||||||
var ret = new List<string>();
|
|
||||||
foreach (var fi in results)
|
|
||||||
ret.Add(fi.FullName);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Analyzes a cue file and its dependencies.
|
|
||||||
/// This should run as fast as possible.. no deep inspection of files
|
|
||||||
/// </summary>
|
|
||||||
public void AnalyzeCueFile(AnalyzeCueJob job)
|
|
||||||
{
|
|
||||||
//TODO - handle CD text file
|
|
||||||
|
|
||||||
job.OUT_FileInfos = new List<AnalyzeCueJob.CueFileInfo>();
|
|
||||||
|
|
||||||
var cue = job.IN_CueFile;
|
|
||||||
|
|
||||||
//first, collect information about all the input files
|
|
||||||
foreach (var cmd in cue.Commands)
|
|
||||||
{
|
|
||||||
var f = cmd as CueFile.Command.FILE;
|
|
||||||
if (f == null) continue;
|
|
||||||
|
|
||||||
//TODO TODO TODO TODO
|
|
||||||
//TODO TODO TODO TODO
|
|
||||||
//TODO TODO TODO TODO
|
|
||||||
//smart audio file resolving only for AUDIO types. not BINARY or MOTOROLA or AIFF or ECM or what have you
|
|
||||||
|
|
||||||
var options = Resolver.Resolve(f.Path);
|
|
||||||
string choice = null;
|
|
||||||
if (options.Count == 0)
|
|
||||||
{
|
|
||||||
job.Error("Couldn't resolve referenced cue file: " + f.Path);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
choice = options[0];
|
|
||||||
if (options.Count > 1)
|
|
||||||
job.Warn("Multiple options resolving referenced cue file; choosing: " + Path.GetFileName(choice));
|
|
||||||
}
|
|
||||||
|
|
||||||
var cfi = new AnalyzeCueJob.CueFileInfo();
|
|
||||||
job.OUT_FileInfos.Add(cfi);
|
|
||||||
|
|
||||||
cfi.FullPath = choice;
|
|
||||||
|
|
||||||
//determine the CueFileInfo's type, based on extension and extra checking
|
|
||||||
//TODO - once we reorganize the file ID stuff, do legit checks here (this is completely redundant with the fileID system
|
|
||||||
//TODO - decode vs stream vs unpossible policies in input policies object (including ffmpeg availability-checking callback (results can be cached))
|
|
||||||
string blobPathExt = Path.GetExtension(choice).ToUpperInvariant();
|
|
||||||
if (blobPathExt == ".BIN" || blobPathExt == ".IMG") cfi.Type = AnalyzeCueJob.CueFileType.BIN;
|
|
||||||
else if (blobPathExt == ".ISO") cfi.Type = AnalyzeCueJob.CueFileType.BIN;
|
|
||||||
else if (blobPathExt == ".WAV")
|
|
||||||
{
|
|
||||||
//quickly, check the format. turn it to DecodeAudio if it can't be supported
|
|
||||||
//TODO - fix exception-throwing inside
|
|
||||||
//TODO - verify stream-disposing semantics
|
|
||||||
var fs = File.OpenRead(choice);
|
|
||||||
using (var blob = new Disc.Blob_WaveFile())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
blob.Load(fs);
|
|
||||||
cfi.Type = AnalyzeCueJob.CueFileType.WAVE;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
cfi.Type = AnalyzeCueJob.CueFileType.DecodeAudio;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (blobPathExt == ".APE") cfi.Type = AnalyzeCueJob.CueFileType.DecodeAudio;
|
|
||||||
else if (blobPathExt == ".MP3") cfi.Type = AnalyzeCueJob.CueFileType.DecodeAudio;
|
|
||||||
else if (blobPathExt == ".MPC") cfi.Type = AnalyzeCueJob.CueFileType.DecodeAudio;
|
|
||||||
else if (blobPathExt == ".FLAC") cfi.Type = AnalyzeCueJob.CueFileType.DecodeAudio;
|
|
||||||
else if (blobPathExt == ".ECM")
|
|
||||||
{
|
|
||||||
cfi.Type = AnalyzeCueJob.CueFileType.ECM;
|
|
||||||
if (!Disc.Blob_ECM.IsECM(choice))
|
|
||||||
{
|
|
||||||
job.Error("an ECM file was specified or detected, but it isn't a valid ECM file: " + Path.GetFileName(choice));
|
|
||||||
cfi.Type = AnalyzeCueJob.CueFileType.Unknown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
job.Error("Unknown cue file type. Since it's likely an unsupported compression, this is an error: ", Path.GetFileName(choice));
|
|
||||||
cfi.Type = AnalyzeCueJob.CueFileType.Unknown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO - check for mismatches between track types and file types, or is that best done when interpreting the commands?
|
|
||||||
|
|
||||||
//some quick checks:
|
|
||||||
if (job.OUT_FileInfos.Count == 0)
|
|
||||||
job.Error("Cue file doesn't specify any input files!");
|
|
||||||
|
|
||||||
//we can't readily analyze the length of files here, because we'd have to be interpreting the commands to know the track types. Not really worth the trouble
|
|
||||||
//we could check the format of the wav file here, though
|
|
||||||
|
|
||||||
//score the cost of loading the file
|
|
||||||
bool needsCodec = false;
|
|
||||||
job.OUT_LoadTime = 0;
|
|
||||||
foreach (var cfi in job.OUT_FileInfos)
|
|
||||||
{
|
|
||||||
if (cfi.Type == AnalyzeCueJob.CueFileType.DecodeAudio)
|
|
||||||
{
|
|
||||||
needsCodec = true;
|
|
||||||
job.OUT_LoadTime = Math.Max(job.OUT_LoadTime, 10);
|
|
||||||
}
|
|
||||||
if (cfi.Type == AnalyzeCueJob.CueFileType.SeekAudio)
|
|
||||||
needsCodec = true;
|
|
||||||
if (cfi.Type == AnalyzeCueJob.CueFileType.ECM)
|
|
||||||
job.OUT_LoadTime = Math.Max(job.OUT_LoadTime, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
//check whether processing was available
|
|
||||||
if (needsCodec)
|
|
||||||
{
|
|
||||||
FFMpeg ffmpeg = new FFMpeg();
|
|
||||||
if (!ffmpeg.QueryServiceAvailable())
|
|
||||||
job.Warn("Decoding service will be required for further processing, but is not available");
|
|
||||||
}
|
|
||||||
|
|
||||||
job.FinishLog();
|
|
||||||
}
|
|
||||||
} //partial class
|
|
||||||
} //namespace
|
|
|
@ -0,0 +1,398 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.IO;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
//this would be a good place for structural validation
|
||||||
|
//after this step, we won't want to have to do stuff like that (it will gunk up already sticky code)
|
||||||
|
|
||||||
|
namespace BizHawk.Emulation.DiscSystem
|
||||||
|
{
|
||||||
|
partial class CUE_Format2
|
||||||
|
{
|
||||||
|
internal class CompiledCDText
|
||||||
|
{
|
||||||
|
public string Songwriter;
|
||||||
|
public string Performer;
|
||||||
|
public string Title;
|
||||||
|
public string ISRC;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class CompiledCueIndex
|
||||||
|
{
|
||||||
|
public int Number;
|
||||||
|
public Timestamp FileMSF;
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format("I#{0:D2} {1}", Number, FileMSF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// What type of file we're looking at.. each one would require a different ingestion handler
|
||||||
|
/// </summary>
|
||||||
|
public enum CompiledCueFileType
|
||||||
|
{
|
||||||
|
Unknown,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// a raw BIN that can be mounted directly
|
||||||
|
/// </summary>
|
||||||
|
BIN,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// a raw WAV that can be mounted directly
|
||||||
|
/// </summary>
|
||||||
|
WAVE,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// an ECM file that can be mounted directly (once the index is generated)
|
||||||
|
/// </summary>
|
||||||
|
ECM,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An encoded audio file which can be seeked on the fly, therefore roughly mounted on the fly
|
||||||
|
/// THIS ISN'T SUPPORTED YET
|
||||||
|
/// </summary>
|
||||||
|
SeekAudio,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An encoded audio file which can't be seeked on the fly. It must be decoded to a temp buffer, or pre-discohawked
|
||||||
|
/// </summary>
|
||||||
|
DecodeAudio,
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class CompiledCueFile
|
||||||
|
{
|
||||||
|
public string FullPath;
|
||||||
|
public CompiledCueFileType Type;
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return string.Format("{0}: {1}", Type, Path.GetFileName(FullPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class CompiledDiscInfo
|
||||||
|
{
|
||||||
|
public int FirstRecordedTrackNumber, LastRecordedTrackNumber;
|
||||||
|
public DiscTOCRaw.SessionFormat SessionFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class CompiledCueTrack
|
||||||
|
{
|
||||||
|
public int BlobIndex;
|
||||||
|
public int Number;
|
||||||
|
public CompiledCDText CDTextData = new CompiledCDText();
|
||||||
|
public Timestamp PregapLength, PostgapLength;
|
||||||
|
public CueFile.TrackFlags Flags = CueFile.TrackFlags.None;
|
||||||
|
public CueFile.TrackType TrackType = CueFile.TrackType.Unknown;
|
||||||
|
|
||||||
|
public List<CompiledCueIndex> Indexes = new List<CompiledCueIndex>();
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
var idx = Indexes.Find((i) => i.Number == 1);
|
||||||
|
if (idx == null)
|
||||||
|
return string.Format("T#{0:D2} NO INDEX 1", Number);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var indexlist = string.Join("|", Indexes);
|
||||||
|
return string.Format("T#{0:D2} {1}:{2} ({3})", Number, BlobIndex, idx.FileMSF, indexlist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class CompileCueJob : LoggedJob
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// input: the CueFile to analyze
|
||||||
|
/// </summary>
|
||||||
|
public CueFile IN_CueFile;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The context used for this compiling job
|
||||||
|
/// </summary>
|
||||||
|
public CUE_Format2 IN_CueFormat;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// output: high level disc info
|
||||||
|
/// </summary>
|
||||||
|
public CompiledDiscInfo OUT_CompiledDiscInfo;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// output: CD-Text set at the global level (before any track commands)
|
||||||
|
/// </summary>
|
||||||
|
public CompiledCDText OUT_GlobalCDText;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// output: The compiled file info
|
||||||
|
/// </summary>
|
||||||
|
public List<CompiledCueFile> OUT_CompiledCueFiles;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// output: The compiled track info
|
||||||
|
/// </summary>
|
||||||
|
public List<CompiledCueTrack> OUT_CompiledCueTracks;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// output: An integer between 0 and 10 indicating how costly it will be to load this disc completely.
|
||||||
|
/// Activites like decoding non-seekable media will increase the load time.
|
||||||
|
/// 0 - Requires no noticeable time
|
||||||
|
/// 1 - Requires minimal processing (indexing ECM)
|
||||||
|
/// 10 - Requires ages, decoding audio data, etc.
|
||||||
|
/// </summary>
|
||||||
|
public int OUT_LoadTime;
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------
|
||||||
|
|
||||||
|
CompiledCDText curr_cdtext;
|
||||||
|
int curr_blobIndex = -1;
|
||||||
|
CompiledCueTrack curr_track = null;
|
||||||
|
bool discinfo_session1Format_determined = false;
|
||||||
|
|
||||||
|
void UpdateDiscInfo(CueFile.Command.TRACK trackCommand)
|
||||||
|
{
|
||||||
|
if (OUT_CompiledDiscInfo.FirstRecordedTrackNumber == 0)
|
||||||
|
OUT_CompiledDiscInfo.FirstRecordedTrackNumber = trackCommand.Number;
|
||||||
|
OUT_CompiledDiscInfo.LastRecordedTrackNumber = trackCommand.Number;
|
||||||
|
if (!discinfo_session1Format_determined)
|
||||||
|
{
|
||||||
|
switch (trackCommand.Type)
|
||||||
|
{
|
||||||
|
case CueFile.TrackType.Mode2_2336:
|
||||||
|
case CueFile.TrackType.Mode2_2352:
|
||||||
|
OUT_CompiledDiscInfo.SessionFormat = DiscTOCRaw.SessionFormat.Type20_CDXA;
|
||||||
|
discinfo_session1Format_determined = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CueFile.TrackType.CDI_2336:
|
||||||
|
case CueFile.TrackType.CDI_2352:
|
||||||
|
OUT_CompiledDiscInfo.SessionFormat = DiscTOCRaw.SessionFormat.Type10_CDI;
|
||||||
|
discinfo_session1Format_determined = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddFile(CueFile.Command.FILE f)
|
||||||
|
{
|
||||||
|
curr_blobIndex++;
|
||||||
|
|
||||||
|
var Resolver = IN_CueFormat.Resolver;
|
||||||
|
|
||||||
|
//TODO - smart audio file resolving only for AUDIO types. not BINARY or MOTOROLA or AIFF or ECM or what have you
|
||||||
|
|
||||||
|
var options = Resolver.Resolve(f.Path);
|
||||||
|
string choice = null;
|
||||||
|
if (options.Count == 0)
|
||||||
|
{
|
||||||
|
Error("Couldn't resolve referenced cue file: " + f.Path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
choice = options[0];
|
||||||
|
if (options.Count > 1)
|
||||||
|
Warn("Multiple options resolving referenced cue file; choosing: " + Path.GetFileName(choice));
|
||||||
|
}
|
||||||
|
|
||||||
|
var cfi = new CompiledCueFile();
|
||||||
|
OUT_CompiledCueFiles.Add(cfi);
|
||||||
|
|
||||||
|
cfi.FullPath = choice;
|
||||||
|
|
||||||
|
//determine the CueFileInfo's type, based on extension and extra checking
|
||||||
|
//TODO - once we reorganize the file ID stuff, do legit checks here (this is completely redundant with the fileID system
|
||||||
|
//TODO - decode vs stream vs unpossible policies in input policies object (including ffmpeg availability-checking callback (results can be cached))
|
||||||
|
string blobPathExt = Path.GetExtension(choice).ToUpperInvariant();
|
||||||
|
if (blobPathExt == ".BIN" || blobPathExt == ".IMG") cfi.Type = CompiledCueFileType.BIN;
|
||||||
|
else if (blobPathExt == ".ISO") cfi.Type = CompiledCueFileType.BIN;
|
||||||
|
else if (blobPathExt == ".WAV")
|
||||||
|
{
|
||||||
|
//quickly, check the format. turn it to DecodeAudio if it can't be supported
|
||||||
|
//TODO - fix exception-throwing inside
|
||||||
|
//TODO - verify stream-disposing semantics
|
||||||
|
var fs = File.OpenRead(choice);
|
||||||
|
using (var blob = new Disc.Blob_WaveFile())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
blob.Load(fs);
|
||||||
|
cfi.Type = CompiledCueFileType.WAVE;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
cfi.Type = CompiledCueFileType.DecodeAudio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (blobPathExt == ".APE") cfi.Type = CompiledCueFileType.DecodeAudio;
|
||||||
|
else if (blobPathExt == ".MP3") cfi.Type = CompiledCueFileType.DecodeAudio;
|
||||||
|
else if (blobPathExt == ".MPC") cfi.Type = CompiledCueFileType.DecodeAudio;
|
||||||
|
else if (blobPathExt == ".FLAC") cfi.Type = CompiledCueFileType.DecodeAudio;
|
||||||
|
else if (blobPathExt == ".ECM")
|
||||||
|
{
|
||||||
|
cfi.Type = CompiledCueFileType.ECM;
|
||||||
|
if (!Disc.Blob_ECM.IsECM(choice))
|
||||||
|
{
|
||||||
|
Error("an ECM file was specified or detected, but it isn't a valid ECM file: " + Path.GetFileName(choice));
|
||||||
|
cfi.Type = CompiledCueFileType.Unknown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Error("Unknown cue file type. Since it's likely an unsupported compression, this is an error: ", Path.GetFileName(choice));
|
||||||
|
cfi.Type = CompiledCueFileType.Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO - check for mismatches between track types and file types, or is that best done when interpreting the commands?
|
||||||
|
}
|
||||||
|
|
||||||
|
void FinalAnalysis()
|
||||||
|
{
|
||||||
|
//some quick checks:
|
||||||
|
if (OUT_CompiledCueFiles.Count == 0)
|
||||||
|
Error("Cue file doesn't specify any input files!");
|
||||||
|
|
||||||
|
//we can't readily analyze the length of files here, because we'd have to be interpreting the commands to know the track types. Not really worth the trouble
|
||||||
|
//we could check the format of the wav file here, though
|
||||||
|
|
||||||
|
//score the cost of loading the file
|
||||||
|
bool needsCodec = false;
|
||||||
|
OUT_LoadTime = 0;
|
||||||
|
foreach (var cfi in OUT_CompiledCueFiles)
|
||||||
|
{
|
||||||
|
if (cfi.Type == CompiledCueFileType.DecodeAudio)
|
||||||
|
{
|
||||||
|
needsCodec = true;
|
||||||
|
OUT_LoadTime = Math.Max(OUT_LoadTime, 10);
|
||||||
|
}
|
||||||
|
if (cfi.Type == CompiledCueFileType.SeekAudio)
|
||||||
|
needsCodec = true;
|
||||||
|
if (cfi.Type == CompiledCueFileType.ECM)
|
||||||
|
OUT_LoadTime = Math.Max(OUT_LoadTime, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//check whether processing was available
|
||||||
|
if (needsCodec)
|
||||||
|
{
|
||||||
|
FFMpeg ffmpeg = new FFMpeg();
|
||||||
|
if (!ffmpeg.QueryServiceAvailable())
|
||||||
|
Warn("Decoding service will be required for further processing, but is not available");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CloseTrack()
|
||||||
|
{
|
||||||
|
if (curr_track == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
//normalize: if an index 0 is missing, add it here
|
||||||
|
if (curr_track.Indexes[0].Number != 0)
|
||||||
|
{
|
||||||
|
var index0 = new CompiledCueIndex();
|
||||||
|
var index1 = curr_track.Indexes[1];
|
||||||
|
index0.Number = 0;
|
||||||
|
index0.FileMSF = index1.FileMSF;
|
||||||
|
curr_track.Indexes.Insert(0, index0);
|
||||||
|
}
|
||||||
|
|
||||||
|
OUT_CompiledCueTracks.Add(curr_track);
|
||||||
|
curr_track = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenTrack(CueFile.Command.TRACK trackCommand)
|
||||||
|
{
|
||||||
|
curr_track = new CompiledCueTrack();
|
||||||
|
|
||||||
|
//spill cdtext data into this track
|
||||||
|
curr_cdtext = curr_track.CDTextData;
|
||||||
|
|
||||||
|
curr_track.BlobIndex = curr_blobIndex;
|
||||||
|
curr_track.Number = trackCommand.Number;
|
||||||
|
curr_track.TrackType = trackCommand.Type;
|
||||||
|
|
||||||
|
//default flags
|
||||||
|
if (curr_track.TrackType != CueFile.TrackType.Audio)
|
||||||
|
curr_track.Flags = CueFile.TrackFlags.DATA;
|
||||||
|
|
||||||
|
UpdateDiscInfo(trackCommand);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddIndex(CueFile.Command.INDEX indexCommand)
|
||||||
|
{
|
||||||
|
var newindex = new CompiledCueIndex();
|
||||||
|
newindex.FileMSF = indexCommand.Timestamp;
|
||||||
|
newindex.Number = indexCommand.Number;
|
||||||
|
curr_track.Indexes.Add(newindex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run()
|
||||||
|
{
|
||||||
|
//params
|
||||||
|
var cue = IN_CueFile;
|
||||||
|
|
||||||
|
OUT_GlobalCDText = new CompiledCDText();
|
||||||
|
OUT_CompiledDiscInfo = new CompiledDiscInfo();
|
||||||
|
OUT_CompiledCueFiles = new List<CompiledCueFile>();
|
||||||
|
OUT_CompiledCueTracks = new List<CompiledCueTrack>();
|
||||||
|
|
||||||
|
//global cd text will acquire the cdtext commands set before track commands
|
||||||
|
curr_cdtext = OUT_GlobalCDText;
|
||||||
|
|
||||||
|
for (int i = 0; i < cue.Commands.Count; i++)
|
||||||
|
{
|
||||||
|
var cmd = cue.Commands[i];
|
||||||
|
|
||||||
|
//these commands get dealt with globally. nothing to be done here
|
||||||
|
//(but in the future we need to accumulate them into the compile pass output)
|
||||||
|
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;
|
||||||
|
|
||||||
|
//CD-text and related
|
||||||
|
if (cmd is CueFile.Command.PERFORMER) curr_cdtext.Performer = (cmd as CueFile.Command.PERFORMER).Value;
|
||||||
|
if (cmd is CueFile.Command.SONGWRITER) curr_cdtext.Songwriter = (cmd as CueFile.Command.SONGWRITER).Value;
|
||||||
|
if (cmd is CueFile.Command.TITLE) curr_cdtext.Title = (cmd as CueFile.Command.TITLE).Value;
|
||||||
|
if (cmd is CueFile.Command.ISRC) curr_cdtext.ISRC = (cmd as CueFile.Command.ISRC).Value;
|
||||||
|
|
||||||
|
//flags can only be set when a track command is running
|
||||||
|
if (cmd is CueFile.Command.FLAGS)
|
||||||
|
{
|
||||||
|
curr_track.Flags = (cmd as CueFile.Command.FLAGS).Flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd is CueFile.Command.TRACK)
|
||||||
|
{
|
||||||
|
CloseTrack();
|
||||||
|
OpenTrack(cmd as CueFile.Command.TRACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd is CueFile.Command.FILE)
|
||||||
|
{
|
||||||
|
AddFile(cmd as CueFile.Command.FILE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd is CueFile.Command.INDEX)
|
||||||
|
{
|
||||||
|
AddIndex(cmd as CueFile.Command.INDEX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CloseTrack();
|
||||||
|
|
||||||
|
|
||||||
|
} //Run()
|
||||||
|
} //class CompileCueJob
|
||||||
|
|
||||||
|
} //partial class CUE_Format2
|
||||||
|
|
||||||
|
} //namespace BizHawk.Emulation.DiscSystem
|
|
@ -14,7 +14,5 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
/// The CueFileResolver to be used by this instance
|
/// The CueFileResolver to be used by this instance
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public CueFileResolver Resolver;
|
public CueFileResolver Resolver;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -32,12 +32,12 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
/// For this job, virtually all nonsense input is treated as errors, but the process will try to recover as best it can.
|
/// For this job, virtually all nonsense input is treated as errors, but the process will try to recover as best it can.
|
||||||
/// The user should still reject any jobs which generated errors
|
/// The user should still reject any jobs which generated errors
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class LoadCueJob : LoggedJob
|
internal class LoadCueJob : LoggedJob
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The results of the analysis job, a prerequisite for this
|
/// The results of the compile job, a prerequisite for this
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public AnalyzeCueJob IN_AnalyzeJob;
|
public CompileCueJob IN_CompileJob;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The resulting disc
|
/// The resulting disc
|
||||||
|
@ -54,10 +54,6 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
DiscTOCRaw.SessionFormat sloshy_session1Format = DiscTOCRaw.SessionFormat.Type00_CDROM_CDDA;
|
DiscTOCRaw.SessionFormat sloshy_session1Format = DiscTOCRaw.SessionFormat.Type00_CDROM_CDDA;
|
||||||
bool sloshy_session1Format_determined = false;
|
bool sloshy_session1Format_determined = false;
|
||||||
|
|
||||||
//current cdtext and ISRC state
|
|
||||||
string cdtext_songwriter = null, cdtext_performer = null, cdtext_title = null;
|
|
||||||
string isrc = null;
|
|
||||||
|
|
||||||
//current blob file state
|
//current blob file state
|
||||||
int file_cfi_index = -1;
|
int file_cfi_index = -1;
|
||||||
IBlob file_blob = null;
|
IBlob file_blob = null;
|
||||||
|
@ -76,33 +72,6 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
BurnType burntype_current;
|
BurnType burntype_current;
|
||||||
Timestamp burn_pregap_timestamp;
|
Timestamp burn_pregap_timestamp;
|
||||||
|
|
||||||
//TODO - could this be determiend in an entirely different job from the main TOC entries?
|
|
||||||
void UpdateSloshyTrackData(CueFile.Command.TRACK track)
|
|
||||||
{
|
|
||||||
if (sloshy_firstRecordedTrackNumber == -1)
|
|
||||||
sloshy_firstRecordedTrackNumber = track.Number;
|
|
||||||
sloshy_lastRecordedTrackNumber = track.Number;
|
|
||||||
if(!sloshy_session1Format_determined)
|
|
||||||
{
|
|
||||||
switch (track.Type)
|
|
||||||
{
|
|
||||||
case CueFile.TrackType.Mode2_2336:
|
|
||||||
case CueFile.TrackType.Mode2_2352:
|
|
||||||
sloshy_session1Format = DiscTOCRaw.SessionFormat.Type20_CDXA;
|
|
||||||
sloshy_session1Format_determined = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CueFile.TrackType.CDI_2336:
|
|
||||||
case CueFile.TrackType.CDI_2352:
|
|
||||||
sloshy_session1Format = DiscTOCRaw.SessionFormat.Type10_CDI;
|
|
||||||
sloshy_session1Format_determined = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BeginBurnPregap()
|
void BeginBurnPregap()
|
||||||
{
|
{
|
||||||
|
@ -124,50 +93,50 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
|
|
||||||
void ProcessFile(CueFile.Command.FILE file)
|
void ProcessFile(CueFile.Command.FILE file)
|
||||||
{
|
{
|
||||||
//if we're currently in a file, finish it
|
////if we're currently in a file, finish it
|
||||||
if (file_currentCommand != null)
|
//if (file_currentCommand != null)
|
||||||
BurnToEOF();
|
// BurnToEOF();
|
||||||
|
|
||||||
//open the new blob
|
////open the new blob
|
||||||
file_currentCommand = file;
|
//file_currentCommand = file;
|
||||||
file_msf = 0;
|
//file_msf = 0;
|
||||||
var cfi = IN_AnalyzeJob.OUT_FileInfos[++file_cfi_index];
|
//var cfi = IN_CompileJob.OUT_FileInfos[++file_cfi_index];
|
||||||
|
|
||||||
//mount the file
|
////mount the file
|
||||||
if (cfi.Type == AnalyzeCueJob.CueFileType.BIN || cfi.Type == AnalyzeCueJob.CueFileType.Unknown)
|
//if (cfi.Type == AnalyzeCueJob.CueFileType.BIN || cfi.Type == AnalyzeCueJob.CueFileType.Unknown)
|
||||||
{
|
//{
|
||||||
//raw files:
|
// //raw files:
|
||||||
var blob = new Disc.Blob_RawFile { PhysicalPath = cfi.FullPath };
|
// var blob = new Disc.Blob_RawFile { PhysicalPath = cfi.FullPath };
|
||||||
OUT_Disc.DisposableResources.Add(file_blob = blob);
|
// OUT_Disc.DisposableResources.Add(file_blob = blob);
|
||||||
file_len = blob.Length;
|
// file_len = blob.Length;
|
||||||
}
|
//}
|
||||||
else if (cfi.Type == AnalyzeCueJob.CueFileType.ECM)
|
//else if (cfi.Type == AnalyzeCueJob.CueFileType.ECM)
|
||||||
{
|
//{
|
||||||
var blob = new Disc.Blob_ECM();
|
// var blob = new Disc.Blob_ECM();
|
||||||
OUT_Disc.DisposableResources.Add(file_blob = blob);
|
// OUT_Disc.DisposableResources.Add(file_blob = blob);
|
||||||
blob.Load(cfi.FullPath);
|
// blob.Load(cfi.FullPath);
|
||||||
file_len = blob.Length;
|
// file_len = blob.Length;
|
||||||
}
|
//}
|
||||||
else if (cfi.Type == AnalyzeCueJob.CueFileType.WAVE)
|
//else if (cfi.Type == AnalyzeCueJob.CueFileType.WAVE)
|
||||||
{
|
//{
|
||||||
var blob = new Disc.Blob_WaveFile();
|
// var blob = new Disc.Blob_WaveFile();
|
||||||
OUT_Disc.DisposableResources.Add(file_blob = blob);
|
// OUT_Disc.DisposableResources.Add(file_blob = blob);
|
||||||
blob.Load(cfi.FullPath);
|
// blob.Load(cfi.FullPath);
|
||||||
file_len = blob.Length;
|
// file_len = blob.Length;
|
||||||
}
|
//}
|
||||||
else if (cfi.Type == AnalyzeCueJob.CueFileType.DecodeAudio)
|
//else if (cfi.Type == AnalyzeCueJob.CueFileType.DecodeAudio)
|
||||||
{
|
//{
|
||||||
FFMpeg ffmpeg = new FFMpeg();
|
// FFMpeg ffmpeg = new FFMpeg();
|
||||||
if (!ffmpeg.QueryServiceAvailable())
|
// 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.)");
|
// 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();
|
// AudioDecoder dec = new AudioDecoder();
|
||||||
byte[] buf = dec.AcquireWaveData(cfi.FullPath);
|
// byte[] buf = dec.AcquireWaveData(cfi.FullPath);
|
||||||
var blob = new Disc.Blob_WaveFile();
|
// var blob = new Disc.Blob_WaveFile();
|
||||||
OUT_Disc.DisposableResources.Add(file_blob = blob);
|
// OUT_Disc.DisposableResources.Add(file_blob = blob);
|
||||||
blob.Load(new MemoryStream(buf));
|
// blob.Load(new MemoryStream(buf));
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BurnToEOF()
|
void BurnToEOF()
|
||||||
|
@ -254,97 +223,96 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
|
|
||||||
public void Run()
|
public void Run()
|
||||||
{
|
{
|
||||||
//params
|
////params
|
||||||
var cue = IN_AnalyzeJob.IN_CueFile;
|
//var cue = IN_AnalyzeJob.IN_CueFile;
|
||||||
OUT_Disc = new Disc();
|
//OUT_Disc = new Disc();
|
||||||
|
|
||||||
//add sectors for the "mandatory track 1 pregap", which isn't stored in the CCD file
|
////add sectors for the "mandatory track 1 pregap", which isn't stored in the CCD file
|
||||||
//THIS IS JUNK. MORE CORRECTLY SYNTHESIZE IT
|
////THIS IS JUNK. MORE CORRECTLY SYNTHESIZE IT
|
||||||
for (int i = 0; i < 150; i++)
|
//for (int i = 0; i < 150; i++)
|
||||||
{
|
//{
|
||||||
var zero_sector = new Sector_Zero();
|
// var zero_sector = new Sector_Zero();
|
||||||
var zero_subSector = new ZeroSubcodeSector();
|
// var zero_subSector = new ZeroSubcodeSector();
|
||||||
var se_leadin = new SectorEntry(zero_sector);
|
// var se_leadin = new SectorEntry(zero_sector);
|
||||||
se_leadin.SubcodeSector = zero_subSector;
|
// se_leadin.SubcodeSector = zero_subSector;
|
||||||
OUT_Disc.Sectors.Add(se_leadin);
|
// OUT_Disc.Sectors.Add(se_leadin);
|
||||||
}
|
//}
|
||||||
|
|
||||||
//now for the magic. Process commands in order
|
////now for the magic. Process commands in order
|
||||||
for (int i = 0; i < cue.Commands.Count; i++)
|
//for (int i = 0; i < cue.Commands.Count; i++)
|
||||||
{
|
//{
|
||||||
var cmd = cue.Commands[i];
|
// var cmd = cue.Commands[i];
|
||||||
|
|
||||||
//these commands get dealt with globally. nothing to be done here
|
// //these commands get dealt with globally. nothing to be done here
|
||||||
if (cmd is CueFile.Command.CATALOG || cmd is CueFile.Command.CDTEXTFILE) continue;
|
// if (cmd is CueFile.Command.CATALOG || cmd is CueFile.Command.CDTEXTFILE) continue;
|
||||||
|
|
||||||
//nothing to be done for comments
|
// //nothing to be done for comments
|
||||||
if (cmd is CueFile.Command.REM) continue;
|
// if (cmd is CueFile.Command.REM) continue;
|
||||||
if (cmd is CueFile.Command.COMMENT) continue;
|
// if (cmd is CueFile.Command.COMMENT) continue;
|
||||||
|
|
||||||
//handle cdtext and ISRC state updates, theyre kind of like little registers
|
// //handle cdtext and ISRC state updates, theyre kind of like little registers
|
||||||
if (cmd is CueFile.Command.PERFORMER)
|
// if (cmd is CueFile.Command.PERFORMER)
|
||||||
cdtext_performer = (cmd as CueFile.Command.PERFORMER).Value;
|
// cdtext_performer = (cmd as CueFile.Command.PERFORMER).Value;
|
||||||
if (cmd is CueFile.Command.SONGWRITER)
|
// if (cmd is CueFile.Command.SONGWRITER)
|
||||||
cdtext_songwriter = (cmd as CueFile.Command.SONGWRITER).Value;
|
// cdtext_songwriter = (cmd as CueFile.Command.SONGWRITER).Value;
|
||||||
if (cmd is CueFile.Command.TITLE)
|
// if (cmd is CueFile.Command.TITLE)
|
||||||
cdtext_title = (cmd as CueFile.Command.TITLE).Value;
|
// cdtext_title = (cmd as CueFile.Command.TITLE).Value;
|
||||||
if (cmd is CueFile.Command.ISRC)
|
// if (cmd is CueFile.Command.ISRC)
|
||||||
isrc = (cmd as CueFile.Command.ISRC).Value;
|
// 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
|
// //flags are also a kind of a register. but the flags value is reset by the track command
|
||||||
if (cmd is CueFile.Command.FLAGS)
|
// if (cmd is CueFile.Command.FLAGS)
|
||||||
{
|
// {
|
||||||
track_pendingFlags = (cmd as CueFile.Command.FLAGS).Flags;
|
// track_pendingFlags = (cmd as CueFile.Command.FLAGS).Flags;
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (cmd is CueFile.Command.TRACK)
|
// if (cmd is CueFile.Command.TRACK)
|
||||||
{
|
// {
|
||||||
var track = cmd as CueFile.Command.TRACK;
|
// var track = cmd as CueFile.Command.TRACK;
|
||||||
|
|
||||||
//register the track for further processing when an GENERATION command appears
|
// //register the track for further processing when an GENERATION command appears
|
||||||
track_pendingCommand = track;
|
// track_pendingCommand = track;
|
||||||
track_pendingFlags = CueFile.TrackFlags.None;
|
// track_pendingFlags = CueFile.TrackFlags.None;
|
||||||
|
|
||||||
UpdateSloshyTrackData(track);
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
if (cmd is CueFile.Command.FILE)
|
// if (cmd is CueFile.Command.FILE)
|
||||||
{
|
// {
|
||||||
ProcessFile(cmd as CueFile.Command.FILE);
|
// ProcessFile(cmd as CueFile.Command.FILE);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (cmd is CueFile.Command.INDEX)
|
// if (cmd is CueFile.Command.INDEX)
|
||||||
{
|
// {
|
||||||
ProcessIndex(cmd as CueFile.Command.INDEX);
|
// ProcessIndex(cmd as CueFile.Command.INDEX);
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
|
|
||||||
BurnToEOF();
|
//BurnToEOF();
|
||||||
|
|
||||||
//add RawTOCEntries A0 A1 A2 to round out the TOC
|
////add RawTOCEntries A0 A1 A2 to round out the TOC
|
||||||
var TOCMiscInfo = new Synthesize_A0A1A2_Job {
|
//var TOCMiscInfo = new Synthesize_A0A1A2_Job {
|
||||||
IN_FirstRecordedTrackNumber = sloshy_firstRecordedTrackNumber,
|
// IN_FirstRecordedTrackNumber = sloshy_firstRecordedTrackNumber,
|
||||||
IN_LastRecordedTrackNumber = sloshy_lastRecordedTrackNumber,
|
// IN_LastRecordedTrackNumber = sloshy_lastRecordedTrackNumber,
|
||||||
IN_Session1Format = sloshy_session1Format,
|
// IN_Session1Format = sloshy_session1Format,
|
||||||
IN_LeadoutTimestamp = new Timestamp(OUT_Disc.Sectors.Count)
|
// IN_LeadoutTimestamp = new Timestamp(OUT_Disc.Sectors.Count)
|
||||||
};
|
//};
|
||||||
TOCMiscInfo.Run(OUT_Disc.RawTOCEntries);
|
//TOCMiscInfo.Run(OUT_Disc.RawTOCEntries);
|
||||||
|
|
||||||
//generate the TOCRaw from the RawTocEntries
|
////generate the TOCRaw from the RawTocEntries
|
||||||
var tocSynth = new DiscTOCRaw.SynthesizeFromRawTOCEntriesJob() { Entries = OUT_Disc.RawTOCEntries };
|
//var tocSynth = new DiscTOCRaw.SynthesizeFromRawTOCEntriesJob() { Entries = OUT_Disc.RawTOCEntries };
|
||||||
tocSynth.Run();
|
//tocSynth.Run();
|
||||||
OUT_Disc.TOCRaw = tocSynth.Result;
|
//OUT_Disc.TOCRaw = tocSynth.Result;
|
||||||
|
|
||||||
//generate lead-out track with some canned number of sectors
|
////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
|
////TODO - move this somewhere else and make it controllable depending on which console is loading up the disc
|
||||||
//TODO - we're not doing this yet
|
////TODO - we're not doing this yet
|
||||||
//var synthLeadoutJob = new Disc.SynthesizeLeadoutJob { Disc = disc, Length = 150 };
|
////var synthLeadoutJob = new Disc.SynthesizeLeadoutJob { Disc = disc, Length = 150 };
|
||||||
//synthLeadoutJob.Run();
|
////synthLeadoutJob.Run();
|
||||||
|
|
||||||
//blech, old crap, maybe
|
////blech, old crap, maybe
|
||||||
OUT_Disc.Structure.Synthesize_TOCPointsFromSessions();
|
//OUT_Disc.Structure.Synthesize_TOCPointsFromSessions();
|
||||||
|
|
||||||
FinishLog();
|
//FinishLog();
|
||||||
|
|
||||||
} //Run()
|
} //Run()
|
||||||
} //class LoadCueJob
|
} //class LoadCueJob
|
||||||
|
|
|
@ -109,7 +109,7 @@ namespace BizHawk.Emulation.DiscSystem
|
||||||
Mode1_2048, //CDROM Mode1 Data (cooked)
|
Mode1_2048, //CDROM Mode1 Data (cooked)
|
||||||
Mode1_2352, //CDROM Mode1 Data (raw)
|
Mode1_2352, //CDROM Mode1 Data (raw)
|
||||||
Mode2_2336, //CDROM-XA Mode2 Data (could contain form 1 or form 2)
|
Mode2_2336, //CDROM-XA Mode2 Data (could contain form 1 or form 2)
|
||||||
Mode2_2352, //CDROM-XA Mode2 Data (raw--whats the reason to distinguish this from the other 2352?)
|
Mode2_2352, //CDROM-XA Mode2 Data (but there's no reason to distinguish this from Mode1_2352 other than to alert us that the entire session should be XA
|
||||||
CDI_2336, //CDI Mode2 Data
|
CDI_2336, //CDI Mode2 Data
|
||||||
CDI_2352 //CDI Mode2 Data
|
CDI_2352 //CDI Mode2 Data
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.IO;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace BizHawk.Emulation.DiscSystem
|
||||||
|
{
|
||||||
|
partial class CUE_Format2
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The CUE module user's hook for controlling how cue member file paths get resolved
|
||||||
|
/// </summary>
|
||||||
|
public class CueFileResolver
|
||||||
|
{
|
||||||
|
public bool caseSensitive = false;
|
||||||
|
public bool IsHardcodedResolve { get; private set; }
|
||||||
|
string baseDir;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieving the FullName from a FileInfo can be slow (and probably other operations), so this will cache all the needed values
|
||||||
|
/// TODO - could we treat it like an actual cache and only fill the FullName if it's null?
|
||||||
|
/// </summary>
|
||||||
|
struct MyFileInfo
|
||||||
|
{
|
||||||
|
public string FullName;
|
||||||
|
public FileInfo FileInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
DirectoryInfo diBasedir;
|
||||||
|
MyFileInfo[] fisBaseDir;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// sets the base directory and caches the list of files in the directory
|
||||||
|
/// </summary>
|
||||||
|
public void SetBaseDirectory(string baseDir)
|
||||||
|
{
|
||||||
|
this.baseDir = baseDir;
|
||||||
|
diBasedir = new DirectoryInfo(baseDir);
|
||||||
|
//list all files, so we dont scan repeatedly.
|
||||||
|
fisBaseDir = MyFileInfosFromFileInfos(diBasedir.GetFiles());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// TODO - doesnt seem like we're using this...
|
||||||
|
/// </summary>
|
||||||
|
public void SetHardcodeResolve(IDictionary<string, string> hardcodes)
|
||||||
|
{
|
||||||
|
IsHardcodedResolve = true;
|
||||||
|
fisBaseDir = new MyFileInfo[hardcodes.Count];
|
||||||
|
int i = 0;
|
||||||
|
foreach (var kvp in hardcodes)
|
||||||
|
{
|
||||||
|
fisBaseDir[i++] = new MyFileInfo { FullName = kvp.Key, FileInfo = new FileInfo(kvp.Value) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MyFileInfo[] MyFileInfosFromFileInfos(FileInfo[] fis)
|
||||||
|
{
|
||||||
|
var myfis = new MyFileInfo[fis.Length];
|
||||||
|
for (int i = 0; i < fis.Length; i++)
|
||||||
|
{
|
||||||
|
myfis[i].FileInfo = fis[i];
|
||||||
|
myfis[i].FullName = fis[i].FullName;
|
||||||
|
}
|
||||||
|
return myfis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs cue-intelligent logic to acquire a file requested by the cue.
|
||||||
|
/// Returns the resulting full path(s).
|
||||||
|
/// If there are multiple options, it returns them all
|
||||||
|
/// </summary>
|
||||||
|
public List<string> Resolve(string path)
|
||||||
|
{
|
||||||
|
string targetFile = Path.GetFileName(path);
|
||||||
|
string targetFragment = Path.GetFileNameWithoutExtension(path);
|
||||||
|
|
||||||
|
DirectoryInfo di = null;
|
||||||
|
MyFileInfo[] fileInfos;
|
||||||
|
if (!string.IsNullOrEmpty(Path.GetDirectoryName(path)))
|
||||||
|
{
|
||||||
|
di = new FileInfo(path).Directory;
|
||||||
|
//fileInfos = di.GetFiles(Path.GetFileNameWithoutExtension(path)); //does this work?
|
||||||
|
fileInfos = MyFileInfosFromFileInfos(di.GetFiles()); //we (probably) have to enumerate all the files to do a search anyway, so might as well do this
|
||||||
|
//TODO - dont do the search until a resolve fails
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
di = diBasedir;
|
||||||
|
fileInfos = fisBaseDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
var results = new List<FileInfo>();
|
||||||
|
foreach (var fi in fileInfos)
|
||||||
|
{
|
||||||
|
var ext = Path.GetExtension(fi.FullName).ToLowerInvariant();
|
||||||
|
|
||||||
|
//some choices are always bad: (we're looking for things like .bin and .wav)
|
||||||
|
//it's a little unclear whether we should go for a whitelist or a blacklist here.
|
||||||
|
//there's similar numbers of cases either way.
|
||||||
|
//perhaps we could code both (and prefer choices from the whitelist)
|
||||||
|
if (ext == ".cue" || ext == ".sbi" || ext == ".ccd" || ext == ".sub")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
|
||||||
|
string fragment = Path.GetFileNameWithoutExtension(fi.FullName);
|
||||||
|
//match files with differing extensions
|
||||||
|
int cmp = string.Compare(fragment, targetFragment, !caseSensitive);
|
||||||
|
if (cmp != 0)
|
||||||
|
//match files with another extension added on (likely to be mygame.bin.ecm)
|
||||||
|
cmp = string.Compare(fragment, targetFile, !caseSensitive);
|
||||||
|
if (cmp == 0)
|
||||||
|
results.Add(fi.FileInfo);
|
||||||
|
|
||||||
|
}
|
||||||
|
var ret = new List<string>();
|
||||||
|
foreach (var fi in results)
|
||||||
|
ret.Add(fi.FullName);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue