Cleaning up some things.

This commit is contained in:
CloneTrooper1019
2019-05-18 23:44:51 -05:00
parent 34642f5656
commit 9c3a673d95
47 changed files with 303 additions and 351 deletions

View File

@ -15,40 +15,23 @@ namespace RobloxFiles
public string ClassName;
/// <summary>A list of properties that are defined under this Instance.</summary>
public Dictionary<string, Property> Properties = new Dictionary<string, Property>();
private Dictionary<string, Property> props = new Dictionary<string, Property>();
public IReadOnlyDictionary<string, Property> Properties => props;
private List<Instance> Children = new List<Instance>();
private Instance rawParent;
private Instance parent;
/// <summary>The name of this Instance, if a Name property is defined.</summary>
public string Name => ReadProperty("Name", ClassName);
public override string ToString() => Name;
internal string XmlReferent;
/// <summary>A unique identifier for this instance when being serialized as XML.</summary>
public string XmlReferent { get; internal set; }
/// <summary>Creates an instance using the provided ClassName.</summary>
/// <param name="className">The ClassName to use for this Instance.</param>
public Instance(string className = "Instance")
{
ClassName = className;
}
/// <summary>Indicates whether the parent of this object is locked.</summary>
public bool ParentLocked { get; protected set; }
/// <summary>Creates an instance using the provided ClassName and Name.</summary>
/// <param name="className">The ClassName to use for this Instance.</param>
/// <param name="name">The Name to use for this Instance.</param>
public Instance(string className = "Instance", string name = "Instance")
{
Property propName = new Property()
{
Type = PropertyType.String,
Instance = this,
Name = "Name",
Value = name,
};
ClassName = className;
AddProperty(ref propName);
}
/// <summary>Indicates whether this object should be serialized.</summary>
public bool Archivable = true;
/// <summary>Returns true if this Instance is an ancestor to the provided Instance.</summary>
/// <param name="descendant">The instance whose descendance will be tested against this Instance.</param>
@ -72,6 +55,23 @@ namespace RobloxFiles
return ancestor.IsAncestorOf(this);
}
public string Name
{
get
{
Property propName = GetProperty("Name");
if (propName == null)
SetProperty("Name", "Instance");
return propName.Value.ToString();
}
set
{
SetProperty("Name", value);
}
}
/// <summary>
/// The parent of this Instance, or null if the instance is the root of a tree.<para/>
/// Setting the value of this property will throw an exception if:<para/>
@ -82,21 +82,24 @@ namespace RobloxFiles
{
get
{
return rawParent;
return parent;
}
set
{
if (ParentLocked)
throw new Exception("The Parent property of this instance is locked.");
if (IsAncestorOf(value))
throw new Exception("Parent would result in circular reference.");
if (Parent == this)
throw new Exception("Attempt to set parent to self.");
if (rawParent != null)
rawParent.Children.Remove(this);
if (parent != null)
parent.Children.Remove(this);
value.Children.Add(this);
rawParent = value;
parent = value;
}
}
@ -180,6 +183,11 @@ namespace RobloxFiles
return ancestor;
}
/// <summary>
/// Returns the first ancestor of this Instance whose ClassName is the provided string className.
/// If the instance is not found, this returns null.
/// </summary>
/// <param name="name">The Name of the Instance to find.</param>
public Instance FindFirstAncestorOfClass(string className)
{
Instance ancestor = Parent;
@ -196,7 +204,8 @@ namespace RobloxFiles
}
/// <summary>
/// Returns the first Instance whose ClassName is the provided string className. If the instance is not found, this returns null.
/// Returns the first Instance whose ClassName is the provided string className.
/// If the instance is not found, this returns null.
/// </summary>
/// <param name="className">The ClassName of the Instance to find.</param>
public Instance FindFirstChildOfClass(string className, bool recursive = false)
@ -228,21 +237,77 @@ namespace RobloxFiles
}
/// <summary>
/// Looks for a property with the specified property name, and returns it as an object.
/// Returns a Property object if a property with the specified name is defined in this Instance.
/// </summary>
public Property GetProperty(string name)
{
Property result = null;
if (Properties.ContainsKey(name))
result = Properties[name];
return result;
}
/// <summary>
/// Finds or creates a property with the specified name, and sets its value to the provided object.
/// Returns the property object that had its value set, if the value is not null.
/// </summary>
public Property SetProperty(string name, object value, PropertyType? preferType = null)
{
Property prop = GetProperty(name) ?? new Property()
{
Type = preferType ?? PropertyType.Unknown,
Name = name
};
if (preferType == null)
{
object oldValue = prop.Value;
Type oldType = oldValue?.GetType();
Type newType = value?.GetType();
if (oldType != newType)
{
if (value == null)
{
RemoveProperty(name);
return prop;
}
string typeName = newType.Name;
if (value is Instance)
typeName = "Ref";
else if (value is int)
typeName = "Int";
else if (value is long)
typeName = "Int64";
Enum.TryParse(typeName, out prop.Type);
}
}
prop.Value = value;
if (prop.Instance == null)
AddProperty(ref prop);
return prop;
}
/// <summary>
/// Looks for a property with the specified property name, and returns its value as an object.
/// <para/>The resulting value may be null if the property is not serialized.
/// <para/>You can use the templated ReadProperty overload to fetch it as a specific type with a default value provided.
/// </summary>
/// <param name="propertyName">The name of the property to be fetched from this Instance.</param>
/// <returns>An object reference to the value of the specified property, if it exists.</returns>
///
public object ReadProperty(string propertyName)
{
Property property = null;
if (Properties.ContainsKey(propertyName))
property = Properties[propertyName];
return (property != null ? property.Value : null);
Property property = GetProperty(propertyName);
return property?.Value;
}
/// <summary>
@ -291,52 +356,39 @@ namespace RobloxFiles
/// <summary>
/// Adds a property by reference to this Instance's property list.
/// This is used during the file loading procedure.
/// </summary>
/// <param name="prop">A reference to the property that will be added.</param>
internal void AddProperty(ref Property prop)
{
Properties.Add(prop.Name, prop);
prop.Instance = this;
if (prop.Name == "Name")
{
Property nameProp = GetProperty("Name");
if (nameProp != null)
{
nameProp.Value = prop.Value;
return;
}
}
props.Add(prop.Name, prop);
}
/// <summary>
/// Allows you to access a child/descendant of this Instance, and/or one of its properties.<para/>
/// The provided string should be a period-separated (.) path to what you wish to access.<para/>
/// This will throw an exception if any part of the path cannot be found.<para/>
///
/// ~ Examples ~<para/>
/// var terrain = robloxFile["Workspace.Terrain"] as Instance;<para/>
/// var currentCamera = robloxFile["Workspace.CurrentCamera"] as Property;<para/>
///
/// Removes a property with the provided name if a property with the provided name exists.
/// </summary>
public object this[string accessor]
/// <param name="name">The name of the property to be removed.</param>
/// <returns>True if a property with the provided name was removed.</returns>
public bool RemoveProperty(string name)
{
get
{
Instance result = this;
Property prop = GetProperty(name);
foreach (string name in accessor.Split('.'))
{
Instance next = result.FindFirstChild(name);
if (prop != null)
prop.Instance = null;
if (next == null)
{
// Check if there is any property with this name.
Property prop = null;
if (result.Properties.ContainsKey(name))
prop = result.Properties[name];
else
throw new Exception(name + " is not a valid member of " + result.Name);
return prop;
}
result = next;
}
return result;
}
return props.Remove(name);
}
}
}

View File

@ -39,13 +39,13 @@ namespace RobloxFiles
public class Property
{
public string Name;
public Instance Instance;
public Instance Instance { get; internal set; }
public PropertyType Type;
public object Value;
public string XmlToken = "";
private byte[] RawBuffer = null;
public byte[] RawBuffer { get; internal set; }
public bool HasRawBuffer
{
@ -116,15 +116,5 @@ namespace RobloxFiles
return string.Join(" ", typeName, Name, '=', valueLabel);
}
internal void SetRawBuffer(byte[] buffer)
{
RawBuffer = buffer;
}
public byte[] GetRawBuffer()
{
return RawBuffer;
}
}
}