Scoring/Guarded.cs
using System;

namespace SquishyTerry.Scoring;

/// <summary>
/// A number kept obfuscated in memory: stored XOR'd with a fresh random key on every write, plus
/// a checksum. Memory scanners (Cheat Engine and the like) can't find the value by searching for
/// it, and editing the stored bits breaks the checksum, which flags <see cref="Tampered"/>.
/// </summary>
sealed class Guarded
{
	static readonly Random Rng = new();

	int _key, _masked, _check;

	/// <summary>Set once the stored bits have been changed from outside; never clears.</summary>
	public bool Tampered { get; private set; }

	public Guarded( float value = 0f ) => Value = value;

	public float Value
	{
		get
		{
			if ( Checksum( _masked, _key ) != _check )
			{
				Tampered = true;
				return 0f;
			}
			return BitConverter.Int32BitsToSingle( _masked ^ _key );
		}
		set
		{
			_key = Rng.Next( int.MinValue, int.MaxValue );
			_masked = BitConverter.SingleToInt32Bits( value ) ^ _key;
			_check = Checksum( _masked, _key );
		}
	}

	static int Checksum( int masked, int key )
	{
		unchecked
		{
			uint h = (uint)masked * 2654435761u;
			h ^= (uint)key >> 7;
			h *= 2246822519u;
			return (int)(h ^ 0x2F6B3A1Du);
		}
	}
}