using System; using System.Collections.Generic; // Required for List using System.IO; // For File operations using System.Runtime.InteropServices; // Required for StructLayout and Marshal namespace Zorro { /// /// Provides generic methods for loading and saving arrays of structures /// from/to binary files using marshalling. /// This class is intended for internal use within this assembly. /// internal static class BinaryFileHandler { public static void SaveArrayAsBlock(string filePath, T[] data) where T : struct { if (data == null || data.Length == 0) { File.WriteAllBytes(filePath, Array.Empty()); return; } int structSize = Marshal.SizeOf(typeof(T)); byte[] byteArray = new byte[data.Length * structSize]; IntPtr buffer = Marshal.AllocHGlobal(structSize); try { for (int i = 0; i < data.Length; i++) { Marshal.StructureToPtr(data[i], buffer, false); Marshal.Copy(buffer, byteArray, i * structSize, structSize); } File.WriteAllBytes(filePath, byteArray); } finally { Marshal.FreeHGlobal(buffer); } } public static T[]? LoadArrayAsBlock(string filePath) where T : struct { if (!File.Exists(filePath)) { return null; } byte[] byteArray = File.ReadAllBytes(filePath); if (byteArray.Length == 0) { return Array.Empty(); } int structSize = Marshal.SizeOf(typeof(T)); if (byteArray.Length % structSize != 0) { throw new ArgumentException($"File size ({byteArray.Length} bytes) is not a valid multiple of the structure size ({structSize} bytes) for type {typeof(T).Name}. The file may be corrupted or not in the expected format."); } int itemCount = byteArray.Length / structSize; T[] dataArray = new T[itemCount]; IntPtr buffer = Marshal.AllocHGlobal(structSize); try { for (int i = 0; i < itemCount; i++) { Marshal.Copy(byteArray, i * structSize, buffer, structSize); dataArray[i] = Marshal.PtrToStructure(buffer); } } finally { Marshal.FreeHGlobal(buffer); } return dataArray; } } [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)] public struct ZorroT1 { public double OADate; public float Value; /// /// Returns the time as a UTC DateTime object. /// Note: DateTime.FromOADate returns a DateTime with Kind=Unspecified. /// We specify it as UTC as Zorro DATEs are GMT/UTC. /// public DateTime TimeUtc => DateTime.SpecifyKind(DateTime.FromOADate(OADate), DateTimeKind.Utc); public ZorroT1(double oaDate, float value) { OADate = oaDate; Value = value; } public ZorroT1(DateTime dateTime, float value) { // Ensure the DateTime is converted to its UTC equivalent for OADate OADate = dateTime.ToUniversalTime().ToOADate(); Value = value; } public static void SaveToFile(string filePath, ZorroT1[] data) => BinaryFileHandler.SaveArrayAsBlock(filePath, data); public static ZorroT1[]? LoadFromFile(string filePath) => BinaryFileHandler.LoadArrayAsBlock(filePath); } [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)] public struct ZorroT2 { public double OADate; public float Price; public float Volume; public DateTime TimeUtc => DateTime.SpecifyKind(DateTime.FromOADate(OADate), DateTimeKind.Utc); public ZorroT2(double oaDate, float price, float volume) { OADate = oaDate; Price = price; Volume = volume; } public ZorroT2(DateTime dateTime, float price, float volume) { OADate = dateTime.ToUniversalTime().ToOADate(); Price = price; Volume = volume; } public static void SaveToFile(string filePath, ZorroT2[] data) => BinaryFileHandler.SaveArrayAsBlock(filePath, data); public static ZorroT2[]? LoadFromFile(string filePath) => BinaryFileHandler.LoadArrayAsBlock(filePath); } [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)] public struct ZorroT6 { public double OADate; public float High; public float Low; public float Open; public float Close; public float AdditionalValue1; public float AdditionalValue2; public DateTime TimeUtc => DateTime.SpecifyKind(DateTime.FromOADate(OADate), DateTimeKind.Utc); public ZorroT6(double oaDate, float high, float low, float open, float close, float additionalValue1, float additionalValue2) { OADate = oaDate; High = high; Low = low; Open = open; Close = close; AdditionalValue1 = additionalValue1; AdditionalValue2 = additionalValue2; } public ZorroT6(DateTime dateTime, float high, float low, float open, float close, float additionalValue1, float additionalValue2) { OADate = dateTime.ToUniversalTime().ToOADate(); High = high; Low = low; Open = open; Close = close; AdditionalValue1 = additionalValue1; AdditionalValue2 = additionalValue2; } public static void SaveToFile(string filePath, ZorroT6[] data) => BinaryFileHandler.SaveArrayAsBlock(filePath, data); public static ZorroT6[]? LoadFromFile(string filePath) => BinaryFileHandler.LoadArrayAsBlock(filePath); } // --- Builder Classes for Incremental Array Construction --- public class ZorroT1ArrayBuilder { private readonly List _items; public ZorroT1ArrayBuilder(int initialCapacity = 0) { _items = initialCapacity > 0 ? new List(initialCapacity) : new List(); } public ZorroT1ArrayBuilder Add(DateTime time, float value) { // Constructor of ZorroT1 now handles UTC conversion _items.Add(new ZorroT1(time, value)); return this; } public ZorroT1ArrayBuilder Add(double oaDate, float value) { _items.Add(new ZorroT1(oaDate, value)); return this; } public ZorroT1ArrayBuilder Add(ZorroT1 item) { _items.Add(item); return this; } public ZorroT1[] ToArray() => _items.ToArray(); public void Clear() => _items.Clear(); public int Count => _items.Count; public void SaveToFile(string filePath) { ZorroT1.SaveToFile(filePath, _items.ToArray()); } } public class ZorroT2ArrayBuilder { private readonly List _items; public ZorroT2ArrayBuilder(int initialCapacity = 0) { _items = initialCapacity > 0 ? new List(initialCapacity) : new List(); } public ZorroT2ArrayBuilder Add(DateTime time, float price, float volume) { _items.Add(new ZorroT2(time, price, volume)); return this; } public ZorroT2ArrayBuilder Add(double oaDate, float price, float volume) { _items.Add(new ZorroT2(oaDate, price, volume)); return this; } public ZorroT2ArrayBuilder Add(ZorroT2 item) { _items.Add(item); return this; } public ZorroT2[] ToArray() => _items.ToArray(); public void Clear() => _items.Clear(); public int Count => _items.Count; public void SaveToFile(string filePath) { ZorroT2.SaveToFile(filePath, _items.ToArray()); } } public class ZorroT6ArrayBuilder { private readonly List _items; public ZorroT6ArrayBuilder(int initialCapacity = 0) { _items = initialCapacity > 0 ? new List(initialCapacity) : new List(); } public ZorroT6ArrayBuilder Add(DateTime time, float high, float low, float open, float close, float additionalValue1, float additionalValue2) { _items.Add(new ZorroT6(time, high, low, open, close, additionalValue1, additionalValue2)); return this; } public ZorroT6ArrayBuilder Add(double oaDate, float high, float low, float open, float close, float additionalValue1, float additionalValue2) { _items.Add(new ZorroT6(oaDate, high, low, open, close, additionalValue1, additionalValue2)); return this; } public ZorroT6ArrayBuilder Add(ZorroT6 item) { _items.Add(item); return this; } public ZorroT6[] ToArray() => _items.ToArray(); public void Clear() => _items.Clear(); public int Count => _items.Count; public void SaveToFile(string filePath) { ZorroT6.SaveToFile(filePath, _items.ToArray()); } } }