From fe7da7c5b5e6158979b8813b8817ed4cc0d4cce1 Mon Sep 17 00:00:00 2001 From: adelikat Date: Sun, 3 Nov 2013 23:45:44 +0000 Subject: [PATCH] move disc stuff out of BizHawk.Emulation into a new project BizHawk.Emulation.DiscSystem, updated namesspaces in those files, set up other projects with the right references and usings --- .../BizHawk.Client.Common.csproj | 4 + BizHawk.Client.Common/Global.cs | 2 +- BizHawk.Client.DiscoHawk/AudioExtractor.cs | 9 +- .../BizHawk.Client.DiscoHawk.csproj | 10 +- BizHawk.Client.DiscoHawk/DiscoHawk.cs | 4 +- BizHawk.Client.DiscoHawk/DiscoHawkDialog.cs | 5 +- BizHawk.Client.DiscoHawk/MainDiscoForm.cs | 4 +- BizHawk.Client.DiscoHawk/ProgressDialog.cs | 6 +- .../BizHawk.Client.EmuHawk.csproj | 4 + BizHawk.Client.EmuHawk/MainForm.cs | 8 +- .../BizHawk.Emulation.DiscSystem.csproj | 84 + .../Blobs/Blob_ECM.cs | 2 +- .../Blobs/Blob_WaveFile.cs | 2 +- .../Blobs/RiffMaster.cs | 348 ++++ BizHawk.Emulation.DiscSystem/Buffer.cs | 118 ++ .../CCD_format.cs | 2 +- .../CUE_format.cs | 2 +- .../Decoding.cs | 2 +- .../Disc.API.cs | 8 +- .../Disc.ID.cs | 4 +- .../Disc.cs | 2 +- .../DiscTOC.cs | 3 +- .../ECM.cs | 2 +- .../GPL_ECM.cs | 0 .../Properties/AssemblyInfo.cs | 36 + .../Subcode.cs | 2 +- .../TOC_format.cs | 2 +- BizHawk.Emulation.DiscSystem/Util.cs | 922 ++++++++++ .../cdfs/EndianBitConverter.cs | 3 +- .../cdfs/ISODirectoryNode.cs | 156 ++ BizHawk.Emulation.DiscSystem/cdfs/ISOFile.cs | 140 ++ .../cdfs/ISOFileNode.cs | 26 + BizHawk.Emulation.DiscSystem/cdfs/ISONode.cs | 46 + .../cdfs/ISONodeRecord.cs | 217 +++ .../cdfs/ISOVolumeDescriptor.cs | 326 ++++ .../docs/notes.txt | 0 BizHawk.Emulation/BizHawk.Emulation.csproj | 46 +- .../Consoles/PC Engine/PCEngine.cs | 2 +- .../Consoles/PC Engine/ScsiCDBus.cs | 1485 +++++++++-------- .../Consoles/Sega/Saturn/Yabause.cs | 3 +- .../DiscSystem/Blobs/RiffMaster.cs | 346 ---- .../DiscSystem/cdfs/ISODirectoryNode.cs | 145 -- BizHawk.Emulation/DiscSystem/cdfs/ISOFile.cs | 135 -- .../DiscSystem/cdfs/ISOFileNode.cs | 22 - BizHawk.Emulation/DiscSystem/cdfs/ISONode.cs | 43 - .../DiscSystem/cdfs/ISONodeRecord.cs | 208 --- .../DiscSystem/cdfs/ISOVolumeDescriptor.cs | 320 ---- BizHawk.Emulation/Sound/CDAudio.cs | 507 +++--- BizHawk.sln | 14 + 49 files changed, 3492 insertions(+), 2295 deletions(-) create mode 100644 BizHawk.Emulation.DiscSystem/BizHawk.Emulation.DiscSystem.csproj rename {BizHawk.Emulation/DiscSystem => BizHawk.Emulation.DiscSystem}/Blobs/Blob_ECM.cs (96%) rename {BizHawk.Emulation/DiscSystem => BizHawk.Emulation.DiscSystem}/Blobs/Blob_WaveFile.cs (94%) create mode 100644 BizHawk.Emulation.DiscSystem/Blobs/RiffMaster.cs create mode 100644 BizHawk.Emulation.DiscSystem/Buffer.cs rename {BizHawk.Emulation/DiscSystem => BizHawk.Emulation.DiscSystem}/CCD_format.cs (78%) rename {BizHawk.Emulation/DiscSystem => BizHawk.Emulation.DiscSystem}/CUE_format.cs (96%) rename {BizHawk.Emulation/DiscSystem => BizHawk.Emulation.DiscSystem}/Decoding.cs (95%) rename {BizHawk.Emulation/DiscSystem => BizHawk.Emulation.DiscSystem}/Disc.API.cs (92%) rename {BizHawk.Emulation/DiscSystem => BizHawk.Emulation.DiscSystem}/Disc.ID.cs (93%) rename {BizHawk.Emulation/DiscSystem => BizHawk.Emulation.DiscSystem}/Disc.cs (96%) rename {BizHawk.Emulation/DiscSystem => BizHawk.Emulation.DiscSystem}/DiscTOC.cs (96%) rename {BizHawk.Emulation/DiscSystem => BizHawk.Emulation.DiscSystem}/ECM.cs (96%) rename {BizHawk.Emulation/DiscSystem => BizHawk.Emulation.DiscSystem}/GPL_ECM.cs (100%) create mode 100644 BizHawk.Emulation.DiscSystem/Properties/AssemblyInfo.cs rename {BizHawk.Emulation/DiscSystem => BizHawk.Emulation.DiscSystem}/Subcode.cs (93%) rename {BizHawk.Emulation/DiscSystem => BizHawk.Emulation.DiscSystem}/TOC_format.cs (67%) create mode 100644 BizHawk.Emulation.DiscSystem/Util.cs rename {BizHawk.Emulation/DiscSystem => BizHawk.Emulation.DiscSystem}/cdfs/EndianBitConverter.cs (95%) create mode 100644 BizHawk.Emulation.DiscSystem/cdfs/ISODirectoryNode.cs create mode 100644 BizHawk.Emulation.DiscSystem/cdfs/ISOFile.cs create mode 100644 BizHawk.Emulation.DiscSystem/cdfs/ISOFileNode.cs create mode 100644 BizHawk.Emulation.DiscSystem/cdfs/ISONode.cs create mode 100644 BizHawk.Emulation.DiscSystem/cdfs/ISONodeRecord.cs create mode 100644 BizHawk.Emulation.DiscSystem/cdfs/ISOVolumeDescriptor.cs rename {BizHawk.Emulation/DiscSystem => BizHawk.Emulation.DiscSystem}/docs/notes.txt (100%) delete mode 100644 BizHawk.Emulation/DiscSystem/Blobs/RiffMaster.cs delete mode 100644 BizHawk.Emulation/DiscSystem/cdfs/ISODirectoryNode.cs delete mode 100644 BizHawk.Emulation/DiscSystem/cdfs/ISOFile.cs delete mode 100644 BizHawk.Emulation/DiscSystem/cdfs/ISOFileNode.cs delete mode 100644 BizHawk.Emulation/DiscSystem/cdfs/ISONode.cs delete mode 100644 BizHawk.Emulation/DiscSystem/cdfs/ISONodeRecord.cs delete mode 100644 BizHawk.Emulation/DiscSystem/cdfs/ISOVolumeDescriptor.cs diff --git a/BizHawk.Client.Common/BizHawk.Client.Common.csproj b/BizHawk.Client.Common/BizHawk.Client.Common.csproj index 8d0cef30d6..0cfa24639a 100644 --- a/BizHawk.Client.Common/BizHawk.Client.Common.csproj +++ b/BizHawk.Client.Common/BizHawk.Client.Common.csproj @@ -147,6 +147,10 @@ {866F8D13-0678-4FF9-80A4-A3993FD4D8A3} BizHawk.Common + + {f51946ea-827f-4d82-b841-1f2f6d060312} + BizHawk.Emulation.DiscSystem + {197D4314-8A9F-49BA-977D-54ACEFAEB6BA} BizHawk.Emulation diff --git a/BizHawk.Client.Common/Global.cs b/BizHawk.Client.Common/Global.cs index 021f6e31c4..ea702843c1 100644 --- a/BizHawk.Client.Common/Global.cs +++ b/BizHawk.Client.Common/Global.cs @@ -1,4 +1,4 @@ -using BizHawk.DiscSystem; +using BizHawk.Emulation.DiscSystem; namespace BizHawk.Client.Common { diff --git a/BizHawk.Client.DiscoHawk/AudioExtractor.cs b/BizHawk.Client.DiscoHawk/AudioExtractor.cs index 20a5ebf561..aa045c84ce 100644 --- a/BizHawk.Client.DiscoHawk/AudioExtractor.cs +++ b/BizHawk.Client.DiscoHawk/AudioExtractor.cs @@ -1,11 +1,12 @@ using System; -using System.Windows.Forms; using System.Collections.Generic; +using System.Diagnostics; +using System.Windows.Forms; +using System.IO; using System.Linq; using System.Text; -using BizHawk.DiscSystem; -using System.IO; -using System.Diagnostics; + +using BizHawk.Emulation.DiscSystem; namespace BizHawk.Client.DiscoHawk { diff --git a/BizHawk.Client.DiscoHawk/BizHawk.Client.DiscoHawk.csproj b/BizHawk.Client.DiscoHawk/BizHawk.Client.DiscoHawk.csproj index 4e46b00a18..16b6611ef5 100644 --- a/BizHawk.Client.DiscoHawk/BizHawk.Client.DiscoHawk.csproj +++ b/BizHawk.Client.DiscoHawk/BizHawk.Client.DiscoHawk.csproj @@ -149,13 +149,13 @@ - - {197D4314-8A9F-49BA-977D-54ACEFAEB6BA} - BizHawk.Emulation - + - + + {f51946ea-827f-4d82-b841-1f2f6d060312} + BizHawk.Emulation.DiscSystem + "$(SolutionDir)subwcrev.bat" "$(ProjectDir)" diff --git a/BizHawk.Client.DiscoHawk/DiscoHawk.cs b/BizHawk.Client.DiscoHawk/DiscoHawk.cs index 9c14985c07..ea31ab6995 100644 --- a/BizHawk.Client.DiscoHawk/DiscoHawk.cs +++ b/BizHawk.Client.DiscoHawk/DiscoHawk.cs @@ -6,6 +6,8 @@ using System.Reflection; using System.Collections.Generic; using System.IO; +using BizHawk.Emulation.DiscSystem; + //cue format preferences notes //pcejin - @@ -55,7 +57,7 @@ namespace BizHawk.Client.DiscoHawk var ffmpegPath = Path.Combine(GetExeDirectoryAbsolute(), "ffmpeg.exe"); if (!File.Exists(ffmpegPath)) ffmpegPath = Path.Combine(Path.Combine(GetExeDirectoryAbsolute(), "dll"), "ffmpeg.exe"); - DiscSystem.FFMpeg.FFMpegPath = ffmpegPath; + FFMpeg.FFMpegPath = ffmpegPath; AudioExtractor.FFmpegPath = ffmpegPath; new DiscoHawk().Run(args); } diff --git a/BizHawk.Client.DiscoHawk/DiscoHawkDialog.cs b/BizHawk.Client.DiscoHawk/DiscoHawkDialog.cs index dc3b885e81..32c679a7c6 100644 --- a/BizHawk.Client.DiscoHawk/DiscoHawkDialog.cs +++ b/BizHawk.Client.DiscoHawk/DiscoHawkDialog.cs @@ -4,11 +4,12 @@ using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; +using System.IO; using System.Linq; using System.Text; -using System.IO; using System.Windows.Forms; -using BizHawk.DiscSystem; + +using BizHawk.Emulation.DiscSystem; namespace BizHawk.Client.DiscoHawk { diff --git a/BizHawk.Client.DiscoHawk/MainDiscoForm.cs b/BizHawk.Client.DiscoHawk/MainDiscoForm.cs index 563d87bff1..7717589fb8 100644 --- a/BizHawk.Client.DiscoHawk/MainDiscoForm.cs +++ b/BizHawk.Client.DiscoHawk/MainDiscoForm.cs @@ -9,7 +9,7 @@ using System.Windows.Forms; using System.IO; using System.Threading; -using BizHawk.DiscSystem; +using BizHawk.Emulation.DiscSystem; namespace BizHawk.Client.DiscoHawk { @@ -38,7 +38,7 @@ namespace BizHawk.Client.DiscoHawk public static CueBinPrefs GetCuePrefs() { - var prefs = new DiscSystem.CueBinPrefs(); + var prefs = new CueBinPrefs(); prefs.AnnotateCue = true; // TODO? checkCueProp_Annotations.Checked; prefs.OneBlobPerTrack = false; //TODO? checkCueProp_OneBlobPerTrack.Checked; prefs.ReallyDumpBin = false; diff --git a/BizHawk.Client.DiscoHawk/ProgressDialog.cs b/BizHawk.Client.DiscoHawk/ProgressDialog.cs index c7895f028a..b3c1e718ab 100644 --- a/BizHawk.Client.DiscoHawk/ProgressDialog.cs +++ b/BizHawk.Client.DiscoHawk/ProgressDialog.cs @@ -8,17 +8,19 @@ using System.Linq; using System.Text; using System.Windows.Forms; +using BizHawk.Emulation.DiscSystem; + namespace BizHawk.Client.DiscoHawk { public partial class ProgressDialog : Form { - public ProgressDialog(DiscSystem.ProgressReport pr) + public ProgressDialog(ProgressReport pr) { InitializeComponent(); this.pr = pr; } - DiscSystem.ProgressReport pr; + ProgressReport pr; private void btnCancel_Click(object sender, EventArgs e) { diff --git a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj index a1edbcb140..d69c96ddd0 100644 --- a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj +++ b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj @@ -1006,6 +1006,10 @@ {866F8D13-0678-4FF9-80A4-A3993FD4D8A3} BizHawk.Common + + {f51946ea-827f-4d82-b841-1f2f6d060312} + BizHawk.Emulation.DiscSystem + {197D4314-8A9F-49BA-977D-54ACEFAEB6BA} BizHawk.Emulation diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index f476892a0f..74afbead9c 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -7,7 +7,9 @@ using System.Linq; using System.Text; using System.Threading; using System.Windows.Forms; -using BizHawk.DiscSystem; + +using BizHawk.Common; +using BizHawk.Client.Common; using BizHawk.Emulation; using BizHawk.Emulation.Computers.Commodore64; using BizHawk.Emulation.Consoles.Calculator; @@ -20,9 +22,7 @@ using BizHawk.Emulation.Consoles.Nintendo.N64; using BizHawk.Emulation.Consoles.Nintendo.SNES; using BizHawk.Emulation.Consoles.Sega; using BizHawk.Emulation.Consoles.TurboGrafx; - -using BizHawk.Common; -using BizHawk.Client.Common; +using BizHawk.Emulation.DiscSystem; namespace BizHawk.Client.EmuHawk { diff --git a/BizHawk.Emulation.DiscSystem/BizHawk.Emulation.DiscSystem.csproj b/BizHawk.Emulation.DiscSystem/BizHawk.Emulation.DiscSystem.csproj new file mode 100644 index 0000000000..d9c0b900bd --- /dev/null +++ b/BizHawk.Emulation.DiscSystem/BizHawk.Emulation.DiscSystem.csproj @@ -0,0 +1,84 @@ + + + + + Debug + AnyCPU + {F51946EA-827F-4D82-B841-1F2F6D060312} + Library + Properties + BizHawk.Emulation.DiscSystem + BizHawk.Emulation.DiscSystem + v4.0 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {866f8d13-0678-4ff9-80a4-a3993fd4d8a3} + BizHawk.Common + + + + + \ No newline at end of file diff --git a/BizHawk.Emulation/DiscSystem/Blobs/Blob_ECM.cs b/BizHawk.Emulation.DiscSystem/Blobs/Blob_ECM.cs similarity index 96% rename from BizHawk.Emulation/DiscSystem/Blobs/Blob_ECM.cs rename to BizHawk.Emulation.DiscSystem/Blobs/Blob_ECM.cs index f3ea4c7ec8..f69d7114e8 100644 --- a/BizHawk.Emulation/DiscSystem/Blobs/Blob_ECM.cs +++ b/BizHawk.Emulation.DiscSystem/Blobs/Blob_ECM.cs @@ -29,7 +29,7 @@ using System.Collections.Generic; using BizHawk.Common; -namespace BizHawk.DiscSystem +namespace BizHawk.Emulation.DiscSystem { partial class Disc { diff --git a/BizHawk.Emulation/DiscSystem/Blobs/Blob_WaveFile.cs b/BizHawk.Emulation.DiscSystem/Blobs/Blob_WaveFile.cs similarity index 94% rename from BizHawk.Emulation/DiscSystem/Blobs/Blob_WaveFile.cs rename to BizHawk.Emulation.DiscSystem/Blobs/Blob_WaveFile.cs index 70be70fad8..dc6f05597c 100644 --- a/BizHawk.Emulation/DiscSystem/Blobs/Blob_WaveFile.cs +++ b/BizHawk.Emulation.DiscSystem/Blobs/Blob_WaveFile.cs @@ -2,7 +2,7 @@ using System.Linq; using System.IO; -namespace BizHawk.DiscSystem +namespace BizHawk.Emulation.DiscSystem { partial class Disc { diff --git a/BizHawk.Emulation.DiscSystem/Blobs/RiffMaster.cs b/BizHawk.Emulation.DiscSystem/Blobs/RiffMaster.cs new file mode 100644 index 0000000000..a9cc0a4e24 --- /dev/null +++ b/BizHawk.Emulation.DiscSystem/Blobs/RiffMaster.cs @@ -0,0 +1,348 @@ +using System; +using System.IO; +using System.Collections.Generic; + +/// +/// Parses a RIFF file into a live data structure. +/// References to large blobs remain mostly on disk in the file which RiffMaster keeps a reference too. Dispose it to close the file. +/// You can modify blobs however you want and write the file back out to a new path, if youre careful (that was the original point of this) +/// Please be sure to test round-tripping when you make any changes. This architecture is a bit tricky to use, but it works if youre careful. +/// + +namespace BizHawk.Emulation.DiscSystem +{ + class RiffMaster : IDisposable + { + public RiffMaster() { } + + public void WriteFile(string fname) + { + using (FileStream fs = new FileStream(fname, FileMode.Create, FileAccess.Write, FileShare.Read)) + WriteStream(fs); + } + + public Stream BaseStream; + public void LoadFile(string fname) + { + var fs = new FileStream(fname, FileMode.Open, FileAccess.Read, FileShare.Read); + LoadStream(fs); + } + + public void Dispose() + { + if (BaseStream != null) BaseStream.Dispose(); + BaseStream = null; + } + + private static string ReadTag(BinaryReader br) + { + return "" + br.ReadChar() + br.ReadChar() + br.ReadChar() + br.ReadChar(); + } + + protected static void WriteTag(BinaryWriter bw, string tag) + { + for (int i = 0; i < 4; i++) + bw.Write(tag[i]); + bw.Flush(); + } + + public abstract class RiffChunk + { + public string tag; + + /// + /// writes this chunk to the stream, including padding + /// + public abstract void WriteStream(Stream s); + + /// + /// distinct from a size or a length, the `volume` is the volume of bytes occupied by the chunk on disk (accounting for padding). + /// + /// + public abstract long GetVolume(); + + /// + /// transforms into a derived class depending on tag + /// + public abstract RiffChunk Morph(); + } + + static class Util + { + public static void CopyStream(Stream src, Stream dest, long len) + { + const int size = 0x2000; + byte[] buffer = new byte[size]; + while (len > 0) + { + long todo = len; + if (len > size) todo = size; + int n = src.Read(buffer, 0, (int)todo); + dest.Write(buffer, 0, n); + len -= n; + } + } + } + + public class RiffSubchunk : RiffChunk + { + public long Position; + public uint Length; + public Stream Source; + public override void WriteStream(Stream s) + { + BinaryWriter bw = new BinaryWriter(s); + WriteTag(bw, tag); + bw.Write(Length); + bw.Flush(); + + Source.Position = Position; + Util.CopyStream(Source, s, Length); + + //all chunks are supposed to be 16bit padded + if (Length % 2 != 0) + s.WriteByte(0); + } + public override long GetVolume() + { + long ret = Length; + if (ret % 2 != 0) ret++; + return ret; + } + + public byte[] ReadAll() + { + int msSize = (int)Math.Min((long)int.MaxValue, Length); + MemoryStream ms = new MemoryStream(msSize); + Source.Position = Position; + Util.CopyStream(Source, ms, Length); + return ms.ToArray(); + } + + public override RiffChunk Morph() + { + switch (tag) + { + case "fmt ": return new RiffSubchunk_fmt(this); + } + return this; + } + } + + public class RiffSubchunk_fmt : RiffSubchunk + { + public enum FORMAT_TAG : ushort + { + WAVE_FORMAT_UNKNOWN = (0x0000), + WAVE_FORMAT_PCM = (0x0001), + WAVE_FORMAT_ADPCM = (0x0002), + WAVE_FORMAT_ALAW = (0x0006), + WAVE_FORMAT_MULAW = (0x0007), + WAVE_FORMAT_OKI_ADPCM = (0x0010), + WAVE_FORMAT_DIGISTD = (0x0015), + WAVE_FORMAT_DIGIFIX = (0x0016), + IBM_FORMAT_MULAW = (0x0101), + IBM_FORMAT_ALAW = (0x0102), + IBM_FORMAT_ADPCM = (0x0103), + } + public FORMAT_TAG format_tag; + public ushort channels; + public uint samplesPerSec; + public uint avgBytesPerSec; + public ushort blockAlign; + public ushort bitsPerSample; + public RiffSubchunk_fmt(RiffSubchunk origin) + { + tag = "fmt "; + BinaryReader br = new BinaryReader(new MemoryStream(origin.ReadAll())); + format_tag = (FORMAT_TAG)br.ReadUInt16(); + channels = br.ReadUInt16(); + samplesPerSec = br.ReadUInt32(); + avgBytesPerSec = br.ReadUInt32(); + blockAlign = br.ReadUInt16(); + bitsPerSample = br.ReadUInt16(); + } + public override void WriteStream(Stream s) + { + Flush(); + base.WriteStream(s); + } + void Flush() + { + MemoryStream ms = new MemoryStream(); + BinaryWriter bw = new BinaryWriter(ms); + bw.Write((ushort)format_tag); + bw.Write(channels); + bw.Write(samplesPerSec); + bw.Write(avgBytesPerSec); + bw.Write(blockAlign); + bw.Write(bitsPerSample); + bw.Flush(); + Source = ms; + Position = 0; + Length = (uint)ms.Length; + } + + public override long GetVolume() + { + Flush(); + return base.GetVolume(); + } + } + + public class RiffContainer : RiffChunk + { + public RiffChunk GetSubchunk(string tag, string type) + { + foreach (RiffChunk rc in subchunks) + if (rc.tag == tag) + { + if (type == null) return rc; + RiffContainer cont = rc as RiffContainer; + if (cont != null && cont.type == type) + return rc; + } + return null; + } + + public RiffContainer() + { + tag = "LIST"; + } + public string type; + public List subchunks = new List(); + public override void WriteStream(Stream s) + { + BinaryWriter bw = new BinaryWriter(s); + WriteTag(bw, tag); + long size = GetVolume(); + if (size > uint.MaxValue) throw new FormatException("File too big to write out"); + bw.Write((uint)size); + WriteTag(bw, type); + bw.Flush(); + foreach (RiffChunk rc in subchunks) + rc.WriteStream(s); + if (size % 2 != 0) + s.WriteByte(0); + } + public override long GetVolume() + { + long len = 4; + foreach (RiffChunk rc in subchunks) + len += rc.GetVolume() + 8; + return len; + } + + public override RiffChunk Morph() + { + switch (type) + { + case "INFO": return new RiffContainer_INFO(this); + } + return this; + } + } + + public class RiffContainer_INFO : RiffContainer + { + public Dictionary dictionary = new Dictionary(); + public RiffContainer_INFO() { type = "INFO"; } + public RiffContainer_INFO(RiffContainer rc) + { + subchunks = rc.subchunks; + type = "INFO"; + foreach (RiffChunk chunk in subchunks) + { + RiffSubchunk rsc = chunk as RiffSubchunk; + if (chunk == null) + throw new FormatException("Invalid subchunk of INFO list"); + dictionary[rsc.tag] = System.Text.Encoding.ASCII.GetString(rsc.ReadAll()); + } + } + + private void Flush() + { + subchunks.Clear(); + foreach (KeyValuePair kvp in dictionary) + { + RiffSubchunk rs = new RiffSubchunk(); + rs.tag = kvp.Key; + rs.Source = new MemoryStream(System.Text.Encoding.ASCII.GetBytes(kvp.Value)); + rs.Position = 0; + rs.Length = (uint)rs.Source.Length; + subchunks.Add(rs); + } + } + + public override long GetVolume() + { + Flush(); + return base.GetVolume(); + } + + public override void WriteStream(Stream s) + { + Flush(); + base.WriteStream(s); + } + } + + public RiffContainer riff; + + private long readCounter; + private RiffChunk ReadChunk(BinaryReader br) + { + RiffChunk ret; + string tag = ReadTag(br); readCounter += 4; + uint size = br.ReadUInt32(); readCounter += 4; + if (size > int.MaxValue) + throw new FormatException("chunk too big"); + if (tag == "RIFF" || tag == "LIST") + { + RiffContainer rc = new RiffContainer(); + rc.tag = tag; + rc.type = ReadTag(br); readCounter += 4; + long readEnd = readCounter - 4 + size; + while (readEnd > readCounter) + rc.subchunks.Add(ReadChunk(br)); + ret = rc.Morph(); + } + else + { + RiffSubchunk rsc = new RiffSubchunk(); + rsc.tag = tag; + rsc.Source = br.BaseStream; + rsc.Position = br.BaseStream.Position; + rsc.Length = size; + readCounter += size; + ret = rsc.Morph(); + } + if (size % 2 != 0) + { + br.ReadByte(); + readCounter += 1; + } + return ret; + + } + + public void WriteStream(Stream s) + { + 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); + if (chunk.tag != "RIFF") throw new FormatException("can't recognize riff chunk"); + riff = (RiffContainer)chunk; + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation.DiscSystem/Buffer.cs b/BizHawk.Emulation.DiscSystem/Buffer.cs new file mode 100644 index 0000000000..e2cd4adec6 --- /dev/null +++ b/BizHawk.Emulation.DiscSystem/Buffer.cs @@ -0,0 +1,118 @@ +using System; +using System.Runtime.InteropServices; + +//TODO: quick fix, this is the same file as BizHawk.Emulation, move it to BizHawk.Common +namespace BizHawk.Emulation.DiscSystem +{ + /// + /// Implements a data simple data buffer with proper life cycle and no bounds checking + /// + public unsafe class CBuffer : IDisposable + { + public GCHandle hnd; + public T[] arr; + public void* ptr; + public byte* byteptr; + public int len; + public int itemsize; + + public static CBuffer malloc(int amt, int itemsize) + { + return new CBuffer(amt, itemsize); + } + + public void Write08(uint addr, byte val) { byteptr[addr] = val; } + public void Write16(uint addr, ushort val) { *(ushort*)(byteptr + addr) = val; } + public void Write32(uint addr, uint val) { *(uint*)(byteptr + addr) = val; } + public void Write64(uint addr, ulong val) { *(ulong*)(byteptr + addr) = val; } + public byte Read08(uint addr) { return byteptr[addr]; } + public ushort Read16(uint addr) { return *(ushort*)(byteptr + addr); } + public uint Read32(uint addr) { return *(uint*)(byteptr + addr); } + public ulong Read64(uint addr) { return *(ulong*)(byteptr + addr); } + public void Write08(int addr, byte val) { byteptr[addr] = val; } + public void Write16(int addr, ushort val) { *(ushort*)(byteptr + addr) = val; } + public void Write32(int addr, uint val) { *(uint*)(byteptr + addr) = val; } + public void Write64(int addr, ulong val) { *(ulong*)(byteptr + addr) = val; } + public byte Read08(int addr) { return byteptr[addr]; } + public ushort Read16(int addr) { return *(ushort*)(byteptr + addr); } + public uint Read32(int addr) { return *(uint*)(byteptr + addr); } + public ulong Read64(int addr) { return *(ulong*)(byteptr + addr); } + + public CBuffer(T[] arr, int itemsize) + { + this.itemsize = itemsize; + len = arr.Length; + this.arr = arr; + hnd = GCHandle.Alloc(arr, GCHandleType.Pinned); + ptr = hnd.AddrOfPinnedObject().ToPointer(); + byteptr = (byte*)ptr; + } + public CBuffer(int amt, int itemsize) + { + this.itemsize = itemsize; + len = amt; + arr = new T[amt]; + hnd = GCHandle.Alloc(arr, GCHandleType.Pinned); + ptr = hnd.AddrOfPinnedObject().ToPointer(); + byteptr = (byte*)ptr; + Util.memset(byteptr, 0, len * itemsize); + } + + public void Dispose() + { + if (arr != null) + hnd.Free(); + arr = null; + } + + ~CBuffer() { Dispose(); } + } + + public class ByteBuffer : CBuffer + { + public ByteBuffer(int amt) : base(amt,1) { } + public ByteBuffer(byte[] arr) : base(arr,1) { } + public byte this[int index] + { + #if DEBUG + get { return arr[index]; } + set { arr[index] = value; } + #else + set { Write08(index, value); } + get { return Read08(index);} + #endif + } + } + + public class IntBuffer : CBuffer + { + public IntBuffer(int amt) : base(amt, 4) { } + public IntBuffer(int[] arr) : base(arr,4) { } + public int this[int index] + { + #if DEBUG + get { return arr[index]; } + set { arr[index] = value; } + #else + set { Write32(index<<2, (uint) value); } + get { return (int)Read32(index<<2);} + #endif + } + } + + public class ShortBuffer : CBuffer + { + public ShortBuffer(int amt) : base(amt, 2) { } + public ShortBuffer(short[] arr) : base(arr, 2) { } + public short this[int index] + { +#if DEBUG + get { return arr[index]; } + set { arr[index] = value; } +#else + set { Write32(index << 1, (uint)value); } + get { return (short)Read16(index << 1); } +#endif + } + } +} \ No newline at end of file diff --git a/BizHawk.Emulation/DiscSystem/CCD_format.cs b/BizHawk.Emulation.DiscSystem/CCD_format.cs similarity index 78% rename from BizHawk.Emulation/DiscSystem/CCD_format.cs rename to BizHawk.Emulation.DiscSystem/CCD_format.cs index c435401afc..6d9bd83bae 100644 --- a/BizHawk.Emulation/DiscSystem/CCD_format.cs +++ b/BizHawk.Emulation.DiscSystem/CCD_format.cs @@ -1,4 +1,4 @@ -namespace BizHawk.DiscSystem +namespace BizHawk.Emulation.DiscSystem { //TBD CCD format //check out ccd2iso linux program? diff --git a/BizHawk.Emulation/DiscSystem/CUE_format.cs b/BizHawk.Emulation.DiscSystem/CUE_format.cs similarity index 96% rename from BizHawk.Emulation/DiscSystem/CUE_format.cs rename to BizHawk.Emulation.DiscSystem/CUE_format.cs index 6d96471a9b..9738561648 100644 --- a/BizHawk.Emulation/DiscSystem/CUE_format.cs +++ b/BizHawk.Emulation.DiscSystem/CUE_format.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; //this rule is not supported correctly: `The first track number can be greater than one, but all track numbers after the first must be sequential.` -namespace BizHawk.DiscSystem +namespace BizHawk.Emulation.DiscSystem { partial class Disc { diff --git a/BizHawk.Emulation/DiscSystem/Decoding.cs b/BizHawk.Emulation.DiscSystem/Decoding.cs similarity index 95% rename from BizHawk.Emulation/DiscSystem/Decoding.cs rename to BizHawk.Emulation.DiscSystem/Decoding.cs index 48413b7060..70e4860839 100644 --- a/BizHawk.Emulation/DiscSystem/Decoding.cs +++ b/BizHawk.Emulation.DiscSystem/Decoding.cs @@ -6,7 +6,7 @@ using System.Text.RegularExpressions; using System.IO; using System.Collections.Generic; -namespace BizHawk.DiscSystem +namespace BizHawk.Emulation.DiscSystem { public class FFMpeg { diff --git a/BizHawk.Emulation/DiscSystem/Disc.API.cs b/BizHawk.Emulation.DiscSystem/Disc.API.cs similarity index 92% rename from BizHawk.Emulation/DiscSystem/Disc.API.cs rename to BizHawk.Emulation.DiscSystem/Disc.API.cs index c37a30b848..774b5fcda1 100644 --- a/BizHawk.Emulation/DiscSystem/Disc.API.cs +++ b/BizHawk.Emulation.DiscSystem/Disc.API.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; //main apis for emulator core routine use -namespace BizHawk.DiscSystem +namespace BizHawk.Emulation.DiscSystem { public class DiscReferenceException : Exception { @@ -154,12 +154,12 @@ namespace BizHawk.DiscSystem ReadABA_2048(lba + 150, buffer, offset); } - internal void ReadABA_2352(int aba, byte[] buffer, int offset) + public void ReadABA_2352(int aba, byte[] buffer, int offset) { Sectors[aba].Sector.Read_2352(buffer, offset); } - internal void ReadABA_2048(int aba, byte[] buffer, int offset) + public void ReadABA_2048(int aba, byte[] buffer, int offset) { Sectors[aba].Sector.Read_2048(buffer, offset); } @@ -188,7 +188,7 @@ namespace BizHawk.DiscSystem READLBA_Flat_Implementation(disc_offset, buffer, offset, length, (a, b, c) => ReadLBA_2048(a, b, c), secsize, lba_buf, ref sectorHint); } - internal void READLBA_Flat_Implementation(long disc_offset, byte[] buffer, int offset, int length, Action sectorReader, int sectorSize, byte[] sectorBuf, ref int sectorBufferHint) + public void READLBA_Flat_Implementation(long disc_offset, byte[] buffer, int offset, int length, Action sectorReader, int sectorSize, byte[] sectorBuf, ref int sectorBufferHint) { //hint is the sector number which is already read. to avoid repeatedly reading the sector from the disc in case of several small reads, so that sectorBuf can be used as a sector cache while (length > 0) diff --git a/BizHawk.Emulation/DiscSystem/Disc.ID.cs b/BizHawk.Emulation.DiscSystem/Disc.ID.cs similarity index 93% rename from BizHawk.Emulation/DiscSystem/Disc.ID.cs rename to BizHawk.Emulation.DiscSystem/Disc.ID.cs index c05d8b9790..f89ebdf269 100644 --- a/BizHawk.Emulation/DiscSystem/Disc.ID.cs +++ b/BizHawk.Emulation.DiscSystem/Disc.ID.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; //disc type identification logic -namespace BizHawk.DiscSystem +namespace BizHawk.Emulation.DiscSystem { public enum DiscType { @@ -52,7 +52,7 @@ namespace BizHawk.DiscSystem //we dont know how to detect TurboCD. //an emulator frontend will likely just guess TurboCD if the disc is UnknownFormat - var iso = new ISOParser.ISOFile(); + var iso = new ISOFile(); bool isIso = iso.Parse(DiscStream.Open_LBA_2048(this)); if (isIso) diff --git a/BizHawk.Emulation/DiscSystem/Disc.cs b/BizHawk.Emulation.DiscSystem/Disc.cs similarity index 96% rename from BizHawk.Emulation/DiscSystem/Disc.cs rename to BizHawk.Emulation.DiscSystem/Disc.cs index 95ee451138..4cefa4c0ff 100644 --- a/BizHawk.Emulation/DiscSystem/Disc.cs +++ b/BizHawk.Emulation.DiscSystem/Disc.cs @@ -63,7 +63,7 @@ using System.Collections.Generic; //cue sheets may use mode1_2048 (and the error coding needs to be regenerated to get accurate raw data) or mode1_2352 (the entire sector is present) //audio is a different mode, seems to be just 2352 bytes with no sync, header or error correction. i guess the CIRC error correction is still there -namespace BizHawk.DiscSystem +namespace BizHawk.Emulation.DiscSystem { public partial class Disc : IDisposable { diff --git a/BizHawk.Emulation/DiscSystem/DiscTOC.cs b/BizHawk.Emulation.DiscSystem/DiscTOC.cs similarity index 96% rename from BizHawk.Emulation/DiscSystem/DiscTOC.cs rename to BizHawk.Emulation.DiscSystem/DiscTOC.cs index b4dc4648f3..d51f09e6b9 100644 --- a/BizHawk.Emulation/DiscSystem/DiscTOC.cs +++ b/BizHawk.Emulation.DiscSystem/DiscTOC.cs @@ -4,9 +4,8 @@ using System.Text.RegularExpressions; using System.IO; using System.Collections.Generic; -namespace BizHawk.DiscSystem +namespace BizHawk.Emulation.DiscSystem { - public class DiscTOC { /// diff --git a/BizHawk.Emulation/DiscSystem/ECM.cs b/BizHawk.Emulation.DiscSystem/ECM.cs similarity index 96% rename from BizHawk.Emulation/DiscSystem/ECM.cs rename to BizHawk.Emulation.DiscSystem/ECM.cs index ff91cddf73..0274a6742c 100644 --- a/BizHawk.Emulation/DiscSystem/ECM.cs +++ b/BizHawk.Emulation.DiscSystem/ECM.cs @@ -28,7 +28,7 @@ //Corlett's ECM uses our same fundamental approach as well. //I can't figure out what winUAE is doing. -namespace BizHawk.DiscSystem +namespace BizHawk.Emulation.DiscSystem { static class ECM { diff --git a/BizHawk.Emulation/DiscSystem/GPL_ECM.cs b/BizHawk.Emulation.DiscSystem/GPL_ECM.cs similarity index 100% rename from BizHawk.Emulation/DiscSystem/GPL_ECM.cs rename to BizHawk.Emulation.DiscSystem/GPL_ECM.cs diff --git a/BizHawk.Emulation.DiscSystem/Properties/AssemblyInfo.cs b/BizHawk.Emulation.DiscSystem/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..8ff2cd7f44 --- /dev/null +++ b/BizHawk.Emulation.DiscSystem/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("BizHawk.Emulation.DiscSystem")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("BizHawk.Emulation.DiscSystem")] +[assembly: AssemblyCopyright("Copyright © 2013")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("1ad4da36-4e98-4d1a-bd75-17d63c0e30dc")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/BizHawk.Emulation/DiscSystem/Subcode.cs b/BizHawk.Emulation.DiscSystem/Subcode.cs similarity index 93% rename from BizHawk.Emulation/DiscSystem/Subcode.cs rename to BizHawk.Emulation.DiscSystem/Subcode.cs index f2c89d9d9f..b2272f0d04 100644 --- a/BizHawk.Emulation/DiscSystem/Subcode.cs +++ b/BizHawk.Emulation.DiscSystem/Subcode.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; //a decent little subcode reference //http://www.jbum.com/cdg_revealed.html -namespace BizHawk.DiscSystem +namespace BizHawk.Emulation.DiscSystem { //this has been checked against mednafen's and seems to match //there are a few dozen different ways to do CRC16-CCITT diff --git a/BizHawk.Emulation/DiscSystem/TOC_format.cs b/BizHawk.Emulation.DiscSystem/TOC_format.cs similarity index 67% rename from BizHawk.Emulation/DiscSystem/TOC_format.cs rename to BizHawk.Emulation.DiscSystem/TOC_format.cs index 224e20578d..e2a3a36072 100644 --- a/BizHawk.Emulation/DiscSystem/TOC_format.cs +++ b/BizHawk.Emulation.DiscSystem/TOC_format.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace BizHawk.DiscSystem +namespace BizHawk.Emulation.DiscSystem { //TBD TOC format public class TOCFormat diff --git a/BizHawk.Emulation.DiscSystem/Util.cs b/BizHawk.Emulation.DiscSystem/Util.cs new file mode 100644 index 0000000000..77ba261277 --- /dev/null +++ b/BizHawk.Emulation.DiscSystem/Util.cs @@ -0,0 +1,922 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; + +using BizHawk.Common; + +namespace BizHawk.Emulation.DiscSystem +{ + //TODO: QUICK FIX, this is the same file as BizHawk.Emulation, move to BizHawk.Common and get rid of this + public static class Colors + { + public static int ARGB(byte red, byte green, byte blue) + { + return (int)((uint)((red << 0x10) | (green << 8) | blue | (0xFF << 0x18))); + } + + public static int ARGB(byte red, byte green, byte blue, byte alpha) + { + return (int)((uint)((red << 0x10) | (green << 8) | blue | (alpha << 0x18))); + } + + public static int Luminosity(byte lum) + { + return (int)((uint)((lum << 0x10) | (lum << 8) | lum | (0xFF << 0x18))); + } + } + + public unsafe static class Util + { + static readonly char[] HexConvArr = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + static System.Runtime.InteropServices.GCHandle HexConvHandle; + public static char* HexConvPtr; + static unsafe Util() + { + HexConvHandle = System.Runtime.InteropServices.GCHandle.Alloc(HexConvArr, System.Runtime.InteropServices.GCHandleType.Pinned); + HexConvPtr = (char*)HexConvHandle.AddrOfPinnedObject().ToPointer(); + } + + public static string Hash_MD5(byte[] data, int offset, int len) + { + using (var md5 = System.Security.Cryptography.MD5.Create()) + { + md5.TransformFinalBlock(data, offset, len); + return Util.BytesToHexString(md5.Hash); + } + } + + public static string Hash_SHA1(byte[] data, int offset, int len) + { + using (var sha1 = System.Security.Cryptography.SHA1.Create()) + { + sha1.TransformFinalBlock(data, offset, len); + return Util.BytesToHexString(sha1.Hash); + } + } + + public static bool IsPowerOfTwo(int x) + { + if (x == 0) return true; + if (x == 1) return true; + return (x & (x - 1)) == 0; + } + + public static int SaveRamBytesUsed(byte[] SaveRAM) + { + for (int j = SaveRAM.Length - 1; j >= 0; j--) + if (SaveRAM[j] != 0) + return j + 1; + return 0; + } + + // Read bytes from a BinaryReader and translate them into the UTF-8 string they represent. + public static string ReadStringFixedAscii(this BinaryReader r, int bytes) + { + byte[] read = new byte[bytes]; + for (int b = 0; b < bytes; b++) + read[b] = r.ReadByte(); + return System.Text.Encoding.UTF8.GetString(read); + } + + public static string ReadStringAsciiZ(this BinaryReader r) + { + StringBuilder sb = new StringBuilder(); + for(;;) + { + int b = r.ReadByte(); + if(b <= 0) break; + sb.Append((char)b); + } + return sb.ToString(); + } + + /// + /// conerts bytes to an uppercase string of hex numbers in upper case without any spacing or anything + /// //could be extension method + /// + public static string BytesToHexString(byte[] bytes) + { + StringBuilder sb = new StringBuilder(); + foreach (byte b in bytes) + sb.AppendFormat("{0:X2}", b); + return sb.ToString(); + } + + //could be extension method + public static byte[] HexStringToBytes(string str) + { + MemoryStream ms = new MemoryStream(); + if (str.Length % 2 != 0) throw new ArgumentException(); + int len = str.Length / 2; + for (int i = 0; i < len; i++) + { + int d = 0; + for (int j = 0; j < 2; j++) + { + char c = char.ToLower(str[i * 2 + j]); + if (c >= '0' && c <= '9') + d += (c - '0'); + else if (c >= 'a' && c <= 'f') + d += (c - 'a') + 10; + else throw new ArgumentException(); + if (j == 0) d <<= 4; + } + ms.WriteByte((byte)d); + } + return ms.ToArray(); + } + + //could be extension method + public static void WriteByteBuffer(BinaryWriter bw, byte[] data) + { + if (data == null) bw.Write(0); + else + { + bw.Write(data.Length); + bw.Write(data); + } + } + + public static short[] ByteBufferToShortBuffer(byte[] buf) + { + int num = buf.Length / 2; + short[] ret = new short[num]; + for (int i = 0; i < num; i++) + { + ret[i] = (short)(buf[i * 2] | (buf[i * 2 + 1] << 8)); + } + return ret; + } + + public static byte[] ShortBufferToByteBuffer(short[] buf) + { + int num = buf.Length; + byte[] ret = new byte[num * 2]; + for (int i = 0; i < num; i++) + { + ret[i * 2 + 0] = (byte)(buf[i] & 0xFF); + ret[i * 2 + 1] = (byte)((buf[i] >> 8) & 0xFF); + } + return ret; + } + + public static uint[] ByteBufferToUintBuffer(byte[] buf) + { + int num = buf.Length / 4; + uint[] ret = new uint[num]; + for (int i = 0; i < num; i++) + { + ret[i] = (uint)(buf[i * 4] | (buf[i * 4 + 1] << 8) | (buf[i * 4 + 2] << 16) | (buf[i * 4 + 3] << 24)); + } + return ret; + } + + public static byte[] UintBufferToByteBuffer(uint[] buf) + { + int num = buf.Length; + byte[] ret = new byte[num * 4]; + for (int i = 0; i < num; i++) + { + ret[i * 4 + 0] = (byte)(buf[i] & 0xFF); + ret[i * 4 + 1] = (byte)((buf[i] >> 8) & 0xFF); + ret[i * 4 + 2] = (byte)((buf[i] >> 16) & 0xFF); + ret[i * 4 + 3] = (byte)((buf[i] >> 24) & 0xFF); + } + return ret; + } + + public static int[] ByteBufferToIntBuffer(byte[] buf) + { + int num = buf.Length / 4; + int[] ret = new int[num]; + for (int i = 0; i < num; i++) + { + ret[i] = buf[(i * 4) + 3]; + ret[i] <<= 8; + ret[i] |= buf[(i * 4) + 2]; + ret[i] <<= 8; + ret[i] |= buf[(i * 4) + 1]; + ret[i] <<= 8; + ret[i] |= buf[(i * 4)]; + } + return ret; + } + + public static byte[] IntBufferToByteBuffer(int[] buf) + { + int num = buf.Length; + byte[] ret = new byte[num * 4]; + for (int i = 0; i < num; i++) + { + ret[i * 4 + 0] = (byte)(buf[i] & 0xFF); + ret[i * 4 + 1] = (byte)((buf[i] >> 8) & 0xFF); + ret[i * 4 + 2] = (byte)((buf[i] >> 16) & 0xFF); + ret[i * 4 + 3] = (byte)((buf[i] >> 24) & 0xFF); + } + return ret; + } + + public static byte[] ReadByteBuffer(BinaryReader br, bool return_null) + { + int len = br.ReadInt32(); + if (len == 0 && return_null) return null; + byte[] ret = new byte[len]; + int ofs = 0; + while (len > 0) + { + int done = br.Read(ret, ofs, len); + ofs += done; + len -= done; + } + return ret; + } + + public static unsafe int memcmp(void* a, string b, int len) + { + fixed (byte* bp = System.Text.Encoding.ASCII.GetBytes(b)) + return memcmp(a, bp, len); + } + + public static unsafe int memcmp(void* a, void* b, int len) + { + byte* ba = (byte*)a; + byte* bb = (byte*)b; + for (int i = 0; i < len; i++) + { + byte _a = ba[i]; + byte _b = bb[i]; + int c = _a - _b; + if (c != 0) return c; + } + return 0; + } + + public static unsafe void memset(void* ptr, int val, int len) + { + byte* bptr = (byte*)ptr; + for (int i = 0; i < len; i++) + bptr[i] = (byte)val; + } + + public static unsafe void memset32(void* ptr, int val, int len) + { + System.Diagnostics.Debug.Assert(len % 4 == 0); + int dwords = len / 4; + int* dwptr = (int*)ptr; + for (int i = 0; i < dwords; i++) + dwptr[i] = val; + } + + public static byte[] ReadAllBytes(Stream stream) + { + const int BUFF_SIZE = 4096; + byte[] buffer = new byte[BUFF_SIZE]; + + int bytesRead = 0; + var inStream = new BufferedStream(stream); + var outStream = new MemoryStream(); + + while ((bytesRead = inStream.Read(buffer, 0, BUFF_SIZE)) > 0) + { + outStream.Write(buffer, 0, bytesRead); + } + + return outStream.ToArray(); + } + + public static byte BinToBCD(this byte v) + { + return (byte) (((v / 10) * 16) + (v % 10)); + } + + public static byte BCDtoBin(this byte v) + { + return (byte) (((v / 16) * 10) + (v % 16)); + } + + public static string FormatFileSize(long filesize) + { + Decimal size = (Decimal)filesize; + + Decimal OneKiloByte = 1024M; + Decimal OneMegaByte = OneKiloByte * 1024M; + Decimal OneGigaByte = OneMegaByte * 1024M; + + string suffix; + if (size > 1024 * 1024 * 1024) + { + size /= 1024 * 1024 * 1024; + suffix = "GB"; + } + else if (size > 1024 * 1024) + { + size /= 1024 * 1024; + suffix = "MB"; + } + else if (size > 1024) + { + size /= 1024; + suffix = "KB"; + } + else + { + suffix = " B"; + } + + string precision = "2"; + return String.Format("{0:N" + precision + "}{1}", size, suffix); + } + } + + public unsafe class Serializer + { + BinaryReader br; + BinaryWriter bw; + TextReader tr; + TextWriter tw; + public BinaryReader BinaryReader { get { return br; } } + public BinaryWriter BinaryWriter { get { return bw; } } + public TextReader TextReader { get { return tr; } } + public TextWriter TextWriter { get { return tw; } } + public Serializer() { } + public Serializer(BinaryWriter _bw) { StartWrite(_bw); } + public Serializer(BinaryReader _br) { StartRead(_br); } + public Serializer(TextWriter _tw) { StartWrite(_tw); } + public Serializer(TextReader _tr) { StartRead(_tr); } + public static Serializer CreateBinaryWriter(BinaryWriter _bw) { return new Serializer(_bw); } + public static Serializer CreateBinaryReader(BinaryReader _br) { return new Serializer(_br); } + public static Serializer CreateTextWriter(TextWriter _tw) { return new Serializer(_tw); } + public static Serializer CreateTextReader(TextReader _tr) { return new Serializer(_tr); } + public void StartWrite(BinaryWriter _bw) { this.bw = _bw; isReader = false; } + public void StartRead(BinaryReader _br) { this.br = _br; isReader = true; } + public void StartWrite(TextWriter _tw) { this.tw = _tw; isReader = false; isText = true; } + public void StartRead(TextReader _tr) { + this.tr = _tr; + isReader = true; + isText = true; + BeginTextBlock(); + } + + public bool IsReader { get { return isReader; } } + public bool IsWriter { get { return !IsReader; } } + public bool IsText { get { return isText; } } + bool isText; + bool isReader; + + Stack sections = new Stack(); + + class Section : Dictionary + { + public string Name; + public Dictionary Items = new Dictionary(); + } + + Section ReaderSection, CurrSection; + Stack
SectionStack = new Stack
(); + + void BeginTextBlock() + { + if (!IsText) return; + if (IsWriter) return; + + ReaderSection = new Section(); + ReaderSection.Name = ""; + Stack
ss = new Stack
(); + ss.Push(ReaderSection); + Section curs = ReaderSection; + + var rxEnd = new System.Text.RegularExpressions.Regex(@"\[/(.*?)\]",System.Text.RegularExpressions.RegexOptions.Compiled); + var rxBegin = new System.Text.RegularExpressions.Regex(@"\[(.*?)\]",System.Text.RegularExpressions.RegexOptions.Compiled); + + //read the entire file into a data structure for flexi-parsing + string str; + while ((str = tr.ReadLine()) != null) + { + var end = rxEnd.Match(str); + var begin = rxBegin.Match(str); + if (end.Success) + { + string name = end.Groups[1].Value; + if (name != curs.Name) throw new InvalidOperationException("Mis-formed savestate blob"); + curs = ss.Pop(); + // consume no data past the end of the last proper section + if (curs == ReaderSection) + { + CurrSection = curs; + return; + } + } + else if (begin.Success) + { + string name = begin.Groups[1].Value; + ss.Push(curs); + var news = new Section(); + news.Name = name; + if (!curs.ContainsKey(name)) + curs[name] = news; + else + throw new Exception(string.Format("Duplicate key \"{0}\" in serializer savestate!", name)); + curs = news; + } + else + { + //add to current section + if (str.Trim().Length == 0) continue; + var parts = str.Split(' '); + var key = parts[0]; + //UGLY: adds whole string instead of splitting the key. later, split the key, and have the individual Sync methods give up that responsibility + if (!curs.Items.ContainsKey(key)) + curs.Items[key] = parts[1]; + else + throw new Exception(string.Format("Duplicate key \"{0}\" in serializer savestate!", key)); + } + } + + CurrSection = ReaderSection; + } + + public void BeginSection(string name) + { + sections.Push(name); + if (IsText) + if (IsWriter) { tw.WriteLine("[{0}]", name); } + else + { + SectionStack.Push(CurrSection); + CurrSection = CurrSection[name]; + } + } + + public void EndSection() + { + string name = sections.Pop(); + if (IsText) + if (IsWriter) tw.WriteLine("[/{0}]", name); + else + { + CurrSection = SectionStack.Pop(); + } + } + + string Item(string key) + { + return CurrSection.Items[key]; + } + + bool Present(string key) + { + return CurrSection.Items.ContainsKey(key); + } + + public void SyncEnum(string name, ref T val) where T : struct + { + if (typeof(T).BaseType != typeof(System.Enum)) + throw new InvalidOperationException(); + if (isText) SyncEnumText(name, ref val); + else if (IsReader) val = (T)Enum.ToObject(typeof(T), br.ReadInt32()); + else bw.Write(Convert.ToInt32(val)); + } + + public void SyncEnumText(string name, ref T val) where T : struct + { + if (IsReader) { if (Present(name)) val = (T)Enum.Parse(typeof(T), Item(name)); } + else tw.WriteLine("{0} {1}", name, val.ToString()); + } + + void SyncBuffer(string name, int elemsize, int len, void* ptr) + { + if (IsReader) + { + byte[] temp = null; + Sync(name, ref temp, false); + int todo = Math.Min(temp.Length, len * elemsize); + System.Runtime.InteropServices.Marshal.Copy(temp, 0, new IntPtr(ptr), todo); + } + else + { + int todo = len * elemsize; + byte[] temp = new byte[todo]; + System.Runtime.InteropServices.Marshal.Copy(new IntPtr(ptr), temp, 0, todo); + Sync(name, ref temp, false); + } + } + + public void Sync(string name, ref ByteBuffer byteBuf) + { + SyncBuffer(name, 1, byteBuf.len, byteBuf.ptr); + } + + public void Sync(string name, ref IntBuffer byteBuf) + { + SyncBuffer(name, 4, byteBuf.len, byteBuf.ptr); + } + + public void Sync(string name, ref byte[] val, bool use_null) + { + if (IsText) SyncText(name, ref val, use_null); + else if (IsReader) val = Util.ReadByteBuffer(br, use_null); + else Util.WriteByteBuffer(bw, val); + } + public void SyncText(string name, ref byte[] val, bool use_null) + { + if (IsReader) + { + if(Present(name)) val = Util.HexStringToBytes(Item(name)); + if (val != null && val.Length == 0 && use_null) val = null; + } + else + { + byte[] temp = val; + if (temp == null) temp = new byte[0]; + tw.WriteLine("{0} {1}", name, Util.BytesToHexString(temp)); + } + } + + public void Sync(string name, ref short[] val, bool use_null) + { + if (IsText) SyncText(name, ref val, use_null); + else if (IsReader) + { + val = Util.ByteBufferToShortBuffer(Util.ReadByteBuffer(br, false)); + if (val == null && !use_null) val = new short[0]; + } + else Util.WriteByteBuffer(bw, Util.ShortBufferToByteBuffer(val)); + } + public void SyncText(string name, ref short[] val, bool use_null) + { + if (IsReader) + { + if (Present(name)) + { + byte[] bytes = Util.HexStringToBytes(Item(name)); + val = Util.ByteBufferToShortBuffer(bytes); + } + if (val != null && val.Length == 0 && use_null) val = null; + } + else + { + short[] temp = val; + if (temp == null) temp = new short[0]; + tw.WriteLine("{0} {1}", name, Util.BytesToHexString(Util.ShortBufferToByteBuffer(temp))); + } + } + + public void Sync(string name, ref int[] val, bool use_null) + { + if (IsText) SyncText(name, ref val, use_null); + else if (IsReader) + { + val = Util.ByteBufferToIntBuffer(Util.ReadByteBuffer(br, false)); + if (val == null && !use_null) val = new int[0]; + } + else Util.WriteByteBuffer(bw, Util.IntBufferToByteBuffer(val)); + } + public void SyncText(string name, ref int[] val, bool use_null) + { + if (IsReader) + { + if (Present(name)) + { + byte[] bytes = Util.HexStringToBytes(Item(name)); + val = Util.ByteBufferToIntBuffer(bytes); + } + if (val != null && val.Length == 0 && use_null) val = null; + } + else + { + int[] temp = val; + if (temp == null) temp = new int[0]; + tw.WriteLine("{0} {1}", name, Util.BytesToHexString(Util.IntBufferToByteBuffer(temp))); + } + } + + public void Sync(string name, ref uint[] val, bool use_null) + { + if (IsText) SyncText(name, ref val, use_null); + else if (IsReader) + { + val = Util.ByteBufferToUintBuffer(Util.ReadByteBuffer(br, false)); + if (val == null && !use_null) val = new uint[0]; + } + else Util.WriteByteBuffer(bw, Util.UintBufferToByteBuffer(val)); + } + public void SyncText(string name, ref uint[] val, bool use_null) + { + if (IsReader) + { + if(Present(name)) + { + byte[] bytes = Util.HexStringToBytes(Item(name)); + val = Util.ByteBufferToUintBuffer(bytes); + } + if (val != null && val.Length == 0 && use_null) val = null; + } + else + { + uint[] temp = val; + if (temp == null) temp = new uint[0]; + tw.WriteLine("{0} {1}", name, Util.BytesToHexString(Util.UintBufferToByteBuffer(temp))); + } + } + + public void Sync(string name, ref Bit val) + { + if (IsText) SyncText(name, ref val); + else if (IsReader) Read(ref val); + else Write(ref val); + } + public void SyncText(string name, ref Bit val) + { + if (IsReader) ReadText(name, ref val); + else WriteText(name, ref val); + } + public void Sync(string name, ref byte val) + { + if (IsText) SyncText(name, ref val); + else if (IsReader) Read(ref val); + else Write(ref val); + } + void SyncText(string name, ref byte val) + { + if (IsReader) ReadText(name, ref val); + else WriteText(name, ref val); + } + public void Sync(string name, ref ushort val) + { + if (IsText) SyncText(name, ref val); + else if (IsReader) Read(ref val); + else Write(ref val); + } + void SyncText(string name, ref ushort val) + { + if (IsReader) ReadText(name, ref val); + else WriteText(name, ref val); + } + public void Sync(string name, ref uint val) + { + if (IsText) SyncText(name, ref val); + else if (IsReader) Read(ref val); + else Write(ref val); + } + void SyncText(string name, ref uint val) + { + if (IsReader) ReadText(name, ref val); + else WriteText(name, ref val); + } + public void Sync(string name, ref sbyte val) + { + if (IsText) SyncText(name, ref val); + else if (IsReader) Read(ref val); + else Write(ref val); + } + void SyncText(string name, ref sbyte val) + { + if (IsReader) ReadText(name, ref val); + else WriteText(name, ref val); + } + public void Sync(string name, ref short val) + { + if (IsText) SyncText(name, ref val); + else if (IsReader) Read(ref val); + else Write(ref val); + } + void SyncText(string name, ref short val) + { + if (IsReader) ReadText(name, ref val); + else WriteText(name, ref val); + } + public void Sync(string name, ref int val) + { + if (IsText) SyncText(name, ref val); + else if (IsReader) Read(ref val); + else Write(ref val); + } + void SyncText(string name, ref int val) + { + if (IsReader) ReadText(name, ref val); + else WriteText(name, ref val); + } + public void Sync(string name, ref bool val) + { + if (IsText) SyncText(name, ref val); + else if (IsReader) Read(ref val); + else Write(ref val); + } + void SyncText(string name, ref bool val) + { + if (IsReader) ReadText(name, ref val); + else WriteText(name, ref val); + } + public void SyncFixedString(string name, ref string val, int length) + { + //TODO - this could be made more efficient perhaps just by writing values right out of the string.. + + if (IsReader) + { + char[] buf = new char[length]; + if (isText) + { + tr.Read(buf, 0, length); + } + else + { + br.Read(buf, 0, length); + } + int len = 0; + for (; len < length; len++) + { + if (buf[len] == 0) break; + } + val = new string(buf, 0, len); + } + else + { + if (name.Length > length) throw new InvalidOperationException("SyncFixedString too long"); + char[] buf = val.ToCharArray(); + char[] remainder = new char[length - buf.Length]; + if (IsText) + { + tw.Write(buf); + tw.Write(remainder); + } + else + { + bw.Write(buf); + bw.Write(remainder); + } + } + } + + void Read(ref Bit val) { val = br.ReadBit(); } + void Write(ref Bit val) { bw.WriteBit(val); } + void ReadText(string name, ref Bit val) { if(Present(name)) val = (Bit)int.Parse(Item(name)); } + void WriteText(string name, ref Bit val) { tw.WriteLine("{0} {1}", name, (int)val); } + + void Read(ref byte val) { val = br.ReadByte(); } + void Write(ref byte val) { bw.Write(val); } + void ReadText(string name, ref byte val) { if (Present(name)) val = byte.Parse(Item(name).Replace("0x", ""), NumberStyles.HexNumber); } + void WriteText(string name, ref byte val) { tw.WriteLine("{0} 0x{1:X2}", name, val); } + + void Read(ref ushort val) { val = br.ReadUInt16(); } + void Write(ref ushort val) { bw.Write(val); } + void ReadText(string name, ref ushort val) { if (Present(name)) val = ushort.Parse(Item(name).Replace("0x", ""), NumberStyles.HexNumber); } + void WriteText(string name, ref ushort val) { tw.WriteLine("{0} 0x{1:X4}", name, val); } + + void Read(ref uint val) { val = br.ReadUInt32(); } + void Write(ref uint val) { bw.Write(val); } + void ReadText(string name, ref uint val) { if (Present(name)) val = uint.Parse(Item(name).Replace("0x", ""), NumberStyles.HexNumber); } + void WriteText(string name, ref uint val) { tw.WriteLine("{0} 0x{1:X8}", name, val); } + + void Read(ref sbyte val) { val = br.ReadSByte(); } + void Write(ref sbyte val) { bw.Write(val); } + void ReadText(string name, ref sbyte val) { if (Present(name)) val = sbyte.Parse(Item(name).Replace("0x", ""), NumberStyles.HexNumber); } + void WriteText(string name, ref sbyte val) { tw.WriteLine("{0} 0x{1:X2}", name, val); } + + void Read(ref short val) { val = br.ReadInt16(); } + void Write(ref short val) { bw.Write(val); } + void ReadText(string name, ref short val) { if (Present(name)) val = short.Parse(Item(name).Replace("0x", ""), NumberStyles.HexNumber); } + void WriteText(string name, ref short val) { tw.WriteLine("{0} 0x{1:X4}", name, val); } + + void Read(ref int val) { val = br.ReadInt32(); } + void Write(ref int val) { bw.Write(val); } + void ReadText(string name, ref int val) { if (Present(name)) val = int.Parse(Item(name).Replace("0x", ""), NumberStyles.HexNumber); } + void WriteText(string name, ref int val) { tw.WriteLine("{0} 0x{1:X8}", name, val); } + + void Read(ref bool val) { val = br.ReadBoolean(); } + void Write(ref bool val) { bw.Write(val); } + void ReadText(string name, ref bool val) { if (Present(name)) val = bool.Parse(Item(name)); } + void WriteText(string name, ref bool val) { tw.WriteLine("{0} {1}", name, val); } + } + + + public static class BITREV + { + public static byte[] byte_8; + static BITREV() + { + make_byte_8(); + } + static void make_byte_8() + { + int bits = 8; + int n = 1 << 8; + byte_8 = new byte[n]; + + int m = 1; + int a = n >> 1; + int j = 2; + + byte_8[0] = 0; + byte_8[1] = (byte)a; + + while ((--bits) != 0) + { + m <<= 1; + a >>= 1; + for (int i = 0; i < m; i++) + byte_8[j++] = (byte)(byte_8[i] + a); + } + } + + public static uint reverse_32(uint v) + { + return (uint)((byte_8[v & 0xff] << 24) | + (byte_8[(v >> 8) & 0xff] << 16) | + (byte_8[(v >> 16) & 0xff] << 8) | + (byte_8[(v >> 24) & 0xff])); + } + } + + /// + /// a Dictionary-of-lists with key K and values List<V> + /// + [Serializable] + public class Bag : BagBase>, List> { } + + /// + /// a Dictionary-of-lists with key K and values List<V> + /// + [Serializable] + public class SortedBag : BagBase>, List> { } + + /// + /// A dictionary that creates new values on the fly as necessary so that any key you need will be defined. + /// + /// dictionary keys + /// dictionary values + public class WorkingDictionary : Dictionary where V : new() + { + public new V this[K key] + { + get + { + V temp; + if (!TryGetValue(key, out temp)) + temp = this[key] = new V(); + return temp; + } + set { base[key] = value; } + } + } + + /// + /// base class for Bag and SortedBag + /// + /// dictionary keys + /// list values + /// dictionary type + /// list type + [Serializable] + public class BagBase : IEnumerable + where D : IDictionary, new() + where L : IList, IEnumerable, new() + { + D dictionary = new D(); + public void Add(K key, V val) + { + this[key].Add(val); + } + + public bool ContainsKey(K key) { return dictionary.ContainsKey(key); } + + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + public IEnumerator GetEnumerator() + { + foreach (L lv in dictionary.Values) + foreach (V v in lv) + yield return v; + } + + public IEnumerable KeyValuePairEnumerator { get { return dictionary; } } + + /// + /// the list of keys contained herein + /// + public IList Keys { get { return new List(dictionary.Keys); } } + + public L this[K key] + { + get + { + L slot; + if (!dictionary.TryGetValue(key, out slot)) + dictionary[key] = slot = new L(); + return slot; + } + set + { + dictionary[key] = value; + } + } + } + + public class NotTestedException : Exception + { + } +} diff --git a/BizHawk.Emulation/DiscSystem/cdfs/EndianBitConverter.cs b/BizHawk.Emulation.DiscSystem/cdfs/EndianBitConverter.cs similarity index 95% rename from BizHawk.Emulation/DiscSystem/cdfs/EndianBitConverter.cs rename to BizHawk.Emulation.DiscSystem/cdfs/EndianBitConverter.cs index 2eeae53bd5..382cd7c39a 100644 --- a/BizHawk.Emulation/DiscSystem/cdfs/EndianBitConverter.cs +++ b/BizHawk.Emulation.DiscSystem/cdfs/EndianBitConverter.cs @@ -2,7 +2,8 @@ using System.Collections.Generic; using System.Text; -namespace ISOParser { +namespace BizHawk.Emulation.DiscSystem +{ /// /// Helper class to convert big and little endian numbers from a byte /// array to a value. diff --git a/BizHawk.Emulation.DiscSystem/cdfs/ISODirectoryNode.cs b/BizHawk.Emulation.DiscSystem/cdfs/ISODirectoryNode.cs new file mode 100644 index 0000000000..53cd7f09b1 --- /dev/null +++ b/BizHawk.Emulation.DiscSystem/cdfs/ISODirectoryNode.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace BizHawk.Emulation.DiscSystem +{ + /// + /// Representation of a directory in the file system. + /// + public class ISODirectoryNode : ISONode + { + #region Public Properties + + /// + /// The children in this directory. + /// + public Dictionary Children; + + #endregion + + #region Construction + + /// + /// Constructor. + /// + /// The node for this directory. + public ISODirectoryNode(ISONodeRecord record) + : base(record) + { + this.Children = new Dictionary(); + } + + #endregion + + #region Parsing + + /// + /// Parse the children based on the data in this directory. + /// + /// The stream to parse from. + /// The set of already handled + /// files/directories. + public void Parse(Stream s, Dictionary visited) + { + // Go to the beginning of the set of directories + s.Seek(this.Offset * ISOFile.SECTOR_SIZE, SeekOrigin.Begin); + + List records = new List(); + + // Read the directory entries + while (s.Position < ((this.Offset * ISOFile.SECTOR_SIZE) + this.Length)) + { + ISONode node; + ISONodeRecord record; + + // Read the record + record = new ISONodeRecord(); + record.Parse(s); + + + //zero 24-jun-2013 - improved validity checks + //theres nothing here! + if (record.Length == 0) + { + break; + } + else + { + // Check if we already have this node + if (visited.ContainsKey(record.OffsetOfData)) + { + // Get the node + node = visited[record.OffsetOfData]; + } + else + { + // Create the node from the record + if (record.IsFile()) + { + node = new ISOFileNode(record); + } + else if (record.IsDirectory()) + { + node = new ISODirectoryNode(record); + } + else + { + node = new ISONode(record); + } + + // Keep track that we've now seen the node and are parsing it + visited.Add(node.Offset, node); + } + + // Add the node as a child + this.Children.Add(record.Name, node); + } + } + + long currentPosition = s.Position; + + // Iterate over directories... + foreach (KeyValuePair child in this.Children) + { + // Parse this node + if (child.Key != ISONodeRecord.CURRENT_DIRECTORY && + child.Key != ISONodeRecord.PARENT_DIRECTORY && + child.Value is ISODirectoryNode) + { + ((ISODirectoryNode)child.Value).Parse(s, visited); + } + } + + s.Seek(currentPosition, SeekOrigin.Begin); + } + + #endregion + + #region Printing + + /// + /// Print out this node's children. + /// + /// The number of "tabs" to indent this directory. + public void Print(int depth) + { + // Get the tabs string + string tabs = ""; + for (int i = 0; i < depth; i++) + { + tabs += " "; + } + + // Get the names and sort + string[] names = this.Children.Keys.ToArray(); + Array.Sort(names); + + // Print the directory names recursively + foreach (string s in names) + { + ISONode n = this.Children[s]; + Console.WriteLine(tabs + s); + if (s != ISONodeRecord.CURRENT_DIRECTORY && + s != ISONodeRecord.PARENT_DIRECTORY && + n is ISODirectoryNode) + { + ((ISODirectoryNode)n).Print(depth + 1); + } + } + } + + #endregion + } +} diff --git a/BizHawk.Emulation.DiscSystem/cdfs/ISOFile.cs b/BizHawk.Emulation.DiscSystem/cdfs/ISOFile.cs new file mode 100644 index 0000000000..fb848fa2fe --- /dev/null +++ b/BizHawk.Emulation.DiscSystem/cdfs/ISOFile.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace BizHawk.Emulation.DiscSystem +{ + /// + /// This class is meant to parse disk images as specified by ISO9660. + /// Specifically, it should work for most disk images that are created + /// by the stanard disk imaging software. This class is by no means + /// robust to all variations of ISO9660. + /// Also, this class does not currently support the UDF file system. + /// + /// TODO: Add functions to enumerate a directory or visit a file... + /// + /// The information for building class came from three primary sources: + /// 1. The ISO9660 wikipedia article: + /// http://en.wikipedia.org/wiki/ISO_9660 + /// 2. ISO9660 Simplified for DOS/Windows + /// http://alumnus.caltech.edu/~pje/iso9660.html + /// 3. The ISO 9660 File System + /// http://users.telenet.be/it3.consultants.bvba/handouts/ISO9960.html + /// + public class ISOFile + { + #region Constants + + /// + /// We are hard coding the SECTOR_SIZE + /// + public const int SECTOR_SIZE = 2048; + + #endregion + + #region Public Members + + /// + /// This is a list of all the volume descriptors in the disk image. + /// NOTE: The first entry should be the primary volume. + /// + public List VolumeDescriptors; + + /// + /// The Directory that is the root of this file system + /// + public ISODirectoryNode Root; + + #endregion + + #region Construction + + /// + /// Construct the ISO file data structures, but leave everything + /// blank. + /// + public ISOFile() + { + } + + #endregion + + #region Parsing + + /// + /// Parse the given stream to populate the iso information + /// + /// The stream which we are using to parse the image. + /// Should already be located at the start of the image. + public bool Parse(Stream s, int startSector = 16) + { + this.VolumeDescriptors = new List(); + Root = null; + + long startPosition = s.Position; + byte[] buffer = new byte[ISOFile.SECTOR_SIZE]; + + // Seek through the first volume descriptor + s.Seek(startPosition + (SECTOR_SIZE * startSector), SeekOrigin.Begin); + + // Read one of more volume descriptors + do + { + //zero 24-jun-2013 - improved validity checks + + ISOVolumeDescriptor desc = new ISOVolumeDescriptor(); + bool isValid = desc.Parse(s); + if (!isValid) return false; + + if (desc.IsTerminator()) + break; + else if (desc.Type < 4) + this.VolumeDescriptors.Add(desc); + else + //found a volume descriptor of incorrect type.. maybe this isnt a cdfs + //supposedly these exist.. wait for one to show up + return false; + + } while (true); + + //zero 24-jun-2013 - well, my very first test iso had 2 volume descriptors. + // Check to make sure we only read one volume descriptor + // Finding more could be an error with the disk. + //if (this.VolumeDescriptors.Count != 1) { + // Console.WriteLine("Strange ISO format..."); + // return; + //} + + //zero 24-jun-2013 - if theres no volume descriptors, we're gonna call this not a cdfs + if (VolumeDescriptors.Count == 0) return false; + + // Visit all the directories and get the offset of each directory/file + + // We need to keep track of the directories and files we have visited in case there are loops. + Dictionary visitedNodes = new Dictionary(); + + // Create (and visit) the root node + this.Root = new ISODirectoryNode(this.VolumeDescriptors[0].RootDirectoryRecord); + visitedNodes.Add(this.Root.Offset, this.Root); + this.Root.Parse(s, visitedNodes); + + return true; + } + + #endregion + + #region Printing + + /// + /// Print the directory tree for the image. + /// + public void Print() + { + // DEBUGGING: Now print out the directory structure + this.Root.Print(0); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.DiscSystem/cdfs/ISOFileNode.cs b/BizHawk.Emulation.DiscSystem/cdfs/ISOFileNode.cs new file mode 100644 index 0000000000..53dbeabfa3 --- /dev/null +++ b/BizHawk.Emulation.DiscSystem/cdfs/ISOFileNode.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BizHawk.Emulation.DiscSystem +{ + /// + /// Representation of a file in the file system. + /// + public class ISOFileNode : ISONode + { + #region Construction + + /// + /// Constructor. + /// + /// The record to construct from. + public ISOFileNode(ISONodeRecord record) + : base(record) + { + // Do Nothing + } + + #endregion + } +} diff --git a/BizHawk.Emulation.DiscSystem/cdfs/ISONode.cs b/BizHawk.Emulation.DiscSystem/cdfs/ISONode.cs new file mode 100644 index 0000000000..c9784a3f23 --- /dev/null +++ b/BizHawk.Emulation.DiscSystem/cdfs/ISONode.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BizHawk.Emulation.DiscSystem +{ + /// + /// Abstract class to represent a file/directory node + /// + public class ISONode + { + #region Public Properties + + /// + /// The record this node was created from. + /// + public ISONodeRecord FirstRecord; + + /// + /// The sector offset of the file/directory data + /// + public long Offset; + /// + /// The byte length of the file/directory data. + /// + public long Length; + + #endregion + + #region Construction + + /// + /// Constructor. + /// TODO: Make this constructor protected??? + /// + /// The ISONodeRecord to construct from. + public ISONode(ISONodeRecord record) + { + this.FirstRecord = record; + this.Offset = record.OffsetOfData; + this.Length = record.LengthOfData; + } + + #endregion + } +} diff --git a/BizHawk.Emulation.DiscSystem/cdfs/ISONodeRecord.cs b/BizHawk.Emulation.DiscSystem/cdfs/ISONodeRecord.cs new file mode 100644 index 0000000000..1913f3d897 --- /dev/null +++ b/BizHawk.Emulation.DiscSystem/cdfs/ISONodeRecord.cs @@ -0,0 +1,217 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace BizHawk.Emulation.DiscSystem +{ + /// + /// Class to represent the file/directory information read from the disk. + /// + public class ISONodeRecord + { + #region Constants + + /// + /// String representing the current directory entry + /// + public const string CURRENT_DIRECTORY = "."; + + /// + /// String representing the parent directory entry + /// + public const string PARENT_DIRECTORY = ".."; + + #endregion + + #region Public Properties + + /// + /// The length of the record in bytes. + /// + public byte Length; + + /// + /// The file offset of the data for this file/directory (in sectors). + /// + public long OffsetOfData; + /// + /// The length of the data for this file/directory (in bytes). + /// + public long LengthOfData; + + /// + /// The file/directory creation year since 1900. + /// + public byte Year; + /// + /// The file/directory creation month. + /// + public byte Month; + /// + /// The file/directory creation day. + /// + public byte Day; + /// + /// The file/directory creation hour. + /// + public byte Hour; + /// + /// The file/directory creation minute. + /// + public byte Minute; + /// + /// The file/directory creation second. + /// + public byte Second; + /// + /// The file time offset from GMT. + /// + public byte TimeZoneOffset; + + /// + /// Flags representing the attributes of this file/directory. + /// + public byte Flags; + + /// + /// The length of the file/directory name. + /// + public byte NameLength; + /// + /// The file/directory name. + /// + public string Name; + + #endregion + + #region Construction + + /// + /// Constructor + /// + public ISONodeRecord() + { + // Set initial values + this.Length = 0; + + this.OffsetOfData = 0; + this.LengthOfData = 0; + + this.Year = 0; + this.Month = 0; + this.Day = 0; + this.Hour = 0; + this.Minute = 0; + this.Second = 0; + this.TimeZoneOffset = 0; + + this.Flags = 0; + + this.NameLength = 0; + this.Name = null; + } + + #endregion + + #region File/Directory Methods + + /// + /// Return true if the record represents a file. + /// + /// True if a file. + public bool IsFile() + { + return ((this.Flags >> 1) & 0x01) == 0; + } + + /// + /// Return true if the record represents a directory. + /// + /// True if a directory. + public bool IsDirectory() + { + return ((this.Flags >> 1) & 0x01) == 1; + } + + #endregion + + #region Parsing + + /// + /// Parse the record from an array and offset. + /// + /// The array to parse from. + /// The offset to start parsing at. + public void Parse(byte[] data, int cursor) + { + // Put the array into a memory stream and pass to the main parsing function + MemoryStream s = new MemoryStream(data); + s.Seek(cursor, SeekOrigin.Begin); + this.Parse(s); + } + + /// + /// Parse the node record from the given stream. + /// + /// The stream to parse from. + public void Parse(Stream s) + { + EndianBitConverter bc = EndianBitConverter.CreateForLittleEndian(); + long startPosition = s.Position; + byte[] buffer = new byte[ISOFile.SECTOR_SIZE]; + + // Get the length + s.Read(buffer, 0, 1); + this.Length = buffer[0]; + + //the number of sectors in the attribute record + s.Read(buffer, 0, 1); + + // Read Data Offset + s.Read(buffer, 0, 8); + this.OffsetOfData = (long)bc.ToInt32(buffer); + + // Read Data Length + s.Read(buffer, 0, 8); + this.LengthOfData = (long)bc.ToInt32(buffer); + + // Read the time and flags + s.Read(buffer, 0, 8); + this.Year = buffer[0]; + this.Month = buffer[1]; + this.Day = buffer[2]; + this.Hour = buffer[3]; + this.Minute = buffer[4]; + this.Second = buffer[5]; + this.TimeZoneOffset = buffer[6]; + + this.Flags = buffer[7]; + + s.Read(buffer, 0, 6); + + // Read the name length + s.Read(buffer, 0, 1); + this.NameLength = buffer[0]; + + // Read the directory name + s.Read(buffer, 0, this.NameLength); + if (this.NameLength == 1 && (buffer[0] == 0 || buffer[0] == 1)) + { + if (buffer[0] == 0) + this.Name = ISONodeRecord.CURRENT_DIRECTORY; + else + this.Name = ISONodeRecord.PARENT_DIRECTORY; + } + else + { + this.Name = ASCIIEncoding.ASCII.GetString(buffer, 0, this.NameLength); + } + + // Seek to end + s.Seek(startPosition + this.Length, SeekOrigin.Begin); + } + + #endregion + } +} diff --git a/BizHawk.Emulation.DiscSystem/cdfs/ISOVolumeDescriptor.cs b/BizHawk.Emulation.DiscSystem/cdfs/ISOVolumeDescriptor.cs new file mode 100644 index 0000000000..4dda3f59e4 --- /dev/null +++ b/BizHawk.Emulation.DiscSystem/cdfs/ISOVolumeDescriptor.cs @@ -0,0 +1,326 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace BizHawk.Emulation.DiscSystem +{ + /// + /// Represents a volume descriptor for a disk image. + /// + public class ISOVolumeDescriptor + { + #region Constants + + /// + /// We are handling the parsing by reading the entire header and + /// extracting the appropriate bytes. + /// + /// This is done for performance reasons. + /// + private const int LENGTH_SHORT_IDENTIFIER = 32; + private const int LENGTH_IDENTIFIER = 37; + private const int LENGTH_LONG_IDENTIFIER = 128; + private const int LENGTH_ROOT_DIRECTORY_RECORD = 34; + private const int LENGTH_TIME = 17; + private const int LENGTH_RESERVED = 512; + + #endregion + + #region Public Properties + + /// + /// The type of this volume description, only 1 and 255 are supported + /// + public byte Type; + + /// + /// The system identifier + /// + public byte[] SystemIdentifier; + /// + /// The volume identifier + /// + public byte[] VolumeIdentifier; + + /// + /// The number of sectors on the disk + /// + public int NumberOfSectors; + + /// + /// Volume Set Size (should be 1) + /// + public int VolumeSetSize; + /// + /// Volume Sequence Number (should be 1) + /// + public int VolumeSequenceNumber; + /// + /// Sector Size (should be 2048) + /// + public int SectorSize; + + /// + /// Size of the path table + /// + public int PathTableSize; + /// + /// Sector offset of the first path table + /// + public int OffsetOfFirstLittleEndianPathTable; + /// + /// Sector offset of the second path table + /// + public int OffsetOfSecondLittleEndianPathTable; + /// + /// Sector offset of the first path table + /// + public int OffsetOfFirstBigEndianPathTable; + /// + /// Sector offset of the second path table + /// + public int OffsetOfSecondBigEndianPathTable; + + /// + /// The root directory record + /// + public ISONodeRecord RootDirectoryRecord; + + /// + /// The volumen set identifier + /// + public byte[] VolumeSetIdentifier; + /// + /// The publisher identifier + /// + public byte[] PublisherIdentifier; + /// + /// The data preparer identifier + /// + public byte[] DataPreparerIdentifier; + /// + /// The application identifier + /// + public byte[] ApplicationIdentifier; + + /// + /// The copyright identifier + /// + public byte[] CopyrightFileIdentifier; + /// + /// The abstract file identifier + /// + public byte[] AbstractFileIdentifier; + /// + /// The bibliographical file identifier + /// + public byte[] BibliographicalFileIdentifier; + + /// + /// The time and date the volume was created + /// + public byte[] VolumeCreationDateTime; + /// + /// The time and date the volume was last modified + /// + public byte[] LastModifiedDateTime; + /// + /// The time and date the volume expires + /// + public byte[] ExpirationDateTime; + /// + /// The time and data when the volume is effective + /// + public byte[] EffectiveDateTime; + + /// + /// Extra reserved data + /// + public byte[] Reserved; + + #endregion + + #region Construction + + /// + /// Constructor. + /// + public ISOVolumeDescriptor() + { + // Set everything to the default value + this.Type = 0; + + this.SystemIdentifier = new byte[LENGTH_SHORT_IDENTIFIER]; + this.VolumeIdentifier = new byte[LENGTH_SHORT_IDENTIFIER]; + + this.NumberOfSectors = 0; + + this.VolumeSetSize = 1; + this.VolumeSequenceNumber = 1; + this.SectorSize = ISOFile.SECTOR_SIZE; + + this.PathTableSize = 0; + this.OffsetOfFirstLittleEndianPathTable = 0; + this.OffsetOfSecondLittleEndianPathTable = 0; + this.OffsetOfFirstBigEndianPathTable = 0; + this.OffsetOfSecondBigEndianPathTable = 0; + + this.RootDirectoryRecord = new ISONodeRecord(); + + this.VolumeSetIdentifier = new byte[LENGTH_LONG_IDENTIFIER]; + this.PublisherIdentifier = new byte[LENGTH_LONG_IDENTIFIER]; + this.DataPreparerIdentifier = new byte[LENGTH_LONG_IDENTIFIER]; + this.ApplicationIdentifier = new byte[LENGTH_LONG_IDENTIFIER]; + + this.CopyrightFileIdentifier = new byte[LENGTH_IDENTIFIER]; + this.AbstractFileIdentifier = new byte[LENGTH_IDENTIFIER]; + this.BibliographicalFileIdentifier = new byte[LENGTH_IDENTIFIER]; + + this.VolumeCreationDateTime = new byte[LENGTH_TIME]; + this.LastModifiedDateTime = new byte[LENGTH_TIME]; + this.ExpirationDateTime = new byte[LENGTH_TIME]; + this.EffectiveDateTime = new byte[LENGTH_TIME]; + + this.Reserved = new byte[LENGTH_RESERVED]; + } + + #endregion + + #region Parsing + + /// + /// Parse the volume descriptor header. + /// + /// The stream to parse from. + public bool Parse(Stream s) + { + EndianBitConverter bc = EndianBitConverter.CreateForLittleEndian(); + EndianBitConverter bcBig = EndianBitConverter.CreateForBigEndian(); + long startPosition = s.Position; + byte[] buffer = new byte[ISOFile.SECTOR_SIZE]; + + // Read the entire structure + s.Read(buffer, 0, ISOFile.SECTOR_SIZE); + + // Get the type + this.Type = buffer[0]; + + //zero 24-jun-2013 - validate + // "CD001" + 0x01 + if (buffer[1] == 'C' && buffer[2] == 'D' && buffer[3] == '0' && buffer[4] == '0' && buffer[5] == '1' && buffer[6] == 0x01) + { + //it seems to be a valid volume descriptor + } + else + { + return false; + } + + // Handle the primary volume information + if (this.Type == 1) + { + int cursor = 8; + // Get the system identifier + Array.Copy(buffer, cursor, + this.SystemIdentifier, 0, LENGTH_SHORT_IDENTIFIER); + cursor += LENGTH_SHORT_IDENTIFIER; + + // Get the volume identifier + Array.Copy(buffer, cursor, + this.VolumeIdentifier, 0, LENGTH_SHORT_IDENTIFIER); + cursor += LENGTH_SHORT_IDENTIFIER; + + cursor += 8; + + // Get the total number of sectors + this.NumberOfSectors = bc.ToInt32(buffer, cursor); + cursor += 8; + + cursor += 32; + + this.VolumeSetSize = bc.ToInt16(buffer, cursor); + cursor += 4; + this.VolumeSequenceNumber = bc.ToInt16(buffer, cursor); + cursor += 4; + this.SectorSize = bc.ToInt16(buffer, cursor); + cursor += 4; + + this.PathTableSize = bc.ToInt32(buffer, cursor); + cursor += 8; + this.OffsetOfFirstLittleEndianPathTable = bc.ToInt32(buffer, cursor); + cursor += 4; + this.OffsetOfSecondLittleEndianPathTable = bc.ToInt32(buffer, cursor); + cursor += 4; + this.OffsetOfFirstLittleEndianPathTable = bcBig.ToInt32(buffer, cursor); + cursor += 4; + this.OffsetOfSecondLittleEndianPathTable = bcBig.ToInt32(buffer, cursor); + cursor += 4; + + this.RootDirectoryRecord.Parse(buffer, cursor); + cursor += LENGTH_ROOT_DIRECTORY_RECORD; + + Array.Copy(buffer, cursor, + this.VolumeSetIdentifier, 0, LENGTH_LONG_IDENTIFIER); + cursor += LENGTH_LONG_IDENTIFIER; + Array.Copy(buffer, cursor, + this.PublisherIdentifier, 0, LENGTH_LONG_IDENTIFIER); + cursor += LENGTH_LONG_IDENTIFIER; + Array.Copy(buffer, cursor, + this.DataPreparerIdentifier, 0, LENGTH_LONG_IDENTIFIER); + cursor += LENGTH_LONG_IDENTIFIER; + Array.Copy(buffer, cursor, + this.ApplicationIdentifier, 0, LENGTH_LONG_IDENTIFIER); + cursor += LENGTH_LONG_IDENTIFIER; + + Array.Copy(buffer, cursor, + this.CopyrightFileIdentifier, 0, LENGTH_IDENTIFIER); + cursor += LENGTH_IDENTIFIER; + Array.Copy(buffer, cursor, + this.AbstractFileIdentifier, 0, LENGTH_IDENTIFIER); + cursor += LENGTH_IDENTIFIER; + Array.Copy(buffer, cursor, + this.BibliographicalFileIdentifier, 0, LENGTH_IDENTIFIER); + cursor += LENGTH_IDENTIFIER; + + Array.Copy(buffer, cursor, + this.VolumeCreationDateTime, 0, LENGTH_TIME); + cursor += LENGTH_TIME; + Array.Copy(buffer, cursor, + this.LastModifiedDateTime, 0, LENGTH_TIME); + cursor += LENGTH_TIME; + Array.Copy(buffer, cursor, + this.ExpirationDateTime, 0, LENGTH_TIME); + cursor += LENGTH_TIME; + Array.Copy(buffer, cursor, + this.EffectiveDateTime, 0, LENGTH_TIME); + cursor += LENGTH_TIME; + + cursor += 1; + + cursor += 1; + + Array.Copy(buffer, cursor, + this.Reserved, 0, LENGTH_RESERVED); + cursor += LENGTH_RESERVED; + } + + return true; + } + + #endregion + + #region Type Information + + /// + /// Returns true if this is the terminator volume descriptor. + /// + /// True if the terminator. + public bool IsTerminator() + { + return (this.Type == 255); + } + + #endregion + } +} diff --git a/BizHawk.Emulation/DiscSystem/docs/notes.txt b/BizHawk.Emulation.DiscSystem/docs/notes.txt similarity index 100% rename from BizHawk.Emulation/DiscSystem/docs/notes.txt rename to BizHawk.Emulation.DiscSystem/docs/notes.txt diff --git a/BizHawk.Emulation/BizHawk.Emulation.csproj b/BizHawk.Emulation/BizHawk.Emulation.csproj index f7780701e3..e774e0304c 100644 --- a/BizHawk.Emulation/BizHawk.Emulation.csproj +++ b/BizHawk.Emulation/BizHawk.Emulation.csproj @@ -457,47 +457,6 @@ - - - Code - - - Code - - - Code - - - - - - - - - - Code - - - - Code - - - Code - - - - Code - - - Code - - - - Code - - - Code - @@ -560,7 +519,6 @@ - @@ -588,6 +546,10 @@ {866F8D13-0678-4FF9-80A4-A3993FD4D8A3} BizHawk.Common + + {f51946ea-827f-4d82-b841-1f2f6d060312} + BizHawk.Emulation.DiscSystem + diff --git a/BizHawk.Emulation/Consoles/PC Engine/PCEngine.cs b/BizHawk.Emulation/Consoles/PC Engine/PCEngine.cs index e639655a0d..44d1b4ef1d 100644 --- a/BizHawk.Emulation/Consoles/PC Engine/PCEngine.cs +++ b/BizHawk.Emulation/Consoles/PC Engine/PCEngine.cs @@ -5,8 +5,8 @@ using System.IO; using BizHawk.Common; using BizHawk.Emulation.CPUs.H6280; +using BizHawk.Emulation.DiscSystem; using BizHawk.Emulation.Sound; -using BizHawk.DiscSystem; namespace BizHawk.Emulation.Consoles.TurboGrafx { diff --git a/BizHawk.Emulation/Consoles/PC Engine/ScsiCDBus.cs b/BizHawk.Emulation/Consoles/PC Engine/ScsiCDBus.cs index 713cad4e99..4d473510a4 100644 --- a/BizHawk.Emulation/Consoles/PC Engine/ScsiCDBus.cs +++ b/BizHawk.Emulation/Consoles/PC Engine/ScsiCDBus.cs @@ -1,842 +1,847 @@ using System; -using BizHawk.DiscSystem; -using BizHawk.Emulation.Sound; using System.IO; using System.Globalization; using BizHawk.Common; +using BizHawk.Emulation.DiscSystem; +using BizHawk.Emulation.Sound; namespace BizHawk.Emulation.Consoles.TurboGrafx { - // TODO we can adjust this to have Think take the number of cycles and not require - // a reference to Cpu.TotalExecutedCycles - // which incidentally would allow us to put it back to an int from a long if we wanted to + // TODO we can adjust this to have Think take the number of cycles and not require + // a reference to Cpu.TotalExecutedCycles + // which incidentally would allow us to put it back to an int from a long if we wanted to - public sealed class ScsiCDBus - { - const int STATUS_GOOD = 0; - const int STATUS_CHECK_CONDITION = 1; - const int STATUS_CONDITION_MET = 2; - const int STATUS_BUSY = 4; - const int STATUS_INTERMEDIATE = 8; + public sealed class ScsiCDBus + { + const int STATUS_GOOD = 0; + const int STATUS_CHECK_CONDITION = 1; + const int STATUS_CONDITION_MET = 2; + const int STATUS_BUSY = 4; + const int STATUS_INTERMEDIATE = 8; - const int SCSI_TEST_UNIT_READY = 0x00; - const int SCSI_REQUEST_SENSE = 0x03; - const int SCSI_READ = 0x08; - const int SCSI_AUDIO_START_POS = 0xD8; - const int SCSI_AUDIO_END_POS = 0xD9; - const int SCSI_PAUSE = 0xDA; - const int SCSI_READ_SUBCODE_Q = 0xDD; - const int SCSI_READ_TOC = 0xDE; + const int SCSI_TEST_UNIT_READY = 0x00; + const int SCSI_REQUEST_SENSE = 0x03; + const int SCSI_READ = 0x08; + const int SCSI_AUDIO_START_POS = 0xD8; + const int SCSI_AUDIO_END_POS = 0xD9; + const int SCSI_PAUSE = 0xDA; + const int SCSI_READ_SUBCODE_Q = 0xDD; + const int SCSI_READ_TOC = 0xDE; - bool bsy, sel, cd, io, msg, req, ack, atn, rst; - bool signalsChanged; + bool bsy, sel, cd, io, msg, req, ack, atn, rst; + bool signalsChanged; - public bool BSY - { - get { return bsy; } - set { - if (value != BSY) signalsChanged = true; - bsy = value; - } - } - public bool SEL - { - get { return sel; } - set - { - if (value != SEL) signalsChanged = true; - sel = value; - } - } - public bool CD // CONTROL = true, DATA = false - { - get { return cd; } - set - { - if (value != CD) signalsChanged = true; - cd = value; - } - } - public bool IO // INPUT = true, OUTPUT = false - { - get { return io; } - set - { - if (value != IO) signalsChanged = true; - io = value; - } - } - public bool MSG - { - get { return msg; } - set - { - if (value != MSG) signalsChanged = true; - msg = value; - } - } - public bool REQ - { - get { return req; } - set - { - if (value != REQ) signalsChanged = true; - req = value; - } - } - public bool ACK - { - get { return ack; } - set - { - if (value != ACK) signalsChanged = true; - ack = value; - } - } - public bool ATN - { - get { return atn; } - set - { - if (value != ATN) signalsChanged = true; - atn = value; - } - } - public bool RST - { - get { return rst; } - set - { - if (value != RST) signalsChanged = true; - rst = value; - } - } - public byte DataBits { get; set; } // data bits + public bool BSY + { + get { return bsy; } + set + { + if (value != BSY) signalsChanged = true; + bsy = value; + } + } + public bool SEL + { + get { return sel; } + set + { + if (value != SEL) signalsChanged = true; + sel = value; + } + } + public bool CD // CONTROL = true, DATA = false + { + get { return cd; } + set + { + if (value != CD) signalsChanged = true; + cd = value; + } + } + public bool IO // INPUT = true, OUTPUT = false + { + get { return io; } + set + { + if (value != IO) signalsChanged = true; + io = value; + } + } + public bool MSG + { + get { return msg; } + set + { + if (value != MSG) signalsChanged = true; + msg = value; + } + } + public bool REQ + { + get { return req; } + set + { + if (value != REQ) signalsChanged = true; + req = value; + } + } + public bool ACK + { + get { return ack; } + set + { + if (value != ACK) signalsChanged = true; + ack = value; + } + } + public bool ATN + { + get { return atn; } + set + { + if (value != ATN) signalsChanged = true; + atn = value; + } + } + public bool RST + { + get { return rst; } + set + { + if (value != RST) signalsChanged = true; + rst = value; + } + } + public byte DataBits { get; set; } // data bits - enum BusPhase - { - BusFree, - Command, - DataIn, - DataOut, - MessageIn, - MessageOut, - Status - } + enum BusPhase + { + BusFree, + Command, + DataIn, + DataOut, + MessageIn, + MessageOut, + Status + } - bool busPhaseChanged; - BusPhase Phase = BusPhase.BusFree; + bool busPhaseChanged; + BusPhase Phase = BusPhase.BusFree; - bool MessageCompleted; - bool StatusCompleted; - byte MessageValue; + bool MessageCompleted; + bool StatusCompleted; + byte MessageValue; - QuickList CommandBuffer = new QuickList(10); // 10 = biggest command - public QuickQueue DataIn = new QuickQueue(2048); // one data sector + QuickList CommandBuffer = new QuickList(10); // 10 = biggest command + public QuickQueue DataIn = new QuickQueue(2048); // one data sector - // ******** Data Transfer / READ command support ******** - - public long DataReadWaitTimer; - public bool DataReadInProgress; - public bool DataTransferWasDone; - public bool DataTransferInProgress; - public int CurrentReadingSector; - public int SectorsLeftToRead; + // ******** Data Transfer / READ command support ******** - // ******** Resources ******** + public long DataReadWaitTimer; + public bool DataReadInProgress; + public bool DataTransferWasDone; + public bool DataTransferInProgress; + public int CurrentReadingSector; + public int SectorsLeftToRead; - PCEngine pce; - public Disc disc; - int audioStartLBA; - int audioEndLBA; + // ******** Resources ******** - public ScsiCDBus(PCEngine pce, Disc disc) - { - this.pce = pce; - this.disc = disc; - } + PCEngine pce; + public Disc disc; + int audioStartLBA; + int audioEndLBA; - public void Think() - { - if (RST) - { - ResetDevice(); - return; - } - - if (DataReadInProgress && pce.Cpu.TotalExecutedCycles > DataReadWaitTimer) - { - if (SectorsLeftToRead > 0) - pce.CoreComm.DriveLED = true; + public ScsiCDBus(PCEngine pce, Disc disc) + { + this.pce = pce; + this.disc = disc; + } - if (DataIn.Count == 0) - { - // read in a sector and shove it in the queue - disc.ReadLBA_2048(CurrentReadingSector, DataIn.GetBuffer(), 0); - DataIn.SignalBufferFilled(2048); - CurrentReadingSector++; - SectorsLeftToRead--; + public void Think() + { + if (RST) + { + ResetDevice(); + return; + } - pce.IntDataTransferReady = true; + if (DataReadInProgress && pce.Cpu.TotalExecutedCycles > DataReadWaitTimer) + { + if (SectorsLeftToRead > 0) + pce.CoreComm.DriveLED = true; - // If more sectors, should set the next think-clock to however long it takes to read 1 sector - // but I dont. I dont think transfers actually happen sector by sector - // like this, they probably become available as the bits come off the disc. - // but lets get some basic functionality before we go crazy. - // Idunno, maybe they do come in a sector at a time. + if (DataIn.Count == 0) + { + // read in a sector and shove it in the queue + disc.ReadLBA_2048(CurrentReadingSector, DataIn.GetBuffer(), 0); + DataIn.SignalBufferFilled(2048); + CurrentReadingSector++; + SectorsLeftToRead--; + + pce.IntDataTransferReady = true; + + // If more sectors, should set the next think-clock to however long it takes to read 1 sector + // but I dont. I dont think transfers actually happen sector by sector + // like this, they probably become available as the bits come off the disc. + // but lets get some basic functionality before we go crazy. + // Idunno, maybe they do come in a sector at a time. //note to vecna: maybe not at the sector level, but at a level > 1 sample and <= 1 sector, samples come out in blocks //due to the way they are jumbled up (seriously, like put into a blender) for error correction purposes. //we may as well assume that the cd audio decoding magic works at the level of one sector, but it isnt one sample. - if (SectorsLeftToRead == 0) - { - DataReadInProgress = false; - DataTransferWasDone = true; - } - SetPhase(BusPhase.DataIn); - } - } + if (SectorsLeftToRead == 0) + { + DataReadInProgress = false; + DataTransferWasDone = true; + } + SetPhase(BusPhase.DataIn); + } + } - do - { - signalsChanged = false; - busPhaseChanged = false; + do + { + signalsChanged = false; + busPhaseChanged = false; - if (SEL && !BSY) - { - SetPhase(BusPhase.Command); - } - else if (ATN && !REQ && !ACK) - { - SetPhase(BusPhase.MessageOut); - } - else switch (Phase) - { - case BusPhase.Command: ThinkCommandPhase(); break; - case BusPhase.DataIn: ThinkDataInPhase(); break; - case BusPhase.DataOut: ThinkDataOutPhase(); break; - case BusPhase.MessageIn: ThinkMessageInPhase(); break; - case BusPhase.MessageOut: ThinkMessageOutPhase(); break; - case BusPhase.Status: ThinkStatusPhase(); break; - default: break; - } - } while (signalsChanged || busPhaseChanged); - } + if (SEL && !BSY) + { + SetPhase(BusPhase.Command); + } + else if (ATN && !REQ && !ACK) + { + SetPhase(BusPhase.MessageOut); + } + else switch (Phase) + { + case BusPhase.Command: ThinkCommandPhase(); break; + case BusPhase.DataIn: ThinkDataInPhase(); break; + case BusPhase.DataOut: ThinkDataOutPhase(); break; + case BusPhase.MessageIn: ThinkMessageInPhase(); break; + case BusPhase.MessageOut: ThinkMessageOutPhase(); break; + case BusPhase.Status: ThinkStatusPhase(); break; + default: break; + } + } while (signalsChanged || busPhaseChanged); + } - void ResetDevice() - { - CD = false; - IO = false; - MSG = false; - REQ = false; - ACK = false; - ATN = false; - DataBits = 0; - Phase = BusPhase.BusFree; + void ResetDevice() + { + CD = false; + IO = false; + MSG = false; + REQ = false; + ACK = false; + ATN = false; + DataBits = 0; + Phase = BusPhase.BusFree; - CommandBuffer.Clear(); - DataIn.Clear(); - DataReadInProgress = false; - pce.CDAudio.Stop(); - } + CommandBuffer.Clear(); + DataIn.Clear(); + DataReadInProgress = false; + pce.CDAudio.Stop(); + } - void ThinkCommandPhase() - { - if (REQ && ACK) - { - CommandBuffer.Add(DataBits); - REQ = false; - } + void ThinkCommandPhase() + { + if (REQ && ACK) + { + CommandBuffer.Add(DataBits); + REQ = false; + } - if (!REQ && !ACK && CommandBuffer.Count > 0) - { - bool complete = CheckCommandBuffer(); + if (!REQ && !ACK && CommandBuffer.Count > 0) + { + bool complete = CheckCommandBuffer(); - if (complete) - { - CommandBuffer.Clear(); - } - else - { - REQ = true; // needs more data! - } - } - } + if (complete) + { + CommandBuffer.Clear(); + } + else + { + REQ = true; // needs more data! + } + } + } - void ThinkDataInPhase() - { - if (REQ && ACK) - { - REQ = false; - } - else if (!REQ && !ACK) - { - if (DataIn.Count > 0) - { - DataBits = DataIn.Dequeue(); - REQ = true; - } else { - // data transfer is finished - - pce.IntDataTransferReady = false; - if (DataTransferWasDone) - { - DataTransferInProgress = false; - DataTransferWasDone = false; - pce.IntDataTransferComplete = true; - } - SetStatusMessage(STATUS_GOOD, 0); - } - } - } + void ThinkDataInPhase() + { + if (REQ && ACK) + { + REQ = false; + } + else if (!REQ && !ACK) + { + if (DataIn.Count > 0) + { + DataBits = DataIn.Dequeue(); + REQ = true; + } + else + { + // data transfer is finished - void ThinkDataOutPhase() - { - Console.WriteLine("*********** DATA OUT PHASE, DOES THIS HAPPEN? ****************"); - SetPhase(BusPhase.BusFree); - } + pce.IntDataTransferReady = false; + if (DataTransferWasDone) + { + DataTransferInProgress = false; + DataTransferWasDone = false; + pce.IntDataTransferComplete = true; + } + SetStatusMessage(STATUS_GOOD, 0); + } + } + } - void ThinkMessageInPhase() - { - if (REQ && ACK) - { - REQ = false; - MessageCompleted = true; - } + void ThinkDataOutPhase() + { + Console.WriteLine("*********** DATA OUT PHASE, DOES THIS HAPPEN? ****************"); + SetPhase(BusPhase.BusFree); + } - if (!REQ && !ACK && MessageCompleted) - { - MessageCompleted = false; - SetPhase(BusPhase.BusFree); - } - } + void ThinkMessageInPhase() + { + if (REQ && ACK) + { + REQ = false; + MessageCompleted = true; + } - void ThinkMessageOutPhase() - { - Console.WriteLine("******* IN MESSAGE OUT PHASE. DOES THIS EVER HAPPEN? ********"); - SetPhase(BusPhase.BusFree); - } - - void ThinkStatusPhase() - { - if (REQ && ACK) - { - REQ = false; - StatusCompleted = true; - } - if (!REQ && !ACK && StatusCompleted) - { - StatusCompleted = false; - DataBits = MessageValue; - SetPhase(BusPhase.MessageIn); - } - } + if (!REQ && !ACK && MessageCompleted) + { + MessageCompleted = false; + SetPhase(BusPhase.BusFree); + } + } - // returns true if command completed, false if more data bytes needed - bool CheckCommandBuffer() - { - switch (CommandBuffer[0]) - { - case SCSI_TEST_UNIT_READY: - if (CommandBuffer.Count < 6) return false; - SetStatusMessage(STATUS_GOOD, 0); - return true; + void ThinkMessageOutPhase() + { + Console.WriteLine("******* IN MESSAGE OUT PHASE. DOES THIS EVER HAPPEN? ********"); + SetPhase(BusPhase.BusFree); + } - case SCSI_READ: - if (CommandBuffer.Count < 6) return false; - CommandRead(); - return true; + void ThinkStatusPhase() + { + if (REQ && ACK) + { + REQ = false; + StatusCompleted = true; + } + if (!REQ && !ACK && StatusCompleted) + { + StatusCompleted = false; + DataBits = MessageValue; + SetPhase(BusPhase.MessageIn); + } + } - case SCSI_AUDIO_START_POS: - if (CommandBuffer.Count < 10) return false; - CommandAudioStartPos(); - return true; + // returns true if command completed, false if more data bytes needed + bool CheckCommandBuffer() + { + switch (CommandBuffer[0]) + { + case SCSI_TEST_UNIT_READY: + if (CommandBuffer.Count < 6) return false; + SetStatusMessage(STATUS_GOOD, 0); + return true; - case SCSI_AUDIO_END_POS: - if (CommandBuffer.Count < 10) return false; - CommandAudioEndPos(); - return true; + case SCSI_READ: + if (CommandBuffer.Count < 6) return false; + CommandRead(); + return true; - case SCSI_PAUSE: - if (CommandBuffer.Count < 10) return false; - CommandPause(); - return true; + case SCSI_AUDIO_START_POS: + if (CommandBuffer.Count < 10) return false; + CommandAudioStartPos(); + return true; - case SCSI_READ_SUBCODE_Q: - if (CommandBuffer.Count < 10) return false; - CommandReadSubcodeQ(); - return true; + case SCSI_AUDIO_END_POS: + if (CommandBuffer.Count < 10) return false; + CommandAudioEndPos(); + return true; - case SCSI_READ_TOC: - if (CommandBuffer.Count < 10) return false; - CommandReadTOC(); - return true; + case SCSI_PAUSE: + if (CommandBuffer.Count < 10) return false; + CommandPause(); + return true; - default: - Console.WriteLine("UNRECOGNIZED SCSI COMMAND! {0:X2}", CommandBuffer[0]); - SetStatusMessage(STATUS_GOOD, 0); - break; - } - return false; - } + case SCSI_READ_SUBCODE_Q: + if (CommandBuffer.Count < 10) return false; + CommandReadSubcodeQ(); + return true; - void CommandRead() - { - int sector = (CommandBuffer[1] & 0x1f) << 16; - sector |= CommandBuffer[2] << 8; - sector |= CommandBuffer[3]; + case SCSI_READ_TOC: + if (CommandBuffer.Count < 10) return false; + CommandReadTOC(); + return true; - DataReadInProgress = true; - DataTransferInProgress = true; - CurrentReadingSector = sector; - SectorsLeftToRead = CommandBuffer[4]; + default: + Console.WriteLine("UNRECOGNIZED SCSI COMMAND! {0:X2}", CommandBuffer[0]); + SetStatusMessage(STATUS_GOOD, 0); + break; + } + return false; + } - if (CommandBuffer[4] == 0) - SectorsLeftToRead = 256; + void CommandRead() + { + int sector = (CommandBuffer[1] & 0x1f) << 16; + sector |= CommandBuffer[2] << 8; + sector |= CommandBuffer[3]; - DataReadWaitTimer = pce.Cpu.TotalExecutedCycles + 5000; // figure out proper read delay later - pce.CDAudio.Stop(); - } + DataReadInProgress = true; + DataTransferInProgress = true; + CurrentReadingSector = sector; + SectorsLeftToRead = CommandBuffer[4]; - void CommandAudioStartPos() - { - switch (CommandBuffer[9] & 0xC0) - { - case 0x00: // Set start offset in LBA units - audioStartLBA = (CommandBuffer[3] << 16) | (CommandBuffer[4] << 8) | CommandBuffer[5]; - break; + if (CommandBuffer[4] == 0) + SectorsLeftToRead = 256; - case 0x40: // Set start offset in MSF units - byte m = CommandBuffer[2].BCDtoBin(); - byte s = CommandBuffer[3].BCDtoBin(); - byte f = CommandBuffer[4].BCDtoBin(); - audioStartLBA = Disc.ConvertMSFtoLBA(m, s, f); - break; + DataReadWaitTimer = pce.Cpu.TotalExecutedCycles + 5000; // figure out proper read delay later + pce.CDAudio.Stop(); + } - case 0x80: // Set start offset in track units - byte trackNo = CommandBuffer[2].BCDtoBin(); - audioStartLBA = disc.TOC.Sessions[0].Tracks[trackNo - 1].Indexes[1].aba - 150; - break; - } + void CommandAudioStartPos() + { + switch (CommandBuffer[9] & 0xC0) + { + case 0x00: // Set start offset in LBA units + audioStartLBA = (CommandBuffer[3] << 16) | (CommandBuffer[4] << 8) | CommandBuffer[5]; + break; - if (CommandBuffer[1] == 0) - { - pce.CDAudio.PlayStartingAtLba(audioStartLBA); - pce.CDAudio.Pause(); - } else { - pce.CDAudio.PlayStartingAtLba(audioStartLBA); - } + case 0x40: // Set start offset in MSF units + byte m = CommandBuffer[2].BCDtoBin(); + byte s = CommandBuffer[3].BCDtoBin(); + byte f = CommandBuffer[4].BCDtoBin(); + audioStartLBA = Disc.ConvertMSFtoLBA(m, s, f); + break; - SetStatusMessage(STATUS_GOOD, 0); - pce.IntDataTransferComplete = true; - } + case 0x80: // Set start offset in track units + byte trackNo = CommandBuffer[2].BCDtoBin(); + audioStartLBA = disc.TOC.Sessions[0].Tracks[trackNo - 1].Indexes[1].aba - 150; + break; + } - void CommandAudioEndPos() - { - switch (CommandBuffer[9] & 0xC0) - { - case 0x00: // Set end offset in LBA units - audioEndLBA = (CommandBuffer[3] << 16) | (CommandBuffer[4] << 8) | CommandBuffer[5]; - break; + if (CommandBuffer[1] == 0) + { + pce.CDAudio.PlayStartingAtLba(audioStartLBA); + pce.CDAudio.Pause(); + } + else + { + pce.CDAudio.PlayStartingAtLba(audioStartLBA); + } - case 0x40: // Set end offset in MSF units - byte m = CommandBuffer[2].BCDtoBin(); - byte s = CommandBuffer[3].BCDtoBin(); - byte f = CommandBuffer[4].BCDtoBin(); - audioEndLBA = Disc.ConvertMSFtoLBA(m, s, f); - break; + SetStatusMessage(STATUS_GOOD, 0); + pce.IntDataTransferComplete = true; + } - case 0x80: // Set end offset in track units - byte trackNo = CommandBuffer[2].BCDtoBin(); - if (trackNo - 1 >= disc.TOC.Sessions[0].Tracks.Count) - audioEndLBA = disc.LBACount; - else - audioEndLBA = disc.TOC.Sessions[0].Tracks[trackNo - 1].Indexes[1].aba - 150; - break; - } + void CommandAudioEndPos() + { + switch (CommandBuffer[9] & 0xC0) + { + case 0x00: // Set end offset in LBA units + audioEndLBA = (CommandBuffer[3] << 16) | (CommandBuffer[4] << 8) | CommandBuffer[5]; + break; - switch (CommandBuffer[1]) - { - case 0: // end immediately - pce.CDAudio.Stop(); - break; - case 1: // play in loop mode. I guess this constitues A-B looping - pce.CDAudio.PlayStartingAtLba(audioStartLBA); - pce.CDAudio.EndLBA = audioEndLBA; - pce.CDAudio.PlayMode = CDAudio.PlaybackMode.LoopOnCompletion; - break; - case 2: // Play audio, fire IRQ2 when end position reached, maybe - pce.CDAudio.PlayStartingAtLba(audioStartLBA); - pce.CDAudio.EndLBA = audioEndLBA; - pce.CDAudio.PlayMode = CDAudio.PlaybackMode.CallbackOnCompletion; - break; - case 3: // Play normal - pce.CDAudio.PlayStartingAtLba(audioStartLBA); - pce.CDAudio.EndLBA = audioEndLBA; - pce.CDAudio.PlayMode = CDAudio.PlaybackMode.StopOnCompletion; - break; - } - SetStatusMessage(STATUS_GOOD, 0); - } - - void CommandPause() - { - pce.CDAudio.Stop(); - SetStatusMessage(STATUS_GOOD, 0); - } + case 0x40: // Set end offset in MSF units + byte m = CommandBuffer[2].BCDtoBin(); + byte s = CommandBuffer[3].BCDtoBin(); + byte f = CommandBuffer[4].BCDtoBin(); + audioEndLBA = Disc.ConvertMSFtoLBA(m, s, f); + break; - void CommandReadSubcodeQ() - { - bool playing = pce.CDAudio.Mode != CDAudio.CDAudioMode.Stopped; - var sectorEntry = disc.ReadLBA_SectorEntry(playing ? pce.CDAudio.CurrentSector : CurrentReadingSector); + case 0x80: // Set end offset in track units + byte trackNo = CommandBuffer[2].BCDtoBin(); + if (trackNo - 1 >= disc.TOC.Sessions[0].Tracks.Count) + audioEndLBA = disc.LBACount; + else + audioEndLBA = disc.TOC.Sessions[0].Tracks[trackNo - 1].Indexes[1].aba - 150; + break; + } - DataIn.Clear(); + switch (CommandBuffer[1]) + { + case 0: // end immediately + pce.CDAudio.Stop(); + break; + case 1: // play in loop mode. I guess this constitues A-B looping + pce.CDAudio.PlayStartingAtLba(audioStartLBA); + pce.CDAudio.EndLBA = audioEndLBA; + pce.CDAudio.PlayMode = CDAudio.PlaybackMode.LoopOnCompletion; + break; + case 2: // Play audio, fire IRQ2 when end position reached, maybe + pce.CDAudio.PlayStartingAtLba(audioStartLBA); + pce.CDAudio.EndLBA = audioEndLBA; + pce.CDAudio.PlayMode = CDAudio.PlaybackMode.CallbackOnCompletion; + break; + case 3: // Play normal + pce.CDAudio.PlayStartingAtLba(audioStartLBA); + pce.CDAudio.EndLBA = audioEndLBA; + pce.CDAudio.PlayMode = CDAudio.PlaybackMode.StopOnCompletion; + break; + } + SetStatusMessage(STATUS_GOOD, 0); + } + + void CommandPause() + { + pce.CDAudio.Stop(); + SetStatusMessage(STATUS_GOOD, 0); + } + + void CommandReadSubcodeQ() + { + bool playing = pce.CDAudio.Mode != CDAudio.CDAudioMode.Stopped; + var sectorEntry = disc.ReadLBA_SectorEntry(playing ? pce.CDAudio.CurrentSector : CurrentReadingSector); + + DataIn.Clear(); + + switch (pce.CDAudio.Mode) + { + case CDAudio.CDAudioMode.Playing: DataIn.Enqueue(0); break; + case CDAudio.CDAudioMode.Paused: DataIn.Enqueue(2); break; + case CDAudio.CDAudioMode.Stopped: DataIn.Enqueue(3); break; + } - switch (pce.CDAudio.Mode) - { - case CDAudio.CDAudioMode.Playing: DataIn.Enqueue(0); break; - case CDAudio.CDAudioMode.Paused: DataIn.Enqueue(2); break; - case CDAudio.CDAudioMode.Stopped: DataIn.Enqueue(3); break; - } - DataIn.Enqueue(sectorEntry.q_status); // I do not know what status is DataIn.Enqueue(sectorEntry.q_tno.BCDValue); // track DataIn.Enqueue(sectorEntry.q_index.BCDValue); // index DataIn.Enqueue(sectorEntry.q_min.BCDValue); // M(rel) DataIn.Enqueue(sectorEntry.q_sec.BCDValue); // S(rel) DataIn.Enqueue(sectorEntry.q_frame.BCDValue); // F(rel) - DataIn.Enqueue(sectorEntry.q_amin.BCDValue); // M(abs) + DataIn.Enqueue(sectorEntry.q_amin.BCDValue); // M(abs) DataIn.Enqueue(sectorEntry.q_asec.BCDValue); // S(abs) - DataIn.Enqueue(sectorEntry.q_aframe.BCDValue); // F(abs) + DataIn.Enqueue(sectorEntry.q_aframe.BCDValue); // F(abs) - SetPhase(BusPhase.DataIn); - } + SetPhase(BusPhase.DataIn); + } - void CommandReadTOC() - { - switch (CommandBuffer[1]) - { - case 0: // return number of tracks - { - DataIn.Clear(); - DataIn.Enqueue(0x01); - DataIn.Enqueue(((byte) disc.TOC.Sessions[0].Tracks.Count).BinToBCD()); - SetPhase(BusPhase.DataIn); - break; - } - case 1: // return total disc length in minutes/seconds/frames - { - int totalLbaLength = disc.LBACount; + void CommandReadTOC() + { + switch (CommandBuffer[1]) + { + case 0: // return number of tracks + { + DataIn.Clear(); + DataIn.Enqueue(0x01); + DataIn.Enqueue(((byte)disc.TOC.Sessions[0].Tracks.Count).BinToBCD()); + SetPhase(BusPhase.DataIn); + break; + } + case 1: // return total disc length in minutes/seconds/frames + { + int totalLbaLength = disc.LBACount; - byte m, s, f; - Disc.ConvertLBAtoMSF(totalLbaLength, out m, out s, out f); + byte m, s, f; + Disc.ConvertLBAtoMSF(totalLbaLength, out m, out s, out f); - DataIn.Clear(); - DataIn.Enqueue(m.BinToBCD()); - DataIn.Enqueue(s.BinToBCD()); - DataIn.Enqueue(f.BinToBCD()); - SetPhase(BusPhase.DataIn); - break; - } - case 2: // Return starting position of specified track in MSF format - { - int track = CommandBuffer[2].BCDtoBin(); - var tracks = disc.TOC.Sessions[0].Tracks; - if (CommandBuffer[2] > 0x99) - throw new Exception("invalid track number BCD request... is something I need to handle?"); - if (track == 0) track = 1; - track--; + DataIn.Clear(); + DataIn.Enqueue(m.BinToBCD()); + DataIn.Enqueue(s.BinToBCD()); + DataIn.Enqueue(f.BinToBCD()); + SetPhase(BusPhase.DataIn); + break; + } + case 2: // Return starting position of specified track in MSF format + { + int track = CommandBuffer[2].BCDtoBin(); + var tracks = disc.TOC.Sessions[0].Tracks; + if (CommandBuffer[2] > 0x99) + throw new Exception("invalid track number BCD request... is something I need to handle?"); + if (track == 0) track = 1; + track--; - - int lbaPos; - - if (track > tracks.Count) - lbaPos = disc.TOC.Sessions[0].length_aba - 150; - else - lbaPos = tracks[track].Indexes[1].aba - 150; - - byte m, s, f; - Disc.ConvertLBAtoMSF(lbaPos, out m, out s, out f); - - DataIn.Clear(); - DataIn.Enqueue(m.BinToBCD()); - DataIn.Enqueue(s.BinToBCD()); - DataIn.Enqueue(f.BinToBCD()); - if (track > tracks.Count || disc.TOC.Sessions[0].Tracks[track].TrackType == ETrackType.Audio) - DataIn.Enqueue(0); - else - DataIn.Enqueue(4); - SetPhase(BusPhase.DataIn); - break; - } - default: - Console.WriteLine("unimplemented READ TOC command argument!"); - break; - } - } + int lbaPos; - void SetStatusMessage(byte status, byte message) - { - MessageValue = message; - StatusCompleted = false; - MessageCompleted = false; - DataBits = status == STATUS_GOOD ? (byte) 0x00 : (byte) 0x01; - SetPhase(BusPhase.Status); - } + if (track > tracks.Count) + lbaPos = disc.TOC.Sessions[0].length_aba - 150; + else + lbaPos = tracks[track].Indexes[1].aba - 150; - void SetPhase(BusPhase phase) - { - if (Phase == phase) - return; + byte m, s, f; + Disc.ConvertLBAtoMSF(lbaPos, out m, out s, out f); - Phase = phase; - busPhaseChanged = true; + DataIn.Clear(); + DataIn.Enqueue(m.BinToBCD()); + DataIn.Enqueue(s.BinToBCD()); + DataIn.Enqueue(f.BinToBCD()); - switch (phase) - { - case BusPhase.BusFree: - BSY = false; - MSG = false; - CD = false; - IO = false; - REQ = false; - pce.IntDataTransferComplete = false; - break; - case BusPhase.Command: - BSY = true; - MSG = false; - CD = true; - IO = false; - REQ = true; - break; - case BusPhase.DataIn: - BSY = true; - MSG = false; - CD = false; - IO = true; - REQ = false; - break; - case BusPhase.DataOut: - BSY = true; - MSG = false; - CD = false; - IO = false; - REQ = true; - break; - case BusPhase.MessageIn: - BSY = true; - MSG = true; - CD = true; - IO = true; - REQ = true; - break; - case BusPhase.MessageOut: - BSY = true; - MSG = true; - CD = true; - IO = false; - REQ = true; - break; - case BusPhase.Status: - BSY = true; - MSG = false; - CD = true; - IO = true; - REQ = true; - break; - } - } - - // *************************************************************************** + if (track > tracks.Count || disc.TOC.Sessions[0].Tracks[track].TrackType == ETrackType.Audio) + DataIn.Enqueue(0); + else + DataIn.Enqueue(4); + SetPhase(BusPhase.DataIn); + break; + } + default: + Console.WriteLine("unimplemented READ TOC command argument!"); + break; + } + } - public void SaveStateBinary(BinaryWriter writer) - { - writer.Write(BSY); - writer.Write(SEL); - writer.Write(CD); - writer.Write(IO); - writer.Write(MSG); - writer.Write(REQ); - writer.Write(ACK); - writer.Write(ATN); - writer.Write(RST); - writer.Write(DataBits); - writer.Write((byte)Phase); + void SetStatusMessage(byte status, byte message) + { + MessageValue = message; + StatusCompleted = false; + MessageCompleted = false; + DataBits = status == STATUS_GOOD ? (byte)0x00 : (byte)0x01; + SetPhase(BusPhase.Status); + } - writer.Write(MessageCompleted); - writer.Write(StatusCompleted); - writer.Write(MessageValue); + void SetPhase(BusPhase phase) + { + if (Phase == phase) + return; - writer.Write(DataReadWaitTimer); - writer.Write(DataReadInProgress); - writer.Write(DataTransferWasDone); - writer.Write(DataTransferInProgress); - writer.Write(CurrentReadingSector); - writer.Write((byte)SectorsLeftToRead); + Phase = phase; + busPhaseChanged = true; - writer.Write(CommandBuffer.buffer); - writer.Write((byte)CommandBuffer.Position); + switch (phase) + { + case BusPhase.BusFree: + BSY = false; + MSG = false; + CD = false; + IO = false; + REQ = false; + pce.IntDataTransferComplete = false; + break; + case BusPhase.Command: + BSY = true; + MSG = false; + CD = true; + IO = false; + REQ = true; + break; + case BusPhase.DataIn: + BSY = true; + MSG = false; + CD = false; + IO = true; + REQ = false; + break; + case BusPhase.DataOut: + BSY = true; + MSG = false; + CD = false; + IO = false; + REQ = true; + break; + case BusPhase.MessageIn: + BSY = true; + MSG = true; + CD = true; + IO = true; + REQ = true; + break; + case BusPhase.MessageOut: + BSY = true; + MSG = true; + CD = true; + IO = false; + REQ = true; + break; + case BusPhase.Status: + BSY = true; + MSG = false; + CD = true; + IO = true; + REQ = true; + break; + } + } - writer.Write(DataIn.buffer); - writer.Write((short)DataIn.head); - writer.Write((short)DataIn.tail); - writer.Write((short)DataIn.size); + // *************************************************************************** - writer.Write(audioStartLBA); - writer.Write(audioEndLBA); - } + public void SaveStateBinary(BinaryWriter writer) + { + writer.Write(BSY); + writer.Write(SEL); + writer.Write(CD); + writer.Write(IO); + writer.Write(MSG); + writer.Write(REQ); + writer.Write(ACK); + writer.Write(ATN); + writer.Write(RST); + writer.Write(DataBits); + writer.Write((byte)Phase); - public void LoadStateBinary(BinaryReader reader) - { - BSY = reader.ReadBoolean(); - SEL = reader.ReadBoolean(); - CD = reader.ReadBoolean(); - IO = reader.ReadBoolean(); - MSG = reader.ReadBoolean(); - REQ = reader.ReadBoolean(); - ACK = reader.ReadBoolean(); - ATN = reader.ReadBoolean(); - RST = reader.ReadBoolean(); - DataBits = reader.ReadByte(); - Phase = (BusPhase) Enum.ToObject(typeof(BusPhase), reader.ReadByte()); + writer.Write(MessageCompleted); + writer.Write(StatusCompleted); + writer.Write(MessageValue); - MessageCompleted = reader.ReadBoolean(); - StatusCompleted = reader.ReadBoolean(); - MessageValue = reader.ReadByte(); - - DataReadWaitTimer = reader.ReadInt64(); - DataReadInProgress = reader.ReadBoolean(); - DataTransferWasDone = reader.ReadBoolean(); - DataTransferInProgress = reader.ReadBoolean(); - CurrentReadingSector = reader.ReadInt32(); - SectorsLeftToRead = reader.ReadByte(); + writer.Write(DataReadWaitTimer); + writer.Write(DataReadInProgress); + writer.Write(DataTransferWasDone); + writer.Write(DataTransferInProgress); + writer.Write(CurrentReadingSector); + writer.Write((byte)SectorsLeftToRead); - CommandBuffer.buffer = reader.ReadBytes(10); - CommandBuffer.Position = reader.ReadByte(); + writer.Write(CommandBuffer.buffer); + writer.Write((byte)CommandBuffer.Position); - DataIn.buffer = reader.ReadBytes(2048); - DataIn.head = reader.ReadInt16(); - DataIn.tail = reader.ReadInt16(); - DataIn.size = reader.ReadInt16(); + writer.Write(DataIn.buffer); + writer.Write((short)DataIn.head); + writer.Write((short)DataIn.tail); + writer.Write((short)DataIn.size); - audioStartLBA = reader.ReadInt32(); - audioEndLBA = reader.ReadInt32(); - } + writer.Write(audioStartLBA); + writer.Write(audioEndLBA); + } - public void SaveStateText(TextWriter writer) - { - writer.WriteLine("[SCSI]"); - writer.WriteLine("BSY {0}",BSY); - writer.WriteLine("SEL {0}", SEL); - writer.WriteLine("CD {0}", CD); - writer.WriteLine("IO {0}", IO); - writer.WriteLine("MSG {0}", MSG); - writer.WriteLine("REQ {0}", REQ); - writer.WriteLine("ACK {0}", ACK); - writer.WriteLine("ATN {0}", ATN); - writer.WriteLine("RST {0}", RST); - writer.WriteLine("DataBits {0:X2}", DataBits); - writer.WriteLine("BusPhase "+Enum.GetName(typeof(BusPhase), Phase)); - writer.WriteLine(); - writer.WriteLine("MessageCompleted {0}", MessageCompleted); - writer.WriteLine("StatusCompleted {0}", StatusCompleted); - writer.WriteLine("MessageValue {0}", MessageValue); - writer.WriteLine(); - writer.WriteLine("DataReadWaitTimer {0}", DataReadWaitTimer); - writer.WriteLine("DataReadInProgress {0}", DataReadInProgress); - writer.WriteLine("DataTransferWasDone {0}", DataTransferWasDone); - writer.WriteLine("DataTransferInProgress {0}", DataTransferInProgress); - writer.WriteLine("CurrentReadingSector {0}", CurrentReadingSector); - writer.WriteLine("SectorsLeftToRead {0}", SectorsLeftToRead); - writer.WriteLine(); - writer.WriteLine("AudioStartLBA {0}", audioStartLBA); - writer.WriteLine("AudioEndLBA {0}", audioEndLBA); - writer.WriteLine(); - writer.Write("CommandBuffer "); CommandBuffer.buffer.SaveAsHex(writer); - writer.WriteLine("CommandBufferPosition {0}", CommandBuffer.Position); - writer.WriteLine(); - writer.Write("DataInBuffer "); DataIn.buffer.SaveAsHex(writer); - writer.WriteLine("DataInHead {0}", DataIn.head); - writer.WriteLine("DataInTail {0}", DataIn.tail); - writer.WriteLine("DataInSize {0}", DataIn.size); - writer.WriteLine("[/SCSI]\n"); - } + public void LoadStateBinary(BinaryReader reader) + { + BSY = reader.ReadBoolean(); + SEL = reader.ReadBoolean(); + CD = reader.ReadBoolean(); + IO = reader.ReadBoolean(); + MSG = reader.ReadBoolean(); + REQ = reader.ReadBoolean(); + ACK = reader.ReadBoolean(); + ATN = reader.ReadBoolean(); + RST = reader.ReadBoolean(); + DataBits = reader.ReadByte(); + Phase = (BusPhase)Enum.ToObject(typeof(BusPhase), reader.ReadByte()); - public void LoadStateText(TextReader reader) - { - while (true) - { - string[] args = reader.ReadLine().Split(' '); - if (args[0].Trim() == "") continue; - if (args[0] == "[/SCSI]") break; - if (args[0] == "BSY") - BSY = bool.Parse(args[1]); - else if (args[0] == "SEL") - SEL = bool.Parse(args[1]); - else if (args[0] == "CD") - CD = bool.Parse(args[1]); - else if (args[0] == "IO") - IO = bool.Parse(args[1]); - else if (args[0] == "MSG") - MSG = bool.Parse(args[1]); - else if (args[0] == "REQ") - REQ = bool.Parse(args[1]); - else if (args[0] == "ACK") - ACK = bool.Parse(args[1]); - else if (args[0] == "ATN") - ATN = bool.Parse(args[1]); - else if (args[0] == "RST") - RST = bool.Parse(args[1]); - else if (args[0] == "DataBits") - DataBits = byte.Parse(args[1], NumberStyles.HexNumber); - else if (args[0] == "BusPhase") - Phase = (BusPhase)Enum.Parse(typeof(BusPhase), args[1]); + MessageCompleted = reader.ReadBoolean(); + StatusCompleted = reader.ReadBoolean(); + MessageValue = reader.ReadByte(); - else if (args[0] == "MessageCompleted") - MessageCompleted = bool.Parse(args[1]); - else if (args[0] == "StatusCompleted") - StatusCompleted = bool.Parse(args[1]); - else if (args[0] == "MessageValue") - MessageValue = byte.Parse(args[1]); + DataReadWaitTimer = reader.ReadInt64(); + DataReadInProgress = reader.ReadBoolean(); + DataTransferWasDone = reader.ReadBoolean(); + DataTransferInProgress = reader.ReadBoolean(); + CurrentReadingSector = reader.ReadInt32(); + SectorsLeftToRead = reader.ReadByte(); - else if (args[0] == "DataReadWaitTimer") - DataReadWaitTimer = long.Parse(args[1]); - else if (args[0] == "DataReadInProgress") - DataReadInProgress = bool.Parse(args[1]); - else if (args[0] == "DataTransferWasDone") - DataTransferWasDone = bool.Parse(args[1]); - else if (args[0] == "DataTransferInProgress") - DataTransferInProgress = bool.Parse(args[1]); - else if (args[0] == "CurrentReadingSector") - CurrentReadingSector = int.Parse(args[1]); - else if (args[0] == "SectorsLeftToRead") - SectorsLeftToRead = int.Parse(args[1]); + CommandBuffer.buffer = reader.ReadBytes(10); + CommandBuffer.Position = reader.ReadByte(); - else if (args[0] == "AudioStartLBA") - audioStartLBA = int.Parse(args[1]); - else if (args[0] == "AudioEndLBA") - audioEndLBA = int.Parse(args[1]); + DataIn.buffer = reader.ReadBytes(2048); + DataIn.head = reader.ReadInt16(); + DataIn.tail = reader.ReadInt16(); + DataIn.size = reader.ReadInt16(); - else if (args[0] == "CommandBuffer") - CommandBuffer.buffer.ReadFromHex(args[1]); - else if (args[0] == "CommandBufferPosition") - CommandBuffer.Position = int.Parse(args[1]); + audioStartLBA = reader.ReadInt32(); + audioEndLBA = reader.ReadInt32(); + } - else if (args[0] == "DataInBuffer") - DataIn.buffer.ReadFromHex(args[1]); - else if (args[0] == "DataInHead") - DataIn.head = int.Parse(args[1]); - else if (args[0] == "DataInTail") - DataIn.tail = int.Parse(args[1]); - else if (args[0] == "DataInSize") - DataIn.size = int.Parse(args[1]); + public void SaveStateText(TextWriter writer) + { + writer.WriteLine("[SCSI]"); + writer.WriteLine("BSY {0}", BSY); + writer.WriteLine("SEL {0}", SEL); + writer.WriteLine("CD {0}", CD); + writer.WriteLine("IO {0}", IO); + writer.WriteLine("MSG {0}", MSG); + writer.WriteLine("REQ {0}", REQ); + writer.WriteLine("ACK {0}", ACK); + writer.WriteLine("ATN {0}", ATN); + writer.WriteLine("RST {0}", RST); + writer.WriteLine("DataBits {0:X2}", DataBits); + writer.WriteLine("BusPhase " + Enum.GetName(typeof(BusPhase), Phase)); + writer.WriteLine(); + writer.WriteLine("MessageCompleted {0}", MessageCompleted); + writer.WriteLine("StatusCompleted {0}", StatusCompleted); + writer.WriteLine("MessageValue {0}", MessageValue); + writer.WriteLine(); + writer.WriteLine("DataReadWaitTimer {0}", DataReadWaitTimer); + writer.WriteLine("DataReadInProgress {0}", DataReadInProgress); + writer.WriteLine("DataTransferWasDone {0}", DataTransferWasDone); + writer.WriteLine("DataTransferInProgress {0}", DataTransferInProgress); + writer.WriteLine("CurrentReadingSector {0}", CurrentReadingSector); + writer.WriteLine("SectorsLeftToRead {0}", SectorsLeftToRead); + writer.WriteLine(); + writer.WriteLine("AudioStartLBA {0}", audioStartLBA); + writer.WriteLine("AudioEndLBA {0}", audioEndLBA); + writer.WriteLine(); + writer.Write("CommandBuffer "); CommandBuffer.buffer.SaveAsHex(writer); + writer.WriteLine("CommandBufferPosition {0}", CommandBuffer.Position); + writer.WriteLine(); + writer.Write("DataInBuffer "); DataIn.buffer.SaveAsHex(writer); + writer.WriteLine("DataInHead {0}", DataIn.head); + writer.WriteLine("DataInTail {0}", DataIn.tail); + writer.WriteLine("DataInSize {0}", DataIn.size); + writer.WriteLine("[/SCSI]\n"); + } - else - Console.WriteLine("Skipping unrecognized identifier " + args[0]); - } - } - } + public void LoadStateText(TextReader reader) + { + while (true) + { + string[] args = reader.ReadLine().Split(' '); + if (args[0].Trim() == "") continue; + if (args[0] == "[/SCSI]") break; + if (args[0] == "BSY") + BSY = bool.Parse(args[1]); + else if (args[0] == "SEL") + SEL = bool.Parse(args[1]); + else if (args[0] == "CD") + CD = bool.Parse(args[1]); + else if (args[0] == "IO") + IO = bool.Parse(args[1]); + else if (args[0] == "MSG") + MSG = bool.Parse(args[1]); + else if (args[0] == "REQ") + REQ = bool.Parse(args[1]); + else if (args[0] == "ACK") + ACK = bool.Parse(args[1]); + else if (args[0] == "ATN") + ATN = bool.Parse(args[1]); + else if (args[0] == "RST") + RST = bool.Parse(args[1]); + else if (args[0] == "DataBits") + DataBits = byte.Parse(args[1], NumberStyles.HexNumber); + else if (args[0] == "BusPhase") + Phase = (BusPhase)Enum.Parse(typeof(BusPhase), args[1]); + + else if (args[0] == "MessageCompleted") + MessageCompleted = bool.Parse(args[1]); + else if (args[0] == "StatusCompleted") + StatusCompleted = bool.Parse(args[1]); + else if (args[0] == "MessageValue") + MessageValue = byte.Parse(args[1]); + + else if (args[0] == "DataReadWaitTimer") + DataReadWaitTimer = long.Parse(args[1]); + else if (args[0] == "DataReadInProgress") + DataReadInProgress = bool.Parse(args[1]); + else if (args[0] == "DataTransferWasDone") + DataTransferWasDone = bool.Parse(args[1]); + else if (args[0] == "DataTransferInProgress") + DataTransferInProgress = bool.Parse(args[1]); + else if (args[0] == "CurrentReadingSector") + CurrentReadingSector = int.Parse(args[1]); + else if (args[0] == "SectorsLeftToRead") + SectorsLeftToRead = int.Parse(args[1]); + + else if (args[0] == "AudioStartLBA") + audioStartLBA = int.Parse(args[1]); + else if (args[0] == "AudioEndLBA") + audioEndLBA = int.Parse(args[1]); + + else if (args[0] == "CommandBuffer") + CommandBuffer.buffer.ReadFromHex(args[1]); + else if (args[0] == "CommandBufferPosition") + CommandBuffer.Position = int.Parse(args[1]); + + else if (args[0] == "DataInBuffer") + DataIn.buffer.ReadFromHex(args[1]); + else if (args[0] == "DataInHead") + DataIn.head = int.Parse(args[1]); + else if (args[0] == "DataInTail") + DataIn.tail = int.Parse(args[1]); + else if (args[0] == "DataInSize") + DataIn.size = int.Parse(args[1]); + + else + Console.WriteLine("Skipping unrecognized identifier " + args[0]); + } + } + } } \ No newline at end of file diff --git a/BizHawk.Emulation/Consoles/Sega/Saturn/Yabause.cs b/BizHawk.Emulation/Consoles/Sega/Saturn/Yabause.cs index 924c375776..0e6d7df1e4 100644 --- a/BizHawk.Emulation/Consoles/Sega/Saturn/Yabause.cs +++ b/BizHawk.Emulation/Consoles/Sega/Saturn/Yabause.cs @@ -6,6 +6,7 @@ using System.Runtime.InteropServices; using System.IO; using BizHawk.Common; +using BizHawk.Emulation.DiscSystem; namespace BizHawk.Emulation.Consoles.Sega.Saturn { @@ -24,7 +25,7 @@ namespace BizHawk.Emulation.Consoles.Sega.Saturn static Yabause AttachedCore = null; GCHandle VideoHandle; - DiscSystem.Disc CD; + Disc CD; GCHandle SoundHandle; bool Disposed = false; diff --git a/BizHawk.Emulation/DiscSystem/Blobs/RiffMaster.cs b/BizHawk.Emulation/DiscSystem/Blobs/RiffMaster.cs deleted file mode 100644 index 758d741752..0000000000 --- a/BizHawk.Emulation/DiscSystem/Blobs/RiffMaster.cs +++ /dev/null @@ -1,346 +0,0 @@ -using System; -using System.IO; -using System.Collections.Generic; - -/// -/// Parses a RIFF file into a live data structure. -/// References to large blobs remain mostly on disk in the file which RiffMaster keeps a reference too. Dispose it to close the file. -/// You can modify blobs however you want and write the file back out to a new path, if youre careful (that was the original point of this) -/// Please be sure to test round-tripping when you make any changes. This architecture is a bit tricky to use, but it works if youre careful. -/// -class RiffMaster : IDisposable -{ - public RiffMaster() { } - - public void WriteFile(string fname) - { - using (FileStream fs = new FileStream(fname, FileMode.Create, FileAccess.Write, FileShare.Read)) - WriteStream(fs); - } - - public Stream BaseStream; - public void LoadFile(string fname) - { - var fs = new FileStream(fname, FileMode.Open, FileAccess.Read, FileShare.Read); - LoadStream(fs); - } - - public void Dispose() - { - if (BaseStream != null) BaseStream.Dispose(); - BaseStream = null; - } - - private static string ReadTag(BinaryReader br) - { - return "" + br.ReadChar() + br.ReadChar() + br.ReadChar() + br.ReadChar(); - } - - protected static void WriteTag(BinaryWriter bw, string tag) - { - for (int i = 0; i < 4; i++) - bw.Write(tag[i]); - bw.Flush(); - } - - public abstract class RiffChunk - { - public string tag; - - /// - /// writes this chunk to the stream, including padding - /// - public abstract void WriteStream(Stream s); - - /// - /// distinct from a size or a length, the `volume` is the volume of bytes occupied by the chunk on disk (accounting for padding). - /// - /// - public abstract long GetVolume(); - - /// - /// transforms into a derived class depending on tag - /// - public abstract RiffChunk Morph(); - } - - static class Util - { - public static void CopyStream(Stream src, Stream dest, long len) - { - const int size = 0x2000; - byte[] buffer = new byte[size]; - while (len > 0) - { - long todo = len; - if (len > size) todo = size; - int n = src.Read(buffer, 0, (int)todo); - dest.Write(buffer, 0, n); - len -= n; - } - } - } - - public class RiffSubchunk : RiffChunk - { - public long Position; - public uint Length; - public Stream Source; - public override void WriteStream(Stream s) - { - BinaryWriter bw = new BinaryWriter(s); - WriteTag(bw, tag); - bw.Write(Length); - bw.Flush(); - - Source.Position = Position; - Util.CopyStream(Source, s, Length); - - //all chunks are supposed to be 16bit padded - if (Length % 2 != 0) - s.WriteByte(0); - } - public override long GetVolume() - { - long ret = Length; - if (ret % 2 != 0) ret++; - return ret; - } - - public byte[] ReadAll() - { - int msSize = (int)Math.Min((long)int.MaxValue, Length); - MemoryStream ms = new MemoryStream(msSize); - Source.Position = Position; - Util.CopyStream(Source, ms, Length); - return ms.ToArray(); - } - - public override RiffChunk Morph() - { - switch (tag) - { - case "fmt ": return new RiffSubchunk_fmt(this); - } - return this; - } - } - - public class RiffSubchunk_fmt : RiffSubchunk - { - public enum FORMAT_TAG : ushort - { - WAVE_FORMAT_UNKNOWN = (0x0000), - WAVE_FORMAT_PCM = (0x0001), - WAVE_FORMAT_ADPCM = (0x0002), - WAVE_FORMAT_ALAW = (0x0006), - WAVE_FORMAT_MULAW = (0x0007), - WAVE_FORMAT_OKI_ADPCM = (0x0010), - WAVE_FORMAT_DIGISTD = (0x0015), - WAVE_FORMAT_DIGIFIX = (0x0016), - IBM_FORMAT_MULAW = (0x0101), - IBM_FORMAT_ALAW = (0x0102), - IBM_FORMAT_ADPCM = (0x0103), - } - public FORMAT_TAG format_tag; - public ushort channels; - public uint samplesPerSec; - public uint avgBytesPerSec; - public ushort blockAlign; - public ushort bitsPerSample; - public RiffSubchunk_fmt(RiffSubchunk origin) - { - tag = "fmt "; - BinaryReader br = new BinaryReader(new MemoryStream(origin.ReadAll())); - format_tag = (FORMAT_TAG)br.ReadUInt16(); - channels = br.ReadUInt16(); - samplesPerSec = br.ReadUInt32(); - avgBytesPerSec = br.ReadUInt32(); - blockAlign = br.ReadUInt16(); - bitsPerSample = br.ReadUInt16(); - } - public override void WriteStream(Stream s) - { - Flush(); - base.WriteStream(s); - } - void Flush() - { - MemoryStream ms = new MemoryStream(); - BinaryWriter bw = new BinaryWriter(ms); - bw.Write((ushort)format_tag); - bw.Write(channels); - bw.Write(samplesPerSec); - bw.Write(avgBytesPerSec); - bw.Write(blockAlign); - bw.Write(bitsPerSample); - bw.Flush(); - Source = ms; - Position = 0; - Length = (uint)ms.Length; - } - - public override long GetVolume() - { - Flush(); - return base.GetVolume(); - } - } - - public class RiffContainer : RiffChunk - { - public RiffChunk GetSubchunk(string tag, string type) - { - foreach (RiffChunk rc in subchunks) - if (rc.tag == tag) - { - if (type == null) return rc; - RiffContainer cont = rc as RiffContainer; - if (cont != null && cont.type == type) - return rc; - } - return null; - } - - public RiffContainer() - { - tag = "LIST"; - } - public string type; - public List subchunks = new List(); - public override void WriteStream(Stream s) - { - BinaryWriter bw = new BinaryWriter(s); - WriteTag(bw, tag); - long size = GetVolume(); - if (size > uint.MaxValue) throw new FormatException("File too big to write out"); - bw.Write((uint)size); - WriteTag(bw, type); - bw.Flush(); - foreach (RiffChunk rc in subchunks) - rc.WriteStream(s); - if (size % 2 != 0) - s.WriteByte(0); - } - public override long GetVolume() - { - long len = 4; - foreach (RiffChunk rc in subchunks) - len += rc.GetVolume() + 8; - return len; - } - - public override RiffChunk Morph() - { - switch (type) - { - case "INFO": return new RiffContainer_INFO(this); - } - return this; - } - } - - public class RiffContainer_INFO : RiffContainer - { - public Dictionary dictionary = new Dictionary(); - public RiffContainer_INFO() { type = "INFO"; } - public RiffContainer_INFO(RiffContainer rc) - { - subchunks = rc.subchunks; - type = "INFO"; - foreach (RiffChunk chunk in subchunks) - { - RiffSubchunk rsc = chunk as RiffSubchunk; - if (chunk == null) - throw new FormatException("Invalid subchunk of INFO list"); - dictionary[rsc.tag] = System.Text.Encoding.ASCII.GetString(rsc.ReadAll()); - } - } - - private void Flush() - { - subchunks.Clear(); - foreach (KeyValuePair kvp in dictionary) - { - RiffSubchunk rs = new RiffSubchunk(); - rs.tag = kvp.Key; - rs.Source = new MemoryStream(System.Text.Encoding.ASCII.GetBytes(kvp.Value)); - rs.Position = 0; - rs.Length = (uint)rs.Source.Length; - subchunks.Add(rs); - } - } - - public override long GetVolume() - { - Flush(); - return base.GetVolume(); - } - - public override void WriteStream(Stream s) - { - Flush(); - base.WriteStream(s); - } - } - - public RiffContainer riff; - - private long readCounter; - private RiffChunk ReadChunk(BinaryReader br) - { - RiffChunk ret; - string tag = ReadTag(br); readCounter += 4; - uint size = br.ReadUInt32(); readCounter += 4; - if (size > int.MaxValue) - throw new FormatException("chunk too big"); - if (tag == "RIFF" || tag == "LIST") - { - RiffContainer rc = new RiffContainer(); - rc.tag = tag; - rc.type = ReadTag(br); readCounter += 4; - long readEnd = readCounter - 4 + size; - while (readEnd > readCounter) - rc.subchunks.Add(ReadChunk(br)); - ret = rc.Morph(); - } - else - { - RiffSubchunk rsc = new RiffSubchunk(); - rsc.tag = tag; - rsc.Source = br.BaseStream; - rsc.Position = br.BaseStream.Position; - rsc.Length = size; - readCounter += size; - ret = rsc.Morph(); - } - if (size % 2 != 0) - { - br.ReadByte(); - readCounter += 1; - } - return ret; - - } - - public void WriteStream(Stream s) - { - 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); - if (chunk.tag != "RIFF") throw new FormatException("can't recognize riff chunk"); - riff = (RiffContainer)chunk; - } - - -} \ No newline at end of file diff --git a/BizHawk.Emulation/DiscSystem/cdfs/ISODirectoryNode.cs b/BizHawk.Emulation/DiscSystem/cdfs/ISODirectoryNode.cs deleted file mode 100644 index 6322fde3e5..0000000000 --- a/BizHawk.Emulation/DiscSystem/cdfs/ISODirectoryNode.cs +++ /dev/null @@ -1,145 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.IO; - -namespace ISOParser { - /// - /// Representation of a directory in the file system. - /// - public class ISODirectoryNode : ISONode { - #region Public Properties - - /// - /// The children in this directory. - /// - public Dictionary Children; - - #endregion - - #region Construction - - /// - /// Constructor. - /// - /// The node for this directory. - public ISODirectoryNode( ISONodeRecord record ) - : base( record ) { - this.Children = new Dictionary(); - } - - #endregion - - #region Parsing - - /// - /// Parse the children based on the data in this directory. - /// - /// The stream to parse from. - /// The set of already handled - /// files/directories. - public void Parse(Stream s, Dictionary visited) { - // Go to the beginning of the set of directories - s.Seek(this.Offset*ISOFile.SECTOR_SIZE, SeekOrigin.Begin); - - List records = new List(); - - // Read the directory entries - while(s.Position < ((this.Offset*ISOFile.SECTOR_SIZE)+this.Length)) { - ISONode node; - ISONodeRecord record; - - // Read the record - record = new ISONodeRecord(); - record.Parse(s); - - - //zero 24-jun-2013 - improved validity checks - //theres nothing here! - if (record.Length == 0) - { - break; - } - else - { - // Check if we already have this node - if (visited.ContainsKey(record.OffsetOfData)) - { - // Get the node - node = visited[record.OffsetOfData]; - } - else - { - // Create the node from the record - if (record.IsFile()) - { - node = new ISOFileNode(record); - } - else if (record.IsDirectory()) - { - node = new ISODirectoryNode(record); - } - else - { - node = new ISONode(record); - } - - // Keep track that we've now seen the node and are parsing it - visited.Add(node.Offset, node); - } - - // Add the node as a child - this.Children.Add(record.Name, node); - } - } - - long currentPosition = s.Position; - - // Iterate over directories... - foreach(KeyValuePair child in this.Children) { - // Parse this node - if( child.Key != ISONodeRecord.CURRENT_DIRECTORY && - child.Key != ISONodeRecord.PARENT_DIRECTORY && - child.Value is ISODirectoryNode ) { - ((ISODirectoryNode)child.Value).Parse(s, visited); - } - } - - s.Seek(currentPosition, SeekOrigin.Begin); - } - - #endregion - - #region Printing - - /// - /// Print out this node's children. - /// - /// The number of "tabs" to indent this directory. - public void Print(int depth) { - // Get the tabs string - string tabs = ""; - for (int i = 0; i < depth; i++) { - tabs += " "; - } - - // Get the names and sort - string[] names = this.Children.Keys.ToArray(); - Array.Sort(names); - - // Print the directory names recursively - foreach (string s in names) { - ISONode n = this.Children[s]; - Console.WriteLine(tabs + s); - if (s != ISONodeRecord.CURRENT_DIRECTORY && - s != ISONodeRecord.PARENT_DIRECTORY && - n is ISODirectoryNode) { - ((ISODirectoryNode)n).Print(depth + 1); - } - } - } - - #endregion - } -} diff --git a/BizHawk.Emulation/DiscSystem/cdfs/ISOFile.cs b/BizHawk.Emulation/DiscSystem/cdfs/ISOFile.cs deleted file mode 100644 index 3e9cd89f8e..0000000000 --- a/BizHawk.Emulation/DiscSystem/cdfs/ISOFile.cs +++ /dev/null @@ -1,135 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; - -namespace ISOParser { - /// - /// This class is meant to parse disk images as specified by ISO9660. - /// Specifically, it should work for most disk images that are created - /// by the stanard disk imaging software. This class is by no means - /// robust to all variations of ISO9660. - /// Also, this class does not currently support the UDF file system. - /// - /// TODO: Add functions to enumerate a directory or visit a file... - /// - /// The information for building class came from three primary sources: - /// 1. The ISO9660 wikipedia article: - /// http://en.wikipedia.org/wiki/ISO_9660 - /// 2. ISO9660 Simplified for DOS/Windows - /// http://alumnus.caltech.edu/~pje/iso9660.html - /// 3. The ISO 9660 File System - /// http://users.telenet.be/it3.consultants.bvba/handouts/ISO9960.html - /// - public class ISOFile { - #region Constants - - /// - /// We are hard coding the SECTOR_SIZE - /// - public const int SECTOR_SIZE = 2048; - - #endregion - - #region Public Members - - /// - /// This is a list of all the volume descriptors in the disk image. - /// NOTE: The first entry should be the primary volume. - /// - public List VolumeDescriptors; - - /// - /// The Directory that is the root of this file system - /// - public ISODirectoryNode Root; - - #endregion - - #region Construction - - /// - /// Construct the ISO file data structures, but leave everything - /// blank. - /// - public ISOFile() { - } - - #endregion - - #region Parsing - - /// - /// Parse the given stream to populate the iso information - /// - /// The stream which we are using to parse the image. - /// Should already be located at the start of the image. - public bool Parse(Stream s, int startSector=16) - { - this.VolumeDescriptors = new List(); - Root = null; - - long startPosition = s.Position; - byte[] buffer = new byte[ISOFile.SECTOR_SIZE]; - - // Seek through the first volume descriptor - s.Seek(startPosition + (SECTOR_SIZE * startSector), SeekOrigin.Begin); - - // Read one of more volume descriptors - do { - //zero 24-jun-2013 - improved validity checks - - ISOVolumeDescriptor desc = new ISOVolumeDescriptor(); - bool isValid = desc.Parse(s); - if (!isValid) return false; - - if (desc.IsTerminator()) - break; - else if (desc.Type < 4) - this.VolumeDescriptors.Add(desc); - else - //found a volume descriptor of incorrect type.. maybe this isnt a cdfs - //supposedly these exist.. wait for one to show up - return false; - - } while(true); - - //zero 24-jun-2013 - well, my very first test iso had 2 volume descriptors. - // Check to make sure we only read one volume descriptor - // Finding more could be an error with the disk. - //if (this.VolumeDescriptors.Count != 1) { - // Console.WriteLine("Strange ISO format..."); - // return; - //} - - //zero 24-jun-2013 - if theres no volume descriptors, we're gonna call this not a cdfs - if (VolumeDescriptors.Count == 0) return false; - - // Visit all the directories and get the offset of each directory/file - - // We need to keep track of the directories and files we have visited in case there are loops. - Dictionary visitedNodes = new Dictionary(); - - // Create (and visit) the root node - this.Root = new ISODirectoryNode(this.VolumeDescriptors[0].RootDirectoryRecord); - visitedNodes.Add(this.Root.Offset, this.Root); - this.Root.Parse(s, visitedNodes); - - return true; - } - - #endregion - - #region Printing - - /// - /// Print the directory tree for the image. - /// - public void Print() { - // DEBUGGING: Now print out the directory structure - this.Root.Print(0); - } - - #endregion - } -} diff --git a/BizHawk.Emulation/DiscSystem/cdfs/ISOFileNode.cs b/BizHawk.Emulation/DiscSystem/cdfs/ISOFileNode.cs deleted file mode 100644 index a00c04d48f..0000000000 --- a/BizHawk.Emulation/DiscSystem/cdfs/ISOFileNode.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace ISOParser { - /// - /// Representation of a file in the file system. - /// - public class ISOFileNode : ISONode { - #region Construction - - /// - /// Constructor. - /// - /// The record to construct from. - public ISOFileNode( ISONodeRecord record ) : base( record ) { - // Do Nothing - } - - #endregion - } -} diff --git a/BizHawk.Emulation/DiscSystem/cdfs/ISONode.cs b/BizHawk.Emulation/DiscSystem/cdfs/ISONode.cs deleted file mode 100644 index 54e8b13c07..0000000000 --- a/BizHawk.Emulation/DiscSystem/cdfs/ISONode.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace ISOParser { - /// - /// Abstract class to represent a file/directory node - /// - public class ISONode { - #region Public Properties - - /// - /// The record this node was created from. - /// - public ISONodeRecord FirstRecord; - - /// - /// The sector offset of the file/directory data - /// - public long Offset; - /// - /// The byte length of the file/directory data. - /// - public long Length; - - #endregion - - #region Construction - - /// - /// Constructor. - /// TODO: Make this constructor protected??? - /// - /// The ISONodeRecord to construct from. - public ISONode( ISONodeRecord record ) { - this.FirstRecord = record; - this.Offset = record.OffsetOfData; - this.Length = record.LengthOfData; - } - - #endregion - } -} diff --git a/BizHawk.Emulation/DiscSystem/cdfs/ISONodeRecord.cs b/BizHawk.Emulation/DiscSystem/cdfs/ISONodeRecord.cs deleted file mode 100644 index 86c711fa4f..0000000000 --- a/BizHawk.Emulation/DiscSystem/cdfs/ISONodeRecord.cs +++ /dev/null @@ -1,208 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; - -namespace ISOParser { - /// - /// Class to represent the file/directory information read from the disk. - /// - public class ISONodeRecord { - #region Constants - - /// - /// String representing the current directory entry - /// - public const string CURRENT_DIRECTORY = "."; - - /// - /// String representing the parent directory entry - /// - public const string PARENT_DIRECTORY = ".."; - - #endregion - - #region Public Properties - - /// - /// The length of the record in bytes. - /// - public byte Length; - - /// - /// The file offset of the data for this file/directory (in sectors). - /// - public long OffsetOfData; - /// - /// The length of the data for this file/directory (in bytes). - /// - public long LengthOfData; - - /// - /// The file/directory creation year since 1900. - /// - public byte Year; - /// - /// The file/directory creation month. - /// - public byte Month; - /// - /// The file/directory creation day. - /// - public byte Day; - /// - /// The file/directory creation hour. - /// - public byte Hour; - /// - /// The file/directory creation minute. - /// - public byte Minute; - /// - /// The file/directory creation second. - /// - public byte Second; - /// - /// The file time offset from GMT. - /// - public byte TimeZoneOffset; - - /// - /// Flags representing the attributes of this file/directory. - /// - public byte Flags; - - /// - /// The length of the file/directory name. - /// - public byte NameLength; - /// - /// The file/directory name. - /// - public string Name; - - #endregion - - #region Construction - - /// - /// Constructor - /// - public ISONodeRecord() { - // Set initial values - this.Length = 0; - - this.OffsetOfData = 0; - this.LengthOfData = 0; - - this.Year = 0; - this.Month = 0; - this.Day = 0; - this.Hour = 0; - this.Minute = 0; - this.Second = 0; - this.TimeZoneOffset = 0; - - this.Flags = 0; - - this.NameLength = 0; - this.Name = null; - } - - #endregion - - #region File/Directory Methods - - /// - /// Return true if the record represents a file. - /// - /// True if a file. - public bool IsFile() { - return ((this.Flags >> 1) & 0x01) == 0; - } - - /// - /// Return true if the record represents a directory. - /// - /// True if a directory. - public bool IsDirectory() { - return ((this.Flags >> 1) & 0x01) == 1; - } - - #endregion - - #region Parsing - - /// - /// Parse the record from an array and offset. - /// - /// The array to parse from. - /// The offset to start parsing at. - public void Parse(byte[] data, int cursor) { - // Put the array into a memory stream and pass to the main parsing function - MemoryStream s = new MemoryStream(data); - s.Seek(cursor, SeekOrigin.Begin); - this.Parse(s); - } - - /// - /// Parse the node record from the given stream. - /// - /// The stream to parse from. - public void Parse(Stream s) { - EndianBitConverter bc = EndianBitConverter.CreateForLittleEndian(); - long startPosition = s.Position; - byte[] buffer = new byte[ISOFile.SECTOR_SIZE]; - - // Get the length - s.Read(buffer, 0, 1); - this.Length = buffer[0]; - - //the number of sectors in the attribute record - s.Read(buffer, 0, 1); - - // Read Data Offset - s.Read(buffer, 0, 8); - this.OffsetOfData = (long)bc.ToInt32(buffer); - - // Read Data Length - s.Read(buffer, 0, 8); - this.LengthOfData = (long)bc.ToInt32(buffer); - - // Read the time and flags - s.Read(buffer, 0, 8); - this.Year = buffer[0]; - this.Month = buffer[1]; - this.Day = buffer[2]; - this.Hour = buffer[3]; - this.Minute = buffer[4]; - this.Second = buffer[5]; - this.TimeZoneOffset = buffer[6]; - - this.Flags = buffer[7]; - - s.Read(buffer, 0, 6); - - // Read the name length - s.Read(buffer, 0, 1); - this.NameLength = buffer[0]; - - // Read the directory name - s.Read(buffer, 0, this.NameLength); - if (this.NameLength == 1 && (buffer[0] == 0 || buffer[0] == 1)) { - if (buffer[0] == 0) - this.Name = ISONodeRecord.CURRENT_DIRECTORY; - else - this.Name = ISONodeRecord.PARENT_DIRECTORY; - } - else { - this.Name = ASCIIEncoding.ASCII.GetString(buffer, 0, this.NameLength); - } - - // Seek to end - s.Seek(startPosition + this.Length, SeekOrigin.Begin); - } - - #endregion - } -} diff --git a/BizHawk.Emulation/DiscSystem/cdfs/ISOVolumeDescriptor.cs b/BizHawk.Emulation/DiscSystem/cdfs/ISOVolumeDescriptor.cs deleted file mode 100644 index b9e8945cf4..0000000000 --- a/BizHawk.Emulation/DiscSystem/cdfs/ISOVolumeDescriptor.cs +++ /dev/null @@ -1,320 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; - -namespace ISOParser { - /// - /// Represents a volume descriptor for a disk image. - /// - public class ISOVolumeDescriptor { - #region Constants - - /// - /// We are handling the parsing by reading the entire header and - /// extracting the appropriate bytes. - /// - /// This is done for performance reasons. - /// - private const int LENGTH_SHORT_IDENTIFIER = 32; - private const int LENGTH_IDENTIFIER = 37; - private const int LENGTH_LONG_IDENTIFIER = 128; - private const int LENGTH_ROOT_DIRECTORY_RECORD = 34; - private const int LENGTH_TIME = 17; - private const int LENGTH_RESERVED = 512; - - #endregion - - #region Public Properties - - /// - /// The type of this volume description, only 1 and 255 are supported - /// - public byte Type; - - /// - /// The system identifier - /// - public byte[] SystemIdentifier; - /// - /// The volume identifier - /// - public byte[] VolumeIdentifier; - - /// - /// The number of sectors on the disk - /// - public int NumberOfSectors; - - /// - /// Volume Set Size (should be 1) - /// - public int VolumeSetSize; - /// - /// Volume Sequence Number (should be 1) - /// - public int VolumeSequenceNumber; - /// - /// Sector Size (should be 2048) - /// - public int SectorSize; - - /// - /// Size of the path table - /// - public int PathTableSize; - /// - /// Sector offset of the first path table - /// - public int OffsetOfFirstLittleEndianPathTable; - /// - /// Sector offset of the second path table - /// - public int OffsetOfSecondLittleEndianPathTable; - /// - /// Sector offset of the first path table - /// - public int OffsetOfFirstBigEndianPathTable; - /// - /// Sector offset of the second path table - /// - public int OffsetOfSecondBigEndianPathTable; - - /// - /// The root directory record - /// - public ISONodeRecord RootDirectoryRecord; - - /// - /// The volumen set identifier - /// - public byte[] VolumeSetIdentifier; - /// - /// The publisher identifier - /// - public byte[] PublisherIdentifier; - /// - /// The data preparer identifier - /// - public byte[] DataPreparerIdentifier; - /// - /// The application identifier - /// - public byte[] ApplicationIdentifier; - - /// - /// The copyright identifier - /// - public byte[] CopyrightFileIdentifier; - /// - /// The abstract file identifier - /// - public byte[] AbstractFileIdentifier; - /// - /// The bibliographical file identifier - /// - public byte[] BibliographicalFileIdentifier; - - /// - /// The time and date the volume was created - /// - public byte[] VolumeCreationDateTime; - /// - /// The time and date the volume was last modified - /// - public byte[] LastModifiedDateTime; - /// - /// The time and date the volume expires - /// - public byte[] ExpirationDateTime; - /// - /// The time and data when the volume is effective - /// - public byte[] EffectiveDateTime; - - /// - /// Extra reserved data - /// - public byte[] Reserved; - - #endregion - - #region Construction - - /// - /// Constructor. - /// - public ISOVolumeDescriptor() { - // Set everything to the default value - this.Type = 0; - - this.SystemIdentifier = new byte[LENGTH_SHORT_IDENTIFIER]; - this.VolumeIdentifier = new byte[LENGTH_SHORT_IDENTIFIER]; - - this.NumberOfSectors = 0; - - this.VolumeSetSize = 1; - this.VolumeSequenceNumber = 1; - this.SectorSize = ISOFile.SECTOR_SIZE; - - this.PathTableSize = 0; - this.OffsetOfFirstLittleEndianPathTable = 0; - this.OffsetOfSecondLittleEndianPathTable = 0; - this.OffsetOfFirstBigEndianPathTable = 0; - this.OffsetOfSecondBigEndianPathTable = 0; - - this.RootDirectoryRecord = new ISONodeRecord(); - - this.VolumeSetIdentifier = new byte[LENGTH_LONG_IDENTIFIER]; - this.PublisherIdentifier = new byte[LENGTH_LONG_IDENTIFIER]; - this.DataPreparerIdentifier = new byte[LENGTH_LONG_IDENTIFIER]; - this.ApplicationIdentifier = new byte[LENGTH_LONG_IDENTIFIER]; - - this.CopyrightFileIdentifier = new byte[LENGTH_IDENTIFIER]; - this.AbstractFileIdentifier = new byte[LENGTH_IDENTIFIER]; - this.BibliographicalFileIdentifier = new byte[LENGTH_IDENTIFIER]; - - this.VolumeCreationDateTime = new byte[LENGTH_TIME]; - this.LastModifiedDateTime = new byte[LENGTH_TIME]; - this.ExpirationDateTime = new byte[LENGTH_TIME]; - this.EffectiveDateTime = new byte[LENGTH_TIME]; - - this.Reserved = new byte[LENGTH_RESERVED]; - } - - #endregion - - #region Parsing - - /// - /// Parse the volume descriptor header. - /// - /// The stream to parse from. - public bool Parse(Stream s) { - EndianBitConverter bc = EndianBitConverter.CreateForLittleEndian(); - EndianBitConverter bcBig = EndianBitConverter.CreateForBigEndian(); - long startPosition = s.Position; - byte[] buffer = new byte[ISOFile.SECTOR_SIZE]; - - // Read the entire structure - s.Read(buffer, 0, ISOFile.SECTOR_SIZE); - - // Get the type - this.Type = buffer[0]; - - //zero 24-jun-2013 - validate - // "CD001" + 0x01 - if (buffer[1] == 'C' && buffer[2] == 'D' && buffer[3] == '0' && buffer[4] == '0' && buffer[5] == '1' && buffer[6] == 0x01) - { - //it seems to be a valid volume descriptor - } - else - { - return false; - } - - // Handle the primary volume information - if (this.Type == 1) { - int cursor = 8; - // Get the system identifier - Array.Copy(buffer, cursor, - this.SystemIdentifier, 0, LENGTH_SHORT_IDENTIFIER); - cursor += LENGTH_SHORT_IDENTIFIER; - - // Get the volume identifier - Array.Copy(buffer, cursor, - this.VolumeIdentifier, 0, LENGTH_SHORT_IDENTIFIER); - cursor += LENGTH_SHORT_IDENTIFIER; - - cursor += 8; - - // Get the total number of sectors - this.NumberOfSectors = bc.ToInt32(buffer, cursor); - cursor += 8; - - cursor += 32; - - this.VolumeSetSize = bc.ToInt16(buffer, cursor); - cursor += 4; - this.VolumeSequenceNumber = bc.ToInt16(buffer, cursor); - cursor += 4; - this.SectorSize = bc.ToInt16(buffer, cursor); - cursor += 4; - - this.PathTableSize = bc.ToInt32(buffer, cursor); - cursor += 8; - this.OffsetOfFirstLittleEndianPathTable = bc.ToInt32(buffer, cursor); - cursor += 4; - this.OffsetOfSecondLittleEndianPathTable = bc.ToInt32(buffer, cursor); - cursor += 4; - this.OffsetOfFirstLittleEndianPathTable = bcBig.ToInt32(buffer, cursor); - cursor += 4; - this.OffsetOfSecondLittleEndianPathTable = bcBig.ToInt32(buffer, cursor); - cursor += 4; - - this.RootDirectoryRecord.Parse(buffer, cursor); - cursor += LENGTH_ROOT_DIRECTORY_RECORD; - - Array.Copy(buffer, cursor, - this.VolumeSetIdentifier, 0, LENGTH_LONG_IDENTIFIER); - cursor += LENGTH_LONG_IDENTIFIER; - Array.Copy(buffer, cursor, - this.PublisherIdentifier, 0, LENGTH_LONG_IDENTIFIER); - cursor += LENGTH_LONG_IDENTIFIER; - Array.Copy(buffer, cursor, - this.DataPreparerIdentifier, 0, LENGTH_LONG_IDENTIFIER); - cursor += LENGTH_LONG_IDENTIFIER; - Array.Copy(buffer, cursor, - this.ApplicationIdentifier, 0, LENGTH_LONG_IDENTIFIER); - cursor += LENGTH_LONG_IDENTIFIER; - - Array.Copy(buffer, cursor, - this.CopyrightFileIdentifier, 0, LENGTH_IDENTIFIER); - cursor += LENGTH_IDENTIFIER; - Array.Copy(buffer, cursor, - this.AbstractFileIdentifier, 0, LENGTH_IDENTIFIER); - cursor += LENGTH_IDENTIFIER; - Array.Copy(buffer, cursor, - this.BibliographicalFileIdentifier, 0, LENGTH_IDENTIFIER); - cursor += LENGTH_IDENTIFIER; - - Array.Copy(buffer, cursor, - this.VolumeCreationDateTime, 0, LENGTH_TIME); - cursor += LENGTH_TIME; - Array.Copy(buffer, cursor, - this.LastModifiedDateTime, 0, LENGTH_TIME); - cursor += LENGTH_TIME; - Array.Copy(buffer, cursor, - this.ExpirationDateTime, 0, LENGTH_TIME); - cursor += LENGTH_TIME; - Array.Copy(buffer, cursor, - this.EffectiveDateTime, 0, LENGTH_TIME); - cursor += LENGTH_TIME; - - cursor += 1; - - cursor += 1; - - Array.Copy(buffer, cursor, - this.Reserved, 0, LENGTH_RESERVED); - cursor += LENGTH_RESERVED; - } - - return true; - } - - #endregion - - #region Type Information - - /// - /// Returns true if this is the terminator volume descriptor. - /// - /// True if the terminator. - public bool IsTerminator() { - return (this.Type == 255); - } - - #endregion - } -} diff --git a/BizHawk.Emulation/Sound/CDAudio.cs b/BizHawk.Emulation/Sound/CDAudio.cs index 6d440ec039..8371a4f338 100644 --- a/BizHawk.Emulation/Sound/CDAudio.cs +++ b/BizHawk.Emulation/Sound/CDAudio.cs @@ -1,6 +1,7 @@ using System; using System.IO; -using BizHawk.DiscSystem; + +using BizHawk.Emulation.DiscSystem; // The state of the cd player is quantized to the frame level. // This isn't ideal. But life's too short. @@ -9,295 +10,295 @@ using BizHawk.DiscSystem; namespace BizHawk.Emulation.Sound { - public sealed class CDAudio : ISoundProvider - { - public enum CDAudioMode - { - Stopped, - Playing, - Paused - } + public sealed class CDAudio : ISoundProvider + { + public enum CDAudioMode + { + Stopped, + Playing, + Paused + } - public enum PlaybackMode - { - StopOnCompletion, - NextTrackOnCompletion, - LoopOnCompletion, - CallbackOnCompletion - } + public enum PlaybackMode + { + StopOnCompletion, + NextTrackOnCompletion, + LoopOnCompletion, + CallbackOnCompletion + } - public Action CallbackAction = delegate { }; + public Action CallbackAction = delegate { }; - public Disc Disc; - public CDAudioMode Mode = CDAudioMode.Stopped; - public PlaybackMode PlayMode = PlaybackMode.LoopOnCompletion; + public Disc Disc; + public CDAudioMode Mode = CDAudioMode.Stopped; + public PlaybackMode PlayMode = PlaybackMode.LoopOnCompletion; - public int MaxVolume { get; set; } - public int LogicalVolume = 100; + public int MaxVolume { get; set; } + public int LogicalVolume = 100; - public int StartLBA, EndLBA; - public int PlayingTrack; + public int StartLBA, EndLBA; + public int PlayingTrack; - public int CurrentSector, SectorOffset; // Offset is in SAMPLES, not bytes. Sector is 588 samples long. - int CachedSector; - byte[] SectorCache = new byte[2352]; + public int CurrentSector, SectorOffset; // Offset is in SAMPLES, not bytes. Sector is 588 samples long. + int CachedSector; + byte[] SectorCache = new byte[2352]; - public int FadeOutOverFrames = 0; - public int FadeOutFramesRemaining = 0; + public int FadeOutOverFrames = 0; + public int FadeOutFramesRemaining = 0; - public CDAudio(Disc disc, int maxVolume = short.MaxValue) - { - Disc = disc; - MaxVolume = maxVolume; - } + public CDAudio(Disc disc, int maxVolume = short.MaxValue) + { + Disc = disc; + MaxVolume = maxVolume; + } - public void PlayTrack(int track) - { - if (track < 1 || track > Disc.TOC.Sessions[0].Tracks.Count) - return; + public void PlayTrack(int track) + { + if (track < 1 || track > Disc.TOC.Sessions[0].Tracks.Count) + return; - StartLBA = Disc.TOC.Sessions[0].Tracks[track - 1].Indexes[1].aba - 150; - EndLBA = StartLBA + Disc.TOC.Sessions[0].Tracks[track - 1].length_aba; - PlayingTrack = track; - CurrentSector = StartLBA; - SectorOffset = 0; - Mode = CDAudioMode.Playing; - FadeOutOverFrames = 0; - FadeOutFramesRemaining = 0; - LogicalVolume = 100; - } + StartLBA = Disc.TOC.Sessions[0].Tracks[track - 1].Indexes[1].aba - 150; + EndLBA = StartLBA + Disc.TOC.Sessions[0].Tracks[track - 1].length_aba; + PlayingTrack = track; + CurrentSector = StartLBA; + SectorOffset = 0; + Mode = CDAudioMode.Playing; + FadeOutOverFrames = 0; + FadeOutFramesRemaining = 0; + LogicalVolume = 100; + } - public void PlayStartingAtLba(int lba) - { - var point = Disc.TOC.SeekPoint(lba); - if (point == null || point.Track == null) return; + public void PlayStartingAtLba(int lba) + { + var point = Disc.TOC.SeekPoint(lba); + if (point == null || point.Track == null) return; - PlayingTrack = point.TrackNum; - StartLBA = lba; - EndLBA = point.Track.Indexes[1].aba + point.Track.length_aba - 150; + PlayingTrack = point.TrackNum; + StartLBA = lba; + EndLBA = point.Track.Indexes[1].aba + point.Track.length_aba - 150; - CurrentSector = StartLBA; - SectorOffset = 0; - Mode = CDAudioMode.Playing; - FadeOutOverFrames = 0; - FadeOutFramesRemaining = 0; - LogicalVolume = 100; - } + CurrentSector = StartLBA; + SectorOffset = 0; + Mode = CDAudioMode.Playing; + FadeOutOverFrames = 0; + FadeOutFramesRemaining = 0; + LogicalVolume = 100; + } - public void Stop() - { - Mode = CDAudioMode.Stopped; - FadeOutOverFrames = 0; - FadeOutFramesRemaining = 0; - LogicalVolume = 100; - } + public void Stop() + { + Mode = CDAudioMode.Stopped; + FadeOutOverFrames = 0; + FadeOutFramesRemaining = 0; + LogicalVolume = 100; + } - public void Pause() - { - if (Mode != CDAudioMode.Playing) - return; - Mode = CDAudioMode.Paused; - FadeOutOverFrames = 0; - FadeOutFramesRemaining = 0; - LogicalVolume = 100; - } + public void Pause() + { + if (Mode != CDAudioMode.Playing) + return; + Mode = CDAudioMode.Paused; + FadeOutOverFrames = 0; + FadeOutFramesRemaining = 0; + LogicalVolume = 100; + } - public void Resume() - { - if (Mode != CDAudioMode.Paused) - return; - Mode = CDAudioMode.Playing; - } + public void Resume() + { + if (Mode != CDAudioMode.Paused) + return; + Mode = CDAudioMode.Playing; + } - public void PauseResume() - { - if (Mode == CDAudioMode.Playing) Mode = CDAudioMode.Paused; - else if (Mode == CDAudioMode.Paused) Mode = CDAudioMode.Playing; - else if (Mode == CDAudioMode.Stopped) return; - } + public void PauseResume() + { + if (Mode == CDAudioMode.Playing) Mode = CDAudioMode.Paused; + else if (Mode == CDAudioMode.Paused) Mode = CDAudioMode.Playing; + else if (Mode == CDAudioMode.Stopped) return; + } - public void FadeOut(int frames) - { - FadeOutOverFrames = frames; - FadeOutFramesRemaining = frames; - } + public void FadeOut(int frames) + { + FadeOutOverFrames = frames; + FadeOutFramesRemaining = frames; + } - void EnsureSector() - { - if (CachedSector != CurrentSector) - { - if (CurrentSector >= Disc.LBACount) - Array.Clear(SectorCache, 0, 2352); // request reading past end of available disc - else - Disc.ReadLBA_2352(CurrentSector, SectorCache, 0); - CachedSector = CurrentSector; - } - } + void EnsureSector() + { + if (CachedSector != CurrentSector) + { + if (CurrentSector >= Disc.LBACount) + Array.Clear(SectorCache, 0, 2352); // request reading past end of available disc + else + Disc.ReadLBA_2352(CurrentSector, SectorCache, 0); + CachedSector = CurrentSector; + } + } - public void GetSamples(short[] samples) - { - if (Mode != CDAudioMode.Playing) - return; + public void GetSamples(short[] samples) + { + if (Mode != CDAudioMode.Playing) + return; - if (FadeOutFramesRemaining > 0) - { - FadeOutFramesRemaining--; - LogicalVolume = FadeOutFramesRemaining * 100 / FadeOutOverFrames; - } + if (FadeOutFramesRemaining > 0) + { + FadeOutFramesRemaining--; + LogicalVolume = FadeOutFramesRemaining * 100 / FadeOutOverFrames; + } - EnsureSector(); + EnsureSector(); - int sampleLen = samples.Length / 2; - int offset = 0; - for (int s = 0; s < sampleLen; s++) - { - int sectorOffset = SectorOffset * 4; - short left = (short)((SectorCache[sectorOffset + 1] << 8) | (SectorCache[sectorOffset + 0])); - short right = (short)((SectorCache[sectorOffset + 3] << 8) | (SectorCache[sectorOffset + 2])); + int sampleLen = samples.Length / 2; + int offset = 0; + for (int s = 0; s < sampleLen; s++) + { + int sectorOffset = SectorOffset * 4; + short left = (short)((SectorCache[sectorOffset + 1] << 8) | (SectorCache[sectorOffset + 0])); + short right = (short)((SectorCache[sectorOffset + 3] << 8) | (SectorCache[sectorOffset + 2])); - samples[offset++] += (short) (left * LogicalVolume / 100 * MaxVolume / short.MaxValue); - samples[offset++] += (short) (right * LogicalVolume / 100 * MaxVolume / short.MaxValue); - SectorOffset++; + samples[offset++] += (short)(left * LogicalVolume / 100 * MaxVolume / short.MaxValue); + samples[offset++] += (short)(right * LogicalVolume / 100 * MaxVolume / short.MaxValue); + SectorOffset++; - if (SectorOffset == 588) - { - CurrentSector++; - SectorOffset = 0; + if (SectorOffset == 588) + { + CurrentSector++; + SectorOffset = 0; - if (CurrentSector == EndLBA) - { - switch (PlayMode) - { - case PlaybackMode.NextTrackOnCompletion: - PlayTrack(PlayingTrack + 1); - break; + if (CurrentSector == EndLBA) + { + switch (PlayMode) + { + case PlaybackMode.NextTrackOnCompletion: + PlayTrack(PlayingTrack + 1); + break; - case PlaybackMode.StopOnCompletion: - Stop(); - return; + case PlaybackMode.StopOnCompletion: + Stop(); + return; - case PlaybackMode.LoopOnCompletion: - CurrentSector = StartLBA; - break; + case PlaybackMode.LoopOnCompletion: + CurrentSector = StartLBA; + break; - case PlaybackMode.CallbackOnCompletion: - CallbackAction(); - if (Mode != CDAudioMode.Playing) - return; - break; - } - } + case PlaybackMode.CallbackOnCompletion: + CallbackAction(); + if (Mode != CDAudioMode.Playing) + return; + break; + } + } - EnsureSector(); - } - } - } + EnsureSector(); + } + } + } - public short VolumeLeft - { - get - { - if (Mode != CDAudioMode.Playing) - return 0; + public short VolumeLeft + { + get + { + if (Mode != CDAudioMode.Playing) + return 0; - int offset = SectorOffset * 4; - short sample = (short)((SectorCache[offset + 1] << 8) | (SectorCache[offset + 0])); - return (short) (sample * LogicalVolume / 100); - } - } + int offset = SectorOffset * 4; + short sample = (short)((SectorCache[offset + 1] << 8) | (SectorCache[offset + 0])); + return (short)(sample * LogicalVolume / 100); + } + } - public short VolumeRight - { - get - { - if (Mode != CDAudioMode.Playing) - return 0; + public short VolumeRight + { + get + { + if (Mode != CDAudioMode.Playing) + return 0; - int offset = SectorOffset * 4; - short sample = (short) ((SectorCache[offset + 3] << 8) | (SectorCache[offset + 2])); - return (short)(sample * LogicalVolume / 100); - } - } + int offset = SectorOffset * 4; + short sample = (short)((SectorCache[offset + 3] << 8) | (SectorCache[offset + 2])); + return (short)(sample * LogicalVolume / 100); + } + } - public void DiscardSamples() { } + public void DiscardSamples() { } - public void SaveStateText(TextWriter writer) - { - writer.WriteLine("[CDAudio]"); - writer.WriteLine("Mode "+ Enum.GetName(typeof(CDAudioMode), Mode)); - writer.WriteLine("PlayMode "+ Enum.GetName(typeof(PlaybackMode), PlayMode)); - writer.WriteLine("LogicalVolume {0}", LogicalVolume); - writer.WriteLine("StartLBA {0}", StartLBA); - writer.WriteLine("EndLBA {0}", EndLBA); - writer.WriteLine("PlayingTrack {0}", PlayingTrack); - writer.WriteLine("CurrentSector {0}", CurrentSector); - writer.WriteLine("SectorOffset {0}", SectorOffset); - writer.WriteLine("FadeOutOverFrames {0}", FadeOutOverFrames); - writer.WriteLine("FadeOutFramesRemaining {0}", FadeOutFramesRemaining); - writer.WriteLine("[/CDAudio]"); - writer.WriteLine(); - } + public void SaveStateText(TextWriter writer) + { + writer.WriteLine("[CDAudio]"); + writer.WriteLine("Mode " + Enum.GetName(typeof(CDAudioMode), Mode)); + writer.WriteLine("PlayMode " + Enum.GetName(typeof(PlaybackMode), PlayMode)); + writer.WriteLine("LogicalVolume {0}", LogicalVolume); + writer.WriteLine("StartLBA {0}", StartLBA); + writer.WriteLine("EndLBA {0}", EndLBA); + writer.WriteLine("PlayingTrack {0}", PlayingTrack); + writer.WriteLine("CurrentSector {0}", CurrentSector); + writer.WriteLine("SectorOffset {0}", SectorOffset); + writer.WriteLine("FadeOutOverFrames {0}", FadeOutOverFrames); + writer.WriteLine("FadeOutFramesRemaining {0}", FadeOutFramesRemaining); + writer.WriteLine("[/CDAudio]"); + writer.WriteLine(); + } - public void LoadStateText(TextReader reader) - { - while (true) - { - string[] args = reader.ReadLine().Split(' '); - if (args[0].Trim() == "") continue; - if (args[0] == "[/CDAudio]") break; - if (args[0] == "Mode") - Mode = (CDAudioMode) Enum.Parse(typeof (CDAudioMode), args[1]); - else if (args[0] == "PlayMode") - PlayMode = (PlaybackMode) Enum.Parse(typeof (PlaybackMode), args[1]); - else if (args[0] == "LogicalVolume") - LogicalVolume = int.Parse(args[1]); - else if (args[0] == "StartLBA") - StartLBA = int.Parse(args[1]); - else if (args[0] == "EndLBA") - EndLBA = int.Parse(args[1]); - else if (args[0] == "PlayingTrack") - PlayingTrack = int.Parse(args[1]); - else if (args[0] == "CurrentSector") - CurrentSector = int.Parse(args[1]); - else if (args[0] == "SectorOffset") - SectorOffset = int.Parse(args[1]); - else if (args[0] == "FadeOutOverFrames") - FadeOutOverFrames = int.Parse(args[1]); - else if (args[0] == "FadeOutFramesRemaining") - FadeOutFramesRemaining = int.Parse(args[1]); + public void LoadStateText(TextReader reader) + { + while (true) + { + string[] args = reader.ReadLine().Split(' '); + if (args[0].Trim() == "") continue; + if (args[0] == "[/CDAudio]") break; + if (args[0] == "Mode") + Mode = (CDAudioMode)Enum.Parse(typeof(CDAudioMode), args[1]); + else if (args[0] == "PlayMode") + PlayMode = (PlaybackMode)Enum.Parse(typeof(PlaybackMode), args[1]); + else if (args[0] == "LogicalVolume") + LogicalVolume = int.Parse(args[1]); + else if (args[0] == "StartLBA") + StartLBA = int.Parse(args[1]); + else if (args[0] == "EndLBA") + EndLBA = int.Parse(args[1]); + else if (args[0] == "PlayingTrack") + PlayingTrack = int.Parse(args[1]); + else if (args[0] == "CurrentSector") + CurrentSector = int.Parse(args[1]); + else if (args[0] == "SectorOffset") + SectorOffset = int.Parse(args[1]); + else if (args[0] == "FadeOutOverFrames") + FadeOutOverFrames = int.Parse(args[1]); + else if (args[0] == "FadeOutFramesRemaining") + FadeOutFramesRemaining = int.Parse(args[1]); - else - Console.WriteLine("Skipping unrecognized identifier " + args[0]); - } - EnsureSector(); - } + else + Console.WriteLine("Skipping unrecognized identifier " + args[0]); + } + EnsureSector(); + } - public void SaveStateBinary(BinaryWriter writer) - { - writer.Write((byte)Mode); - writer.Write((byte)PlayMode); - writer.Write(LogicalVolume); - writer.Write(CurrentSector); - writer.Write(SectorOffset); - writer.Write(StartLBA); - writer.Write(EndLBA); - writer.Write(PlayingTrack); - writer.Write((short)FadeOutOverFrames); - writer.Write((short)FadeOutFramesRemaining); - } - - public void LoadStateBinary(BinaryReader reader) - { - Mode = (CDAudioMode) reader.ReadByte(); - PlayMode = (PlaybackMode) reader.ReadByte(); - LogicalVolume = reader.ReadInt32(); - CurrentSector = reader.ReadInt32(); - SectorOffset = reader.ReadInt32(); - StartLBA = reader.ReadInt32(); - EndLBA = reader.ReadInt32(); - PlayingTrack = reader.ReadInt32(); - FadeOutOverFrames = reader.ReadInt16(); - FadeOutFramesRemaining = reader.ReadInt16(); - } - } + public void SaveStateBinary(BinaryWriter writer) + { + writer.Write((byte)Mode); + writer.Write((byte)PlayMode); + writer.Write(LogicalVolume); + writer.Write(CurrentSector); + writer.Write(SectorOffset); + writer.Write(StartLBA); + writer.Write(EndLBA); + writer.Write(PlayingTrack); + writer.Write((short)FadeOutOverFrames); + writer.Write((short)FadeOutFramesRemaining); + } + + public void LoadStateBinary(BinaryReader reader) + { + Mode = (CDAudioMode)reader.ReadByte(); + PlayMode = (PlaybackMode)reader.ReadByte(); + LogicalVolume = reader.ReadInt32(); + CurrentSector = reader.ReadInt32(); + SectorOffset = reader.ReadInt32(); + StartLBA = reader.ReadInt32(); + EndLBA = reader.ReadInt32(); + PlayingTrack = reader.ReadInt32(); + FadeOutOverFrames = reader.ReadInt16(); + FadeOutFramesRemaining = reader.ReadInt16(); + } + } } \ No newline at end of file diff --git a/BizHawk.sln b/BizHawk.sln index 3680a0b6cb..288451e22d 100644 --- a/BizHawk.sln +++ b/BizHawk.sln @@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BizHawk.Client.EmuHawk", "B EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BizHawk.Client.DiscoHawk", "BizHawk.Client.DiscoHawk\BizHawk.Client.DiscoHawk.csproj", "{C4366030-6D03-424B-AE53-F4F43BB217C3}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BizHawk.Emulation.DiscSystem", "BizHawk.Emulation.DiscSystem\BizHawk.Emulation.DiscSystem.csproj", "{F51946EA-827F-4D82-B841-1F2F6D060312}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -85,6 +87,18 @@ Global {C4366030-6D03-424B-AE53-F4F43BB217C3}.Release|Mixed Platforms.Build.0 = Release|Any CPU {C4366030-6D03-424B-AE53-F4F43BB217C3}.Release|Win32.ActiveCfg = Release|Any CPU {C4366030-6D03-424B-AE53-F4F43BB217C3}.Release|x86.ActiveCfg = Release|Any CPU + {F51946EA-827F-4D82-B841-1F2F6D060312}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F51946EA-827F-4D82-B841-1F2F6D060312}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F51946EA-827F-4D82-B841-1F2F6D060312}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {F51946EA-827F-4D82-B841-1F2F6D060312}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {F51946EA-827F-4D82-B841-1F2F6D060312}.Debug|Win32.ActiveCfg = Debug|Any CPU + {F51946EA-827F-4D82-B841-1F2F6D060312}.Debug|x86.ActiveCfg = Debug|Any CPU + {F51946EA-827F-4D82-B841-1F2F6D060312}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F51946EA-827F-4D82-B841-1F2F6D060312}.Release|Any CPU.Build.0 = Release|Any CPU + {F51946EA-827F-4D82-B841-1F2F6D060312}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {F51946EA-827F-4D82-B841-1F2F6D060312}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {F51946EA-827F-4D82-B841-1F2F6D060312}.Release|Win32.ActiveCfg = Release|Any CPU + {F51946EA-827F-4D82-B841-1F2F6D060312}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE