SanAndreasUnity/Assets/Scripts/Importing/Items/ItemFile.cs

282 lines
7.9 KiB
C#
Raw Normal View History

2020-05-31 17:07:22 +00:00
using SanAndreasUnity.Importing.Items.Placements;
using SanAndreasUnity.Utilities;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
#if !ENABLE_IL2CPP
using System.Linq.Expressions;
#endif
using System.Reflection;
using UnityEngine;
2020-05-31 17:07:22 +00:00
namespace SanAndreasUnity.Importing.Items
{
public class SectionAttribute : Attribute
{
public readonly string Section;
public SectionAttribute(string section)
{
Section = section;
}
}
public abstract class ItemBase
{
private readonly string[] _parts;
public int Parts { get { return _parts.Length; } }
protected ItemBase(string line, bool commaSeparated = true)
{
var ws = new[] { ' ', '\t' };
if (commaSeparated)
{
_parts = line.Split(',')
.SelectMany(x => x.Split(ws, StringSplitOptions.RemoveEmptyEntries))
.Select(x => x.Trim())
.ToArray();
}
else
{
_parts = line.Split(ws, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Trim())
.Where(x => x.Length > 0)
.ToArray();
}
}
protected ItemBase(BinaryReader reader)
{
}
public string GetString(int index)
{
return _parts[index];
}
public byte GetByte(int index)
{
return byte.Parse(_parts[index], CultureInfo.InvariantCulture);
}
public int GetInt(int index)
{
return int.Parse(_parts[index], CultureInfo.InvariantCulture);
}
public int GetInt(int index, NumberStyles numberStyles)
{
return int.Parse(_parts[index], numberStyles);
}
public float GetSingle(int index)
{
return float.Parse(_parts[index], CultureInfo.InvariantCulture);
}
public double GetDouble(int index)
{
return double.Parse(_parts[index], CultureInfo.InvariantCulture);
}
2019-10-20 23:54:52 +00:00
public UnityEngine.Vector3 GetUnityVec3(ref int index, bool invertYAndZ)
{
float x = GetSingle(index++);
float y = GetSingle(index++);
float z = GetSingle(index++);
if (invertYAndZ)
return new UnityEngine.Vector3(x, z, y);
else
return new UnityEngine.Vector3(x, y, z);
}
2020-05-31 17:07:22 +00:00
}
public abstract class Definition : ItemBase
{
protected Definition(string line, bool commaSeparated = true)
: base(line, commaSeparated) { }
}
public interface IObjectDefinition
{
int Id { get; }
}
public abstract class Placement : ItemBase
{
protected Placement(string line, bool commaSeparated = true)
: base(line, commaSeparated) { }
protected Placement(BinaryReader reader)
: base(reader) { }
}
public class ItemFile<TType>
where TType : ItemBase
{
private delegate TType ItemCtor(string line);
private static readonly Dictionary<string, ItemCtor> _sCtors;
static ItemFile()
{
_sCtors = new Dictionary<string, ItemCtor>();
foreach (var type in Assembly.GetExecutingAssembly().GetTypes())
{
var attrib = (SectionAttribute)type.GetCustomAttributes(typeof(SectionAttribute), false).FirstOrDefault();
if (attrib == null) continue;
if (!typeof(TType).IsAssignableFrom(type)) continue;
var ctor = type.GetConstructor(new[] { typeof(string) });
#if !ENABLE_IL2CPP
var line = Expression.Parameter(typeof(string), "line");
var call = Expression.New(ctor, line);
var cast = Expression.Convert(call, typeof(TType));
var lamb = Expression.Lambda<ItemCtor>(cast, line);
_sCtors.Add(attrib.Section, lamb.Compile());
#else
_sCtors.Add(attrib.Section, line => (TType) ctor.Invoke(new object[] { line }));
#endif
}
}
private readonly Dictionary<string, List<TType>> _sections
= new Dictionary<string, List<TType>>();
public ItemFile(string path)
{
string fileName = Path.GetFileName(path);
if (!Archive.ArchiveManager.FileExists(fileName))
{
Debug.LogError($"Item file not found: {path}");
return;
}
using (var stream = Archive.ArchiveManager.ReadFile(fileName))
2019-10-08 15:59:10 +00:00
{
using (var reader = new StreamReader(stream))
{
Load(reader);
}
2019-10-08 15:59:10 +00:00
}
}
public ItemFile(StreamReader reader)
{
Load(reader);
}
void Load(StreamReader reader)
{
2020-05-31 17:07:22 +00:00
List<TType> curSection = null;
ItemCtor curCtor = null;
2019-10-08 15:59:10 +00:00
string line;
while ((line = reader.ReadLine()) != null)
2020-05-31 17:07:22 +00:00
{
2019-10-08 15:59:10 +00:00
var hashIndex = line.IndexOf('#');
if (hashIndex != -1)
2020-05-31 17:07:22 +00:00
{
2019-10-08 15:59:10 +00:00
line = line.Substring(0, hashIndex);
}
2020-05-31 17:07:22 +00:00
2019-10-08 15:59:10 +00:00
line = line.Trim();
2020-05-31 17:07:22 +00:00
2019-10-08 15:59:10 +00:00
if (line.Length == 0) continue;
2020-05-31 17:07:22 +00:00
2019-10-08 15:59:10 +00:00
if (curSection == null)
{
line = line.ToLower();
if (_sections.ContainsKey(line))
{
curSection = _sections[line];
}
else
2020-05-31 17:07:22 +00:00
{
2019-10-08 15:59:10 +00:00
curSection = new List<TType>();
_sections.Add(line, curSection);
2020-05-31 17:07:22 +00:00
}
2019-10-08 15:59:10 +00:00
if (_sCtors.ContainsKey(line))
2020-05-31 17:07:22 +00:00
{
2019-10-08 15:59:10 +00:00
curCtor = _sCtors[line];
2020-05-31 17:07:22 +00:00
}
2019-10-08 15:59:10 +00:00
continue;
}
2020-05-31 17:07:22 +00:00
2019-10-08 15:59:10 +00:00
if (line.Equals("end"))
{
curSection = null;
curCtor = null;
continue;
2020-05-31 17:07:22 +00:00
}
2019-10-08 15:59:10 +00:00
if (curCtor == null) continue;
curSection.Add(curCtor(line));
2020-05-31 17:07:22 +00:00
}
2019-10-08 15:59:10 +00:00
2020-05-31 17:07:22 +00:00
}
public ItemFile(Stream stream)
{
var reader = new BinaryReader(stream);
if (reader.ReadString(4) != "bnry") throw new Exception("Not a binary IPL file.");
var instCount = reader.ReadInt32();
stream.Seek(12, SeekOrigin.Current);
var carsCount = reader.ReadInt32();
stream.Seek(4, SeekOrigin.Current);
var instOffset = reader.ReadInt32();
stream.Seek(28, SeekOrigin.Current);
var carsOffset = reader.ReadInt32();
var insts = new List<TType>();
_sections.Add("inst", insts);
stream.Seek(instOffset, SeekOrigin.Begin);
for (var j = 0; j < instCount; ++j)
{
insts.Add((TType)(ItemBase)new Instance(reader));
}
var cars = new List<TType>();
_sections.Add("cars", cars);
stream.Seek(carsOffset, SeekOrigin.Begin);
for (var j = 0; j < carsCount; ++j)
{
cars.Add((TType)(ItemBase)new ParkedVehicle(reader));
}
}
public IEnumerable<TItem> GetSection<TItem>(string name)
where TItem : TType
{
name = name.ToLower();
return !_sections.ContainsKey(name)
? Enumerable.Empty<TItem>()
: _sections[name].Cast<TItem>();
}
public IEnumerable<TItem> GetItems<TItem>()
where TItem : TType
{
return _sections.SelectMany(x => x.Value.OfType<TItem>());
}
}
}