SanAndreasUnity/Assets/Scripts/Importing/Collision/CollisionFile.cs

321 lines
10 KiB
C#
Raw Normal View History

2020-05-31 17:07:22 +00:00
using SanAndreasUnity.Importing.Archive;
using SanAndreasUnity.Utilities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEngine;
namespace SanAndreasUnity.Importing.Collision
{
public class CollisionFile
{
private class CollisionFileInfo
{
private CollisionFile _value;
public readonly String FileName;
public readonly Version Version;
public readonly long Offset;
public readonly long Length;
public readonly string Name;
public readonly int ModelId;
public CollisionFile Value { get { return _value ?? (_value = Load()); } set { _value = value; } }
public bool IsLoaded { get { return _value != null; } }
public CollisionFileInfo(BinaryReader reader, String fileName, Version version)
{
FileName = fileName;
Version = version;
Length = reader.ReadUInt32() + 4;
Name = reader.ReadString(22);
ModelId = reader.ReadInt16();
Offset = reader.BaseStream.Position - 28;
reader.BaseStream.Seek(Offset + Length, SeekOrigin.Begin);
}
private CollisionFile Load()
{
using (var stream = ArchiveManager.ReadFile(FileName))
{
return new CollisionFile(this, stream);
}
}
}
private static readonly Dictionary<String, CollisionFileInfo> _sModelNameDict
= new Dictionary<string, CollisionFileInfo>(StringComparer.InvariantCultureIgnoreCase);
private static readonly Utilities.AsyncLoader<String, CollisionFile> s_asyncLoader =
new AsyncLoader<String, CollisionFile> (StringComparer.InvariantCultureIgnoreCase);
// load collision file infos
public static void Load(string fileName)
{
var thisFile = new List<CollisionFileInfo>();
using (var stream = ArchiveManager.ReadFile(fileName))
{
var versBuffer = new byte[4];
var reader = new BinaryReader(stream);
while (stream.Position < stream.Length && stream.Read(versBuffer, 0, 4) == 4)
{
if (versBuffer.All(x => x == 0)) break;
Version version;
var versString = Encoding.ASCII.GetString(versBuffer);
if (!Enum.TryParse(versString, out version))
{
if (versString.Substring(0, 3) == "OLL")
{
// Known problem (size off by one). Attempting to fix by adjusting read pointer...
stream.Position -= 1;
version = Version.COLL;
}
else
{
Debug.LogWarningFormat("Error while reading {0} at 0x{1:x} ({2}%)",
fileName, stream.Position - 4, (stream.Position - 4) * 100 / stream.Length);
}
}
var modelInfo = new CollisionFileInfo(reader, fileName, version);
thisFile.Add(modelInfo);
if (!_sModelNameDict.ContainsKey(modelInfo.Name))
{
_sModelNameDict.Add(modelInfo.Name, modelInfo);
}
else
{
_sModelNameDict[modelInfo.Name] = modelInfo;
}
}
}
}
private static void LoadAsync (string collFileName, CollisionFileInfo collFileInfo, System.Action<CollisionFile> onFinish)
{
ArchiveManager.ReadFileAsync (collFileName, (stream) => {
CollisionFile cf = null;
try {
using(stream)
{
cf = new CollisionFile(collFileInfo, stream);
}
} finally {
onFinish (cf);
}
});
}
public static CollisionFile Load(Stream stream)
{
var reader = new BinaryReader(stream);
var version = (Version)Enum.Parse(typeof(Version), reader.ReadString(4));
var info = new CollisionFileInfo(reader, null, version);
return new CollisionFile(info, stream);
}
public static CollisionFile FromName(String name)
{
if (s_asyncLoader.IsObjectLoaded (name))
return s_asyncLoader.GetLoadedObject (name);
UnityEngine.Profiling.Profiler.BeginSample ("CollisionFile.FromName()");
var cf = _sModelNameDict.ContainsKey(name) ? _sModelNameDict[name].Value : null;
UnityEngine.Profiling.Profiler.EndSample ();
s_asyncLoader.AddToLoadedObjects (name, cf);
return cf;
}
public static void FromNameAsync(String name, System.Action<CollisionFile> onFinish)
{
if (!_sModelNameDict.ContainsKey (name))
{
// Debug.LogErrorFormat ("Collision model name {0} doesn't exist in dict", name);
onFinish (null);
return;
}
if (!s_asyncLoader.TryLoadObject (name, onFinish))
return;
// load collision file asyncly
// get the actual name of collision file
string collFileName = _sModelNameDict [name].FileName;
LoadAsync( collFileName, _sModelNameDict[name], (result) =>
{
// update _value variable in appropriate CollisionFileInfo object
_sModelNameDict[name].Value = result;
s_asyncLoader.OnObjectFinishedLoading( name, result, true);
});
}
public readonly string Name;
public readonly int ModelId;
public readonly Bounds Bounds;
public readonly Flags Flags;
public readonly Sphere[] Spheres;
public readonly Box[] Boxes;
public readonly Vertex[] Vertices;
public readonly FaceGroup[] FaceGroups;
public readonly Face[] Faces;
private CollisionFile(CollisionFileInfo info, Stream stream)
{
Name = info.Name;
ModelId = info.ModelId;
var version = info.Version;
var reader = new BinaryReader(stream);
stream.Seek(info.Offset + 28, SeekOrigin.Begin);
Bounds = new Bounds(reader, version);
int spheres, boxes, verts, faces, faceGroups;
long spheresOffset, boxesOffset, vertsOffset,
facesOffset, faceGroupsOffset;
switch (version)
{
case Version.COLL:
{
spheres = reader.ReadInt32();
spheresOffset = stream.Position;
stream.Seek(spheres * Sphere.Size, SeekOrigin.Current);
reader.ReadInt32();
boxes = reader.ReadInt32();
boxesOffset = stream.Position;
stream.Seek(boxes * Box.Size, SeekOrigin.Current);
verts = reader.ReadInt32();
vertsOffset = stream.Position;
stream.Seek(verts * Vertex.SizeV1, SeekOrigin.Current);
faces = reader.ReadInt32();
facesOffset = stream.Position;
faceGroups = 0;
faceGroupsOffset = 0;
break;
}
default:
{
spheres = reader.ReadUInt16();
boxes = reader.ReadUInt16();
faces = reader.ReadUInt16();
reader.ReadInt16();
Flags = (Flags)reader.ReadInt32();
spheresOffset = reader.ReadUInt32() + info.Offset;
boxesOffset = reader.ReadUInt32() + info.Offset;
reader.ReadUInt32();
vertsOffset = reader.ReadUInt32() + info.Offset;
facesOffset = reader.ReadUInt32() + info.Offset;
if (faces > 0 && (Flags & Flags.HasFaceGroups) == Flags.HasFaceGroups)
{
stream.Seek(facesOffset - 4, SeekOrigin.Begin);
faceGroups = reader.ReadInt32();
faceGroupsOffset = facesOffset - 4 - FaceGroup.Size * faceGroups;
}
else
{
faceGroups = 0;
faceGroupsOffset = 0;
}
verts = -1;
break;
}
}
Spheres = new Sphere[spheres];
Boxes = new Box[boxes];
Faces = new Face[faces];
FaceGroups = new FaceGroup[faceGroups];
if (spheres > 0)
{
stream.Seek(spheresOffset, SeekOrigin.Begin);
for (var i = 0; i < spheres; ++i)
{
Spheres[i] = new Sphere(reader, version);
}
}
if (boxes > 0)
{
stream.Seek(boxesOffset, SeekOrigin.Begin);
for (var i = 0; i < boxes; ++i)
{
Boxes[i] = new Box(reader, version);
}
}
if (faces > 0)
{
stream.Seek(facesOffset, SeekOrigin.Begin);
for (var i = 0; i < faces; ++i)
{
Faces[i] = new Face(reader, version);
}
if (verts == -1)
{
verts = Faces.Max(x => x.GetIndices().Max()) + 1;
}
Vertices = new Vertex[verts];
stream.Seek(vertsOffset, SeekOrigin.Begin);
for (var i = 0; i < verts; ++i)
{
Vertices[i] = new Vertex(reader, version);
}
if (faceGroups > 0)
{
stream.Seek(faceGroupsOffset, SeekOrigin.Begin);
for (var i = 0; i < faceGroups; ++i)
{
FaceGroups[i] = new FaceGroup(reader);
}
}
}
else
{
Vertices = new Vertex[0];
}
}
}
}