using SanAndreasUnity.Utilities; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; namespace SanAndreasUnity.Importing.Vehicles { public enum DriveType { Forward, Rear, FourWheel } public enum EngineType { Petrol, Diesel, Electric } public static class Handling { #region Reflection [Flags] private enum ColumnFlags { Default = 0, HexNumber = 1 } [AttributeUsage(AttributeTargets.Property)] private class ColumnAttribute : Attribute { public readonly int Value; public readonly ColumnFlags Flags; public bool IsHexNumber { get { return (Flags & ColumnFlags.HexNumber) == ColumnFlags.HexNumber; } } public ColumnAttribute(int value, ColumnFlags flags = ColumnFlags.Default) { Value = value; Flags = flags; } } [AttributeUsage(AttributeTargets.Class)] private class PrefixAttribute : Attribute { public readonly char Value; public PrefixAttribute(char value) { Value = value; } } public interface IEntry { } public interface IVehicleEntry : IEntry { string Id { get; set; } } public abstract class Entry : IEntry where TEntry : Entry { private static Dictionary> _sParsers; private static void GenerateParsers() { _sParsers = new Dictionary>(); var type = typeof(TEntry); foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) { var attrib = (ColumnAttribute)prop.GetCustomAttributes(typeof(ColumnAttribute), true).FirstOrDefault(); if (attrib == null) continue; var set = prop.GetSetMethod(true); if (set == null) throw new Exception("Set method is inaccessible"); _sParsers.Add(attrib.Value, (s, instance) => { set.Invoke(instance, new object[] { prop.PropertyType != typeof(string) ? Convert.ChangeType(attrib.IsHexNumber ? s.FromHex(prop.PropertyType, CultureInfo.InvariantCulture) : s, prop.PropertyType, CultureInfo.InvariantCulture) : s }); }); } } protected Entry(string line) { if (_sParsers == null) GenerateParsers(); var split = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); var self = (TEntry)this; for (var i = 0; i < split.Length; ++i) if (_sParsers.ContainsKey(i)) _sParsers[i](split[i], self); } } private static Dictionary> _sCtors; private static IEnumerable _sEntries; private static void GenerateCtors() { _sCtors = new Dictionary>(); foreach (var type in typeof(Handling).GetNestedTypes()) { if (type.IsAbstract) continue; if (type.IsInterface) continue; if (!typeof(IEntry).IsAssignableFrom(type)) continue; var attrib = (PrefixAttribute)type.GetCustomAttributes(typeof(PrefixAttribute), true).FirstOrDefault(); var prefix = attrib != null ? attrib.Value : '\0'; _sCtors.Add(prefix, s => { return (IEntry)Activator.CreateInstance(type, new object[] { s }); }); } } #endregion Reflection public class Car : Entry, IVehicleEntry { [Column(0)] public string Id { get; set; } [Column(1)] public float Mass { get; set; } [Column(2)] public float TurnMass { get; set; } [Column(3)] public float Drag { get; set; } [Column(4)] private float _CentreOfMassX { get; set; } [Column(5)] private float _CentreOfMassZ { get; set; } [Column(6)] private float _CentreOfMassY { get; set; } [Column(7)] public int BoyantPercent { get; set; } [Column(8)] public float TractionMult { get; set; } [Column(9)] public float TractionLoss { get; set; } [Column(10)] public float TractionBias { get; set; } [Column(11)] public int TransmissionGears { get; set; } [Column(12)] public float TransmissionMaxVel { get; set; } [Column(13)] public float TransmissionEngineAccel { get; set; } [Column(14)] public float TransmissionEngineInertia { get; set; } [Column(15)] private string _TransmissionDriveType { get; set; } [Column(16)] private string _TransmissionEngineType { get; set; } [Column(17)] public float BrakeDecel { get; set; } [Column(18)] public float BrakeBias { get; set; } [Column(19)] private int _AntiLockBrakes { get; set; } [Column(20)] public float SteeringLock { get; set; } [Column(21)] public float SuspensionForceLevel { get; set; } [Column(22)] public float SuspensionDampingLevel { get; set; } [Column(23)] public float SuspensionHighSpeedDamping { get; set; } [Column(24)] public float SuspensionUpperLimit { get; set; } [Column(25)] public float SuspensionLowerLimit { get; set; } [Column(26)] public float SuspensionFrontRearBias { get; set; } [Column(27)] public float SuspensionAntiDriveMult { get; set; } [Column(28)] public float SeatOffsetDistance { get; set; } [Column(29)] public float CollisionDamageMult { get; set; } [Column(30)] public int MonetaryValue { get; set; } [Column(31, ColumnFlags.HexNumber)] private ulong _ModelFlags { get; set; } [Column(32, ColumnFlags.HexNumber)] private ulong _HandlingFlags { get; set; } [Column(33)] private int _FrontLights { get; set; } [Column(34)] private int _RearLights { get; set; } [Column(35)] public int AnimGroup { get; set; } public UnityEngine.Vector3 CentreOfMass { get { return new UnityEngine.Vector3(_CentreOfMassX, _CentreOfMassY, _CentreOfMassZ); } } public DriveType TransmissionDriveType { get { switch (_TransmissionDriveType) { case "F": return DriveType.Forward; case "R": return DriveType.Rear; case "4": return DriveType.FourWheel; default: throw new FormatException(); } } } public EngineType TransmissionEngineType { get { switch (_TransmissionEngineType) { case "P": return EngineType.Petrol; case "D": return EngineType.Diesel; case "E": return EngineType.Electric; default: throw new FormatException(); } } } public bool AntiLockBrakes { get { return _AntiLockBrakes != 0; } } public Car(string line) : base(line) { } } public static void Load(string path) { _sEntries = LoadInternal(path); } private static IEnumerable LoadInternal(string path) { if (_sCtors == null) GenerateCtors(); //throw new Exception("Constructor count: " + _sCtors.Count); using (var reader = File.OpenText(path)) { string line; while ((line = reader.ReadLine()) != null) { line = line.Trim(); if (line.Length == 0) continue; if (line.StartsWith(";")) continue; var prefix = line[0]; if (char.IsLetterOrDigit(prefix)) { yield return _sCtors['\0'](line); continue; } foreach (var pair in _sCtors) { if (pair.Key != prefix) continue; yield return pair.Value(line.Substring(1).TrimStart()); break; } } } } public static TEntry Get(String id) where TEntry : IVehicleEntry { //throw new Exception("Number of car loaded: " + _sEntries.Count); var entries = _sEntries.OfType(); if (entries == null || (entries != null && (entries.Count() == 0 || entries.All(x => x == null)))) throw new Exception(string.Format("Cars didn't loaded!\nEntries null?: {0}\nEntries count: {1}\nIf this two vars are false & 0, that means that all car instances are null.", entries == null, entries.Count())); return entries.FirstOrDefault(x => x.Id == id); } } }