BizHawk/BizHawk.Emulation.DiscSystem/API_MednaDisc.cs

149 lines
4.9 KiB
C#

using System;
using System.Runtime.InteropServices;
using BizHawk.Common;
namespace BizHawk.Emulation.DiscSystem
{
/// <summary>
/// A resource representing a disc opened and mounted through the MednaDisc component.
/// Does not attempt to virtually present the disc as a BizHawk disc - that will be the
/// responsibility of the user of this code
/// </summary>
public unsafe class MednaDisc : IDisposable
{
/// <exception cref="InvalidOperationException"><see cref="IsLibraryAvailable"/> is <see langword="false"/> (could not load <c>mednadisc.dll</c>), or unmanaged call failed</exception>
public MednaDisc(string pathToDisc)
{
if (!IsLibraryAvailable)
throw new InvalidOperationException($"{nameof(MednaDisc)} library is not available!");
handle = mednadisc_LoadCD(pathToDisc);
if (handle == IntPtr.Zero)
throw new InvalidOperationException($"Failed to load {nameof(MednaDisc)}: {pathToDisc}");
//read the mednafen toc
TOCTracks = new MednadiscTOCTrack[101];
fixed (MednadiscTOCTrack* _tracks = &TOCTracks[0])
fixed(MednadiscTOC* _toc = &TOC)
mednadisc_ReadTOC(handle, _toc, _tracks);
//leave the disc open until this is disposed so we can read sectors from it
}
IntPtr handle;
public MednadiscTOC TOC;
public MednadiscTOCTrack[] TOCTracks;
[ThreadStatic] static byte[] buf2442 = new byte[2448];
[ThreadStatic] static byte[] buf96 = new byte[96];
public void Read_2442(int LBA, byte[] buffer, int offset)
{
//read directly into the target buffer
fixed(byte* pBuffer = &buffer[0])
mednadisc_ReadSector(handle, LBA, pBuffer + offset);
}
#if false
public void ReadSubcodeDeinterleaved(int LBA, byte[] buffer, int offset)
{
fixed (byte* pBuffer = buf2442)
mednadisc_ReadSector(handle, LBA, pBuffer);
SynthUtils.DeinterleaveSubcode(buf2442, 2352, buffer, offset);
}
public void ReadSubcodeChannel(int LBA, int number, byte[] buffer, int offset)
{
fixed (byte* pBuffer = buf2442)
mednadisc_ReadSector(handle, LBA, pBuffer);
SynthUtils.DeinterleaveSubcode(buf2442, 2352, buf96, 0);
for (int i = 0; i < 12; i++)
buffer[offset + i] = buf96[number * 12 + i];
}
public void Read_2352(int LBA, byte[] buffer, int offset)
{
fixed (byte* pBuffer = buf2442)
mednadisc_ReadSector(handle, LBA, pBuffer);
Buffer.BlockCopy(buf2442, 0, buffer, offset, 2352);
}
public void Read_2048(int LBA, byte[] buffer, int offset)
{
//this depends on CD-XA mode and such. so we need to read the mode bytes
//HEY!!!!!! SHOULD THIS BE DONE BASED ON THE CLAIMED TRACK TYPE, OR ON WHATS IN THE SECTOR?
//this is kind of a function of the CD reader.. it's not clear how this function should work.
//YIKES!!!!!!!!!!!!!!
//well, we need to scrutinize it for CCD files anyway, so...
//this sucks.
fixed (byte* pBuffer = buf2442)
mednadisc_ReadSector(handle, LBA, pBuffer);
byte mode = buf2442[15];
if (mode == 1)
Buffer.BlockCopy(buf2442, 16, buffer, offset, 2048);
else
Buffer.BlockCopy(buf2442, 24, buffer, offset, 2048); //PSX assumptions about CD-XA.. BAD BAD BAD
}
#endif
static void CheckLibrary()
{
var lib = OSTailoredCode.LinkedLibManager.LoadOrNull("mednadisc.dll");
_IsLibraryAvailable = lib != null
&& OSTailoredCode.LinkedLibManager.GetProcAddrOrNull(lib.Value, "mednadisc_LoadCD") != null;
if (lib != null) OSTailoredCode.LinkedLibManager.FreeByPtr(lib.Value);
}
static MednaDisc()
{
CheckLibrary();
}
static bool _IsLibraryAvailable;
public static bool IsLibraryAvailable => _IsLibraryAvailable;
public void Dispose()
{
if(handle == IntPtr.Zero) return;
mednadisc_CloseCD(handle);
}
[StructLayout(LayoutKind.Sequential)]
public struct MednadiscTOC
{
public byte first_track;
public byte last_track;
public byte disc_type;
}
[StructLayout(LayoutKind.Explicit)]
public struct MednadiscTOCTrack
{
[FieldOffset(0)] public byte adr;
[FieldOffset(1)] public byte control;
[FieldOffset(4)] public uint lba;
//can't be a bool due to marshalling...
[FieldOffset(8)] public byte _validByte;
public bool Valid => _validByte != 0;
}
[DllImport("mednadisc.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr mednadisc_LoadCD(string path);
[DllImport("mednadisc.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int mednadisc_ReadSector(IntPtr disc, int lba, byte* buf2448);
[DllImport("mednadisc.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void mednadisc_CloseCD(IntPtr disc);
[DllImport("mednadisc.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void mednadisc_ReadTOC(IntPtr disc, MednadiscTOC* toc, MednadiscTOCTrack* tracks101);
}
}