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:
Moritz Bender 2023-08-13 14:11:48 -07:00 committed by GitHub
parent b6f1bae7e1
commit 56ac05f9a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 73 additions and 29 deletions

View File

@ -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";

View File

@ -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();

View File

@ -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
{

View File

@ -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();

View File

@ -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")
{

View File

@ -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);

View File

@ -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}");
}

View File

@ -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)));

View File

@ -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");
}

View File

@ -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())
{