Allow migrating non-SHA1 hashes on movie import (#3733)
* Allow migrating non-SHA1 hashes on movie import * Show more descriptive message on missing hash * actually return null here * Simplify LsmvImport * add more hash HeaderKeys * remove unnecessary IEmulator argument this could potentially fix bugs even * explicitly cast to ReadOnlySpan
This commit is contained in:
parent
b6f1bae7e1
commit
56ac05f9a3
|
@ -15,6 +15,9 @@ namespace BizHawk.Client.Common
|
|||
public const string StartsFromSaveram = "StartsFromSaveRam";
|
||||
public const string SavestateBinaryBase64Blob = "SavestateBinaryBase64Blob"; // this string will not contain base64: ; it's implicit (this is to avoid another big string op to dice off the base64: substring)
|
||||
public const string Sha1 = "SHA1"; // misleading name; either CRC32, MD5, or SHA1, hex-encoded, unprefixed
|
||||
public const string Sha256 = "SHA256";
|
||||
public const string Md5 = "MD5";
|
||||
public const string Crc32 = "CRC32";
|
||||
public const string FirmwareSha1 = "FirmwareSHA1";
|
||||
public const string Pal = "PAL";
|
||||
public const string BoardName = "BoardName";
|
||||
|
|
|
@ -107,7 +107,7 @@ namespace BizHawk.Client.Common.movie.import
|
|||
|
||||
// 020 16-byte md5sum of the ROM used
|
||||
byte[] md5 = r.ReadBytes(16);
|
||||
Result.Movie.HeaderEntries[Md5] = md5.BytesToHexString().ToLower();
|
||||
Result.Movie.HeaderEntries[HeaderKeys.Md5] = md5.BytesToHexString().ToLower();
|
||||
|
||||
// 030 4-byte little-endian unsigned int: version of the emulator used
|
||||
uint emuVersion = r.ReadUInt32();
|
||||
|
|
|
@ -84,7 +84,7 @@ namespace BizHawk.Client.Common
|
|||
byte[] md5 = DecodeBlob(blob);
|
||||
if (md5 != null && md5.Length == 16)
|
||||
{
|
||||
Result.Movie.HeaderEntries[Md5] = md5.BytesToHexString().ToLower();
|
||||
Result.Movie.HeaderEntries[HeaderKeys.Md5] = md5.BytesToHexString().ToLower();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
|
@ -12,7 +12,6 @@ namespace BizHawk.Client.Common
|
|||
ImportResult Import(
|
||||
IDialogParent dialogParent,
|
||||
IMovieSession session,
|
||||
IEmulator emulator,
|
||||
string path,
|
||||
Config config);
|
||||
}
|
||||
|
@ -20,26 +19,14 @@ namespace BizHawk.Client.Common
|
|||
internal abstract class MovieImporter : IMovieImport
|
||||
{
|
||||
protected const string EmulationOrigin = "emuOrigin";
|
||||
protected const string Md5 = "MD5";
|
||||
protected const string MovieOrigin = "MovieOrigin";
|
||||
|
||||
protected IDialogParent _dialogParent;
|
||||
|
||||
protected void MaybeSetCorePreference(string sysID, string coreName, string fileExt)
|
||||
{
|
||||
if (Config.PreferredCores[sysID] != coreName
|
||||
&& _dialogParent.ModalMessageBox2(
|
||||
$"{fileExt} movies will have a better chance of syncing using the {coreName} core. Change your core preference for {sysID} roms to {coreName} now?",
|
||||
icon: EMsgBoxIcon.Question))
|
||||
{
|
||||
Config.PreferredCores[sysID] = coreName;
|
||||
}
|
||||
}
|
||||
private delegate bool MatchesMovieHash(ReadOnlySpan<byte> romData);
|
||||
|
||||
public ImportResult Import(
|
||||
IDialogParent dialogParent,
|
||||
IMovieSession session,
|
||||
IEmulator emulator,
|
||||
string path,
|
||||
Config config)
|
||||
{
|
||||
|
@ -55,17 +42,69 @@ namespace BizHawk.Client.Common
|
|||
|
||||
var newFileName = $"{SourceFile.FullName}.{Bk2Movie.Extension}";
|
||||
Result.Movie = session.Get(newFileName);
|
||||
Result.Movie.Attach(emulator);
|
||||
RunImport();
|
||||
|
||||
if (!Result.Errors.Any())
|
||||
{
|
||||
if (string.IsNullOrEmpty(Result.Movie.Hash))
|
||||
{
|
||||
string hash = null;
|
||||
// try to generate a matching hash from the original ROM
|
||||
if (Result.Movie.HeaderEntries.TryGetValue(HeaderKeys.Crc32, out string crcHash))
|
||||
{
|
||||
hash = PromptForRom(data => string.Equals(CRC32Checksum.ComputeDigestHex(data), crcHash, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
else if (Result.Movie.HeaderEntries.TryGetValue(HeaderKeys.Md5, out string md5Hash))
|
||||
{
|
||||
hash = PromptForRom(data => string.Equals(MD5Checksum.ComputeDigestHex(data), md5Hash, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
else if (Result.Movie.HeaderEntries.TryGetValue(HeaderKeys.Sha256, out string sha256Hash))
|
||||
{
|
||||
hash = PromptForRom(data => string.Equals(SHA256Checksum.ComputeDigestHex(data), sha256Hash, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
if (hash is not null)
|
||||
Result.Movie.Hash = hash;
|
||||
}
|
||||
|
||||
Result.Movie.Save();
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prompts the user for a ROM file that matches the original movie file's hash
|
||||
/// and returns a SHA1 hash of that ROM file.
|
||||
/// </summary>
|
||||
/// <param name="matchesMovieHash">Function that checks whether the ROM data matches the original hash</param>
|
||||
/// <returns>SHA1 hash of the selected ROM file</returns>
|
||||
private string PromptForRom(MatchesMovieHash matchesMovieHash)
|
||||
{
|
||||
string messageBoxText = "Please select the original ROM to finalize the import process.";
|
||||
while (true)
|
||||
{
|
||||
if (!_dialogParent.ModalMessageBox2(messageBoxText, "ROM required to populate hash", useOKCancel: true))
|
||||
return null;
|
||||
|
||||
var result = _dialogParent.ShowFileOpenDialog(
|
||||
filter: RomLoader.RomFilter,
|
||||
initDir: Config.PathEntries.RomAbsolutePath(Result.Movie.SystemID));
|
||||
if (result is null)
|
||||
return null; // skip hash migration when the dialog was canceled
|
||||
|
||||
using var rom = new HawkFile(result);
|
||||
if (rom.IsArchive) rom.BindFirst();
|
||||
var romData = (ReadOnlySpan<byte>) rom.ReadAllBytes();
|
||||
if (romData.Length % 1024 == 512)
|
||||
romData = romData.Slice(512, romData.Length - 512);
|
||||
if (matchesMovieHash(romData))
|
||||
return SHA1Checksum.ComputeDigestHex(romData);
|
||||
|
||||
messageBoxText = "The selected ROM does not match the movie's hash. Please try again.";
|
||||
}
|
||||
}
|
||||
|
||||
protected Config Config { get; private set; }
|
||||
|
||||
protected ImportResult Result { get; } = new ImportResult();
|
||||
|
|
|
@ -4,6 +4,7 @@ using System.IO.Compression;
|
|||
using System.Linq;
|
||||
using System.Text;
|
||||
using BizHawk.Common.IOExtensions;
|
||||
using BizHawk.Common.StringExtensions;
|
||||
using BizHawk.Emulation.Common;
|
||||
using BizHawk.Emulation.Cores;
|
||||
using BizHawk.Emulation.Cores.Nintendo.BSNES;
|
||||
|
@ -214,10 +215,9 @@ namespace BizHawk.Client.Common.movie.import
|
|||
else if (item.FullName.EndsWith(".sha256"))
|
||||
{
|
||||
using var stream = item.Open();
|
||||
string rom = Encoding.UTF8.GetString(stream.ReadAllBytes()).Trim();
|
||||
int pos = item.FullName.LastIndexOf(".sha256");
|
||||
string name = item.FullName.Substring(0, pos);
|
||||
Result.Movie.HeaderEntries[$"SHA256_{name}"] = rom;
|
||||
string sha256Hash = Encoding.UTF8.GetString(stream.ReadAllBytes()).Trim();
|
||||
string name = item.FullName.RemoveSuffix(".sha256");
|
||||
Result.Movie.HeaderEntries[name is "rom" ? HeaderKeys.Sha256 : $"SHA256_{name}"] = sha256Hash;
|
||||
}
|
||||
else if (item.FullName == "savestate")
|
||||
{
|
||||
|
|
|
@ -92,7 +92,7 @@ namespace BizHawk.Client.Common.movie.import
|
|||
|
||||
// 00e4-00f3: binary: rom MD5 digest
|
||||
byte[] md5 = r.ReadBytes(16);
|
||||
Result.Movie.HeaderEntries[Md5] = md5.BytesToHexString().ToLower();
|
||||
Result.Movie.HeaderEntries[HeaderKeys.Md5] = md5.BytesToHexString().ToLower();
|
||||
|
||||
var ss = new SMS.SmsSyncSettings();
|
||||
var cd = new SMSControllerDeck(ss.Port1, ss.Port2, isGameGear, ss.UseKeyboard);
|
||||
|
|
|
@ -3,7 +3,6 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using BizHawk.Emulation.Common;
|
||||
|
||||
namespace BizHawk.Client.Common
|
||||
{
|
||||
|
@ -36,7 +35,6 @@ namespace BizHawk.Client.Common
|
|||
public static ImportResult ImportFile(
|
||||
IDialogParent dialogParent,
|
||||
IMovieSession session,
|
||||
IEmulator emulator,
|
||||
string path,
|
||||
Config config)
|
||||
{
|
||||
|
@ -51,7 +49,7 @@ namespace BizHawk.Client.Common
|
|||
// Create a new instance of the importer class using the no-argument constructor
|
||||
|
||||
return importerType.GetConstructor(Array.Empty<Type>())?.Invoke(Array.Empty<object>()) is IMovieImport importer
|
||||
? importer.Import(dialogParent, session, emulator, path, config)
|
||||
? importer.Import(dialogParent, session, path, config)
|
||||
: ImportResult.Error($"No importer found for file type {ext}");
|
||||
}
|
||||
|
||||
|
|
|
@ -183,7 +183,7 @@ namespace BizHawk.Client.Common.movie.import
|
|||
// 000 3 bytes of zero padding: 00 00 00 003 4-byte integer: CRC32 of the ROM 007 23-byte ascii string
|
||||
r.ReadBytes(3);
|
||||
int crc32 = r.ReadInt32();
|
||||
Result.Movie.HeaderEntries["CRC32"] = crc32.ToString("X08");
|
||||
Result.Movie.HeaderEntries[HeaderKeys.Crc32] = crc32.ToString("X08");
|
||||
|
||||
// the game name copied from the ROM, truncated to 23 bytes (the game name in the ROM is 21 bytes)
|
||||
string gameName = NullTerminated(Encoding.UTF8.GetString(r.ReadBytes(23)));
|
||||
|
|
|
@ -54,7 +54,11 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
SetMainformMovieInfo();
|
||||
|
||||
if (MovieSession.Movie.Hash != Game.Hash)
|
||||
if (string.IsNullOrEmpty(MovieSession.Movie.Hash))
|
||||
{
|
||||
AddOnScreenMessage("Movie is missing hash, skipping hash check");
|
||||
}
|
||||
else if (MovieSession.Movie.Hash != Game.Hash)
|
||||
{
|
||||
AddOnScreenMessage("Warning: Movie hash does not match the ROM");
|
||||
}
|
||||
|
|
|
@ -4150,7 +4150,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
private void ProcessMovieImport(string fn, bool start)
|
||||
{
|
||||
var result = MovieImport.ImportFile(this, MovieSession, Emulator, fn, Config);
|
||||
var result = MovieImport.ImportFile(this, MovieSession, fn, Config);
|
||||
|
||||
if (result.Errors.Any())
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue