using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace com.clusterrr.Famicom.Containers
{
///
/// File header FDS block (block type 3)
///
[StructLayout(LayoutKind.Sequential, Size = 16, Pack = 1)]
public class FdsBlockFileHeader : IFdsBlock, IEquatable
{
///
/// Kind of the file
///
public enum Kind
{
///
/// PRG data
///
Program = 0,
///
/// CHR data
///
Character = 1,
///
/// Nametable data
///
NameTable = 2
}
[MarshalAs(UnmanagedType.U1)]
private readonly byte blockType = 3;
///
/// Valid block type ID
///
public byte ValidTypeID { get => 3; }
///
/// True if block type ID is valid
///
public bool IsValid { get => blockType == 3; }
[MarshalAs(UnmanagedType.U1)]
private byte fileNumber;
///
/// File number
///
public byte FileNumber { get => fileNumber; set => fileNumber = value; }
[MarshalAs(UnmanagedType.U1)]
private byte fileIndicateCode;
///
/// File indicate code (ID specified at disk-read function call)
///
public byte FileIndicateCode { get => fileIndicateCode; set => fileIndicateCode = value; }
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
private byte[] fileName = Encoding.ASCII.GetBytes("FILENAME");
///
/// Filename
///
public string FileName { get => Encoding.ASCII.GetString(fileName).Trim(new char[] { '\0', ' ' }); set => fileName = Encoding.ASCII.GetBytes(value.PadRight(8)).Take(8).ToArray(); }
[MarshalAs(UnmanagedType.U2)]
// the destination address when loading
private ushort fileAddress;
///
/// File address - the destination address when loading
///
public ushort FileAddress { get => fileAddress; set => fileAddress = value; }
[MarshalAs(UnmanagedType.U2)]
private ushort fileSize;
///
/// File size
///
public ushort FileSize { get => fileSize; set => fileSize = value; }
[MarshalAs(UnmanagedType.U1)]
private byte fileKind;
///
/// Kind of the file: program, character or nametable
///
public Kind FileKind { get => (Kind)fileKind; set => fileKind = (byte)value; }
///
/// Length of the block
///
public uint Length { get => 16; }
///
/// Create FdsBlockFileHeader object from raw data
///
/// Data
/// Offset
///
///
public static FdsBlockFileHeader FromBytes(byte[] data, int offset = 0)
{
int rawsize = Marshal.SizeOf(typeof(FdsBlockFileHeader));
if (rawsize > data.Length - offset)
{
if (rawsize <= data.Length - offset + 2)
{
var newRawData = new byte[rawsize];
Array.Copy(data, offset, newRawData, 0, rawsize - 2);
data = newRawData;
offset = 0;
}
else
{
throw new InvalidDataException("Not enough data to fill FdsFileHeaderBlock class. Array length from position: " + (data.Length - offset) + ", struct length: " + rawsize);
}
}
IntPtr buffer = Marshal.AllocHGlobal(rawsize);
Marshal.Copy(data, offset, buffer, rawsize);
FdsBlockFileHeader retobj = (FdsBlockFileHeader)Marshal.PtrToStructure(buffer, typeof(FdsBlockFileHeader));
Marshal.FreeHGlobal(buffer);
return retobj;
}
///
/// Returns raw data
///
/// Data
public byte[] ToBytes()
{
int rawSize = Marshal.SizeOf(this);
IntPtr buffer = Marshal.AllocHGlobal(rawSize);
Marshal.StructureToPtr(this, buffer, false);
byte[] data = new byte[rawSize];
Marshal.Copy(buffer, data, 0, rawSize);
Marshal.FreeHGlobal(buffer);
return data;
}
///
/// String representation
///
/// File name and file kind as string
public override string ToString() => $"{FileName} ({FileKind})";
///
/// Equality comparer
///
/// Other FdsBlockFileHeader object
/// True if objects are equal
public bool Equals(FdsBlockFileHeader other)
{
return Enumerable.SequenceEqual(this.ToBytes(), other.ToBytes());
}
}
}