From 420aec7870b4b4571a0eda9930afdc95160e788a Mon Sep 17 00:00:00 2001 From: Asnivor Date: Wed, 11 Oct 2017 10:57:58 +0100 Subject: [PATCH] DiscIdentifier - Improved detection of PC-FX. Added detection of lots more systems. --- .../DiscIdentifier.cs | 212 ++++++++++++++++-- 1 file changed, 192 insertions(+), 20 deletions(-) diff --git a/BizHawk.Emulation.DiscSystem/DiscIdentifier.cs b/BizHawk.Emulation.DiscSystem/DiscIdentifier.cs index a9add9cf4e..7db33ef622 100644 --- a/BizHawk.Emulation.DiscSystem/DiscIdentifier.cs +++ b/BizHawk.Emulation.DiscSystem/DiscIdentifier.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; //disc type identification logic @@ -8,7 +9,7 @@ namespace BizHawk.Emulation.DiscSystem public enum DiscType { /// - /// Disc contains audio in track 1. Nothing more can readily be determined + /// Disc contains audio in track 1. This may be a PCFX or PCECD game, but if not it is assumed AudioDisc /// AudioDisc, @@ -38,7 +39,7 @@ namespace BizHawk.Emulation.DiscSystem SegaSaturn, /// - /// Its not clear whether we can ever have enough info to ID a turboCD disc (we're using hashes) + /// PC Engine CD /// TurboCD, @@ -50,7 +51,47 @@ namespace BizHawk.Emulation.DiscSystem /// /// By NEC. /// - PCFX + PCFX, + + /// + /// By Panasonic + /// + Panasonic3DO, + + /// + /// Philips + /// + CDi, + + /// + /// Nintendo Gamecube + /// + GameCube, + + /// + /// Nintendo Wii + /// + Wii, + + /// + /// SNK NeoGeo + /// + NeoGeoCD, + + /// + /// Bandai Playdia + /// + Playdia, + + /// + /// Either CDTV or CD32 (I havent found a reliable way of distinguishing between them yet -asni) + /// + Amiga, + + /// + /// Sega Dreamcast + /// + Dreamcast } public class DiscIdentifier @@ -76,24 +117,26 @@ namespace BizHawk.Emulation.DiscSystem /// public DiscType DetectDiscType() { - // not fully tested yet + // PCFX & TurboCD sometimes (if not alltimes) have audio on track 1 - run these before the AudioDisc detection (asni) if (DetectPCFX()) return DiscType.PCFX; - //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) - //NOTE: PCE-CD detection goes through here (no good way to detect PCE cd) - if (!_disc.TOC.TOCItems[1].IsData) + if (DetectTurboCD()) + return DiscType.TurboCD; + + //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) + 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 + //asni - this isn't strictly true - SystemIdentifier in volume descriptor has been observed on occasion (see below) if (DetectSegaSaturn()) return DiscType.SegaSaturn; - // not fully tested yet if (DetectMegaCD()) return DiscType.MegaCD; @@ -102,31 +145,79 @@ namespace BizHawk.Emulation.DiscSystem 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!) + if (Detect3DO()) + return DiscType.Panasonic3DO; - var discView = EDiscStreamView.DiscStreamView_Mode1_2048; + if (DetectCDi()) + return DiscType.CDi; + + if (DetectGameCube()) + return DiscType.GameCube; + + if (DetectWii()) + return DiscType.Wii; + + 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) + { + // its much quicker to detect dreamcast from ISO data. Only do this if ISO is not detected + if (DetectDreamcast()) + return DiscType.Dreamcast; + } + + //*** asni - 20171011 - Suggestion: move this to the beginning of the DetectDiscType() method before any longer running lookups? + //its a cheap win for a lot of systems, but ONLY if the iso.Parse() method is quick running (might have to time it) if (isIso) { var appId = System.Text.Encoding.ASCII.GetString(iso.VolumeDescriptors[0].ApplicationIdentifier).TrimEnd('\0', ' '); + var sysId = System.Text.Encoding.ASCII.GetString(iso.VolumeDescriptors[0].SystemIdentifier).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") + //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; + // in case the appId is not set correctly... + if (iso.Root.Children.Where(a => a.Key == "PSP_GAME").FirstOrDefault().Value as ISODirectoryNode != null) + return DiscType.SonyPSP; - return DiscType.UnknownCDFS; - } + if (sysId == "SEGA SEGASATURN") + return DiscType.SegaSaturn; + + if (sysId.Contains("SEGAKATANA")) + return DiscType.Dreamcast; + + if (sysId == "MEGA_CD") + return DiscType.MegaCD; + + if (sysId == "ASAHI-CDV") + return DiscType.Playdia; + + if (sysId == "CDTV" || sysId == "AMIGA") + return DiscType.Amiga; + foreach (var f in iso.Root.Children) + if (f.Key.ToLower().Contains("cd32")) + return DiscType.Amiga; + + // NeoGeoCD Check + var absTxt = iso.Root.Children.Where(a => a.Key.Contains("ABS.TXT")).ToList(); + if (absTxt.Count > 0) + { + if (SectorContains("abstracted by snk", Convert.ToInt32(absTxt.First().Value.Offset))) + return DiscType.NeoGeoCD; + } + + + return DiscType.UnknownCDFS; + } return DiscType.UnknownFormat; } @@ -165,13 +256,87 @@ namespace BizHawk.Emulation.DiscSystem t++) { var track = _disc.TOC.TOCItems[t]; - if (track.IsData && StringAt("PC-FX:Hu_CD-ROM", 0, track.LBA)) + //asni - this search is less specific - turns out there are discs where 'Hu:' is not present + if (track.IsData && SectorContains("pc-fx", track.LBA)) return true; } return false; } - private byte[] ReadSectorCached(int lba) + //asni 20171011 - this ONLY works if a valid cuefile/ccd is passed into DiscIdentifier. + //if an .iso is presented, the internally manufactured cue data does not work - possibly something to do with + //track 01 being Audio. Not tested, but presumably PCFX has the same issue + bool DetectTurboCD() + { + var toc = _disc.TOC; + for (int t = toc.FirstRecordedTrackNumber; + t <= toc.LastRecordedTrackNumber; + t++) + { + var track = _disc.TOC.TOCItems[t]; + //asni - pcfx games also contain the 'PC Engine' string + if ((track.IsData && SectorContains("pc engine", track.LBA + 1) && !SectorContains("pc-fx", track.LBA + 1))) + return true; + } + return false; + } + + bool Detect3DO() + { + var toc = _disc.TOC; + for (int t = toc.FirstRecordedTrackNumber; + t <= toc.LastRecordedTrackNumber; + t++) + { + var track = _disc.TOC.TOCItems[t]; + if (track.IsData && SectorContains("iamaduckiamaduck", track.LBA)) + return true; + } + return false; + } + + //asni - slightly longer running than the others due to its brute-force nature. Should run later in the method + bool DetectDreamcast() + { + for (int i = 0; i < 10000; i++) + { + if (SectorContains("segakatana", i)) + return true; + } + + return false; + } + + bool DetectCDi() + { + return StringAt("CD-RTOS", 8, 16); + } + + bool DetectGameCube() + { + var data = ReadSectorCached(0); + if (data == null) return false; + byte[] magic = data.Skip(28).Take(4).ToArray(); + string hexString = ""; + foreach (var b in magic) + hexString += b.ToString("X2"); + + return hexString == "C2339F3D"; + } + + bool DetectWii() + { + var data = ReadSectorCached(0); + if (data == null) return false; + byte[] magic = data.Skip(24).Take(4).ToArray(); + string hexString = ""; + foreach (var b in magic) + hexString += b.ToString("X2"); + + return hexString == "5D1C9EA3"; + } + + private byte[] ReadSectorCached(int lba) { //read it if we dont have it cached //we wont be caching very much here, it's no big deal @@ -197,5 +362,12 @@ namespace BizHawk.Emulation.DiscSystem Buffer.BlockCopy(data, n, cmp2, 0, cmp.Length); return System.Linq.Enumerable.SequenceEqual(cmp, cmp2); } + + private bool SectorContains(string s, int lba = 0) + { + var data = ReadSectorCached(lba); + if (data == null) return false; + return System.Text.Encoding.ASCII.GetString(data).ToLower().Contains(s.ToLower()); + } } } \ No newline at end of file