From 778aafa4a38edcb6eed4927eb5c235c214332ded Mon Sep 17 00:00:00 2001 From: adelikat Date: Thu, 7 Nov 2024 11:17:53 -0600 Subject: [PATCH 1/7] proof of concept for our movie parser to return hash data. This sketches out the API, still need to actually parse the hashes out of various file formats --- TASVideos.Parsers/Result/ErrorResult.cs | 1 + TASVideos.Parsers/Result/IParseResult.cs | 7 +++++++ TASVideos.Parsers/Result/SuccessResult.cs | 2 ++ tests/TASVideos.Core.Tests/Services/TestParseResult.cs | 1 + tests/TASVideos.Core.Tests/Services/UserFilesTests.cs | 1 + 5 files changed, 12 insertions(+) diff --git a/TASVideos.Parsers/Result/ErrorResult.cs b/TASVideos.Parsers/Result/ErrorResult.cs index 7f708db12..098c8b0c1 100644 --- a/TASVideos.Parsers/Result/ErrorResult.cs +++ b/TASVideos.Parsers/Result/ErrorResult.cs @@ -18,4 +18,5 @@ internal class ErrorResult(string errorMsg) : IParseResult public double? FrameRateOverride => null; public long? CycleCount => null; public string? Annotations => null; + public Dictionary Hashes => []; } diff --git a/TASVideos.Parsers/Result/IParseResult.cs b/TASVideos.Parsers/Result/IParseResult.cs index d5447f45f..4bb5a56db 100644 --- a/TASVideos.Parsers/Result/IParseResult.cs +++ b/TASVideos.Parsers/Result/IParseResult.cs @@ -73,4 +73,11 @@ public interface IParseResult /// Gets the annotations. These can be general comments, or other user entered descriptions supported by the file format. /// string? Annotations { get; } + + Dictionary Hashes { get; } +} + +public enum HashType +{ + Md5, Sha1, Sha256, Crc32 } diff --git a/TASVideos.Parsers/Result/SuccessResult.cs b/TASVideos.Parsers/Result/SuccessResult.cs index 1356d6feb..69b11fdf1 100644 --- a/TASVideos.Parsers/Result/SuccessResult.cs +++ b/TASVideos.Parsers/Result/SuccessResult.cs @@ -20,6 +20,8 @@ internal class SuccessResult(string fileExtension) : IParseResult public string? Annotations { get; internal set; } internal List WarningList { get; } = []; + + public Dictionary Hashes { get; } = []; } internal static class ParseResultExtensions diff --git a/tests/TASVideos.Core.Tests/Services/TestParseResult.cs b/tests/TASVideos.Core.Tests/Services/TestParseResult.cs index a70ba3286..4df3090e1 100644 --- a/tests/TASVideos.Core.Tests/Services/TestParseResult.cs +++ b/tests/TASVideos.Core.Tests/Services/TestParseResult.cs @@ -16,4 +16,5 @@ internal class TestParseResult : IParseResult public double? FrameRateOverride { get; init; } public long? CycleCount { get; init; } public string? Annotations { get; init; } + public Dictionary Hashes { get; init; } = new Dictionary(); } diff --git a/tests/TASVideos.Core.Tests/Services/UserFilesTests.cs b/tests/TASVideos.Core.Tests/Services/UserFilesTests.cs index afdc0eac4..01f0e9822 100644 --- a/tests/TASVideos.Core.Tests/Services/UserFilesTests.cs +++ b/tests/TASVideos.Core.Tests/Services/UserFilesTests.cs @@ -256,5 +256,6 @@ private class TestParseResult : IParseResult public double? FrameRateOverride => null; public long? CycleCount => null; public string? Annotations => null; + public Dictionary Hashes { get; init; } = new Dictionary(); } } From af09e02a27cd2422f17b7e00b1993db01460faa0 Mon Sep 17 00:00:00 2001 From: adelikat Date: Sun, 10 Nov 2024 20:13:43 -0600 Subject: [PATCH 2/7] bk2 hash parsing --- TASVideos.Parsers/Parsers/Bk2.cs | 21 +++++++++++++++++- .../Bk2ParserTests.cs | 9 ++++++++ .../Bk2SampleFiles/hash-crc32-as-sha1.bk2 | Bin 0 -> 443 bytes 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 tests/TASVideos.MovieParsers.Tests/Bk2SampleFiles/hash-crc32-as-sha1.bk2 diff --git a/TASVideos.Parsers/Parsers/Bk2.cs b/TASVideos.Parsers/Parsers/Bk2.cs index bf166153e..237c82175 100644 --- a/TASVideos.Parsers/Parsers/Bk2.cs +++ b/TASVideos.Parsers/Parsers/Bk2.cs @@ -1,4 +1,6 @@ -namespace TASVideos.MovieParsers.Parsers; +using System.Security.Cryptography; + +namespace TASVideos.MovieParsers.Parsers; [FileExtension("bk2")] internal class Bk2 : Parser, IParser @@ -69,6 +71,23 @@ public async Task Parse(Stream file, long length) return Error("Could not determine the System Code"); } + string romHash = header.GetValueFor("SHA1"); + if (string.IsNullOrEmpty(romHash)) + { + romHash = header.GetValueFor("MD5"); + } + + HashType? hashType = romHash.Length switch { + 2 * SHA1.HashSizeInBytes => HashType.Sha1, + 2 * MD5.HashSizeInBytes => HashType.Md5, + 8/* 2 * Crc32.HashLengthInBytes w/ System.IO.Hashing */ => HashType.Crc32, + _ => null + }; + if (hashType is not null) + { + result.Hashes[hashType.Value] = romHash.ToLower(); + } + int? rerecordVal = header.GetPositiveIntFor(Keys.RerecordCount); if (rerecordVal.HasValue) { diff --git a/tests/TASVideos.MovieParsers.Tests/Bk2ParserTests.cs b/tests/TASVideos.MovieParsers.Tests/Bk2ParserTests.cs index c95310330..554b6795c 100644 --- a/tests/TASVideos.MovieParsers.Tests/Bk2ParserTests.cs +++ b/tests/TASVideos.MovieParsers.Tests/Bk2ParserTests.cs @@ -331,4 +331,13 @@ public async Task Comments_ParseAsAnnotations() var lines = result.Annotations.SplitWithEmpty("\n"); Assert.AreEqual(2, lines.Length); } + + [TestMethod] + public async Task Hashes_ParseCrc32AsSha1() + { + var result = await _bk2Parser.Parse(Embedded("hash-crc32-as-sha1.bk2"), EmbeddedLength("hash-crc32-as-sha1.bk2")); + Assert.AreEqual(1, result.Hashes.Count); + Assert.AreEqual(HashType.Crc32, result.Hashes.First().Key); + Assert.AreEqual("26b9ba0c", result.Hashes.First().Value); + } } diff --git a/tests/TASVideos.MovieParsers.Tests/Bk2SampleFiles/hash-crc32-as-sha1.bk2 b/tests/TASVideos.MovieParsers.Tests/Bk2SampleFiles/hash-crc32-as-sha1.bk2 new file mode 100644 index 0000000000000000000000000000000000000000..884db9f11ff286f1453bb1d63777322d47f94f26 GIT binary patch literal 443 zcmWIWW@h1H00G?vS&_kWl@99w*&qxO^+-)jNiEVVsVFH*ElN$!FG_LFFU>1aFy!S5 z$Vn_o%P-1R@N*63J1;atoRGrK;CR1<^^KU zyn@ma1)uzMkg>L>_i{BTh_pS7{UF!0Zh`SOQQbgi5#Ngfg})j-44s+u?M!oj?klfs z`}AbHjONOlQ4c2{;|zMkaf(udw-51?VymPymz2KIejpF)~OnWZhjc=dST> zMyMh<1u+|!pOMWD0qQ{s0c7K?=KWYC%5#OW>JrxiWIDi`l?`MU6A Date: Mon, 11 Nov 2024 08:44:01 -0600 Subject: [PATCH 3/7] more bk2 tests --- .../Bk2ParserTests.cs | 23 +++++++++++++++--- .../Bk2SampleFiles/hash-crc32-as-md5.bk2 | Bin 0 -> 442 bytes .../Bk2SampleFiles/hash-md5-as-sha1.bk2 | Bin 0 -> 467 bytes .../Bk2SampleFiles/hash-md5.bk2 | Bin 0 -> 466 bytes .../Bk2SampleFiles/hash-missing.bk2 | Bin 0 -> 430 bytes .../Bk2SampleFiles/hash-na.bk2 | Bin 0 -> 438 bytes .../Bk2SampleFiles/hash-sha1-as-md5.bk2 | Bin 0 -> 474 bytes .../Bk2SampleFiles/hash-sha1.bk2 | Bin 0 -> 475 bytes 8 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 tests/TASVideos.MovieParsers.Tests/Bk2SampleFiles/hash-crc32-as-md5.bk2 create mode 100644 tests/TASVideos.MovieParsers.Tests/Bk2SampleFiles/hash-md5-as-sha1.bk2 create mode 100644 tests/TASVideos.MovieParsers.Tests/Bk2SampleFiles/hash-md5.bk2 create mode 100644 tests/TASVideos.MovieParsers.Tests/Bk2SampleFiles/hash-missing.bk2 create mode 100644 tests/TASVideos.MovieParsers.Tests/Bk2SampleFiles/hash-na.bk2 create mode 100644 tests/TASVideos.MovieParsers.Tests/Bk2SampleFiles/hash-sha1-as-md5.bk2 create mode 100644 tests/TASVideos.MovieParsers.Tests/Bk2SampleFiles/hash-sha1.bk2 diff --git a/tests/TASVideos.MovieParsers.Tests/Bk2ParserTests.cs b/tests/TASVideos.MovieParsers.Tests/Bk2ParserTests.cs index 554b6795c..a15eccd82 100644 --- a/tests/TASVideos.MovieParsers.Tests/Bk2ParserTests.cs +++ b/tests/TASVideos.MovieParsers.Tests/Bk2ParserTests.cs @@ -333,11 +333,26 @@ public async Task Comments_ParseAsAnnotations() } [TestMethod] - public async Task Hashes_ParseCrc32AsSha1() + [DataRow("hash-crc32-as-sha1", HashType.Crc32, "26b9ba0c")] + [DataRow("hash-crc32-as-md5", HashType.Crc32, "26b9ba0c")] + [DataRow("hash-md5-as-sha1", HashType.Md5, "811b027eaf99c2def7b933c5208636de")] + [DataRow("hash-md5", HashType.Md5, "811b027eaf99c2def7b933c5208636de")] + [DataRow("hash-sha1", HashType.Sha1, "ea343f4e445a9050d4b4fbac2c77d0693b1d0922")] + [DataRow("hash-sha1-as-md5", HashType.Sha1, "ea343f4e445a9050d4b4fbac2c77d0693b1d0922")] + public async Task Hashes(string filename, HashType hashType, string hash) { - var result = await _bk2Parser.Parse(Embedded("hash-crc32-as-sha1.bk2"), EmbeddedLength("hash-crc32-as-sha1.bk2")); + var result = await _bk2Parser.Parse(Embedded(filename + ".bk2"), EmbeddedLength(filename + ".bk2")); Assert.AreEqual(1, result.Hashes.Count); - Assert.AreEqual(HashType.Crc32, result.Hashes.First().Key); - Assert.AreEqual("26b9ba0c", result.Hashes.First().Value); + Assert.AreEqual(hashType, result.Hashes.First().Key); + Assert.AreEqual(hash, result.Hashes.First().Value); + } + + [TestMethod] + [DataRow("hash-missing")] + [DataRow("hash-na")] + public async Task HashesMissing(string filename) + { + var result = await _bk2Parser.Parse(Embedded(filename + ".bk2"), EmbeddedLength(filename + ".bk2")); + Assert.AreEqual(0, result.Hashes.Count); } } diff --git a/tests/TASVideos.MovieParsers.Tests/Bk2SampleFiles/hash-crc32-as-md5.bk2 b/tests/TASVideos.MovieParsers.Tests/Bk2SampleFiles/hash-crc32-as-md5.bk2 new file mode 100644 index 0000000000000000000000000000000000000000..0e1cb3ab03c4d422e21fa40cd05f113fbcbf2bae GIT binary patch literal 442 zcmWIWW@h1H0D(~F?8q-~?(NnFvOyRm>XDk5l3Ju!Qc+TrT9lfcUzFmUUz%5s;O835%jN50s$gW6WSNv`kQ@LsQUs`(gW>Mf8$NDX@fWgzyxBm^3&fsz z1*IhlKKbb&Q*BT0x`ECjz83`we>Hj-Iy33pndbi7S6B)8(&6PK!9!@^S8>~^%E$$_2?s=s+cU!`%E%A?)-fsWBYv+q6D*FyFr-$#HdnTbk z!iKG1F@Zy(jz_Hd;FN$*Oab1EO!i<;Ve_gA&}AT?049+=&IJ`?WRPHxHc8m&YI2(q zst8U&%*N$sWV3^TdQd_D*?6mYKNgAdTw$!b#I*pK4)A7W1KGs{gu8+COAvSeO}`rKAP` z?Ggc+%E54V>J1;atoRGrK;CR1<^^KUyn@ma1)uzMkaf1F_i{BTh_pS7{UF!0Zh`SO zQQbgi5#Ngfg})j-44s+u?M!oj?klfs`}AbHjONOlQ4c2{;|zMkaf(Z?T1e z3eaUBpa3S3Lxc+|#>gPSP`|G4fSbu}MyMh<1u+|!pOMYZ0qQ{s0c7K?=KWYC%5#OW Y>JrxiWIDi`l?`MU6AHX;qy_-3 z5&;^@!Ekr#4Ij6x_zT%U-fSS|1!B*7Wa}h_q zmiWg?Z@2&6we!Ujm3;@8)5CYpJ(Ex%VZ+w1n7|=X$0OE!a7w@@rT}k7CVQ}Fu?2w& z&}AT?049+GgbOOh$RNRx^5o7VHQj7FTDoTn{i&B&Gi&C8POY=$;40*W% zauQ3@@{4j6{9J>9JfJZmKn)xWcc`0c@_g3maHVA`6JyH`>Qj7FTDoTn{i&B&Gi&C8POY=$;40*W% zauQ3@@{4j6{9J>1xq>|$4HfeA69a(8i2yZnFx;Jb!^bTv{z5j8Hyem~f!H&zptMB6 zCqErzqV4IuTn!2$Z4YBV$Th87V7yIKH_%zc_o6`IuSO3;XC{3+)7+o?$}8JGJ=rd! zx$W#0kj^zfZ?&mY~EA>x(oyqz$CJ#xu9Z<3=#~BBZB!nOl~tm6~QTp w*|_|SY_>m84@w9i8*er5$0AXlD~wf_xE3JO0p6@^AiJ1=a5s>C3gR#T0Q$IdbN~PV literal 0 HcmV?d00001 diff --git a/tests/TASVideos.MovieParsers.Tests/Bk2SampleFiles/hash-sha1-as-md5.bk2 b/tests/TASVideos.MovieParsers.Tests/Bk2SampleFiles/hash-sha1-as-md5.bk2 new file mode 100644 index 0000000000000000000000000000000000000000..5fff0d20f8260994e32567080efaec539f6f53fa GIT binary patch literal 474 zcmWIWW@h1H0DnB)FU-9CACPeq@tuKwJ0??zbM5yzcjBz!H}0L zASbaTEx#yN!Ou0Am&@11R3SCd*u*%^B-O;kG||$))F8zq$s{c)G1(~D+&sm=%+ff? zFvY;q$S444r3lb)4u-o^Z}_-n#b3w<@@4}uFA#g?6_l1J_~fU9Y_&bTm#aZRr0rqs z2f3zo3yimk>IOQC_+At!{MG1T=**;VXPWzSUwLKQrzhKGG*{k?dN}zQZ?Hy5x44(A zx#yMQ+-(W3w!}YHdb|DiuAMKQsO&qyoF2Y&?wN!F2^+S4#RLwCIv%m+gHr-NF$H)t zGTDPYjV<fGz_81u%&mFkDbEMg|Fnw>brw?k2Yxp^D%X#B5xCMmD<`s0Sqkkd3#R g_hXSL&lSe1OI!<(=>Tt5HjrIRK)4%7bAlZJ0OJ>dYXATM literal 0 HcmV?d00001 diff --git a/tests/TASVideos.MovieParsers.Tests/Bk2SampleFiles/hash-sha1.bk2 b/tests/TASVideos.MovieParsers.Tests/Bk2SampleFiles/hash-sha1.bk2 new file mode 100644 index 0000000000000000000000000000000000000000..9a020aad3aaf1fb4bfdfd4890feb442c26a43cef GIT binary patch literal 475 zcmWIWW@h1H0D-+O*^yVZq~H1g*&qxO^+-)jNiEVVsVFH*ElN$!FG_LFFU>1aFy!S5 z$Vn_o%P-1R@N*63K zYwmfaICopZt1a=5mELauy=&)-Co206FsFy_oO>prK*ENtUonA0qK-$b`QVg*PfP*c zj7;`mUtJrxiWIDi`l?`MU6A Date: Mon, 11 Nov 2024 13:28:33 -0600 Subject: [PATCH 4/7] ltm hash parsing --- TASVideos.Parsers/Parsers/Ltm.cs | 10 +++++- .../LtmSampleFiles/config.ini | 21 ++++++++++++ .../LtmSampleFiles/hash.ltm | Bin 0 -> 458 bytes .../LtmSampleFiles/invalid-hash.ltm | Bin 0 -> 433 bytes .../LtmSampleFiles/missing-hash.ltm | Bin 0 -> 428 bytes .../LtmSampleFiles/no-hash.ltm | Bin 0 -> 425 bytes .../TASVideos.MovieParsers.Tests/LtmTests.cs | 30 ++++++++++++++++++ 7 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 tests/TASVideos.MovieParsers.Tests/LtmSampleFiles/config.ini create mode 100644 tests/TASVideos.MovieParsers.Tests/LtmSampleFiles/hash.ltm create mode 100644 tests/TASVideos.MovieParsers.Tests/LtmSampleFiles/invalid-hash.ltm create mode 100644 tests/TASVideos.MovieParsers.Tests/LtmSampleFiles/missing-hash.ltm create mode 100644 tests/TASVideos.MovieParsers.Tests/LtmSampleFiles/no-hash.ltm diff --git a/TASVideos.Parsers/Parsers/Ltm.cs b/TASVideos.Parsers/Parsers/Ltm.cs index 52e26b6f7..30160d7ac 100644 --- a/TASVideos.Parsers/Parsers/Ltm.cs +++ b/TASVideos.Parsers/Parsers/Ltm.cs @@ -17,7 +17,7 @@ internal class Ltm : Parser, IParser private const string VariableFramerateHeader = "variable_framerate="; private const string LengthSecondsHeader = "length_sec="; private const string LengthNanosecondsHeader = "length_nsec="; - + private const string Md5 = "md5="; public async Task Parse(Stream file, long length) { var result = new SuccessResult(FileExtension) @@ -94,6 +94,14 @@ public async Task Parse(Stream file, long length) { lengthNanoseconds = ParseDoubleFromConfig(s); } + else if (s.StartsWith(Md5)) + { + var md5 = ParseStringFromConfig(s); + if (md5.Length == 32) + { + result.Hashes.Add(HashType.Md5, md5.ToLower()); + } + } } break; diff --git a/tests/TASVideos.MovieParsers.Tests/LtmSampleFiles/config.ini b/tests/TASVideos.MovieParsers.Tests/LtmSampleFiles/config.ini new file mode 100644 index 000000000..bd80824c8 --- /dev/null +++ b/tests/TASVideos.MovieParsers.Tests/LtmSampleFiles/config.ini @@ -0,0 +1,21 @@ +[General] +authors= +auto_restart=false +frame_count=307 +framerate_den=1 +framerate_num=60 +game_name=flashplayer +initial_time_nsec=0 +initial_time_sec=1 +keyboard_support=true +length_nsec=116666666 +length_sec=5 +libtas_major_version=1 +libtas_minor_version=4 +libtas_patch_version=1 +md5=abcdefab +mouse_support=true +nb_controllers=0 +rerecord_count=0 +savestate_frame_count=307 +variable_framerate=false diff --git a/tests/TASVideos.MovieParsers.Tests/LtmSampleFiles/hash.ltm b/tests/TASVideos.MovieParsers.Tests/LtmSampleFiles/hash.ltm new file mode 100644 index 0000000000000000000000000000000000000000..66be9c6210d8506ccba68d40c686a467a6300c4f GIT binary patch literal 458 zcmV;*0X6;~iwFp>O)_T$0A_4qb7%nVmCufwFc8LlM(R881d$E?vm1#+lx%ybD)nAf zXaXj2Yiwkjs4L~+Yj2uvc6-

1vnX0prhOGklr>FvbQ5${N22zXF;991%jq@qx$j z4CjbYR+Nn4k`jao!Hl6Cqbo4_04~qbJLRi}&{@|n^Ob9$l^?RlipaGxGAI8^1)6yy zG}d%#w@^l*$#|*%c`0K53qmR7yev?Prp4U<*WlYHX`}=FJ%cgqt@Bm7v9*)Qmr!+} zz05iXy{sEMnoyNkJj~;+AnR6|id@Xi*jF59yA*HYrRp^J{h;Aqx-3>%P@wCe(yEtD zg`a9sil_aQ_gf3Dt^GI*HYO3=C^Ie1F6>X+2;nErY7$wd)i!{yd-!2peUQ#88?*Z> zD{~>!vphg(_7~B8%PO(q{Emu`n>D_{;z{ z-+(gpf5FH#`yb(}JnI#5{Cxa^mw35aE)vJ=xiEbR&dkir%=`y_1MhO#PXG`A04uTS AsQ>@~ literal 0 HcmV?d00001 diff --git a/tests/TASVideos.MovieParsers.Tests/LtmSampleFiles/invalid-hash.ltm b/tests/TASVideos.MovieParsers.Tests/LtmSampleFiles/invalid-hash.ltm new file mode 100644 index 0000000000000000000000000000000000000000..655985d9d6cde9562b46b43e03ac5367185fdde7 GIT binary patch literal 433 zcmV;i0Z#rOiwFoEQ8H%)0A_4qb7%nVmBDVKFc5})wzTiS6GS$_AVuQP(;ikU?R~XE z0}P3Gjg4#*ZKXVX$4%3w?V+cnRm*q4__qg!Uo#l2brGU=Hmu_R$S#2(s;Z*#o~Q9d zSu$Cue65sN7m^7ng;FdN> zKt6aFvFY4wW4#vD{hXpAHa*%}p3Ut%)}`RvwBORD9t?!tWZ)J(rz(pYOcQll4cOJ< zxkeH_?H}H@4t(E)d72z05&ewZpxwsZF-*$x$VpA2;zqX-LNmf&=bJtHpdDrRC~JEr z7bkgw*zL}CkG-m)?Rp%b<)fPeKF`CpWQ2|08ACDj1s*+i&i6-uNC>be_mUegecOZA z&>C7e8TMc>doQ$|<`~#T5%P`mzsRZog%U#1KVY5uKUeaa{m;Zz-t_@F{5bp+8?m|B btP%(QQh2}8Tv%9GShyf}KbuzM01yBG`}5m` literal 0 HcmV?d00001 diff --git a/tests/TASVideos.MovieParsers.Tests/LtmSampleFiles/missing-hash.ltm b/tests/TASVideos.MovieParsers.Tests/LtmSampleFiles/missing-hash.ltm new file mode 100644 index 0000000000000000000000000000000000000000..0621f2314f4648c7f336a099e39bc9adf0f2be94 GIT binary patch literal 428 zcmV;d0aN}TiwFpsP%>u(0A_4qb7%nVmBEVJFc5}yHuN3*1VOT`7y>@*sfSYNy%aOC z$B9~5GSWD7ArIftrs-z4hdm{`?D{)c)~}H@{LIMUoF^dTU0l&`64yX7bzSrLz~gvk zy%ws{MX9wcDUQ>Bx0 zV~M}^3-7lcLf^)Dnmi{#VaCj&+tL2CO{w*XvzkPmS+gaG?Fc`8Xb%{o@toa1*|-b2 zI?EGKx4(!Ud);KCpCdj!-)-3dN5Namv7C9pfSnKh$rlzP9N4ww!gIe45DaWBFPx3~ zYpi%}jGJbP;;KmX!vDW2xc{Z*n}F|tGWCC<)h+v9$eWz?204B?ew7=!x!bG~$LzWA W`X#!su&}Uj1^xiyS?2oy5C8zd^4f|3 literal 0 HcmV?d00001 diff --git a/tests/TASVideos.MovieParsers.Tests/LtmSampleFiles/no-hash.ltm b/tests/TASVideos.MovieParsers.Tests/LtmSampleFiles/no-hash.ltm new file mode 100644 index 0000000000000000000000000000000000000000..3e50c8900341c28fc056d9edc397d7fbcfe7a217 GIT binary patch literal 425 zcmV;a0apGWiwFp3P%>u(0A_4qb7%nVm9cKyFc5}03+OxO2@FyaEx>?=OdW~>-HSpv z%O{&HMGEA}XaGNak8zzi$&e*Bj6~lcia(Dc_2Z5N=R5%!@8W{~lDGttsq32C2cFs! z%cW42E=sLsSt%ivl3I&giYqWhf{^Fplk&p?^*#)%{4R8`!7tfUM&#Bw%++hD5HlZy z&b!`h7seTJG2W_wT^C&cwW=yDRl#u_T^4iwUxS}t(P04lD}#x4KEx)y_%>kjg_<60 z#HdcxoL2L*3?L)iA5RK>EJ<7(N z$<;|7fx6vU^x&t6&zE*<{&z>gTgzd*>3{({A2vrzSctG^!;%3nt=dB{u(mvK^60;( z;wCU|oG6NmBGnuBf0gQAYCZ}03@B6m7g}Aj{)N2CS~tky+u^%h$<^IzkvL>8g&WD! TnVFfHnfK)%4x{q-01yBGnOf0f literal 0 HcmV?d00001 diff --git a/tests/TASVideos.MovieParsers.Tests/LtmTests.cs b/tests/TASVideos.MovieParsers.Tests/LtmTests.cs index 531c5940d..7264557f2 100644 --- a/tests/TASVideos.MovieParsers.Tests/LtmTests.cs +++ b/tests/TASVideos.MovieParsers.Tests/LtmTests.cs @@ -125,4 +125,34 @@ public async Task VariableFramerate() Assert.AreEqual(30.002721239119342, result.FrameRateOverride); AssertNoWarningsOrErrors(result); } + + [TestMethod] + public async Task Hash() + { + var result = await _ltmParser.Parse(Embedded("hash.ltm"), EmbeddedLength("hash.ltm")); + Assert.AreEqual(1, result.Hashes.Count); + Assert.AreEqual(HashType.Md5, result.Hashes.First().Key); + Assert.AreEqual("7d66e47fdc0807927c40ce1491c68ad3", result.Hashes.First().Value); + } + + [TestMethod] + public async Task NoHash() + { + var result = await _ltmParser.Parse(Embedded("no-hash.ltm"), EmbeddedLength("no-hash.ltm")); + Assert.AreEqual(0, result.Hashes.Count); + } + + [TestMethod] + public async Task MissingHash() + { + var result = await _ltmParser.Parse(Embedded("missing-hash.ltm"), EmbeddedLength("missing-hash.ltm")); + Assert.AreEqual(0, result.Hashes.Count); + } + + [TestMethod] + public async Task InvalidHash() + { + var result = await _ltmParser.Parse(Embedded("invalid-hash.ltm"), EmbeddedLength("invalid-hash.ltm")); + Assert.AreEqual(0, result.Hashes.Count); + } } From 50360a782d3e4032912100d5fb4753d8db808fe3 Mon Sep 17 00:00:00 2001 From: adelikat Date: Mon, 11 Nov 2024 15:50:26 -0600 Subject: [PATCH 5/7] oops --- .../LtmSampleFiles/config.ini | 21 ------------------- 1 file changed, 21 deletions(-) delete mode 100644 tests/TASVideos.MovieParsers.Tests/LtmSampleFiles/config.ini diff --git a/tests/TASVideos.MovieParsers.Tests/LtmSampleFiles/config.ini b/tests/TASVideos.MovieParsers.Tests/LtmSampleFiles/config.ini deleted file mode 100644 index bd80824c8..000000000 --- a/tests/TASVideos.MovieParsers.Tests/LtmSampleFiles/config.ini +++ /dev/null @@ -1,21 +0,0 @@ -[General] -authors= -auto_restart=false -frame_count=307 -framerate_den=1 -framerate_num=60 -game_name=flashplayer -initial_time_nsec=0 -initial_time_sec=1 -keyboard_support=true -length_nsec=116666666 -length_sec=5 -libtas_major_version=1 -libtas_minor_version=4 -libtas_patch_version=1 -md5=abcdefab -mouse_support=true -nb_controllers=0 -rerecord_count=0 -savestate_frame_count=307 -variable_framerate=false From eb3ab4286720d5b0ad29acf9790917e4ac92a7fb Mon Sep 17 00:00:00 2001 From: adelikat Date: Mon, 11 Nov 2024 16:28:53 -0600 Subject: [PATCH 6/7] fm2 hash parsing --- TASVideos.Parsers/Parsers/Fm2.cs | 39 ++++++++++++++++++- .../Fm2ParserTests.cs | 16 ++++++++ .../Fm2SampleFiles/hash-invalid.fm2 | 2 + .../Fm2SampleFiles/hash-missing.fm2 | 0 .../Fm2SampleFiles/hash.fm2 | 1 + 5 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 tests/TASVideos.MovieParsers.Tests/Fm2SampleFiles/hash-invalid.fm2 create mode 100644 tests/TASVideos.MovieParsers.Tests/Fm2SampleFiles/hash-missing.fm2 create mode 100644 tests/TASVideos.MovieParsers.Tests/Fm2SampleFiles/hash.fm2 diff --git a/TASVideos.Parsers/Parsers/Fm2.cs b/TASVideos.Parsers/Parsers/Fm2.cs index 33fb91233..fbc516578 100644 --- a/TASVideos.Parsers/Parsers/Fm2.cs +++ b/TASVideos.Parsers/Parsers/Fm2.cs @@ -1,4 +1,6 @@ -namespace TASVideos.MovieParsers.Parsers; +using System.Text; + +namespace TASVideos.MovieParsers.Parsers; [FileExtension("fm2")] internal class Fm2 : Parser, IParser @@ -55,9 +57,43 @@ public async Task Parse(Stream file, long length) result.StartType = MovieStartType.Savestate; } + var hashLine = header.GetValueFor(Keys.RomChecksum); + if (!string.IsNullOrWhiteSpace(hashLine)) + { + var hashSplit = hashLine.Split(':'); + var base64Line = hashSplit.Length == 2 ? hashSplit[1] : ""; + if (!string.IsNullOrWhiteSpace(base64Line)) + { + try + { + byte[] data = Convert.FromBase64String(base64Line); + string hash = BytesToHexString(data.AsSpan()); + if (hash.Length == 32) + { + result.Hashes.Add(HashType.Md5, hash.ToLower()); + } + } + catch + { + // Treat an invalid base64 hash as a missing hash + } + } + } + return result; } + private static string BytesToHexString(ReadOnlySpan bytes) + { + StringBuilder sb = new(capacity: 2 * bytes.Length, maxCapacity: 2 * bytes.Length); + foreach (var b in bytes) + { + sb.Append($"{b:X2}"); + } + + return sb.ToString(); + } + private static class Keys { public const string RerecordCount = "rerecordcount"; @@ -66,5 +102,6 @@ private static class Keys public const string Length = "length"; public const string Fds = "fds"; public const string StartsFromSavestate = "savestate"; + public const string RomChecksum = "romChecksum"; } } diff --git a/tests/TASVideos.MovieParsers.Tests/Fm2ParserTests.cs b/tests/TASVideos.MovieParsers.Tests/Fm2ParserTests.cs index 6bad4daa2..40e499128 100644 --- a/tests/TASVideos.MovieParsers.Tests/Fm2ParserTests.cs +++ b/tests/TASVideos.MovieParsers.Tests/Fm2ParserTests.cs @@ -97,4 +97,20 @@ public async Task BinaryWithoutFrameCount() AssertNoWarnings(result); Assert.AreEqual(1, result.Errors.Count()); } + + [TestMethod] + public async Task Hash() + { + var result = await _fm2Parser.Parse(Embedded("hash.fm2"), EmbeddedLength("hash.fm2")); + Assert.AreEqual(1, result.Hashes.Count); + Assert.AreEqual(HashType.Md5, result.Hashes.First().Key); + Assert.AreEqual("e9d82f825725c616b0be66ac85dc1b7a", result.Hashes.First().Value); + } + + [TestMethod] + public async Task InvalidHash() + { + var result = await _fm2Parser.Parse(Embedded("hash-invalid.fm2"), EmbeddedLength("hash-invalid.fm2")); + Assert.AreEqual(0, result.Hashes.Count); + } } diff --git a/tests/TASVideos.MovieParsers.Tests/Fm2SampleFiles/hash-invalid.fm2 b/tests/TASVideos.MovieParsers.Tests/Fm2SampleFiles/hash-invalid.fm2 new file mode 100644 index 000000000..3d6aa99bb --- /dev/null +++ b/tests/TASVideos.MovieParsers.Tests/Fm2SampleFiles/hash-invalid.fm2 @@ -0,0 +1,2 @@ +romChecksum base64:ThisIsNotBase64 + diff --git a/tests/TASVideos.MovieParsers.Tests/Fm2SampleFiles/hash-missing.fm2 b/tests/TASVideos.MovieParsers.Tests/Fm2SampleFiles/hash-missing.fm2 new file mode 100644 index 000000000..e69de29bb diff --git a/tests/TASVideos.MovieParsers.Tests/Fm2SampleFiles/hash.fm2 b/tests/TASVideos.MovieParsers.Tests/Fm2SampleFiles/hash.fm2 new file mode 100644 index 000000000..c268b7ddb --- /dev/null +++ b/tests/TASVideos.MovieParsers.Tests/Fm2SampleFiles/hash.fm2 @@ -0,0 +1 @@ +romChecksum base64:6DgvglcLxhawvMAshDwbeQ== \ No newline at end of file From 453fc503ad7ed7b857186f80fdee2c17fee64bb5 Mon Sep 17 00:00:00 2001 From: adelikat Date: Mon, 11 Nov 2024 16:46:59 -0600 Subject: [PATCH 7/7] another test --- tests/TASVideos.MovieParsers.Tests/Fm2ParserTests.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/TASVideos.MovieParsers.Tests/Fm2ParserTests.cs b/tests/TASVideos.MovieParsers.Tests/Fm2ParserTests.cs index 40e499128..1443e46b9 100644 --- a/tests/TASVideos.MovieParsers.Tests/Fm2ParserTests.cs +++ b/tests/TASVideos.MovieParsers.Tests/Fm2ParserTests.cs @@ -113,4 +113,11 @@ public async Task InvalidHash() var result = await _fm2Parser.Parse(Embedded("hash-invalid.fm2"), EmbeddedLength("hash-invalid.fm2")); Assert.AreEqual(0, result.Hashes.Count); } + + [TestMethod] + public async Task MissingHash() + { + var result = await _fm2Parser.Parse(Embedded("hash-missing.fm2"), EmbeddedLength("hash-missing.fm2")); + Assert.AreEqual(0, result.Hashes.Count); + } }