diff --git a/BizHawk.Emulation.DiscSystem/CUE/CUE_Analyze.cs b/BizHawk.Emulation.DiscSystem/CUE/CUE_Analyze.cs
deleted file mode 100644
index 31fb4c5263..0000000000
--- a/BizHawk.Emulation.DiscSystem/CUE/CUE_Analyze.cs
+++ /dev/null
@@ -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
- {
- ///
- /// input: the CueFile to analyze
- ///
- public CueFile IN_CueFile;
-
- ///
- /// 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.
- ///
- public int OUT_LoadTime;
-
- ///
- /// Analyzed-information about a file member of a cue
- ///
- public class CueFileInfo
- {
- public string FullPath;
- public CueFileType Type;
- public override string ToString()
- {
- return string.Format("{0}: {1}", Type, Path.GetFileName(FullPath));
- }
- }
-
- ///
- /// What type of file we're looking at.. each one would require a different ingestion handler
- ///
- public enum CueFileType
- {
- Unknown,
-
- ///
- /// a raw BIN that can be mounted directly
- ///
- BIN,
-
- ///
- /// a raw WAV that can be mounted directly
- ///
- WAVE,
-
- ///
- /// an ECM file that can be mounted directly (once the index is generated)
- ///
- ECM,
-
- ///
- /// An encoded audio file which can be seeked on the fly, therefore roughly mounted on the fly
- /// THIS ISN'T SUPPORTED YET
- ///
- SeekAudio,
-
- ///
- /// An encoded audio file which can't be seeked on the fly. It must be decoded to a temp buffer, or pre-discohawked
- ///
- DecodeAudio,
- }
-
- ///
- /// For each file referenced by the cue file, info about it
- ///
- public List OUT_FileInfos;
- }
-
- public class CueFileResolver
- {
- public bool caseSensitive = false;
- public bool IsHardcodedResolve { get; private set; }
- string baseDir;
-
- ///
- /// 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?
- ///
- struct MyFileInfo
- {
- public string FullName;
- public FileInfo FileInfo;
- }
-
- DirectoryInfo diBasedir;
- MyFileInfo[] fisBaseDir;
-
- ///
- /// sets the base directory and caches the list of files in the directory
- ///
- public void SetBaseDirectory(string baseDir)
- {
- this.baseDir = baseDir;
- diBasedir = new DirectoryInfo(baseDir);
- //list all files, so we dont scan repeatedly.
- fisBaseDir = MyFileInfosFromFileInfos(diBasedir.GetFiles());
- }
-
- ///
- /// TODO - doesnt seem like we're using this...
- ///
- public void SetHardcodeResolve(IDictionary 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;
- }
-
- ///
- /// 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
- ///
- public List 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();
- 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();
- foreach (var fi in results)
- ret.Add(fi.FullName);
- return ret;
- }
- }
-
- ///
- /// Analyzes a cue file and its dependencies.
- /// This should run as fast as possible.. no deep inspection of files
- ///
- public void AnalyzeCueFile(AnalyzeCueJob job)
- {
- //TODO - handle CD text file
-
- job.OUT_FileInfos = new List();
-
- 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
\ No newline at end of file
diff --git a/BizHawk.Emulation.DiscSystem/CUE/CUE_Compile.cs b/BizHawk.Emulation.DiscSystem/CUE/CUE_Compile.cs
new file mode 100644
index 0000000000..353d4f53a7
--- /dev/null
+++ b/BizHawk.Emulation.DiscSystem/CUE/CUE_Compile.cs
@@ -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);
+ }
+ }
+
+ ///
+ /// What type of file we're looking at.. each one would require a different ingestion handler
+ ///
+ public enum CompiledCueFileType
+ {
+ Unknown,
+
+ ///
+ /// a raw BIN that can be mounted directly
+ ///
+ BIN,
+
+ ///
+ /// a raw WAV that can be mounted directly
+ ///
+ WAVE,
+
+ ///
+ /// an ECM file that can be mounted directly (once the index is generated)
+ ///
+ ECM,
+
+ ///
+ /// An encoded audio file which can be seeked on the fly, therefore roughly mounted on the fly
+ /// THIS ISN'T SUPPORTED YET
+ ///
+ SeekAudio,
+
+ ///
+ /// An encoded audio file which can't be seeked on the fly. It must be decoded to a temp buffer, or pre-discohawked
+ ///
+ 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 Indexes = new List();
+
+ 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
+ {
+ ///
+ /// input: the CueFile to analyze
+ ///
+ public CueFile IN_CueFile;
+
+ ///
+ /// The context used for this compiling job
+ ///
+ public CUE_Format2 IN_CueFormat;
+
+ ///
+ /// output: high level disc info
+ ///
+ public CompiledDiscInfo OUT_CompiledDiscInfo;
+
+ ///
+ /// output: CD-Text set at the global level (before any track commands)
+ ///
+ public CompiledCDText OUT_GlobalCDText;
+
+ ///
+ /// output: The compiled file info
+ ///
+ public List OUT_CompiledCueFiles;
+
+ ///
+ /// output: The compiled track info
+ ///
+ public List OUT_CompiledCueTracks;
+
+ ///
+ /// 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.
+ ///
+ 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();
+ OUT_CompiledCueTracks = new List();
+
+ //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
\ No newline at end of file
diff --git a/BizHawk.Emulation.DiscSystem/CUE/CUE_Format.cs b/BizHawk.Emulation.DiscSystem/CUE/CUE_Format.cs
index 764417a342..28baa4ed5a 100644
--- a/BizHawk.Emulation.DiscSystem/CUE/CUE_Format.cs
+++ b/BizHawk.Emulation.DiscSystem/CUE/CUE_Format.cs
@@ -14,7 +14,5 @@ namespace BizHawk.Emulation.DiscSystem
/// The CueFileResolver to be used by this instance
///
public CueFileResolver Resolver;
-
-
}
}
\ No newline at end of file
diff --git a/BizHawk.Emulation.DiscSystem/CUE/CUE_Load.cs b/BizHawk.Emulation.DiscSystem/CUE/CUE_Load.cs
index 63e3120eb2..0865566a4c 100644
--- a/BizHawk.Emulation.DiscSystem/CUE/CUE_Load.cs
+++ b/BizHawk.Emulation.DiscSystem/CUE/CUE_Load.cs
@@ -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.
/// The user should still reject any jobs which generated errors
///
- public class LoadCueJob : LoggedJob
+ internal class LoadCueJob : LoggedJob
{
///
- /// The results of the analysis job, a prerequisite for this
+ /// The results of the compile job, a prerequisite for this
///
- public AnalyzeCueJob IN_AnalyzeJob;
+ public CompileCueJob IN_CompileJob;
///
/// The resulting disc
@@ -54,10 +54,6 @@ namespace BizHawk.Emulation.DiscSystem
DiscTOCRaw.SessionFormat sloshy_session1Format = DiscTOCRaw.SessionFormat.Type00_CDROM_CDDA;
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
int file_cfi_index = -1;
IBlob file_blob = null;
@@ -76,33 +72,6 @@ namespace BizHawk.Emulation.DiscSystem
BurnType burntype_current;
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()
{
@@ -124,50 +93,50 @@ namespace BizHawk.Emulation.DiscSystem
void ProcessFile(CueFile.Command.FILE file)
{
- //if we're currently in a file, finish it
- if (file_currentCommand != null)
- BurnToEOF();
+ ////if we're currently in a file, finish it
+ //if (file_currentCommand != null)
+ // BurnToEOF();
- //open the new blob
- file_currentCommand = file;
- file_msf = 0;
- var cfi = IN_AnalyzeJob.OUT_FileInfos[++file_cfi_index];
+ ////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));
- }
+ ////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()
@@ -254,97 +223,96 @@ namespace BizHawk.Emulation.DiscSystem
public void Run()
{
- //params
- var cue = IN_AnalyzeJob.IN_CueFile;
- OUT_Disc = new Disc();
+ ////params
+ //var cue = IN_AnalyzeJob.IN_CueFile;
+ //OUT_Disc = new Disc();
- //add sectors for the "mandatory track 1 pregap", which isn't stored in the CCD file
- //THIS IS JUNK. MORE CORRECTLY SYNTHESIZE IT
- for (int i = 0; i < 150; i++)
- {
- var zero_sector = new Sector_Zero();
- var zero_subSector = new ZeroSubcodeSector();
- var se_leadin = new SectorEntry(zero_sector);
- se_leadin.SubcodeSector = zero_subSector;
- OUT_Disc.Sectors.Add(se_leadin);
- }
+ ////add sectors for the "mandatory track 1 pregap", which isn't stored in the CCD file
+ ////THIS IS JUNK. MORE CORRECTLY SYNTHESIZE IT
+ //for (int i = 0; i < 150; i++)
+ //{
+ // var zero_sector = new Sector_Zero();
+ // var zero_subSector = new ZeroSubcodeSector();
+ // var se_leadin = new SectorEntry(zero_sector);
+ // se_leadin.SubcodeSector = zero_subSector;
+ // OUT_Disc.Sectors.Add(se_leadin);
+ //}
- //now for the magic. Process commands in order
- for (int i = 0; i < cue.Commands.Count; i++)
- {
- var cmd = cue.Commands[i];
+ ////now for the magic. Process commands in order
+ //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
- if (cmd is CueFile.Command.CATALOG || cmd is CueFile.Command.CDTEXTFILE) continue;
+ // //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;
+ // //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;
+ // //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;
- }
+ // //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;
+ // 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;
+ // //register the track for further processing when an GENERATION command appears
+ // track_pendingCommand = track;
+ // track_pendingFlags = CueFile.TrackFlags.None;
- UpdateSloshyTrackData(track);
- }
+ // }
- if (cmd is CueFile.Command.FILE)
- {
- ProcessFile(cmd as CueFile.Command.FILE);
- }
+ // if (cmd is CueFile.Command.FILE)
+ // {
+ // ProcessFile(cmd as CueFile.Command.FILE);
+ // }
- if (cmd is CueFile.Command.INDEX)
- {
- ProcessIndex(cmd as CueFile.Command.INDEX);
- }
- }
+ // if (cmd is CueFile.Command.INDEX)
+ // {
+ // ProcessIndex(cmd as CueFile.Command.INDEX);
+ // }
+ //}
- BurnToEOF();
+ //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);
+ ////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
- //TODO - we're not doing this yet
- //var synthLeadoutJob = new Disc.SynthesizeLeadoutJob { Disc = disc, Length = 150 };
- //synthLeadoutJob.Run();
+ ////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 - we're not doing this yet
+ ////var synthLeadoutJob = new Disc.SynthesizeLeadoutJob { Disc = disc, Length = 150 };
+ ////synthLeadoutJob.Run();
- //blech, old crap, maybe
- OUT_Disc.Structure.Synthesize_TOCPointsFromSessions();
+ ////blech, old crap, maybe
+ //OUT_Disc.Structure.Synthesize_TOCPointsFromSessions();
- FinishLog();
+ //FinishLog();
} //Run()
} //class LoadCueJob
diff --git a/BizHawk.Emulation.DiscSystem/CUE/CUE_Parse.cs b/BizHawk.Emulation.DiscSystem/CUE/CUE_Parse.cs
index f49099e772..33ced561c9 100644
--- a/BizHawk.Emulation.DiscSystem/CUE/CUE_Parse.cs
+++ b/BizHawk.Emulation.DiscSystem/CUE/CUE_Parse.cs
@@ -109,7 +109,7 @@ namespace BizHawk.Emulation.DiscSystem
Mode1_2048, //CDROM Mode1 Data (cooked)
Mode1_2352, //CDROM Mode1 Data (raw)
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_2352 //CDI Mode2 Data
}
diff --git a/BizHawk.Emulation.DiscSystem/CUE/CueFileResolver.cs b/BizHawk.Emulation.DiscSystem/CUE/CueFileResolver.cs
new file mode 100644
index 0000000000..48ecdf7a6e
--- /dev/null
+++ b/BizHawk.Emulation.DiscSystem/CUE/CueFileResolver.cs
@@ -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
+ {
+ ///
+ /// The CUE module user's hook for controlling how cue member file paths get resolved
+ ///
+ public class CueFileResolver
+ {
+ public bool caseSensitive = false;
+ public bool IsHardcodedResolve { get; private set; }
+ string baseDir;
+
+ ///
+ /// 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?
+ ///
+ struct MyFileInfo
+ {
+ public string FullName;
+ public FileInfo FileInfo;
+ }
+
+ DirectoryInfo diBasedir;
+ MyFileInfo[] fisBaseDir;
+
+ ///
+ /// sets the base directory and caches the list of files in the directory
+ ///
+ public void SetBaseDirectory(string baseDir)
+ {
+ this.baseDir = baseDir;
+ diBasedir = new DirectoryInfo(baseDir);
+ //list all files, so we dont scan repeatedly.
+ fisBaseDir = MyFileInfosFromFileInfos(diBasedir.GetFiles());
+ }
+
+ ///
+ /// TODO - doesnt seem like we're using this...
+ ///
+ public void SetHardcodeResolve(IDictionary 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;
+ }
+
+ ///
+ /// 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
+ ///
+ public List 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();
+ 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();
+ foreach (var fi in results)
+ ret.Add(fi.FullName);
+ return ret;
+ }
+ }
+ }
+}
\ No newline at end of file