using System.Buffers.Binary;
using System.Text;
namespace Sunfish.Mac;
/// <summary>
/// Un fichero de Mac clasico separado en sus partes: nombre, Finder Info (tipo, creador,
/// indicadores), fechas, data fork y resource fork. Es lo que comparten MacBinary, BinHex y
/// AppleDouble.
/// </summary>
public sealed class MacFile
{
public string Name { get; set; } = "";
/// <summary>Los 32 bytes de Finder Info (FInfo + FXInfo) tal como los guarda AppleDouble.</summary>
public byte[] FinderInfo { get; set; } = new byte[32];
public byte[] ResourceFork { get; set; } = [];
public DateTime? Created { get; set; }
public DateTime? Modified { get; set; }
public string Type
{
get => Encoding.Latin1.GetString(FinderInfo, 0, 4);
set => Encoding.Latin1.GetBytes(Fit4(value)).CopyTo(FinderInfo, 0);
}
public string Creator
{
get => Encoding.Latin1.GetString(FinderInfo, 4, 4);
set => Encoding.Latin1.GetBytes(Fit4(value)).CopyTo(FinderInfo, 4);
}
public ushort FinderFlags
{
get => BinaryPrimitives.ReadUInt16BigEndian(FinderInfo.AsSpan(8));
set => BinaryPrimitives.WriteUInt16BigEndian(FinderInfo.AsSpan(8), value);
}
/// <summary>Hay algo que el data fork solo no conserva (resource fork, tipo, creador o indicadores).</summary>
public bool HasMacData => ResourceFork.Length > 0 || FinderInfo.Any(b => b != 0);
private static string Fit4(string s) => (s + " ")[..4];
/// <summary>Segundos desde el 1 de enero de 1904 (las fechas del Mac clasico).</summary>
internal static readonly DateTime MacEpoch = new(1904, 1, 1, 0, 0, 0, DateTimeKind.Local);
internal static DateTime? FromMacSeconds(uint seconds) => seconds == 0 ? null : MacEpoch.AddSeconds(seconds);
internal static uint ToMacSeconds(DateTime? time) =>
time is { } t && t > MacEpoch ? (uint)Math.Min(uint.MaxValue, ((t.Kind == DateTimeKind.Utc ? t.ToLocalTime() : t) - MacEpoch).TotalSeconds) : 0;
}
/// <summary>CRC-16/XMODEM (CCITT, polinomio 0x1021, inicio 0): el de MacBinary II/III y BinHex.</summary>
internal static class Crc16
{
public static ushort Compute(ReadOnlySpan<byte> data, ushort crc = 0)
{
foreach (byte b in data)
{
crc ^= (ushort)(b << 8);
for (int i = 0; i < 8; i++)
crc = (ushort)((crc & 0x8000) != 0 ? (crc << 1) ^ 0x1021 : crc << 1);
}
return crc;
}
}