using System;
using System.Linq;
using System.Text;
using System.IO;
using System.Collections.Generic;
namespace BizHawk.Emulation.DiscSystem.CUE
{
///
/// The CUE module user's hook for controlling how cue member file paths get resolved
///
public class CueFileResolver
{
public bool caseSensitive = false;
public bool IsHardcodedResolve { get; private set; }
string baseDir;
///
/// Retrieving the FullName from a FileInfo can be slow (and probably other operations), so this will cache all the needed values
/// TODO - could we treat it like an actual cache and only fill the FullName if it's null?
///
struct MyFileInfo
{
public string FullName;
public FileInfo FileInfo;
}
DirectoryInfo diBasedir;
MyFileInfo[] fisBaseDir;
///
/// sets the base directory and caches the list of files in the directory
///
public void SetBaseDirectory(string baseDir)
{
this.baseDir = baseDir;
diBasedir = new DirectoryInfo(baseDir);
//list all files, so we dont scan repeatedly.
fisBaseDir = MyFileInfosFromFileInfos(diBasedir.GetFiles());
}
///
/// TODO - doesnt seem like we're using this...
///
public void SetHardcodeResolve(IDictionary hardcodes)
{
IsHardcodedResolve = true;
fisBaseDir = new MyFileInfo[hardcodes.Count];
int i = 0;
foreach (var kvp in hardcodes)
{
fisBaseDir[i++] = new MyFileInfo { FullName = kvp.Key, FileInfo = new FileInfo(kvp.Value) };
}
}
MyFileInfo[] MyFileInfosFromFileInfos(FileInfo[] fis)
{
var myfis = new MyFileInfo[fis.Length];
for (int i = 0; i < fis.Length; i++)
{
myfis[i].FileInfo = fis[i];
myfis[i].FullName = fis[i].FullName;
}
return myfis;
}
///
/// Performs cue-intelligent logic to acquire a file requested by the cue.
/// Returns the resulting full path(s).
/// If there are multiple options, it returns them all.
/// Returns the requested path first in the list (if it was found) for more simple use.
/// Kind of an unusual design, I know. Consider them sorted by confidence.
///
public List Resolve(string path)
{
string targetFile = Path.GetFileName(path);
string targetFragment = Path.GetFileNameWithoutExtension(path);
DirectoryInfo di = null;
MyFileInfo[] fileInfos;
if (!string.IsNullOrEmpty(Path.GetDirectoryName(path)))
{
di = new FileInfo(path).Directory;
//fileInfos = di.GetFiles(Path.GetFileNameWithoutExtension(path)); //does this work?
fileInfos = MyFileInfosFromFileInfos(di.GetFiles()); //we (probably) have to enumerate all the files to do a search anyway, so might as well do this
//TODO - dont do the search until a resolve fails
}
else
{
di = diBasedir;
fileInfos = fisBaseDir;
}
var results = new List();
foreach (var fi in fileInfos)
{
var ext = Path.GetExtension(fi.FullName).ToLowerInvariant();
//some choices are always bad: (we're looking for things like .bin and .wav)
//it's a little unclear whether we should go for a whitelist or a blacklist here.
//there's similar numbers of cases either way.
//perhaps we could code both (and prefer choices from the whitelist)
if (ext == ".cue" || ext == ".sbi" || ext == ".ccd" || ext == ".sub")
continue;
//continuing the bad plan: forbid archives (always a wrong choice, not supported anyway)
//we should have a list prioritized by extension and score that way
if (ext == ".7z" || ext == ".rar" || ext == ".zip" || ext == ".bz2" || ext == ".gz")
continue;
string fragment = Path.GetFileNameWithoutExtension(fi.FullName);
//match files with differing extensions
int cmp = string.Compare(fragment, targetFragment, !caseSensitive);
if (cmp != 0)
//match files with another extension added on (likely to be mygame.bin.ecm)
cmp = string.Compare(fragment, targetFile, !caseSensitive);
if (cmp == 0)
{
//take care to add an exact match at the beginning
if (fi.FullName.ToLowerInvariant() == Path.Combine(baseDir,path).ToLowerInvariant())
results.Insert(0, fi.FileInfo);
else
results.Add(fi.FileInfo);
}
}
var ret = new List();
foreach (var fi in results)
ret.Add(fi.FullName);
return ret;
}
}
}