BizHawk/BizHawk.Client.EmuHawk/FileLoader.cs

360 lines
11 KiB
C#

using System;
using System.IO;
using System.Windows.Forms;
using System.Linq;
using System.Collections.Generic;
using BizHawk.Common;
using BizHawk.Emulation.Common.IEmulatorExtensions;
using BizHawk.Emulation.Cores.PCEngine;
using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk
{
partial class MainForm
{
private enum LoadOrdering
{
ROM,
STATE,
WATCH,
CDLFILE,
LUASESSION,
LUASCRIPT,
CHEAT,
MOVIEFILE,
LEGACYMOVIEFILE
}
public struct FileInformation
{
public string DirectoryName { get; }
public string FileName { get; }
public string ArchiveName { get; }
public FileInformation(string directory, string file, string archive)
{
DirectoryName = directory;
FileName = file;
ArchiveName = archive;
}
}
// This is the list from MainForm->RomFilter()'s non-developer build. It needs to be kept up-to-date when new cores are added.
// adelikat: This is annoying and bad. Maybe we could generate RomFilter from this property?
private string[] KnownRomExtensions
{
get
{
if (VersionInfo.DeveloperBuild)
{
return new[]
{
".NES", ".FDS", ".UNF", ".SMS", ".GG", ".SG", ".GB", ".GBC", ".GBA", ".PCE", ".SGX", ".BIN", ".SMD", ".GEN", ".MD", ".SMC", ".SFC", ".A26", ".A78", ".LNX", ".COL", ".ROM", ".M3U", ".CUE", ".CCD", ".SGB", ".Z64", ".V64", ".N64", ".WS", ".WSC", ".XML", ".DSK", ".DO", ".PO", ".PSF", ".MINIPSF", ".NSF",
".EXE", ".PRG", ".D64", "*G64", ".CRT", ".TAP", ".32X", ".MDS"
};
}
return new[] { ".NES", ".FDS", ".UNF", ".SMS", ".GG", ".SG", ".GB", ".GBC", ".GBA", ".PCE", ".SGX", ".BIN", ".SMD", ".GEN", ".MD", ".SMC", ".SFC", ".A26", ".A78", ".LNX", ".COL", ".ROM", ".M3U", ".CUE", ".CCD", ".SGB", ".Z64", ".V64", ".N64", ".WS", ".WSC", ".XML", ".DSK", ".DO", ".PO", ".PSF", ".MINIPSF", ".NSF", ".32X", ".MDS" };
}
}
private readonly string[] _nonArchive = { ".ISO", ".CUE", ".CCD" };
#region Loaders
private void _LoadCDL(string filename, string archive = null)
{
if (GlobalWin.Tools.IsAvailable<CDL>())
{
CDL cdl = GlobalWin.Tools.Load<CDL>();
cdl.LoadFile(filename);
}
}
private void _LoadCheats(string filename, string archive = null)
{
Global.CheatList.Load(filename, false);
GlobalWin.Tools.Load<Cheats>();
}
private void _LoadLegacyMovie(string filename, string archive = null)
{
if (Global.Emulator.IsNull())
{
OpenRom();
}
if (Global.Emulator.IsNull())
{
return;
}
// tries to open a legacy movie format by importing it
string errorMsg;
string warningMsg;
var movie = MovieImport.ImportFile(filename, out errorMsg, out warningMsg);
if (!string.IsNullOrEmpty(errorMsg))
{
MessageBox.Show(errorMsg, "Conversion error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
else
{
// fix movie extension to something palatable for these purposes.
// for instance, something which doesnt clobber movies you already may have had.
// i'm evenly torn between this, and a file in %TEMP%, but since we dont really have a way to clean up this tempfile, i choose this:
StartNewMovie(movie, false);
}
GlobalWin.OSD.AddMessage(warningMsg);
}
private void _LoadLuaFile(string filename, string archive = null)
{
OpenLuaConsole();
if (GlobalWin.Tools.Has<LuaConsole>())
{
GlobalWin.Tools.LuaConsole.LoadLuaFile(filename);
}
}
private void _LoadLuaSession(string filename, string archive = null)
{
OpenLuaConsole();
if (GlobalWin.Tools.Has<LuaConsole>())
{
GlobalWin.Tools.LuaConsole.LoadLuaSession(filename);
}
}
private void _LoadMovie(string filename, string archive = null)
{
if (Global.Emulator.IsNull())
{
OpenRom();
}
if (Global.Emulator.IsNull())
{
return;
}
StartNewMovie(MovieService.Get(filename), false);
}
private void _LoadRom(string filename, string archive = null)
{
var args = new LoadRomArgs();
args.OpenAdvanced = new OpenAdvanced_OpenRom { Path = filename };
LoadRom(filename, args);
}
private void _LoadState(string filename, string archive = null)
{
LoadState(filename, Path.GetFileName(filename));
}
private void _LoadWatch(string filename, string archive = null)
{
GlobalWin.Tools.LoadRamWatch(true);
(GlobalWin.Tools.Get<RamWatch>() as RamWatch).LoadWatchFile(new FileInfo(filename), false);
}
#endregion
private void ProcessFileList(IEnumerable<string> fileList, ref Dictionary<LoadOrdering, List<FileInformation>> sortedFiles, string archive = null)
{
foreach (string file in fileList)
{
var ext = Path.GetExtension(file).ToUpper() ?? "";
FileInformation fileInformation = new FileInformation(Path.GetDirectoryName(file), Path.GetFileName(file), archive);
switch (ext)
{
case ".LUA":
sortedFiles[LoadOrdering.LUASCRIPT].Add(fileInformation);
break;
case ".LUASES":
sortedFiles[LoadOrdering.LUASESSION].Add(fileInformation);
break;
case ".STATE":
sortedFiles[LoadOrdering.STATE].Add(fileInformation);
break;
case ".CHT":
sortedFiles[LoadOrdering.CHEAT].Add(fileInformation);
break;
case ".WCH":
sortedFiles[LoadOrdering.WATCH].Add(fileInformation);
break;
case ".CDL":
sortedFiles[LoadOrdering.CDLFILE].Add(fileInformation);
break;
default:
if (MovieService.IsValidMovieExtension(ext))
{
sortedFiles[LoadOrdering.MOVIEFILE].Add(fileInformation);
}
else if (MovieImport.IsValidMovieExtension(ext))
{
sortedFiles[LoadOrdering.LEGACYMOVIEFILE].Add(fileInformation);
}
else if (KnownRomExtensions.Contains(ext))
{
if (string.IsNullOrEmpty(archive) || !_nonArchive.Contains(ext))
{
sortedFiles[LoadOrdering.ROM].Add(fileInformation);
}
}
else
{
/* Because the existing behaviour for archives is to try loading
* ROMs out of them, that is exactly what we are going to continue
* to do at present. Ideally, the archive should be scanned and
* relevant files should be extracted, but see the note below for
* further details.
*/
int offset = 0;
bool executable = false;
var archiveHandler = new SevenZipSharpArchiveHandler();
if (string.IsNullOrEmpty(archive) && archiveHandler.CheckSignature(file, out offset, out executable))
{
sortedFiles[LoadOrdering.ROM].Add(fileInformation);
}
else
{
// adelikat: adding this hack to restore the default behavior that unrecognized files are treated like roms
sortedFiles[LoadOrdering.ROM].Add(fileInformation);
}
/*
* This is where handling archives would go.
* Right now, that's going to be a HUGE hassle, because of the problem with
* saving things into the archive (no) and with everything requiring filenames
* and not streams (also no), so for the purposes of making drag/drop more robust,
* I am not building this out just yet.
* -- Adam Michaud (Invariel)
int offset = 0;
bool executable = false;
var archiveHandler = new SevenZipSharpArchiveHandler();
// Not going to process nested archives at the moment.
if (String.IsNullOrEmpty (archive) && archiveHandler.CheckSignature(file, out offset, out executable))
{
List<string> fileNames = new List<string>();
var openedArchive = archiveHandler.Construct (file);
foreach (BizHawk.Common.HawkFileArchiveItem item in openedArchive.Scan ())
fileNames.Add(item.Name);
ProcessFileList(fileNames.ToArray(), ref sortedFiles, file);
openedArchive.Dispose();
}
archiveHandler.Dispose();
*/
}
break;
}
}
}
private void _FormDragDrop_internal(object sender, DragEventArgs e)
{
/*
* Refactor, moving the loading of particular files into separate functions that can
* then be used by this code, and loading individual files through the file dialogue.
*
* Step 1:
* Build a dictionary of relevant files from everything that was dragged and dropped.
* This includes peeking into all relevant archives and using their files.
*
* Step 2:
* Perhaps ask the user which of a particular file type they want to use.
* Example: rom1.nes, rom2.smc, rom3.cue are drag-dropped, ask the user which they want to use.
*
* Step 3:
* Load all of the relevant files, in priority order:
* 1) The ROM
* 2) State
* 3) Watch files
* 4) Code Data Logger (CDL)
* 5) LUA sessions
* 6) LUA scripts
* 7) Cheat files
* 8) Movie Playback Files
*
* Bonus:
* Make that order easy to change in the code, heavily suggesting ROM and playback as first and last respectively.
*/
var filePaths = (string[])e.Data.GetData(DataFormats.FileDrop);
Dictionary<LoadOrdering, List<FileInformation>> sortedFiles = new Dictionary<LoadOrdering, List<FileInformation>>();
// Initialize the dictionary's lists.
foreach (LoadOrdering value in Enum.GetValues(typeof(LoadOrdering)))
{
sortedFiles.Add(value, new List<FileInformation>());
}
ProcessFileList(HawkFile.Util_ResolveLinks(filePaths), ref sortedFiles, null);
// For each of the different types of item, if there are no items of that type, skip them.
// If there is exactly one of that type of item, load it.
// If there is more than one, ask.
foreach (LoadOrdering value in Enum.GetValues(typeof(LoadOrdering)))
{
switch (sortedFiles[value].Count)
{
case 0:
break;
case 1:
FileInformation fileInformation = sortedFiles[value].First<FileInformation>();
string filename = Path.Combine(new string[] { fileInformation.DirectoryName, fileInformation.FileName });
switch (value)
{
case LoadOrdering.ROM:
_LoadRom(filename, fileInformation.ArchiveName);
break;
case LoadOrdering.STATE:
_LoadState(filename, fileInformation.ArchiveName);
break;
case LoadOrdering.WATCH:
_LoadWatch(filename, fileInformation.ArchiveName);
break;
case LoadOrdering.CDLFILE:
_LoadCDL(filename, fileInformation.ArchiveName);
break;
case LoadOrdering.LUASESSION:
_LoadLuaSession(filename, fileInformation.ArchiveName);
break;
case LoadOrdering.LUASCRIPT:
_LoadLuaFile(filename, fileInformation.ArchiveName);
break;
case LoadOrdering.CHEAT:
_LoadCheats(filename, fileInformation.ArchiveName);
break;
case LoadOrdering.MOVIEFILE:
case LoadOrdering.LEGACYMOVIEFILE:
// I don't really like this hack, but for now, we only want to load one movie file.
if (sortedFiles[LoadOrdering.MOVIEFILE].Count + sortedFiles[LoadOrdering.LEGACYMOVIEFILE].Count > 1)
break;
if (value == LoadOrdering.MOVIEFILE)
_LoadMovie(filename, fileInformation.ArchiveName);
else
_LoadLegacyMovie(filename, fileInformation.ArchiveName);
break;
}
break;
default:
break;
}
}
}
}
}