Newer
Older
Sunfish / src / Sunfish.Core / Mac / MacFile.cs
@Ivan Dominguez Ivan Dominguez 21 hours ago 2 KB 3.0
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;
    }
}