BizHawk/BizHawk.Emulation.DiscSystem/DiscIdentifier.cs

160 lines
4.5 KiB
C#

using System;
using System.Collections.Generic;
//disc type identification logic
namespace BizHawk.Emulation.DiscSystem
{
public enum DiscType
{
/// <summary>
/// Disc contains audio in track 1. Nothing more can readily be determined
/// </summary>
AudioDisc,
/// <summary>
/// Nothing is known about this data disc type
/// </summary>
UnknownFormat,
/// <summary>
/// This is definitely a CDFS disc, but we can't identify anything more about it
/// </summary>
UnknownCDFS,
/// <summary>
/// Sony PSX
/// </summary>
SonyPSX,
/// <summary>
/// Sony PSP
/// </summary>
SonyPSP,
/// <summary>
/// Sega Saturn
/// </summary>
SegaSaturn,
/// <summary>
/// Its not clear whether we can ever have enough info to ID a turboCD disc (we're using hashes)
/// </summary>
TurboCD,
/// <summary>
/// MegaDrive addon
/// </summary>
MegaCD
}
public class DiscIdentifier
{
public DiscIdentifier(Disc disc)
{
this.disc = disc;
dsr = new DiscSectorReader(disc);
//the first check for mode 0 should be sufficient for blocking attempts to read audio sectors, so dont do this
//dsr.Policy.ThrowExceptions2048 = false;
}
Disc disc;
DiscSectorReader dsr;
Dictionary<int, byte[]> sectorCache = new Dictionary<int,byte[]>();
/// <summary>
/// Attempts to determine the type of the disc.
/// In the future, we might return a struct or a class with more detailed information
/// </summary>
public DiscType DetectDiscType()
{
//check track 1's data type. if it's an audio track, further data-track testing is useless
//furthermore, it's probably senseless (no binary data there to read)
//however a sector could mark itself as audio without actually being.. we'll just wait for that one.
if (dsr.ReadLBA_Mode(disc.TOC.TOCItems[1].LBA) == 0) return DiscType.AudioDisc;
//sega doesnt put anything identifying in the cdfs volume info. but its consistent about putting its own header here in sector 0
if (DetectSegaSaturn()) return DiscType.SegaSaturn;
// not fully tested yet
if (DetectMegaCD()) return DiscType.MegaCD;
// not fully tested yet
if (DetectPSX()) return DiscType.SonyPSX;
//we dont know how to detect TurboCD.
//an emulator frontend will likely just guess TurboCD if the disc is UnknownFormat
//(we can also have a gameDB!)
var discView = EDiscStreamView.DiscStreamView_Mode1_2048;
if (disc.TOC.Session1Format == SessionFormat.Type20_CDXA)
discView = EDiscStreamView.DiscStreamView_Mode2_Form1_2048;
var iso = new ISOFile();
bool isIso = iso.Parse(new DiscStream(disc, discView, 0));
if (isIso)
{
var appId = System.Text.Encoding.ASCII.GetString(iso.VolumeDescriptors[0].ApplicationIdentifier).TrimEnd('\0', ' ');
//for example: PSX magical drop F (JP SLPS_02337) doesn't have the correct iso PVD fields
//but, some PSX games (junky rips) don't have the 'licensed by string' so we'll hope they get caught here
if (appId == "PLAYSTATION")
return DiscType.SonyPSX;
if(appId == "PSP GAME")
return DiscType.SonyPSP;
return DiscType.UnknownCDFS;
}
return DiscType.UnknownFormat;
}
/// <summary>
/// This is reasonable approach to ID saturn.
/// </summary>
bool DetectSegaSaturn()
{
return StringAt("SEGA SEGASATURN", 0);
}
/// <summary>
/// probably wrong
/// </summary>
bool DetectMegaCD()
{
return StringAt("SEGADISCSYSTEM", 0) || StringAt("SEGADISCSYSTEM", 16);
}
bool DetectPSX()
{
if (!StringAt(" Licensed by ",0, 4)) return false;
return (StringAt("Sony Computer Entertainment Euro", 32, 4)
|| StringAt("Sony Computer Entertainment Inc.", 32, 4)
|| StringAt("Sony Computer Entertainment Amer", 32, 4)
|| StringAt("Sony Computer Entertainment of A", 32, 4)
);
}
private bool StringAt(string s, int n, int lba = 0)
{
//read it if we dont have it cached
//we wont be caching very much here, it's no big deal
//identification is not something we want to take a long time
byte[] data;
if (!sectorCache.TryGetValue(lba, out data))
{
data = new byte[2048];
dsr.ReadLBA_2048(lba, data, 0);
sectorCache[lba] = data;
}
byte[] cmp = System.Text.Encoding.ASCII.GetBytes(s);
byte[] cmp2 = new byte[cmp.Length];
Buffer.BlockCopy(data, n, cmp2, 0, cmp.Length);
return System.Linq.Enumerable.SequenceEqual(cmp, cmp2);
}
}
}