2015-06-23 18:57:11 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
|
|
|
|
//disc type identification logic
|
|
|
|
|
|
|
|
|
|
namespace BizHawk.Emulation.DiscSystem
|
|
|
|
|
{
|
|
|
|
|
public enum DiscType
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
2015-07-13 01:23:27 +00:00
|
|
|
|
/// Disc contains audio in track 1. Nothing more can readily be determined
|
|
|
|
|
/// </summary>
|
|
|
|
|
AudioDisc,
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Nothing is known about this data disc type
|
2015-06-23 18:57:11 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
UnknownFormat,
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// This is definitely a CDFS disc, but we can't identify anything more about it
|
|
|
|
|
/// </summary>
|
|
|
|
|
UnknownCDFS,
|
2017-07-09 15:17:42 +00:00
|
|
|
|
|
2015-06-23 18:57:11 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sony PSX
|
|
|
|
|
/// </summary>
|
|
|
|
|
SonyPSX,
|
2017-07-09 15:17:42 +00:00
|
|
|
|
|
2015-06-23 18:57:11 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sony PSP
|
|
|
|
|
/// </summary>
|
|
|
|
|
SonyPSP,
|
2017-07-09 15:17:42 +00:00
|
|
|
|
|
2015-06-23 18:57:11 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sega Saturn
|
|
|
|
|
/// </summary>
|
|
|
|
|
SegaSaturn,
|
2017-07-09 15:17:42 +00:00
|
|
|
|
|
2015-06-23 18:57:11 +00:00
|
|
|
|
/// <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>
|
2017-07-09 15:17:42 +00:00
|
|
|
|
MegaCD,
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// By NEC.
|
|
|
|
|
/// </summary>
|
|
|
|
|
PCFX
|
2015-06-23 18:57:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class DiscIdentifier
|
|
|
|
|
{
|
|
|
|
|
public DiscIdentifier(Disc disc)
|
|
|
|
|
{
|
2017-07-09 15:17:42 +00:00
|
|
|
|
_disc = disc;
|
|
|
|
|
_dsr = new DiscSectorReader(disc);
|
|
|
|
|
|
2015-07-18 22:55:33 +00:00
|
|
|
|
//the first check for mode 0 should be sufficient for blocking attempts to read audio sectors, so dont do this
|
|
|
|
|
//dsr.Policy.ThrowExceptions2048 = false;
|
2015-06-23 18:57:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-07-09 15:17:42 +00:00
|
|
|
|
private readonly Disc _disc;
|
|
|
|
|
private readonly DiscSectorReader _dsr;
|
|
|
|
|
private readonly Dictionary<int, byte[]> _sectorCache = new Dictionary<int, byte[]>();
|
|
|
|
|
|
2015-06-23 18:57:11 +00:00
|
|
|
|
/// <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()
|
|
|
|
|
{
|
2015-07-18 22:55:33 +00:00
|
|
|
|
//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.
|
2015-07-13 01:23:27 +00:00
|
|
|
|
|
2017-07-09 15:17:42 +00:00
|
|
|
|
// not fully tested yet
|
|
|
|
|
if (DetectPCFX())
|
|
|
|
|
return DiscType.PCFX;
|
|
|
|
|
|
|
|
|
|
if (!_disc.TOC.TOCItems[1].IsData)
|
|
|
|
|
return DiscType.AudioDisc;
|
|
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
2015-06-23 18:57:11 +00:00
|
|
|
|
|
|
|
|
|
// not fully tested yet
|
2017-07-09 15:17:42 +00:00
|
|
|
|
if (DetectMegaCD())
|
|
|
|
|
return DiscType.MegaCD;
|
2015-06-23 18:57:11 +00:00
|
|
|
|
|
|
|
|
|
// not fully tested yet
|
2017-07-09 15:17:42 +00:00
|
|
|
|
if (DetectPSX())
|
|
|
|
|
return DiscType.SonyPSX;
|
2015-06-23 18:57:11 +00:00
|
|
|
|
|
|
|
|
|
//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!)
|
|
|
|
|
|
2015-07-20 22:59:34 +00:00
|
|
|
|
var discView = EDiscStreamView.DiscStreamView_Mode1_2048;
|
2017-07-09 15:17:42 +00:00
|
|
|
|
if (_disc.TOC.Session1Format == SessionFormat.Type20_CDXA)
|
2015-07-20 22:59:34 +00:00
|
|
|
|
discView = EDiscStreamView.DiscStreamView_Mode2_Form1_2048;
|
|
|
|
|
|
2015-06-23 18:57:11 +00:00
|
|
|
|
var iso = new ISOFile();
|
2017-07-09 15:17:42 +00:00
|
|
|
|
bool isIso = iso.Parse(new DiscStream(_disc, discView, 0));
|
2015-06-23 18:57:11 +00:00
|
|
|
|
|
|
|
|
|
if (isIso)
|
|
|
|
|
{
|
|
|
|
|
var appId = System.Text.Encoding.ASCII.GetString(iso.VolumeDescriptors[0].ApplicationIdentifier).TrimEnd('\0', ' ');
|
2017-07-09 15:17:42 +00:00
|
|
|
|
|
2015-07-20 22:59:34 +00:00
|
|
|
|
//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")
|
2017-07-09 15:17:42 +00:00
|
|
|
|
return DiscType.SonyPSX;
|
2015-07-20 22:59:34 +00:00
|
|
|
|
|
2017-07-09 15:17:42 +00:00
|
|
|
|
if (appId == "PSP GAME")
|
2015-06-23 18:57:11 +00:00
|
|
|
|
return DiscType.SonyPSP;
|
2017-07-09 15:17:42 +00:00
|
|
|
|
|
2015-06-23 18:57:11 +00:00
|
|
|
|
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()
|
|
|
|
|
{
|
2017-07-09 15:17:42 +00:00
|
|
|
|
if (!StringAt(" Licensed by ", 0, 4)) return false;
|
2015-06-23 18:57:11 +00:00
|
|
|
|
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)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-09 15:17:42 +00:00
|
|
|
|
bool DetectPCFX()
|
|
|
|
|
{
|
|
|
|
|
var toc = _disc.TOC;
|
|
|
|
|
for (int t = toc.FirstRecordedTrackNumber;
|
|
|
|
|
t <= toc.LastRecordedTrackNumber;
|
|
|
|
|
t++)
|
|
|
|
|
{
|
|
|
|
|
var track = _disc.TOC.TOCItems[t];
|
|
|
|
|
if (track.IsData && StringAt("PC-FX:Hu_CD-ROM", 0, track.LBA))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private byte[] ReadSectorCached(int lba)
|
2015-06-23 18:57:11 +00:00
|
|
|
|
{
|
|
|
|
|
//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;
|
2017-07-09 15:17:42 +00:00
|
|
|
|
if (!_sectorCache.TryGetValue(lba, out data))
|
2015-06-23 18:57:11 +00:00
|
|
|
|
{
|
|
|
|
|
data = new byte[2048];
|
2017-07-09 15:17:42 +00:00
|
|
|
|
_dsr.ReadLBA_2048(lba, data, 0);
|
|
|
|
|
_sectorCache[lba] = data;
|
2015-06-23 18:57:11 +00:00
|
|
|
|
}
|
2017-07-09 15:17:42 +00:00
|
|
|
|
return data;
|
|
|
|
|
}
|
2015-06-23 18:57:11 +00:00
|
|
|
|
|
2017-07-09 15:17:42 +00:00
|
|
|
|
private bool StringAt(string s, int n, int lba = 0)
|
|
|
|
|
{
|
|
|
|
|
var data = ReadSectorCached(lba);
|
2015-06-23 18:57:11 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|