diff --git a/BizHawk.Emulation/BizHawk.Emulation.csproj b/BizHawk.Emulation/BizHawk.Emulation.csproj
index 6f157fe239..aa9d8804a6 100644
--- a/BizHawk.Emulation/BizHawk.Emulation.csproj
+++ b/BizHawk.Emulation/BizHawk.Emulation.csproj
@@ -194,6 +194,7 @@
Code
+
Code
@@ -206,9 +207,6 @@
Code
-
- Code
-
Code
diff --git a/BizHawk.Emulation/DiscSystem/Blobs/Blob_WaveFile.cs b/BizHawk.Emulation/DiscSystem/Blobs/Blob_WaveFile.cs
index 96eb46d87c..a48fc75929 100644
--- a/BizHawk.Emulation/DiscSystem/Blobs/Blob_WaveFile.cs
+++ b/BizHawk.Emulation/DiscSystem/Blobs/Blob_WaveFile.cs
@@ -23,13 +23,23 @@ namespace BizHawk.DiscSystem
{
}
+ public void Load(byte[] waveData)
+ {
+ }
+
public void Load(string wavePath)
+ {
+ var stream = new FileStream(wavePath, FileMode.Open, FileAccess.Read, FileShare.Read);
+ Load(stream);
+ }
+
+ public void Load(Stream stream)
{
try
{
RiffSource = null;
var rm = new RiffMaster();
- rm.LoadFile(wavePath);
+ rm.LoadStream(stream);
RiffSource = rm;
//analyze the file to make sure its an OK wave file
@@ -69,6 +79,7 @@ namespace BizHawk.DiscSystem
catch
{
Dispose();
+ throw;
}
}
diff --git a/BizHawk.Emulation/DiscSystem/Blobs/RiffMaster.cs b/BizHawk.Emulation/DiscSystem/Blobs/RiffMaster.cs
index 1f40a82511..3dd832d800 100644
--- a/BizHawk.Emulation/DiscSystem/Blobs/RiffMaster.cs
+++ b/BizHawk.Emulation/DiscSystem/Blobs/RiffMaster.cs
@@ -18,12 +18,11 @@ class RiffMaster : IDisposable
WriteStream(fs);
}
- public FileStream BaseStream;
+ public Stream BaseStream;
public void LoadFile(string fname)
{
- Dispose();
- BaseStream = new FileStream(fname, FileMode.Open, FileAccess.Read, FileShare.Read);
- LoadStream(BaseStream);
+ var fs = new FileStream(fname, FileMode.Open, FileAccess.Read, FileShare.Read);
+ LoadStream(fs);
}
public void Dispose()
@@ -330,8 +329,13 @@ class RiffMaster : IDisposable
riff.WriteStream(s);
}
+ ///
+ /// takes posession of the supplied stream
+ ///
public void LoadStream(Stream s)
{
+ Dispose();
+ BaseStream = s;
readCounter = 0;
BinaryReader br = new BinaryReader(s);
RiffChunk chunk = ReadChunk(br);
diff --git a/BizHawk.Emulation/DiscSystem/CUE_format.cs b/BizHawk.Emulation/DiscSystem/CUE_format.cs
index 668eeaf51c..a0455054f5 100644
--- a/BizHawk.Emulation/DiscSystem/CUE_format.cs
+++ b/BizHawk.Emulation/DiscSystem/CUE_format.cs
@@ -48,23 +48,35 @@ namespace BizHawk.DiscSystem
else if (cue_file.FileType == Cue.CueFileType.Wave)
{
Blob_WaveFile blob = new Blob_WaveFile();
+
try
{
- blob.Load(blobPath);
+ //check for the specified file
+ if (!File.Exists(blobPath))
+ {
+ //if it doesn't exist, then it may be encoded.
+ AudioDecoder dec = new AudioDecoder();
+ byte[] buf = dec.AcquireWaveData(blobPath);
+ blob.Load(new MemoryStream(buf));
+ WasSlowLoad = true;
+ }
+ else
+ {
+ blob.Load(blobPath);
+ }
}
catch (Exception ex)
{
throw new DiscReferenceException(blobPath, ex);
}
- blob_length_lba = (int)(blob.Length / blob_sectorsize);
- blob_leftover = (int)(blob.Length - blob_length_lba * blob_sectorsize);
+ blob_length_lba = (int) (blob.Length/blob_sectorsize);
+ blob_leftover = (int) (blob.Length - blob_length_lba*blob_sectorsize);
cue_blob = blob;
}
else throw new DiscReferenceException(blobPath, new InvalidOperationException("unknown cue file type: " + cue_file.StrFileType));
//TODO - make CueTimestamp better, and also make it a struct, and also just make it DiscTimestamp
- //TODO - wav handling
//TODO - mp3 decode
//start timekeeping for the blob. every time we hit an index, this will advance
diff --git a/BizHawk.Emulation/DiscSystem/Decoding.cs b/BizHawk.Emulation/DiscSystem/Decoding.cs
new file mode 100644
index 0000000000..94bb63280f
--- /dev/null
+++ b/BizHawk.Emulation/DiscSystem/Decoding.cs
@@ -0,0 +1,136 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.IO;
+using System.Collections.Generic;
+
+namespace BizHawk.DiscSystem
+{
+ public class FFMpeg
+ {
+ public static string FFMpegPath;
+
+ public class AudioQueryResult
+ {
+ public bool IsAudio;
+ }
+
+ static string[] Escape(string[] args)
+ {
+ return args.Select(s => s.Contains(" ") ? string.Format("\"{0}\"", s) : s).ToArray();
+ }
+
+ static Regex rxHasAudio = new Regex(@"Stream \#(\d*\.\d*)\: Audio", RegexOptions.Compiled);
+ public AudioQueryResult QueryAudio(string path)
+ {
+ var ret = new AudioQueryResult();
+ string stdout = Run("-i", path);
+ ret.IsAudio = rxHasAudio.Matches(stdout).Count > 0;
+ return ret;
+ }
+
+ string Run(params string[] args)
+ {
+ args = Escape(args);
+ StringBuilder sbCmdline = new StringBuilder();
+ for (int i = 0; i < args.Length; i++)
+ {
+ sbCmdline.Append(args[i]);
+ if (i != args.Length - 1) sbCmdline.Append(' ');
+ }
+
+ ProcessStartInfo oInfo = new ProcessStartInfo(FFMpegPath, sbCmdline.ToString());
+ oInfo.UseShellExecute = false;
+ oInfo.CreateNoWindow = true;
+ oInfo.RedirectStandardOutput = true;
+ oInfo.RedirectStandardError = true;
+
+ Process proc = System.Diagnostics.Process.Start(oInfo);
+ proc.WaitForExit();
+ string result = proc.StandardError.ReadToEnd();
+
+ return result;
+ }
+
+ public byte[] DecodeAudio(string path)
+ {
+ string tempfile = Path.GetTempFileName();
+ try
+ {
+ string runResults = Run("-i", path, "-f", "wav", "-y", tempfile);
+ byte[] ret = File.ReadAllBytes(tempfile);
+ if (ret.Length == 0)
+ throw new InvalidOperationException("Failure running ffmpeg for audio decode. here was its output:\r\n" + runResults);
+ return ret;
+ }
+ finally
+ {
+ File.Delete(tempfile);
+ }
+ }
+ }
+
+ class AudioDecoder
+ {
+ public class AudioDecoder_Exception : Exception
+ {
+ public AudioDecoder_Exception(string message)
+ : base(message)
+ {
+ }
+ }
+
+ public AudioDecoder()
+ {
+ }
+
+ bool CheckForAudio(string path)
+ {
+ FFMpeg ffmpeg = new FFMpeg();
+ var qa = ffmpeg.QueryAudio(path);
+ return qa.IsAudio;
+ }
+
+ ///
+ /// finds audio at a path similar to the provided path (i.e. finds Track01.mp3 for Track01.wav)
+ ///
+ string FindAudio(string audioPath)
+ {
+ string basePath = Path.GetFileNameWithoutExtension(audioPath);
+ //look for potential candidates
+ var di = new DirectoryInfo(Path.GetDirectoryName(audioPath));
+ var fis = di.GetFiles();
+ //first, look for the file type we actually asked for
+ foreach (var fi in fis)
+ {
+ if (fi.FullName.ToUpper() == audioPath.ToUpper())
+ if (CheckForAudio(fi.FullName))
+ return fi.FullName;
+ }
+ //then look for any other type
+ foreach (var fi in fis)
+ {
+ if (Path.GetFileNameWithoutExtension(fi.FullName) == basePath)
+ {
+ if (CheckForAudio(fi.FullName))
+ return fi.FullName;
+ }
+ }
+ return null;
+ }
+
+ public byte[] AcquireWaveData(string audioPath)
+ {
+ string path = FindAudio(audioPath);
+ if (path == null)
+ {
+ throw new AudioDecoder_Exception("Could not find source audio for: " + Path.GetFileName(audioPath));
+ }
+ FFMpeg ffmpeg = new FFMpeg();
+ return ffmpeg.DecodeAudio(path);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/BizHawk.Emulation/DiscSystem/Disc.API.cs b/BizHawk.Emulation/DiscSystem/Disc.API.cs
index 66b14f4d07..2ee9867568 100644
--- a/BizHawk.Emulation/DiscSystem/Disc.API.cs
+++ b/BizHawk.Emulation/DiscSystem/Disc.API.cs
@@ -76,16 +76,27 @@ namespace BizHawk.DiscSystem
Array.Copy(temp, 16, buffer, offset, 2048);
}
- //main API to determine how many LBA sectors are available
+ ///
+ /// main API to determine how many LBA sectors are available
+ ///
public int LBACount { get { return Sectors.Count; } }
- //main api for reading the TOC from a disc
+ ///
+ /// indicates whether this disc took significant work to load from the disc (i.e. decoding of ECM or audio data)
+ /// In this case, the user may appreciate a prompt to export the disc so that it won't take so long next time.
+ ///
+ public bool WasSlowLoad { get; private set; }
+
+ ///
+ /// main api for reading the TOC from a disc
+ ///
public DiscTOC ReadTOC()
{
return TOC;
}
// converts LBA to minute:second:frame format.
+ //TODO - somewhat redundant with CueTimestamp, which is due for refactoring into something not cue-related
public static void ConvertLBAtoMSF(int lba, out byte m, out byte s, out byte f)
{
m = (byte) (lba / 75 / 60);
diff --git a/BizHawk.Emulation/DiscSystem/FFmpeg.cs b/BizHawk.Emulation/DiscSystem/FFmpeg.cs
deleted file mode 100644
index 1a500ce165..0000000000
--- a/BizHawk.Emulation/DiscSystem/FFmpeg.cs
+++ /dev/null
@@ -1,373 +0,0 @@
-//http://jasonjano.wordpress.com/2010/02/09/a-simple-c-wrapper-for-ffmpeg/
-
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Web;
-using System.IO;
-using System.Diagnostics;
-using System.Configuration;
-using System.Text.RegularExpressions;
-
-namespace ffMpeg
-{
- public class Converter
- {
- public static string _ffExe;
- public string WorkingPath; //i.e. temp
-
- public Converter()
- {
- Initialize();
- }
-
- #region Initialization
- private void Initialize()
- {
- }
-
- private string GetWorkingFile()
- {
- //try the stated directory
- if (File.Exists(_ffExe))
- {
- return _ffExe;
- }
-
- //oops, that didn't work, try the base directory
- if (File.Exists(Path.GetFileName(_ffExe)))
- {
- return Path.GetFileName(_ffExe);
- }
-
- //well, now we are really unlucky, let's just return null
- return null;
- }
- #endregion
-
- #region Get the File without creating a file lock
- public static System.Drawing.Image LoadImageFromFile(string fileName)
- {
- System.Drawing.Image theImage = null;
- using (FileStream fileStream = new FileStream(fileName, FileMode.Open,
- FileAccess.Read))
- {
- byte[] img;
- img = new byte[fileStream.Length];
- fileStream.Read(img, 0, img.Length);
- fileStream.Close();
- theImage = System.Drawing.Image.FromStream(new MemoryStream(img));
- img = null;
- }
- GC.Collect();
- return theImage;
- }
-
- public static MemoryStream LoadMemoryStreamFromFile(string fileName)
- {
- MemoryStream ms = null;
- using (FileStream fileStream = new FileStream(fileName, FileMode.Open,
- FileAccess.Read))
- {
- byte[] fil;
- fil = new byte[fileStream.Length];
- fileStream.Read(fil, 0, fil.Length);
- fileStream.Close();
- ms = new MemoryStream(fil);
- }
- GC.Collect();
- return ms;
- }
- #endregion
-
- #region Run the process
- private string RunProcess(string Parameters)
- {
- //create a process info
- ProcessStartInfo oInfo = new ProcessStartInfo(_ffExe, Parameters);
- oInfo.UseShellExecute = false;
- oInfo.CreateNoWindow = true;
- oInfo.RedirectStandardOutput = true;
- oInfo.RedirectStandardError = true;
-
- //Create the output and streamreader to get the output
- string output = null; StreamReader srOutput = null;
-
- //try the process
- try
- {
- //run the process
- Process proc = System.Diagnostics.Process.Start(oInfo);
-
- proc.WaitForExit();
-
- //get the output
- srOutput = proc.StandardError;
-
- //now put it in a string
- output = srOutput.ReadToEnd();
-
- proc.Close();
- }
- catch (Exception)
- {
- output = string.Empty;
- }
- finally
- {
- //now, if we succeded, close out the streamreader
- if (srOutput != null)
- {
- srOutput.Close();
- srOutput.Dispose();
- }
- }
- return output;
- }
- #endregion
-
- #region GetVideoInfo
- public VideoFile GetVideoInfo(MemoryStream inputFile, string Filename)
- {
- string tempfile = Path.Combine(this.WorkingPath, System.Guid.NewGuid().ToString() + Path.GetExtension(Filename));
- FileStream fs = File.Create(tempfile);
- inputFile.WriteTo(fs);
- fs.Flush();
- fs.Close();
- GC.Collect();
-
- VideoFile vf = null;
- try
- {
- vf = new VideoFile(tempfile);
- }
- catch (Exception ex)
- {
- throw ex;
- }
-
- GetVideoInfo(vf);
-
- try
- {
- File.Delete(tempfile);
- }
- catch (Exception)
- {
-
- }
-
- return vf;
- }
- public VideoFile GetVideoInfo(string inputPath)
- {
- VideoFile vf = null;
- try
- {
- vf = new VideoFile(inputPath);
- }
- catch (Exception ex)
- {
- throw ex;
- }
- GetVideoInfo(vf);
- return vf;
- }
- public void GetVideoInfo(VideoFile input)
- {
- //set up the parameters for video info
- string Params = string.Format("-i \"{0}\"", input.Path);
- string output = RunProcess(Params);
- input.RawInfo = output;
-
- //get duration
- Regex re = new Regex("[D|d]uration:.((\\d|:|\\.)*)");
- Match m = re.Match(input.RawInfo);
-
- if (m.Success)
- {
- string duration = m.Groups[1].Value;
- string[] timepieces = duration.Split(new char[] { ':', '.' });
- if (timepieces.Length == 4)
- {
- input.Duration = new TimeSpan(0, Convert.ToInt16(timepieces[0]), Convert.ToInt16(timepieces[1]), Convert.ToInt16(timepieces[2]), Convert.ToInt16(timepieces[3]));
- }
- }
-
- //get audio bit rate
- re = new Regex("[B|b]itrate:.((\\d|:)*)");
- m = re.Match(input.RawInfo);
- double kb = 0.0;
- if (m.Success)
- {
- Double.TryParse(m.Groups[1].Value, out kb);
- }
- input.BitRate = kb;
-
- //get the audio format
- re = new Regex("[A|a]udio:.*");
- m = re.Match(input.RawInfo);
- if (m.Success)
- {
- input.AudioFormat = m.Value;
- }
-
- //get the video format
- re = new Regex("[V|v]ideo:.*");
- m = re.Match(input.RawInfo);
- if (m.Success)
- {
- input.VideoFormat = m.Value;
- }
-
- //get the video format
- re = new Regex("(\\d{2,3})x(\\d{2,3})");
- m = re.Match(input.RawInfo);
- if (m.Success)
- {
- int width = 0; int height = 0;
- int.TryParse(m.Groups[1].Value, out width);
- int.TryParse(m.Groups[2].Value, out height);
- input.Width = width;
- input.Height = height;
- }
- input.infoGathered = true;
- }
- #endregion
-
- #region Convert to FLV
- public OutputPackage ConvertToFLV(MemoryStream inputFile, string Filename)
- {
- string tempfile = Path.Combine(this.WorkingPath, System.Guid.NewGuid().ToString() + Path.GetExtension(Filename));
- FileStream fs = File.Create(tempfile);
- inputFile.WriteTo(fs);
- fs.Flush();
- fs.Close();
- GC.Collect();
-
- VideoFile vf = null;
- try
- {
- vf = new VideoFile(tempfile);
- }
- catch (Exception ex)
- {
- throw ex;
- }
-
- OutputPackage oo = ConvertToFLV(vf);
-
- try
- {
- File.Delete(tempfile);
- }
- catch (Exception)
- {
-
- }
-
- return oo;
- }
- public OutputPackage ConvertToFLV(string inputPath)
- {
- VideoFile vf = null;
- try
- {
- vf = new VideoFile(inputPath);
- }
- catch (Exception ex)
- {
- throw ex;
- }
-
- OutputPackage oo = ConvertToFLV(vf);
- return oo;
- }
- public OutputPackage ConvertToFLV(VideoFile input)
- {
- if (!input.infoGathered)
- {
- GetVideoInfo(input);
- }
- OutputPackage ou = new OutputPackage();
-
- string filename = System.Guid.NewGuid().ToString() + ".flv";
- string finalpath = Path.Combine(this.WorkingPath, filename);
- string Params = string.Format("-i \"{0}\" -y -ar 22050 -ab 64 -f flv \"{1}\"", input.Path, finalpath);
- string output = RunProcess(Params);
-
- if (File.Exists(finalpath))
- {
- ou.VideoStream = LoadMemoryStreamFromFile(finalpath);
- try
- {
- File.Delete(finalpath);
- }
- catch (Exception) { }
- }
- return ou;
- }
- #endregion
- }
-
- public class VideoFile
- {
- #region Properties
- private string _Path;
- public string Path
- {
- get
- {
- return _Path;
- }
- set
- {
- _Path = value;
- }
- }
-
- public TimeSpan Duration { get; set; }
- public double BitRate { get; set; }
- public string AudioFormat { get; set; }
- public string VideoFormat { get; set; }
- public int Height { get; set; }
- public int Width { get; set; }
- public string RawInfo { get; set; }
- public bool infoGathered { get; set; }
- #endregion
-
- #region Constructors
- public VideoFile(string path)
- {
- _Path = path;
- Initialize();
- }
- #endregion
-
- #region Initialization
- private void Initialize()
- {
- this.infoGathered = false;
- //first make sure we have a value for the video file setting
- if (string.IsNullOrEmpty(_Path))
- {
- throw new Exception("Could not find the location of the video file");
- }
-
- //Now see if the video file exists
- if (!File.Exists(_Path))
- {
- throw new Exception("The video file " + _Path + " does not exist.");
- }
- }
- #endregion
- }
-
- public class OutputPackage
- {
- public MemoryStream VideoStream { get; set; }
- public System.Drawing.Image PreviewImage { get; set; }
- public string RawOutput { get; set; }
- public bool Success { get; set; }
- }
-}
diff --git a/BizHawk.MultiClient/Config.cs b/BizHawk.MultiClient/Config.cs
index b48ab4c4fe..4eaca5dc7e 100644
--- a/BizHawk.MultiClient/Config.cs
+++ b/BizHawk.MultiClient/Config.cs
@@ -90,7 +90,7 @@
//BIOS Paths
public string PathPCEBios = ".\\PCECDBios.pce"; //TODO: better default filename
- public string FFMpegPath = "ffmpeg.exe";
+ public string FFMpegPath = "%exe%/ffmpeg.exe";
// General Client Settings
public int TargetZoomFactor = 2;
diff --git a/BizHawk.MultiClient/MainForm.cs b/BizHawk.MultiClient/MainForm.cs
index 70b71d521d..4f5e74494c 100644
--- a/BizHawk.MultiClient/MainForm.cs
+++ b/BizHawk.MultiClient/MainForm.cs
@@ -73,6 +73,7 @@ namespace BizHawk.MultiClient
LogConsole.ShowConsole();
displayLogWindowToolStripMenuItem.Checked = true;
}
+ DiscSystem.FFMpeg.FFMpegPath = PathManager.MakeProgramRelativePath(Global.Config.FFMpegPath);
Global.CheatList = new CheatList();
UpdateStatusSlots();
diff --git a/DiscoHawk/DiscoHawk.cs b/DiscoHawk/DiscoHawk.cs
index cd1a7e2784..dbe6195b8f 100644
--- a/DiscoHawk/DiscoHawk.cs
+++ b/DiscoHawk/DiscoHawk.cs
@@ -1,4 +1,5 @@
using System;
+using System.Reflection;
using System.Collections.Generic;
using System.IO;
@@ -13,9 +14,20 @@ namespace BizHawk
{
class DiscoHawk
{
+
+ public static string GetExeDirectoryAbsolute()
+ {
+ string p = Path.GetDirectoryName(Assembly.GetEntryAssembly().GetName().CodeBase);
+ if (p.Substring(0, 6) == "file:\\")
+ p = p.Remove(0, 6);
+ string z = p;
+ return p;
+ }
+
[STAThread]
static void Main(string[] args)
{
+ DiscSystem.FFMpeg.FFMpegPath = Path.Combine(GetExeDirectoryAbsolute(), "ffmpeg.exe");
new DiscoHawk().Run(args);
}
@@ -91,7 +103,18 @@ namespace BizHawk
//}
//DiscSystem.Disc disc = DiscSystem.Disc.FromCuePath(@"D:\discs\Bomberman_'94_Taikenban_(SCD)(JPN)_-_wav'd\Bomberman '94 Taikenban (SCD)(JPN)_hawked.cue");
- DiscSystem.Disc disc = DiscSystem.Disc.FromCuePath(@"D:\discs\Bomberman_'94_Taikenban_(SCD)(JPN)_-_wav'd\Bomberman '94 Taikenban (SCD)(JPN).cue");
+ //DiscSystem.Disc disc = DiscSystem.Disc.FromCuePath(@"D:\discs\Bomberman_'94_Taikenban_(SCD)(JPN)_-_wav'd\Bomberman '94 Taikenban (SCD)(JPN).cue");
+ //var prefs = new DiscSystem.CueBinPrefs();
+ //prefs.AnnotateCue = false;
+ //prefs.OneBlobPerTrack = false;
+ //prefs.ReallyDumpBin = true;
+ //prefs.OmitRedundantIndex0 = true;
+ //prefs.SingleSession = true;
+ ////var cueBin = disc.DumpCueBin("Bomberman '94 Taikenban (SCD)(JPN)_hawked_hawked", prefs);
+ //var cueBin = disc.DumpCueBin("Bomberman '94 Taikenban (SCD)(JPN)_hawked", prefs);
+ //cueBin.Dump(@"D:\discs\Bomberman_'94_Taikenban_(SCD)(JPN)_-_wav'd", prefs);
+
+ DiscSystem.Disc disc = DiscSystem.Disc.FromCuePath(@"D:\discs\Angels II - Holy Night (J)[Prototype]\Angels II - Holy Night (J)[Prototype].cue");
var prefs = new DiscSystem.CueBinPrefs();
prefs.AnnotateCue = false;
prefs.OneBlobPerTrack = false;
@@ -99,8 +122,8 @@ namespace BizHawk
prefs.OmitRedundantIndex0 = true;
prefs.SingleSession = true;
//var cueBin = disc.DumpCueBin("Bomberman '94 Taikenban (SCD)(JPN)_hawked_hawked", prefs);
- var cueBin = disc.DumpCueBin("Bomberman '94 Taikenban (SCD)(JPN)_hawked", prefs);
- cueBin.Dump(@"D:\discs\Bomberman_'94_Taikenban_(SCD)(JPN)_-_wav'd", prefs);
+ var cueBin = disc.DumpCueBin("test", prefs);
+ cueBin.Dump(@"D:\discs\Angels II - Holy Night (J)[Prototype]", prefs);
}
}