From 617698001a7f2b0ebfd78e32c595f0f7f12e7ffc Mon Sep 17 00:00:00 2001 From: frKieran <172341841+frKieran@users.noreply.github.com> Date: Wed, 7 Oct 2026 23:32:04 -0400 Subject: [PATCH 1/2] support m64 extended format version 4 and keep unmodelled header bytes Mupen 1.5.0-3 (extended version 4) stores cpu_cf and rcp_lag_factor as doubles at 0x030 and 0x038. ToBytes wrote that area as zero padding, so the roundtrip check in LoadBytes threw on every movie recorded with 1.5.0-3. Read and write both fields, and write back the bytes this class doesn't model from the loaded header instead of zeros, so later additions to the format don't break loading. Fixes #66 Co-Authored-By: Claude Opus 5.5 --- STROOP/M64/M64Header.cs | 51 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/STROOP/M64/M64Header.cs b/STROOP/M64/M64Header.cs index 670816cf5..c807521d6 100644 --- a/STROOP/M64/M64Header.cs +++ b/STROOP/M64/M64Header.cs @@ -156,6 +156,41 @@ public uint NumRerecordsHi } } + // 030 8-byte double: CPU counter factor used when recording (extended version 4+) + private double _cpuCf; + + [CategoryAttribute("​​​​​Main"), DisplayName("CPU Counter Factor")] + public double CpuCf + { + get => _cpuCf; + set + { + _cpuCf = value; + NotifyChange(); + } + } + + // 038 8-byte double: RCP lag factor used when recording, 0 if RCP lag emulation was off (extended version 4+) + private double _rcpLagFactor; + + [CategoryAttribute("​​​​​Main"), DisplayName("RCP Lag Factor")] + public double RcpLagFactor + { + get => _rcpLagFactor; + set + { + _rcpLagFactor = value; + NotifyChange(); + } + } + + // The loaded header, so that bytes this class doesn't model (reserved space, fields added by newer + // extended versions) are written back unchanged instead of zeroed + private byte[] _loadedBytes; + + private byte[] UnmodelledBytes(int offset, int length) => + _loadedBytes?.Skip(offset).Take(length).ToArray() ?? new byte[length]; + // 0C4 32-byte ASCII string: internal name of ROM used when recording, directly from ROM private string _romName; @@ -516,7 +551,8 @@ public void LoadBytes(byte[] bytes) { if (bytes.Length != M64Config.HeaderSize) throw new ArgumentOutOfRangeException(); - Signature = BitConverter.ToUInt32(bytes, 0x000); + _loadedBytes = bytes.ToArray(); + Signature =BitConverter.ToUInt32(bytes, 0x000); VersionNumber = BitConverter.ToUInt32(bytes, 0x004); Uid = BitConverter.ToInt32(bytes, 0x008); NumVis = BitConverter.ToInt32(bytes, 0x00C); @@ -546,6 +582,8 @@ public void LoadBytes(byte[] bytes) AuthorshipTag = BitConverter.ToUInt32(bytes, 0x024); BruteforceExtraData = BitConverter.ToUInt32(bytes, 0x028); NumRerecordsHi = BitConverter.ToUInt32(bytes, 0x02C); + CpuCf = BitConverter.ToDouble(bytes, 0x030); + RcpLagFactor = BitConverter.ToDouble(bytes, 0x038); RomName = Encoding.ASCII.GetString(bytes, 0x0C4, 32).TrimEnd('\0'); Crc32 = BitConverter.ToUInt32(bytes, 0x0E4); CountryCode = BitConverter.ToUInt16(bytes, 0x0E8); @@ -574,16 +612,18 @@ public byte[] ToBytes() bytes.AddRange(TypeUtilities.GetBytes(ExtendedFlags)); bytes.AddRange(TypeUtilities.GetBytes(NumInputs)); bytes.AddRange(TypeUtilities.GetBytes((ushort)MovieStartType)); - bytes.AddRange(new byte[2]); + bytes.AddRange(UnmodelledBytes(0x01E, 2)); bytes.AddRange(TypeUtilities.GetBytes(GetControllerFlagsValue())); bytes.AddRange(TypeUtilities.GetBytes(AuthorshipTag)); bytes.AddRange(TypeUtilities.GetBytes(BruteforceExtraData)); bytes.AddRange(TypeUtilities.GetBytes(NumRerecordsHi)); - bytes.AddRange(new byte[148]); + bytes.AddRange(TypeUtilities.GetBytes(CpuCf)); + bytes.AddRange(TypeUtilities.GetBytes(RcpLagFactor)); + bytes.AddRange(UnmodelledBytes(0x040, 132)); bytes.AddRange(TypeUtilities.GetBytes(RomName, 32, Encoding.ASCII)); bytes.AddRange(TypeUtilities.GetBytes(Crc32)); bytes.AddRange(TypeUtilities.GetBytes(CountryCode)); - bytes.AddRange(new byte[56]); + bytes.AddRange(UnmodelledBytes(0x0EA, 56)); bytes.AddRange(TypeUtilities.GetBytes(VideoPlugin, 64, Encoding.ASCII)); bytes.AddRange(TypeUtilities.GetBytes(SoundPlugin, 64, Encoding.ASCII)); bytes.AddRange(TypeUtilities.GetBytes(InputPlugin, 64, Encoding.ASCII)); @@ -656,6 +696,9 @@ public void Clear() AuthorshipTag = 0; BruteforceExtraData = 0; NumRerecordsHi = 0; + CpuCf = 0; + RcpLagFactor = 0; + _loadedBytes = null; VideoPlugin = null; SoundPlugin = null; InputPlugin = null; From 92a732355cadbca9f5febbc45d2ce9ca5d7faa9d Mon Sep 17 00:00:00 2001 From: FramePerfection <1663221+FramePerfection@users.noreply.github.com> Date: Fri, 16 Oct 2026 10:58:46 +0200 Subject: [PATCH 2/2] bump version --- STROOP/Forms/StroopMainForm.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/STROOP/Forms/StroopMainForm.cs b/STROOP/Forms/StroopMainForm.cs index 334716cbd..92529fc69 100644 --- a/STROOP/Forms/StroopMainForm.cs +++ b/STROOP/Forms/StroopMainForm.cs @@ -26,7 +26,7 @@ namespace STROOP public partial class StroopMainForm : Form { // STROOP VERSION NAME - const string _version = "Refactor 0.9.1"; + const string _version = "Refactor 0.9.2"; public event Action Updating;