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

This commit is contained in:
adelikat 2013-11-03 23:45:44 +00:00
parent 7a260abb54
commit fe7da7c5b5
49 changed files with 3492 additions and 2295 deletions

View File

@ -147,6 +147,10 @@
<Project>{866F8D13-0678-4FF9-80A4-A3993FD4D8A3}</Project>
<Name>BizHawk.Common</Name>
</ProjectReference>
<ProjectReference Include="..\BizHawk.Emulation.DiscSystem\BizHawk.Emulation.DiscSystem.csproj">
<Project>{f51946ea-827f-4d82-b841-1f2f6d060312}</Project>
<Name>BizHawk.Emulation.DiscSystem</Name>
</ProjectReference>
<ProjectReference Include="..\BizHawk.Emulation\BizHawk.Emulation.csproj">
<Project>{197D4314-8A9F-49BA-977D-54ACEFAEB6BA}</Project>
<Name>BizHawk.Emulation</Name>

View File

@ -1,4 +1,4 @@
using BizHawk.DiscSystem;
using BizHawk.Emulation.DiscSystem;
namespace BizHawk.Client.Common
{

View File

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

View File

@ -149,13 +149,13 @@
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BizHawk.Emulation\BizHawk.Emulation.csproj">
<Project>{197D4314-8A9F-49BA-977D-54ACEFAEB6BA}</Project>
<Name>BizHawk.Emulation</Name>
</ProjectReference>
<Content Include="discohawk.ico" />
</ItemGroup>
<ItemGroup>
<Content Include="discohawk.ico" />
<ProjectReference Include="..\BizHawk.Emulation.DiscSystem\BizHawk.Emulation.DiscSystem.csproj">
<Project>{f51946ea-827f-4d82-b841-1f2f6d060312}</Project>
<Name>BizHawk.Emulation.DiscSystem</Name>
</ProjectReference>
</ItemGroup>
<PropertyGroup>
<PreBuildEvent Condition=" '$(OS)' == 'Windows_NT' ">"$(SolutionDir)subwcrev.bat" "$(ProjectDir)"</PreBuildEvent>

View File

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

View File

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

View File

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

View File

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

View File

@ -1006,6 +1006,10 @@
<Project>{866F8D13-0678-4FF9-80A4-A3993FD4D8A3}</Project>
<Name>BizHawk.Common</Name>
</ProjectReference>
<ProjectReference Include="..\BizHawk.Emulation.DiscSystem\BizHawk.Emulation.DiscSystem.csproj">
<Project>{f51946ea-827f-4d82-b841-1f2f6d060312}</Project>
<Name>BizHawk.Emulation.DiscSystem</Name>
</ProjectReference>
<ProjectReference Include="..\BizHawk.Emulation\BizHawk.Emulation.csproj">
<Project>{197D4314-8A9F-49BA-977D-54ACEFAEB6BA}</Project>
<Name>BizHawk.Emulation</Name>

View File

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

View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{F51946EA-827F-4D82-B841-1F2F6D060312}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>BizHawk.Emulation.DiscSystem</RootNamespace>
<AssemblyName>BizHawk.Emulation.DiscSystem</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Blobs\Blob_ECM.cs" />
<Compile Include="Blobs\Blob_WaveFile.cs" />
<Compile Include="Blobs\RiffMaster.cs" />
<Compile Include="Buffer.cs" />
<Compile Include="CCD_format.cs" />
<Compile Include="cdfs\EndianBitConverter.cs" />
<Compile Include="cdfs\ISODirectoryNode.cs" />
<Compile Include="cdfs\ISOFile.cs" />
<Compile Include="cdfs\ISOFileNode.cs" />
<Compile Include="cdfs\ISONode.cs" />
<Compile Include="cdfs\ISONodeRecord.cs" />
<Compile Include="cdfs\ISOVolumeDescriptor.cs" />
<Compile Include="CUE_format.cs" />
<Compile Include="Decoding.cs" />
<Compile Include="Disc.API.cs" />
<Compile Include="Disc.cs" />
<Compile Include="Disc.ID.cs" />
<Compile Include="DiscTOC.cs" />
<Compile Include="ECM.cs" />
<Compile Include="GPL_ECM.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Subcode.cs" />
<Compile Include="TOC_format.cs" />
<Compile Include="Util.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BizHawk.Common\BizHawk.Common.csproj">
<Project>{866f8d13-0678-4ff9-80a4-a3993fd4d8a3}</Project>
<Name>BizHawk.Common</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -29,7 +29,7 @@ using System.Collections.Generic;
using BizHawk.Common;
namespace BizHawk.DiscSystem
namespace BizHawk.Emulation.DiscSystem
{
partial class Disc
{

View File

@ -2,7 +2,7 @@
using System.Linq;
using System.IO;
namespace BizHawk.DiscSystem
namespace BizHawk.Emulation.DiscSystem
{
partial class Disc
{

View File

@ -0,0 +1,348 @@
using System;
using System.IO;
using System.Collections.Generic;
/// <summary>
/// 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.
/// </summary>
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;
/// <summary>
/// writes this chunk to the stream, including padding
/// </summary>
public abstract void WriteStream(Stream s);
/// <summary>
/// distinct from a size or a length, the `volume` is the volume of bytes occupied by the chunk on disk (accounting for padding).
///
/// </summary>
public abstract long GetVolume();
/// <summary>
/// transforms into a derived class depending on tag
/// </summary>
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<RiffChunk> subchunks = new List<RiffChunk>();
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<string, string> dictionary = new Dictionary<string, string>();
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<string, string> 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);
}
/// <summary>
/// takes posession of the supplied stream
/// </summary>
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;
}
}
}

View File

@ -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
{
/// <summary>
/// Implements a data simple data buffer with proper life cycle and no bounds checking
/// </summary>
public unsafe class CBuffer<T> : IDisposable
{
public GCHandle hnd;
public T[] arr;
public void* ptr;
public byte* byteptr;
public int len;
public int itemsize;
public static CBuffer<T> malloc(int amt, int itemsize)
{
return new CBuffer<T>(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<byte>
{
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<int>
{
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<short>
{
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
}
}
}

View File

@ -1,4 +1,4 @@
namespace BizHawk.DiscSystem
namespace BizHawk.Emulation.DiscSystem
{
//TBD CCD format
//check out ccd2iso linux program?

View File

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

View File

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

View File

@ -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<int, byte[], int> sectorReader, int sectorSize, byte[] sectorBuf, ref int sectorBufferHint)
public void READLBA_Flat_Implementation(long disc_offset, byte[] buffer, int offset, int length, Action<int, byte[], int> 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)

View File

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

View File

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

View File

@ -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
{
/// <summary>

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
namespace BizHawk.DiscSystem
namespace BizHawk.Emulation.DiscSystem
{
//TBD TOC format
public class TOCFormat

View File

@ -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();
}
/// <summary>
/// conerts bytes to an uppercase string of hex numbers in upper case without any spacing or anything
/// //could be extension method
/// </summary>
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<string> sections = new Stack<string>();
class Section : Dictionary<string, Section>
{
public string Name;
public Dictionary<string, string> Items = new Dictionary<string, string>();
}
Section ReaderSection, CurrSection;
Stack<Section> SectionStack = new Stack<Section>();
void BeginTextBlock()
{
if (!IsText) return;
if (IsWriter) return;
ReaderSection = new Section();
ReaderSection.Name = "";
Stack<Section> ss = new Stack<Section>();
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<T>(string name, ref T val) where T : struct
{
if (typeof(T).BaseType != typeof(System.Enum))
throw new InvalidOperationException();
if (isText) SyncEnumText<T>(name, ref val);
else if (IsReader) val = (T)Enum.ToObject(typeof(T), br.ReadInt32());
else bw.Write(Convert.ToInt32(val));
}
public void SyncEnumText<T>(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]));
}
}
/// <summary>
/// a Dictionary-of-lists with key K and values List&lt;V&gt;
/// </summary>
[Serializable]
public class Bag<K, V> : BagBase<K, V, Dictionary<K, List<V>>, List<V>> { }
/// <summary>
/// a Dictionary-of-lists with key K and values List&lt;V&gt;
/// </summary>
[Serializable]
public class SortedBag<K, V> : BagBase<K, V, SortedDictionary<K, List<V>>, List<V>> { }
/// <summary>
/// A dictionary that creates new values on the fly as necessary so that any key you need will be defined.
/// </summary>
/// <typeparam name="K">dictionary keys</typeparam>
/// <typeparam name="V">dictionary values</typeparam>
public class WorkingDictionary<K, V> : Dictionary<K, V> 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; }
}
}
/// <summary>
/// base class for Bag and SortedBag
/// </summary>
/// <typeparam name="K">dictionary keys</typeparam>
/// <typeparam name="V">list values</typeparam>
/// <typeparam name="D">dictionary type</typeparam>
/// <typeparam name="L">list type</typeparam>
[Serializable]
public class BagBase<K, V, D, L> : IEnumerable<V>
where D : IDictionary<K, L>, new()
where L : IList<V>, IEnumerable<V>, 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<V> GetEnumerator()
{
foreach (L lv in dictionary.Values)
foreach (V v in lv)
yield return v;
}
public IEnumerable KeyValuePairEnumerator { get { return dictionary; } }
/// <summary>
/// the list of keys contained herein
/// </summary>
public IList<K> Keys { get { return new List<K>(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
{
}
}

View File

@ -2,7 +2,8 @@
using System.Collections.Generic;
using System.Text;
namespace ISOParser {
namespace BizHawk.Emulation.DiscSystem
{
/// <summary>
/// Helper class to convert big and little endian numbers from a byte
/// array to a value.

View File

@ -0,0 +1,156 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace BizHawk.Emulation.DiscSystem
{
/// <summary>
/// Representation of a directory in the file system.
/// </summary>
public class ISODirectoryNode : ISONode
{
#region Public Properties
/// <summary>
/// The children in this directory.
/// </summary>
public Dictionary<string, ISONode> Children;
#endregion
#region Construction
/// <summary>
/// Constructor.
/// </summary>
/// <param name="record">The node for this directory.</param>
public ISODirectoryNode(ISONodeRecord record)
: base(record)
{
this.Children = new Dictionary<string, ISONode>();
}
#endregion
#region Parsing
/// <summary>
/// Parse the children based on the data in this directory.
/// </summary>
/// <param name="s">The stream to parse from.</param>
/// <param name="visited">The set of already handled
/// files/directories.</param>
public void Parse(Stream s, Dictionary<long, ISONode> visited)
{
// Go to the beginning of the set of directories
s.Seek(this.Offset * ISOFile.SECTOR_SIZE, SeekOrigin.Begin);
List<ISONodeRecord> records = new List<ISONodeRecord>();
// 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<string, ISONode> 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
/// <summary>
/// Print out this node's children.
/// </summary>
/// <param name="depth">The number of "tabs" to indent this directory.</param>
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
}
}

View File

@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace BizHawk.Emulation.DiscSystem
{
/// <summary>
/// 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
/// </summary>
public class ISOFile
{
#region Constants
/// <summary>
/// We are hard coding the SECTOR_SIZE
/// </summary>
public const int SECTOR_SIZE = 2048;
#endregion
#region Public Members
/// <summary>
/// This is a list of all the volume descriptors in the disk image.
/// NOTE: The first entry should be the primary volume.
/// </summary>
public List<ISOVolumeDescriptor> VolumeDescriptors;
/// <summary>
/// The Directory that is the root of this file system
/// </summary>
public ISODirectoryNode Root;
#endregion
#region Construction
/// <summary>
/// Construct the ISO file data structures, but leave everything
/// blank.
/// </summary>
public ISOFile()
{
}
#endregion
#region Parsing
/// <summary>
/// Parse the given stream to populate the iso information
/// </summary>
/// <param name="s">The stream which we are using to parse the image.
/// Should already be located at the start of the image.</param>
public bool Parse(Stream s, int startSector = 16)
{
this.VolumeDescriptors = new List<ISOVolumeDescriptor>();
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<long, ISONode> visitedNodes = new Dictionary<long, ISONode>();
// 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
/// <summary>
/// Print the directory tree for the image.
/// </summary>
public void Print()
{
// DEBUGGING: Now print out the directory structure
this.Root.Print(0);
}
#endregion
}
}

View File

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BizHawk.Emulation.DiscSystem
{
/// <summary>
/// Representation of a file in the file system.
/// </summary>
public class ISOFileNode : ISONode
{
#region Construction
/// <summary>
/// Constructor.
/// </summary>
/// <param name="record">The record to construct from.</param>
public ISOFileNode(ISONodeRecord record)
: base(record)
{
// Do Nothing
}
#endregion
}
}

View File

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BizHawk.Emulation.DiscSystem
{
/// <summary>
/// Abstract class to represent a file/directory node
/// </summary>
public class ISONode
{
#region Public Properties
/// <summary>
/// The record this node was created from.
/// </summary>
public ISONodeRecord FirstRecord;
/// <summary>
/// The sector offset of the file/directory data
/// </summary>
public long Offset;
/// <summary>
/// The byte length of the file/directory data.
/// </summary>
public long Length;
#endregion
#region Construction
/// <summary>
/// Constructor.
/// TODO: Make this constructor protected???
/// </summary>
/// <param name="record">The ISONodeRecord to construct from.</param>
public ISONode(ISONodeRecord record)
{
this.FirstRecord = record;
this.Offset = record.OffsetOfData;
this.Length = record.LengthOfData;
}
#endregion
}
}

View File

@ -0,0 +1,217 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace BizHawk.Emulation.DiscSystem
{
/// <summary>
/// Class to represent the file/directory information read from the disk.
/// </summary>
public class ISONodeRecord
{
#region Constants
/// <summary>
/// String representing the current directory entry
/// </summary>
public const string CURRENT_DIRECTORY = ".";
/// <summary>
/// String representing the parent directory entry
/// </summary>
public const string PARENT_DIRECTORY = "..";
#endregion
#region Public Properties
/// <summary>
/// The length of the record in bytes.
/// </summary>
public byte Length;
/// <summary>
/// The file offset of the data for this file/directory (in sectors).
/// </summary>
public long OffsetOfData;
/// <summary>
/// The length of the data for this file/directory (in bytes).
/// </summary>
public long LengthOfData;
/// <summary>
/// The file/directory creation year since 1900.
/// </summary>
public byte Year;
/// <summary>
/// The file/directory creation month.
/// </summary>
public byte Month;
/// <summary>
/// The file/directory creation day.
/// </summary>
public byte Day;
/// <summary>
/// The file/directory creation hour.
/// </summary>
public byte Hour;
/// <summary>
/// The file/directory creation minute.
/// </summary>
public byte Minute;
/// <summary>
/// The file/directory creation second.
/// </summary>
public byte Second;
/// <summary>
/// The file time offset from GMT.
/// </summary>
public byte TimeZoneOffset;
/// <summary>
/// Flags representing the attributes of this file/directory.
/// </summary>
public byte Flags;
/// <summary>
/// The length of the file/directory name.
/// </summary>
public byte NameLength;
/// <summary>
/// The file/directory name.
/// </summary>
public string Name;
#endregion
#region Construction
/// <summary>
/// Constructor
/// </summary>
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
/// <summary>
/// Return true if the record represents a file.
/// </summary>
/// <returns>True if a file.</returns>
public bool IsFile()
{
return ((this.Flags >> 1) & 0x01) == 0;
}
/// <summary>
/// Return true if the record represents a directory.
/// </summary>
/// <returns>True if a directory.</returns>
public bool IsDirectory()
{
return ((this.Flags >> 1) & 0x01) == 1;
}
#endregion
#region Parsing
/// <summary>
/// Parse the record from an array and offset.
/// </summary>
/// <param name="data">The array to parse from.</param>
/// <param name="cursor">The offset to start parsing at.</param>
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);
}
/// <summary>
/// Parse the node record from the given stream.
/// </summary>
/// <param name="s">The stream to parse from.</param>
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
}
}

View File

@ -0,0 +1,326 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace BizHawk.Emulation.DiscSystem
{
/// <summary>
/// Represents a volume descriptor for a disk image.
/// </summary>
public class ISOVolumeDescriptor
{
#region Constants
/// <summary>
/// We are handling the parsing by reading the entire header and
/// extracting the appropriate bytes.
///
/// This is done for performance reasons.
/// </summary>
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
/// <summary>
/// The type of this volume description, only 1 and 255 are supported
/// </summary>
public byte Type;
/// <summary>
/// The system identifier
/// </summary>
public byte[] SystemIdentifier;
/// <summary>
/// The volume identifier
/// </summary>
public byte[] VolumeIdentifier;
/// <summary>
/// The number of sectors on the disk
/// </summary>
public int NumberOfSectors;
/// <summary>
/// Volume Set Size (should be 1)
/// </summary>
public int VolumeSetSize;
/// <summary>
/// Volume Sequence Number (should be 1)
/// </summary>
public int VolumeSequenceNumber;
/// <summary>
/// Sector Size (should be 2048)
/// </summary>
public int SectorSize;
/// <summary>
/// Size of the path table
/// </summary>
public int PathTableSize;
/// <summary>
/// Sector offset of the first path table
/// </summary>
public int OffsetOfFirstLittleEndianPathTable;
/// <summary>
/// Sector offset of the second path table
/// </summary>
public int OffsetOfSecondLittleEndianPathTable;
/// <summary>
/// Sector offset of the first path table
/// </summary>
public int OffsetOfFirstBigEndianPathTable;
/// <summary>
/// Sector offset of the second path table
/// </summary>
public int OffsetOfSecondBigEndianPathTable;
/// <summary>
/// The root directory record
/// </summary>
public ISONodeRecord RootDirectoryRecord;
/// <summary>
/// The volumen set identifier
/// </summary>
public byte[] VolumeSetIdentifier;
/// <summary>
/// The publisher identifier
/// </summary>
public byte[] PublisherIdentifier;
/// <summary>
/// The data preparer identifier
/// </summary>
public byte[] DataPreparerIdentifier;
/// <summary>
/// The application identifier
/// </summary>
public byte[] ApplicationIdentifier;
/// <summary>
/// The copyright identifier
/// </summary>
public byte[] CopyrightFileIdentifier;
/// <summary>
/// The abstract file identifier
/// </summary>
public byte[] AbstractFileIdentifier;
/// <summary>
/// The bibliographical file identifier
/// </summary>
public byte[] BibliographicalFileIdentifier;
/// <summary>
/// The time and date the volume was created
/// </summary>
public byte[] VolumeCreationDateTime;
/// <summary>
/// The time and date the volume was last modified
/// </summary>
public byte[] LastModifiedDateTime;
/// <summary>
/// The time and date the volume expires
/// </summary>
public byte[] ExpirationDateTime;
/// <summary>
/// The time and data when the volume is effective
/// </summary>
public byte[] EffectiveDateTime;
/// <summary>
/// Extra reserved data
/// </summary>
public byte[] Reserved;
#endregion
#region Construction
/// <summary>
/// Constructor.
/// </summary>
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
/// <summary>
/// Parse the volume descriptor header.
/// </summary>
/// <param name="s">The stream to parse from.</param>
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
/// <summary>
/// Returns true if this is the terminator volume descriptor.
/// </summary>
/// <returns>True if the terminator.</returns>
public bool IsTerminator()
{
return (this.Type == 255);
}
#endregion
}
}

View File

@ -457,47 +457,6 @@
<Compile Include="Database\Database.cs" />
<Compile Include="Database\FirmwareDatabase.cs" />
<Compile Include="Database\GameInfo.cs" />
<Compile Include="DiscSystem\Blobs\Blob_ECM.cs" />
<Compile Include="DiscSystem\Blobs\Blob_WaveFile.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="DiscSystem\Blobs\RiffMaster.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="DiscSystem\CCD_format.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="DiscSystem\cdfs\EndianBitConverter.cs" />
<Compile Include="DiscSystem\cdfs\ISODirectoryNode.cs" />
<Compile Include="DiscSystem\cdfs\ISOFile.cs" />
<Compile Include="DiscSystem\cdfs\ISOFileNode.cs" />
<Compile Include="DiscSystem\cdfs\ISONode.cs" />
<Compile Include="DiscSystem\cdfs\ISONodeRecord.cs" />
<Compile Include="DiscSystem\cdfs\ISOVolumeDescriptor.cs" />
<Compile Include="DiscSystem\CUE_format.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="DiscSystem\Decoding.cs" />
<Compile Include="DiscSystem\Disc.API.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="DiscSystem\Disc.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="DiscSystem\Disc.Id.cs" />
<Compile Include="DiscSystem\DiscTOC.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="DiscSystem\ECM.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="DiscSystem\GPL_ECM.cs" />
<Compile Include="DiscSystem\Subcode.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="DiscSystem\TOC_format.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Interfaces\Base Implementations\IPS.cs" />
<Compile Include="Interfaces\Base Implementations\Movies.cs" />
<Compile Include="Interfaces\Base Implementations\NullController.cs" />
@ -560,7 +519,6 @@
<Content Include="Consoles\PC Engine\Compat.txt" />
<Content Include="Consoles\Sega\Genesis\Compat.txt" />
<Content Include="Consoles\Sega\SMS\Compat.txt" />
<Content Include="DiscSystem\cdfs\notes.txt" />
<Content Include="Notes.txt" />
</ItemGroup>
<ItemGroup>
@ -588,6 +546,10 @@
<Project>{866F8D13-0678-4FF9-80A4-A3993FD4D8A3}</Project>
<Name>BizHawk.Common</Name>
</ProjectReference>
<ProjectReference Include="..\BizHawk.Emulation.DiscSystem\BizHawk.Emulation.DiscSystem.csproj">
<Project>{f51946ea-827f-4d82-b841-1f2f6d060312}</Project>
<Name>BizHawk.Emulation.DiscSystem</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="Consoles\Atari\docs\stella.pdf" />

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,346 +0,0 @@
using System;
using System.IO;
using System.Collections.Generic;
/// <summary>
/// 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.
/// </summary>
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;
/// <summary>
/// writes this chunk to the stream, including padding
/// </summary>
public abstract void WriteStream(Stream s);
/// <summary>
/// distinct from a size or a length, the `volume` is the volume of bytes occupied by the chunk on disk (accounting for padding).
///
/// </summary>
public abstract long GetVolume();
/// <summary>
/// transforms into a derived class depending on tag
/// </summary>
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<RiffChunk> subchunks = new List<RiffChunk>();
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<string, string> dictionary = new Dictionary<string, string>();
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<string, string> 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);
}
/// <summary>
/// takes posession of the supplied stream
/// </summary>
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;
}
}

View File

@ -1,145 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
namespace ISOParser {
/// <summary>
/// Representation of a directory in the file system.
/// </summary>
public class ISODirectoryNode : ISONode {
#region Public Properties
/// <summary>
/// The children in this directory.
/// </summary>
public Dictionary<string,ISONode> Children;
#endregion
#region Construction
/// <summary>
/// Constructor.
/// </summary>
/// <param name="record">The node for this directory.</param>
public ISODirectoryNode( ISONodeRecord record )
: base( record ) {
this.Children = new Dictionary<string, ISONode>();
}
#endregion
#region Parsing
/// <summary>
/// Parse the children based on the data in this directory.
/// </summary>
/// <param name="s">The stream to parse from.</param>
/// <param name="visited">The set of already handled
/// files/directories.</param>
public void Parse(Stream s, Dictionary<long, ISONode> visited) {
// Go to the beginning of the set of directories
s.Seek(this.Offset*ISOFile.SECTOR_SIZE, SeekOrigin.Begin);
List<ISONodeRecord> records = new List<ISONodeRecord>();
// 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<string,ISONode> 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
/// <summary>
/// Print out this node's children.
/// </summary>
/// <param name="depth">The number of "tabs" to indent this directory.</param>
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
}
}

View File

@ -1,135 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace ISOParser {
/// <summary>
/// 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
/// </summary>
public class ISOFile {
#region Constants
/// <summary>
/// We are hard coding the SECTOR_SIZE
/// </summary>
public const int SECTOR_SIZE = 2048;
#endregion
#region Public Members
/// <summary>
/// This is a list of all the volume descriptors in the disk image.
/// NOTE: The first entry should be the primary volume.
/// </summary>
public List<ISOVolumeDescriptor> VolumeDescriptors;
/// <summary>
/// The Directory that is the root of this file system
/// </summary>
public ISODirectoryNode Root;
#endregion
#region Construction
/// <summary>
/// Construct the ISO file data structures, but leave everything
/// blank.
/// </summary>
public ISOFile() {
}
#endregion
#region Parsing
/// <summary>
/// Parse the given stream to populate the iso information
/// </summary>
/// <param name="s">The stream which we are using to parse the image.
/// Should already be located at the start of the image.</param>
public bool Parse(Stream s, int startSector=16)
{
this.VolumeDescriptors = new List<ISOVolumeDescriptor>();
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<long, ISONode> visitedNodes = new Dictionary<long,ISONode>();
// 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
/// <summary>
/// Print the directory tree for the image.
/// </summary>
public void Print() {
// DEBUGGING: Now print out the directory structure
this.Root.Print(0);
}
#endregion
}
}

View File

@ -1,22 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace ISOParser {
/// <summary>
/// Representation of a file in the file system.
/// </summary>
public class ISOFileNode : ISONode {
#region Construction
/// <summary>
/// Constructor.
/// </summary>
/// <param name="record">The record to construct from.</param>
public ISOFileNode( ISONodeRecord record ) : base( record ) {
// Do Nothing
}
#endregion
}
}

View File

@ -1,43 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace ISOParser {
/// <summary>
/// Abstract class to represent a file/directory node
/// </summary>
public class ISONode {
#region Public Properties
/// <summary>
/// The record this node was created from.
/// </summary>
public ISONodeRecord FirstRecord;
/// <summary>
/// The sector offset of the file/directory data
/// </summary>
public long Offset;
/// <summary>
/// The byte length of the file/directory data.
/// </summary>
public long Length;
#endregion
#region Construction
/// <summary>
/// Constructor.
/// TODO: Make this constructor protected???
/// </summary>
/// <param name="record">The ISONodeRecord to construct from.</param>
public ISONode( ISONodeRecord record ) {
this.FirstRecord = record;
this.Offset = record.OffsetOfData;
this.Length = record.LengthOfData;
}
#endregion
}
}

View File

@ -1,208 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace ISOParser {
/// <summary>
/// Class to represent the file/directory information read from the disk.
/// </summary>
public class ISONodeRecord {
#region Constants
/// <summary>
/// String representing the current directory entry
/// </summary>
public const string CURRENT_DIRECTORY = ".";
/// <summary>
/// String representing the parent directory entry
/// </summary>
public const string PARENT_DIRECTORY = "..";
#endregion
#region Public Properties
/// <summary>
/// The length of the record in bytes.
/// </summary>
public byte Length;
/// <summary>
/// The file offset of the data for this file/directory (in sectors).
/// </summary>
public long OffsetOfData;
/// <summary>
/// The length of the data for this file/directory (in bytes).
/// </summary>
public long LengthOfData;
/// <summary>
/// The file/directory creation year since 1900.
/// </summary>
public byte Year;
/// <summary>
/// The file/directory creation month.
/// </summary>
public byte Month;
/// <summary>
/// The file/directory creation day.
/// </summary>
public byte Day;
/// <summary>
/// The file/directory creation hour.
/// </summary>
public byte Hour;
/// <summary>
/// The file/directory creation minute.
/// </summary>
public byte Minute;
/// <summary>
/// The file/directory creation second.
/// </summary>
public byte Second;
/// <summary>
/// The file time offset from GMT.
/// </summary>
public byte TimeZoneOffset;
/// <summary>
/// Flags representing the attributes of this file/directory.
/// </summary>
public byte Flags;
/// <summary>
/// The length of the file/directory name.
/// </summary>
public byte NameLength;
/// <summary>
/// The file/directory name.
/// </summary>
public string Name;
#endregion
#region Construction
/// <summary>
/// Constructor
/// </summary>
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
/// <summary>
/// Return true if the record represents a file.
/// </summary>
/// <returns>True if a file.</returns>
public bool IsFile() {
return ((this.Flags >> 1) & 0x01) == 0;
}
/// <summary>
/// Return true if the record represents a directory.
/// </summary>
/// <returns>True if a directory.</returns>
public bool IsDirectory() {
return ((this.Flags >> 1) & 0x01) == 1;
}
#endregion
#region Parsing
/// <summary>
/// Parse the record from an array and offset.
/// </summary>
/// <param name="data">The array to parse from.</param>
/// <param name="cursor">The offset to start parsing at.</param>
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);
}
/// <summary>
/// Parse the node record from the given stream.
/// </summary>
/// <param name="s">The stream to parse from.</param>
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
}
}

View File

@ -1,320 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace ISOParser {
/// <summary>
/// Represents a volume descriptor for a disk image.
/// </summary>
public class ISOVolumeDescriptor {
#region Constants
/// <summary>
/// We are handling the parsing by reading the entire header and
/// extracting the appropriate bytes.
///
/// This is done for performance reasons.
/// </summary>
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
/// <summary>
/// The type of this volume description, only 1 and 255 are supported
/// </summary>
public byte Type;
/// <summary>
/// The system identifier
/// </summary>
public byte[] SystemIdentifier;
/// <summary>
/// The volume identifier
/// </summary>
public byte[] VolumeIdentifier;
/// <summary>
/// The number of sectors on the disk
/// </summary>
public int NumberOfSectors;
/// <summary>
/// Volume Set Size (should be 1)
/// </summary>
public int VolumeSetSize;
/// <summary>
/// Volume Sequence Number (should be 1)
/// </summary>
public int VolumeSequenceNumber;
/// <summary>
/// Sector Size (should be 2048)
/// </summary>
public int SectorSize;
/// <summary>
/// Size of the path table
/// </summary>
public int PathTableSize;
/// <summary>
/// Sector offset of the first path table
/// </summary>
public int OffsetOfFirstLittleEndianPathTable;
/// <summary>
/// Sector offset of the second path table
/// </summary>
public int OffsetOfSecondLittleEndianPathTable;
/// <summary>
/// Sector offset of the first path table
/// </summary>
public int OffsetOfFirstBigEndianPathTable;
/// <summary>
/// Sector offset of the second path table
/// </summary>
public int OffsetOfSecondBigEndianPathTable;
/// <summary>
/// The root directory record
/// </summary>
public ISONodeRecord RootDirectoryRecord;
/// <summary>
/// The volumen set identifier
/// </summary>
public byte[] VolumeSetIdentifier;
/// <summary>
/// The publisher identifier
/// </summary>
public byte[] PublisherIdentifier;
/// <summary>
/// The data preparer identifier
/// </summary>
public byte[] DataPreparerIdentifier;
/// <summary>
/// The application identifier
/// </summary>
public byte[] ApplicationIdentifier;
/// <summary>
/// The copyright identifier
/// </summary>
public byte[] CopyrightFileIdentifier;
/// <summary>
/// The abstract file identifier
/// </summary>
public byte[] AbstractFileIdentifier;
/// <summary>
/// The bibliographical file identifier
/// </summary>
public byte[] BibliographicalFileIdentifier;
/// <summary>
/// The time and date the volume was created
/// </summary>
public byte[] VolumeCreationDateTime;
/// <summary>
/// The time and date the volume was last modified
/// </summary>
public byte[] LastModifiedDateTime;
/// <summary>
/// The time and date the volume expires
/// </summary>
public byte[] ExpirationDateTime;
/// <summary>
/// The time and data when the volume is effective
/// </summary>
public byte[] EffectiveDateTime;
/// <summary>
/// Extra reserved data
/// </summary>
public byte[] Reserved;
#endregion
#region Construction
/// <summary>
/// Constructor.
/// </summary>
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
/// <summary>
/// Parse the volume descriptor header.
/// </summary>
/// <param name="s">The stream to parse from.</param>
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
/// <summary>
/// Returns true if this is the terminator volume descriptor.
/// </summary>
/// <returns>True if the terminator.</returns>
public bool IsTerminator() {
return (this.Type == 255);
}
#endregion
}
}

View File

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

View File

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