This repository has been archived on 2025-05-24. You can view files and clone it, but cannot push or open issues or pull requests.
Files
RomVault/ROMVault2/DatReaders/DatReader.cs

602 lines
24 KiB
C#

/******************************************************
* ROMVault2 is written by Gordon J. *
* Contact gordon@romvault.com *
* Copyright 2014 *
******************************************************/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Xml;
using ROMVault2.RvDB;
using ROMVault2.Utils;
namespace ROMVault2.DatReaders
{
public static class DatReader
{
private static BackgroundWorker _bgw;
public static RvDir ReadInDatFile(RvDat datFile, BackgroundWorker bgw)
{
_bgw = bgw;
RvDir newDir = new RvDir(FileType.Dir);
string datFullName = datFile.GetData(RvDat.DatData.DatFullName);
System.IO.Stream fs;
int errorCode = IO.FileStream.OpenFileRead(datFullName, out fs);
if (errorCode != 0)
{
_bgw.ReportProgress(0, new bgwShowError(datFullName, errorCode + ": " + new Win32Exception(errorCode).Message));
return null;
}
System.IO.StreamReader myfile = new System.IO.StreamReader(fs, Program.Enc);
string strLine = myfile.ReadLine();
myfile.Close();
fs.Close();
fs.Dispose();
if (strLine == null)
return null;
if (strLine.ToLower().IndexOf("xml", StringComparison.Ordinal) >= 0)
{
if (!ReadXMLDat(ref newDir, datFullName))
return null;
}
else if (strLine.ToLower().IndexOf("clrmamepro", StringComparison.Ordinal) >= 0 || strLine.ToLower().IndexOf("romvault", StringComparison.Ordinal) >= 0 || strLine.ToLower().IndexOf("game", StringComparison.Ordinal) >= 0)
{
if (!DatCmpReader.ReadDat(ref newDir, datFullName))
return null;
}
else if (strLine.ToLower().IndexOf("doscenter", StringComparison.Ordinal) >= 0)
{
if (!DatDOSReader.ReadDat(ref newDir, datFullName))
return null;
}
else
{
_bgw.ReportProgress(0, new bgwShowError(datFullName, "Invalid DAT File"));
return null;
}
if (newDir.Dat == null)
{
_bgw.ReportProgress(0, new bgwShowError(datFullName, "Invalid Header"));
return null;
}
newDir.Dat.AddData(RvDat.DatData.DatFullName, datFullName);
newDir.Dat.TimeStamp = datFile.TimeStamp;
newDir.Dat.Status = DatUpdateStatus.Correct;
DatSetRemoveUnneededDirs(newDir);
DatSetCheckParentSets(newDir);
DatSetRenameAndRemoveDups(newDir);
if (newDir.Dat.GetData(RvDat.DatData.MergeType).ToLower() == "full")
DatSetMergeSets(newDir);
DatSetCheckCollect(newDir);
DatSetCreateSubDirs(newDir);
if (newDir.Dat.GetData(RvDat.DatData.DirSetup).ToLower() == "nogame")
{
DatSetRemoveGameDir(newDir);
}
return newDir;
}
private static bool ReadXMLDat(ref RvDir tDat, string strFilename)
{
System.IO.Stream fs;
int errorCode = IO.FileStream.OpenFileRead(strFilename, out fs);
if (errorCode != 0)
{
_bgw.ReportProgress(0, new bgwShowError(strFilename, errorCode + ": " + new Win32Exception(errorCode).Message));
return false;
}
XmlDocument doc = new XmlDocument { XmlResolver = null };
try
{
doc.Load(fs);
}
catch (Exception e)
{
fs.Close();
fs.Dispose();
_bgw.ReportProgress(0, new bgwShowError(strFilename, string.Format("Error Occured Reading Dat:\r\n{0}\r\n", e.Message)));
return false;
}
fs.Close();
fs.Dispose();
if (doc.DocumentElement == null)
return false;
XmlNode mame = doc.SelectSingleNode("mame");
if (mame != null)
return DatXmlReader.ReadMameDat(ref tDat, doc);
XmlNode mess = doc.SelectSingleNode("mess");
if (mess != null)
return DatMessBIOSXmlReader.ReadMessDat(ref tDat, doc);
if (doc.DocumentElement != null)
{
XmlNode head = doc.DocumentElement.SelectSingleNode("header");
if (head != null)
return DatXmlReader.ReadDat(ref tDat, doc);
}
XmlNodeList headList = doc.SelectNodes("softwarelist");
if (headList != null)
return DatMessXmlReader.ReadDat(ref tDat, doc);
return false;
}
private static void DatSetRenameAndRemoveDups(RvDir tDat)
{
for (int g = 0; g < tDat.ChildCount; g++)
{
RvDir tDir = (RvDir)tDat.Child(g);
if (tDir.Game == null)
{
DatSetRenameAndRemoveDups(tDir);
}
else
{
for (int r = 0; r < tDir.ChildCount - 1; r++)
{
RvFile f0 = (RvFile)tDir.Child(r);
RvFile f1 = (RvFile)tDir.Child(r + 1);
if (f0.Name != f1.Name)
continue;
if (f0.Size != f1.Size || !ArrByte.bCompare(f0.CRC, f1.CRC))
{
tDir.ChildRemove(r + 1); // remove F1
f1.Name = f1.Name + "_" + ArrByte.ToString(f1.CRC); // rename F1;
int pos = tDir.ChildAdd(f1);
if (pos < r)
r = pos;
// if this rename moved the File back up the list, start checking again from that file.
}
else
{
tDir.ChildRemove(r + 1);
}
r--;
}
}
}
}
private static void DatSetRemoveUnneededDirs(RvDir tDat)
{
for (int g = 0; g < tDat.ChildCount; g++)
{
RvDir tGame = (RvDir)tDat.Child(g);
if (tGame.Game == null)
{
DatSetRemoveUnneededDirs(tGame);
}
else
{
for (int r = 0; r < tGame.ChildCount - 1; r++)
{
// first find any directories, zero length with filename ending in a '/'
// there are RvFiles that are really directories (probably inside a zip file)
RvFile f0 = (RvFile)tGame.Child(r);
if (f0.Name.Length == 0)
continue;
if (f0.Name.Substring(f0.Name.Length - 1, 1) != "/")
continue;
// if the next file contains that found directory, then the directory file can be deleted
RvFile f1 = (RvFile)tGame.Child(r + 1);
if (f1.Name.Length <= f0.Name.Length)
continue;
if (f0.Name != f1.Name.Substring(0, f0.Name.Length))
continue;
tGame.ChildRemove(r);
r--;
}
}
}
}
private static void DatSetCheckParentSets(RvDir tDat)
{
// First we are going to try and fix any missing CRC information by checking for roms with the same names
// in Parent and Child sets, and if the same named rom is found and one has a CRC and the other does not
// then we will set the missing CRC by using the CRC in the other set.
// we keep trying to find fixes until no more fixes are found.
// this is need as the first time round a fix could be found in a parent set from one child set.
// then the second time around that fixed parent set could fix another of its childs sets.
for (int g = 0; g < tDat.ChildCount; g++)
{
RvDir mGame = (RvDir)tDat.Child(g);
if (mGame.Game == null)
// this is a directory so recuse into it
DatSetCheckParentSets(mGame);
}
bool fix = true;
while (fix)
{
fix = false;
// loop around every ROM Set looking for fixes.
for (int g = 0; g < tDat.ChildCount; g++)
{
// get a list of that ROM Sets parents.
RvDir mGame = (RvDir)tDat.Child(g);
if (mGame.Game == null)
continue;
List<RvDir> lstParentGames = new List<RvDir>();
FindParentSet(mGame, tDat, ref lstParentGames);
// if this set have parents
if (lstParentGames.Count == 0)
continue;
// now loop every ROM in the current set.
for (int r = 0; r < mGame.ChildCount; r++)
{
// and loop every ROM of every parent set of this current set.
// and see if anything can be fixed.
bool found = false;
// loop the parent sets
foreach (RvDir romofGame in lstParentGames)
{
// loop the ROMs in the parent sets
for (int r1 = 0; r1 < romofGame.ChildCount; r1++)
{
// only find fixes if the Name and the Size of the ROMs are the same
if (mGame.Child(r).Name != romofGame.Child(r1).Name || ((RvFile)mGame.Child(r)).Size != ((RvFile)romofGame.Child(r1)).Size)
continue;
// now check if one of the matching roms has missing or incorrect CRC information
bool b1 = ((RvFile)mGame.Child(r)).CRC == null;
bool b2 = ((RvFile)romofGame.Child(r1)).CRC == null;
// if one has correct information and the other does not, fix the missing one
if (b1 == b2)
continue;
if (b1)
{
((RvFile)mGame.Child(r)).CRC = ((RvFile)romofGame.Child(r1)).CRC;
((RvFile)mGame.Child(r)).Status = "(CRCFound)";
}
else
{
((RvFile)romofGame.Child(r1)).CRC = ((RvFile)mGame.Child(r)).CRC;
((RvFile)romofGame.Child(r1)).Status = "(CRCFound)";
}
// flag that a fix was found so that we will go all the way around again.
fix = true;
found = true;
break;
}
if (found) break;
}
}
}
}
}
private static void DatSetMergeSets(RvDir tDat)
{
for (int g = tDat.ChildCount - 1; g >= 0; g--)
{
RvDir mGame = (RvDir)tDat.Child(g);
if (mGame.Game == null)
{
DatSetMergeSets(mGame);
continue;
}
List<RvDir> lstParentGames = new List<RvDir>();
FindParentSet(mGame, tDat, ref lstParentGames);
while (lstParentGames.Count > 0 && lstParentGames[lstParentGames.Count - 1].Game.GetData(RvGame.GameData.IsBios).ToLower() == "yes")
lstParentGames.RemoveAt(lstParentGames.Count - 1);
if (lstParentGames.Count <= 0) continue;
RvDir romofGame = lstParentGames[lstParentGames.Count - 1];
bool founderror = false;
for (int r = 0; r < mGame.ChildCount; r++)
{
string name = mGame.Child(r).Name;
string mergename = ((RvFile)mGame.Child(r)).Merge;
for (int r1 = 0; r1 < romofGame.ChildCount; r1++)
{
if ((name == romofGame.Child(r1).Name || mergename == romofGame.Child(r1).Name) &&
(ArrByte.iCompare(((RvFile)mGame.Child(r)).CRC, ((RvFile)romofGame.Child(r1)).CRC) != 0 ||
((RvFile)mGame.Child(r)).Size != ((RvFile)romofGame.Child(r1)).Size))
founderror = true;
}
}
if (founderror)
{
mGame.Game.DeleteData(RvGame.GameData.RomOf);
continue;
}
for (int r = 0; r < mGame.ChildCount; r++)
{
string name = mGame.Child(r).Name;
string mergename = ((RvFile)mGame.Child(r)).Merge;
bool found = false;
for (int r1 = 0; r1 < romofGame.ChildCount; r1++)
{
if ((name == romofGame.Child(r1).Name || mergename == romofGame.Child(r1).Name) &&
(ArrByte.iCompare(((RvFile)mGame.Child(r)).CRC, ((RvFile)romofGame.Child(r1)).CRC) == 0 &&
((RvFile)mGame.Child(r)).Size == ((RvFile)romofGame.Child(r1)).Size))
{
found = true;
break;
}
}
if (!found)
romofGame.ChildAdd(mGame.Child(r));
}
tDat.ChildRemove(g);
}
}
private static void DatSetCheckCollect(RvDir tDat)
{
// now look for merged roms.
// check if a rom exists in a parent set where the Name,Size and CRC all match.
for (int g = 0; g < tDat.ChildCount; g++)
{
RvDir mGame = (RvDir)tDat.Child(g);
if (mGame.Game == null)
DatSetCheckCollect(mGame);
else
{
List<RvDir> lstParentGames = new List<RvDir>();
FindParentSet(mGame, tDat, ref lstParentGames);
if (lstParentGames.Count == 0)
{
for (int r = 0; r < mGame.ChildCount; r++)
RomCheckCollect((RvFile)mGame.Child(r), false);
}
else
{
for (int r = 0; r < mGame.ChildCount; r++)
{
bool found = false;
foreach (RvDir romofGame in lstParentGames)
{
for (int r1 = 0; r1 < romofGame.ChildCount; r1++)
{
if (mGame.Child(r).Name != romofGame.Child(r1).Name) continue;
ulong? Size0 = ((RvFile)mGame.Child(r)).Size;
ulong? Size1 = ((RvFile)romofGame.Child(r1)).Size;
if (Size0 != null && Size1 != null && Size0 != Size1) continue;
byte[] CRC0 = ((RvFile)mGame.Child(r)).CRC;
byte[] CRC1 = ((RvFile)romofGame.Child(r1)).CRC;
if (CRC0 != null && CRC1 != null && !ArrByte.bCompare(CRC0, CRC1)) continue;
byte[] SHA0 = ((RvFile)mGame.Child(r)).SHA1;
byte[] SHA1 = ((RvFile)romofGame.Child(r1)).SHA1;
if (SHA0 != null && SHA1 != null && !ArrByte.bCompare(SHA0, SHA1)) continue;
byte[] MD50 = ((RvFile)mGame.Child(r)).MD5;
byte[] MD51 = ((RvFile)romofGame.Child(r1)).MD5;
if (MD50 != null && MD51 != null && !ArrByte.bCompare(MD50, MD51)) continue;
byte[] chdSHA0 = ((RvFile)mGame.Child(r)).SHA1CHD;
byte[] chdSHA1 = ((RvFile)romofGame.Child(r1)).SHA1CHD;
if (chdSHA0 != null && chdSHA1 != null && !ArrByte.bCompare(chdSHA0, chdSHA1)) continue;
byte[] chdMD50 = ((RvFile)mGame.Child(r)).MD5CHD;
byte[] chdMD51 = ((RvFile)romofGame.Child(r1)).MD5CHD;
if (chdMD50 != null && chdMD51 != null && !ArrByte.bCompare(chdMD50, chdMD51)) continue;
found = true;
break;
}
if (found) break;
}
RomCheckCollect((RvFile)mGame.Child(r), found);
}
}
}
}
}
private static void FindParentSet(RvDir searchGame, RvDir parentDir, ref List<RvDir> lstParentGames)
{
if (searchGame.Game == null)
return;
string parentName = searchGame.Game.GetData(RvGame.GameData.RomOf);
if (String.IsNullOrEmpty(parentName) || parentName==searchGame.Name)
parentName = searchGame.Game.GetData(RvGame.GameData.CloneOf);
if (String.IsNullOrEmpty(parentName) || parentName==searchGame.Name)
return;
int intIndex;
int intResult = parentDir.ChildNameSearch(new RvDir(searchGame.FileType) { Name = parentName }, out intIndex);
if (intResult == 0)
{
RvDir parentGame = (RvDir)parentDir.Child(intIndex);
lstParentGames.Add(parentGame);
FindParentSet(parentGame, parentDir, ref lstParentGames);
}
}
/*
* In the mame Dat:
* status="nodump" has a size but no CRC
* status="baddump" has a size and crc
*/
private static void RomCheckCollect(RvFile tRom, bool merge)
{
if (merge)
{
if (string.IsNullOrEmpty(tRom.Merge))
tRom.Merge = "(Auto Merged)";
tRom.DatStatus = DatStatus.InDatMerged;
return;
}
if (!string.IsNullOrEmpty(tRom.Merge))
tRom.Merge = "(No-Merge) " + tRom.Merge;
if (tRom.Status == "nodump")
{
tRom.CRC = null;
tRom.DatStatus = DatStatus.InDatBad;
return;
}
if (ArrByte.bCompare(tRom.CRC, new byte[] { 0, 0, 0, 0 }) && tRom.Size == 0)
{
tRom.DatStatus = DatStatus.InDatCollect;
return;
}
/*
if (ArrByte.bCompare(tRom.CRC, new byte[] { 0, 0, 0, 0 }) || (tRom.CRC.Length != 8))
{
tRom.CRC = null;
tRom.DatStatus = DatStatus.InDatBad;
return;
}
*/
tRom.DatStatus = DatStatus.InDatCollect;
}
private static void DatSetCreateSubDirs(RvDir tDat)
{
for (int g = 0; g < tDat.ChildCount; g++)
{
if (tDat.Child(g).FileType == FileType.Zip)
continue;
RvDir datGame = (RvDir)tDat.Child(g);
// first do a quick check to see if anything needs done.
bool fixNeeded = false;
for (int r = 0; r < datGame.ChildCount; r++)
{
fixNeeded = datGame.Child(r).Name.Contains("/");
if (fixNeeded)
break;
}
// if nothing needs done skip to next game
if (!fixNeeded)
continue;
RvDir fixedGame = new RvDir(FileType.Dir);
while (datGame.ChildCount > 0)
{
RvBase nextChild = datGame.Child(0);
datGame.ChildRemove(0);
if (nextChild.GetType() == typeof(RvFile))
{
RvFile tFile = (RvFile)nextChild;
if (tFile.Name.Contains("/"))
{
RvDir tBase = fixedGame;
Debug.WriteLine("tFile " + tFile.TreeFullName);
while (tFile.Name.Contains("/"))
{
int dirIndex = tFile.Name.IndexOf("/", StringComparison.Ordinal);
string dirName = tFile.Name.Substring(0, dirIndex);
RvDir tDir = new RvDir(FileType.Dir)
{
Name = dirName,
DatStatus = DatStatus.InDatCollect,
Dat = datGame.Dat
};
int index;
if (tBase.ChildNameSearch(tDir, out index) != 0)
tBase.ChildAdd(tDir, index);
tBase = (RvDir)tBase.Child(index);
tFile.Name = tFile.Name.Substring(tFile.Name.IndexOf("/", StringComparison.Ordinal) + 1);
}
tBase.ChildAdd(tFile);
}
else
fixedGame.ChildAdd(nextChild);
}
else
fixedGame.ChildAdd(nextChild);
}
for (int r = 0; r < fixedGame.ChildCount; r++)
datGame.ChildAdd(fixedGame.Child(r), r);
}
}
private static void DatSetRemoveGameDir(RvDir newDir)
{
if (newDir.ChildCount != 1)
return;
RvDir child = newDir.Child(0) as RvDir;
if (child.FileType != FileType.Dir)
return;
if (child.Game == null)
return;
newDir.ChildRemove(0);
newDir.Game = child.Game;
for (int i = 0; i < child.ChildCount; i++)
newDir.ChildAdd(child.Child(i), i);
}
}
}