// Gets a single timestamp component static int GetTimestampVar(FbxNode timestamp, string element) { var elementNode = timestamp[element]; if (elementNode != null && elementNode.Properties.Count > 0) { var prop = elementNode.Properties[0]; if (prop is int || prop is long) { return((int)prop); } } throw new FbxException(timePath, -1, "Timestamp has no " + element); }
static void Update <T>(FbxNode node, T[] arr) { if (arr == null) { Debug.LogWarning("Array being assigned is null"); return; } if (node.Value != null && node.Value.GetType() != arr.GetType()) { Debug.LogWarning("Array being assigned does not match the previous array's type in " + node.Name + "! Required: " + node.Value.GetType() + ", Got: " + arr.GetType()); return; } node.Value = arr; }
// Gets a single timestamp component static int GetTimestampVar(FbxNode timestamp, string element) { var elementNode = timestamp[element]; if (elementNode != null && elementNode.Properties.Count > 0) { var prop = elementNode.Properties[0]; if (prop is int || prop is long) { return((int)prop); } if (prop is byte) { return((int)(byte)prop); } throw new FbxException(timePath, -1, $"element:{element}, prop={prop} has unrecognised value type : {prop.GetType()}"); } throw new FbxException(timePath, -1, "Timestamp has no " + element); }
/// <summary> /// Reads a single node. /// </summary> /// <remarks> /// This won't read the file header or footer, and as such will fail if the stream is a full FBX file /// </remarks> /// <returns>The node</returns> /// <exception cref="FbxException">The FBX data was malformed /// for the reader's error level</exception> public FbxNode ReadNode() { long endOffset; int endOffset32; long numProperties; int numProperties32; long propertyListLen; int propertyListLen32; // Version 7500 reads qwords instead dwords for properties and property list length if (versionNumber == 7500) { endOffset = binStream.ReadInt64(); numProperties = binStream.ReadInt64(); propertyListLen = binStream.ReadInt64(); } else { endOffset32 = binStream.ReadInt32(); endOffset = Convert.ToInt64(endOffset32); numProperties32 = binStream.ReadInt32(); numProperties = Convert.ToInt64(numProperties32); propertyListLen32 = binStream.ReadInt32(); propertyListLen = Convert.ToInt64(propertyListLen32); } var nameLen = binStream.ReadByte(); var name = nameLen == 0 ? "" : Encoding.ASCII.GetString(binStream.ReadBytes(nameLen)); if (endOffset == 0) { // The end offset should only be 0 in a null node if (errorLevel >= ErrorLevel.Checked && (numProperties != 0 || propertyListLen != 0 || !string.IsNullOrEmpty(name))) { throw new FbxException(binStream.BaseStream.Position, "Invalid node; expected NULL record"); } return(null); } var node = new FbxNode { Name = name }; var propertyEnd = binStream.BaseStream.Position + propertyListLen; // Read properties for (int i = 0; i < numProperties; i++) { node.Properties.Add(ReadProperty()); } if (errorLevel >= ErrorLevel.Checked && binStream.BaseStream.Position != propertyEnd) { throw new FbxException(binStream.BaseStream.Position, "Too many bytes in property list, end point is " + propertyEnd); } // Read nested nodes var listLen = endOffset - binStream.BaseStream.Position; if (errorLevel >= ErrorLevel.Checked && listLen < 0) { throw new FbxException(binStream.BaseStream.Position, "Node has invalid end point"); } if (listLen > 0) { FbxNode nested; do { nested = ReadNode(); node.Nodes.Add(nested); } while (nested != null); if (errorLevel >= ErrorLevel.Checked && binStream.BaseStream.Position != endOffset) { throw new FbxException(binStream.BaseStream.Position, "Too many bytes in node, end point is " + endOffset); } } return(node); }
/// <summary> /// Reads a single node. /// </summary> /// <remarks> /// This won't read the file header or footer, and as such will fail if the stream is a full FBX file /// </remarks> /// <returns>The node</returns> /// <exception cref="FbxException">The FBX data was malformed /// for the reader's error level</exception> public FbxNode ReadNode() { var endOffset = stream.ReadInt32(); var numProperties = stream.ReadInt32(); var propertyListLen = stream.ReadInt32(); var nameLen = stream.ReadByte(); var name = nameLen == 0 ? "" : Encoding.ASCII.GetString(stream.ReadBytes(nameLen)); if (endOffset == 0) { // The end offset should only be 0 in a null node if(errorLevel >= ErrorLevel.Checked && (numProperties != 0 || propertyListLen != 0 || !string.IsNullOrEmpty(name))) throw new FbxException(stream.BaseStream.Position, "Invalid node; expected NULL record"); return null; } var node = new FbxNode {Name = name}; var propertyEnd = stream.BaseStream.Position + propertyListLen; // Read properties for (int i = 0; i < numProperties; i++) node.Properties.Add(ReadProperty()); if(errorLevel >= ErrorLevel.Checked && stream.BaseStream.Position != propertyEnd) throw new FbxException(stream.BaseStream.Position, "Too many bytes in property list, end point is " + propertyEnd); // Read nested nodes var listLen = endOffset - stream.BaseStream.Position; if(errorLevel >= ErrorLevel.Checked && listLen < 0) throw new FbxException(stream.BaseStream.Position, "Node has invalid end point"); if (listLen > 0) { FbxNode nested; do { nested = ReadNode(); node.Nodes.Add(nested); } while (nested != null); if (errorLevel >= ErrorLevel.Checked && stream.BaseStream.Position != endOffset) throw new FbxException(stream.BaseStream.Position, "Too many bytes in node, end point is " + endOffset); } return node; }
/// <summary> /// Reads the next node from the stream /// </summary> /// <returns>The read node, or <c>null</c></returns> public FbxNode ReadNode() { var first = ReadToken(); var id = first as Identifier; if (id == null) { if (first is EndOfStream) { return(null); } throw new FbxException(line, column, "Unexpected '" + first + "', expected an identifier"); } var node = new FbxNode { Name = id.String }; // Read properties object token; bool expectComma = false; while (!'{'.Equals(token = ReadToken()) && !(token is Identifier) && !'}'.Equals(token)) { if (expectComma) { if (!','.Equals(token)) { throw new FbxException(line, column, "Unexpected '" + token + "', expected a ','"); } expectComma = false; continue; } if (token is char) { var c = (char)token; switch (c) { case '*': token = ReadArray(); break; case '}': case ':': case ',': throw new FbxException(line, column, "Unexpected '" + c + "' in property list"); } } node.Properties.Add(token); expectComma = true; // The final comma before the open brace isn't required } // TODO: Merge property list into an array as necessary // Now we're either at an open brace, close brace or a new node if (token is Identifier || '}'.Equals(token)) { prevToken = token; return(node); } // The while loop can't end unless we're at an open brace, so we can continue right on object endBrace; while (!'}'.Equals(endBrace = ReadToken())) { prevToken = endBrace; // If it's not an end brace, the next node will need it node.Nodes.Add(ReadNode()); } if (node.Nodes.Count < 1) // If there's an open brace, we want that to be preserved { node.Nodes.Add(null); } return(node); }
// Writes a single document to the buffer void WriteNode(FbxDocument document, FbxNode node) { if (node == null) { var data = document.Version >= FbxVersion.v7_5 ? nullData7500 : nullData; stream.BaseStream.Write(data, 0, data.Length); } else { nodePath.Push(node.Name ?? ""); var name = string.IsNullOrEmpty(node.Name) ? null : Encoding.ASCII.GetBytes(node.Name); if (name != null && name.Length > byte.MaxValue) { throw new FbxException(stream.BaseStream.Position, "Node name is too long"); } // Header var endOffsetPos = stream.BaseStream.Position; long propertyLengthPos; if (document.Version >= FbxVersion.v7_5) { stream.Write((long)0); // End offset placeholder stream.Write((long)node.Properties.Count); propertyLengthPos = stream.BaseStream.Position; stream.Write((long)0); // Property length placeholder } else { stream.Write(0); // End offset placeholder stream.Write(node.Properties.Count); propertyLengthPos = stream.BaseStream.Position; stream.Write(0); // Property length placeholder } stream.Write((byte)(name?.Length ?? 0)); if (name != null) { stream.Write(name); } // Write properties and length var propertyBegin = stream.BaseStream.Position; for (int i = 0; i < node.Properties.Count; i++) { WriteProperty(node.Properties[i], i); } var propertyEnd = stream.BaseStream.Position; stream.BaseStream.Position = propertyLengthPos; if (document.Version >= FbxVersion.v7_5) { stream.Write((long)(propertyEnd - propertyBegin)); } else { stream.Write((int)(propertyEnd - propertyBegin)); } stream.BaseStream.Position = propertyEnd; // Write child nodes if (node.Nodes.Count > 0) { foreach (var n in node.Nodes) { if (n == null) { continue; } WriteNode(document, n); } WriteNode(document, null); } // Write end offset var dataEnd = stream.BaseStream.Position; stream.BaseStream.Position = endOffsetPos; if (document.Version >= FbxVersion.v7_5) { stream.Write((long)dataEnd); } else { stream.Write((int)dataEnd); } stream.BaseStream.Position = dataEnd; nodePath.Pop(); } }
/// <summary> /// Reads the next node from the stream /// </summary> /// <returns>The read node, or <c>null</c></returns> public FbxNode ReadNode() { var first = ReadToken(); var id = first as Identifier; if (id == null) { if (first is EndOfStream) return null; throw new FbxException(line, column, "Unexpected '" + first + "', expected an identifier"); } var node = new FbxNode {Name = id.String}; // Read properties object token; bool expectComma = false; while (!'{'.Equals(token = ReadToken()) && !(token is Identifier) && !'}'.Equals(token)) { if (expectComma) { if (!','.Equals(token)) throw new FbxException(line, column, "Unexpected '" + token + "', expected a ','"); expectComma = false; continue; } if (token is char) { var c = (char) token; switch (c) { case '*': token = ReadArray(); break; case '}': case ':': case ',': throw new FbxException(line, column, "Unexpected '" + c + "' in property list"); } } node.Properties.Add(token); expectComma = true; // The final comma before the open brace isn't required } // TODO: Merge property list into an array as necessary // Now we're either at an open brace, close brace or a new node if (token is Identifier || '}'.Equals(token)) { prevToken = token; return node; } // The while loop can't end unless we're at an open brace, so we can continue right on object endBrace; while(!'}'.Equals(endBrace = ReadToken())) { prevToken = endBrace; // If it's not an end brace, the next node will need it node.Nodes.Add(ReadNode()); } if(node.Nodes.Count < 1) // If there's an open brace, we want that to be preserved node.Nodes.Add(null); return node; }
/// <summary> /// Reads a single node. /// </summary> /// <remarks> /// This won't read the file header or footer, and as such will fail if the stream is a full FBX file /// </remarks> /// <returns>The node</returns> /// <exception cref="FbxException">The FBX data was malformed /// for the reader's error level</exception> public FbxNode ReadNode() { var endOffset = stream.ReadInt32(); var numProperties = stream.ReadInt32(); var propertyListLen = stream.ReadInt32(); var nameLen = stream.ReadByte(); var name = nameLen == 0 ? "" : Encoding.ASCII.GetString(stream.ReadBytes(nameLen)); if (endOffset == 0) { // The end offset should only be 0 in a null node if (errorLevel >= ErrorLevel.Checked && (numProperties != 0 || propertyListLen != 0 || !string.IsNullOrEmpty(name))) { throw new FbxException(stream.BaseStream.Position, "Invalid node; expected NULL record"); } return(null); } var node = new FbxNode { Name = name }; var propertyEnd = stream.BaseStream.Position + propertyListLen; // Read properties for (int i = 0; i < numProperties; i++) { node.Properties.Add(ReadProperty()); } if (errorLevel >= ErrorLevel.Checked && stream.BaseStream.Position != propertyEnd) { throw new FbxException(stream.BaseStream.Position, "Too many bytes in property list, end point is " + propertyEnd); } // Read nested nodes var listLen = endOffset - stream.BaseStream.Position; if (errorLevel >= ErrorLevel.Checked && listLen < 0) { throw new FbxException(stream.BaseStream.Position, "Node has invalid end point"); } if (listLen > 0) { FbxNode nested; do { nested = ReadNode(); node.Nodes.Add(nested); } while (nested != null); if (errorLevel >= ErrorLevel.Checked && stream.BaseStream.Position != endOffset) { throw new FbxException(stream.BaseStream.Position, "Too many bytes in node, end point is " + endOffset); } } return(node); }
// Writes a single document to the buffer void WriteNode(FbxNode node) { if (node == null) { stream.BaseStream.Write(nullData, 0, nullData.Length); } else { nodePath.Push(node.Name ?? ""); var name = string.IsNullOrEmpty(node.Name) ? null : Encoding.ASCII.GetBytes(node.Name); if(name != null && name.Length > byte.MaxValue) throw new FbxException(stream.BaseStream.Position, "Node name is too long"); // Header var endOffsetPos = stream.BaseStream.Position; stream.Write(0); // End offset placeholder stream.Write(node.Properties.Count); var propertyLengthPos = stream.BaseStream.Position; stream.Write(0); // Property length placeholder stream.Write((byte)(name?.Length ?? 0)); if(name != null) stream.Write(name); // Write properties and length var propertyBegin = stream.BaseStream.Position; for(int i = 0; i < node.Properties.Count; i++) { WriteProperty(node.Properties[i], i); } var propertyEnd = stream.BaseStream.Position; stream.BaseStream.Position = propertyLengthPos; stream.Write((int)(propertyEnd - propertyBegin)); stream.BaseStream.Position = propertyEnd; // Write child nodes if (node.Nodes.Count > 0) { foreach (var n in node.Nodes) { if(n == null) continue; WriteNode(n); } WriteNode(null); } // Write end offset var dataEnd = stream.BaseStream.Position; stream.BaseStream.Position = endOffsetPos; stream.Write((int)dataEnd); stream.BaseStream.Position = dataEnd; nodePath.Pop(); } }
// Adds the given node text to the string void BuildString(FbxNode node, StringBuilder sb, bool writeArrayLength, int indentLevel = 0) { nodePath.Push(node.Name ?? ""); int lineStart = sb.Length; // Write identifier for (int i = 0; i < indentLevel; i++) { sb.Append('\t'); } sb.Append(node.Name).Append(':'); // Write properties var first = true; for (int j = 0; j < node.Properties.Count; j++) { var p = node.Properties[j]; if (p == null) { continue; } if (!first) { sb.Append(','); } sb.Append(' '); if (p is string) { sb.Append('"').Append(p).Append('"'); } else if (p is Array) { var array = (Array)p; var elementType = p.GetType().GetElementType(); // ReSharper disable once PossibleNullReferenceException // We know it's an array, so we don't need to check for null if (array.Rank != 1 || !elementType.IsPrimitive) { throw new FbxException(nodePath, j, "Invalid array type " + p.GetType()); } if (writeArrayLength) { sb.Append('*').Append(array.Length).Append(" {\n"); lineStart = sb.Length; for (int i = -1; i < indentLevel; i++) { sb.Append('\t'); } sb.Append("a: "); } bool pFirst = true; foreach (var v in (Array)p) { if (!pFirst) { sb.Append(','); } var vstr = v.ToString(); if ((sb.Length - lineStart) + vstr.Length >= MaxLineLength) { sb.Append('\n'); lineStart = sb.Length; } sb.Append(vstr); pFirst = false; } if (writeArrayLength) { sb.Append('\n'); for (int i = 0; i < indentLevel; i++) { sb.Append('\t'); } sb.Append('}'); } } else if (p is char) { sb.Append((char)p); } else if (p.GetType().IsPrimitive&& p is IFormattable) { sb.Append(p); } else { throw new FbxException(nodePath, j, "Invalid property type " + p.GetType()); } first = false; } // Write child nodes if (node.Nodes.Count > 0) { sb.Append(" {\n"); foreach (var n in node.Nodes) { if (n == null) { continue; } BuildString(n, sb, writeArrayLength, indentLevel + 1); } for (int i = 0; i < indentLevel; i++) { sb.Append('\t'); } sb.Append('}'); } sb.Append('\n'); nodePath.Pop(); }
// Gets a single timestamp component static int GetTimestampVar(FbxNode timestamp, string element) { var elementNode = timestamp[element]; if (elementNode != null && elementNode.Properties.Count > 0) { var prop = elementNode.Properties[0]; if (prop is int || prop is long) return (int)prop; } throw new FbxException(timePath, -1, "Timestamp has no " + element); }
// Adds the given node text to the string void BuildString(FbxNode node, StringBuilder sb, bool writeArrayLength, int indentLevel = 0) { nodePath.Push(node.Name ?? ""); int lineStart = sb.Length; // Write identifier for (int i = 0; i < indentLevel; i++) sb.Append('\t'); sb.Append(node.Name).Append(':'); // Write properties var first = true; for(int j = 0; j < node.Properties.Count; j++) { var p = node.Properties[j]; if(p == null) continue; if (!first) sb.Append(','); sb.Append(' '); if (p is string) { sb.Append('"').Append(p).Append('"'); } else if (p is Array) { var array = (Array) p; var elementType = p.GetType().GetElementType(); // ReSharper disable once PossibleNullReferenceException // We know it's an array, so we don't need to check for null if (array.Rank != 1 || !elementType.IsPrimitive) throw new FbxException(nodePath, j, "Invalid array type " + p.GetType()); if (writeArrayLength) { sb.Append('*').Append(array.Length).Append(" {\n"); lineStart = sb.Length; for (int i = -1; i < indentLevel; i++) sb.Append('\t'); sb.Append("a: "); } bool pFirst = true; foreach (var v in (Array) p) { if (!pFirst) sb.Append(','); var vstr = v.ToString(); if ((sb.Length - lineStart) + vstr.Length >= MaxLineLength) { sb.Append('\n'); lineStart = sb.Length; } sb.Append(vstr); pFirst = false; } if (writeArrayLength) { sb.Append('\n'); for (int i = 0; i < indentLevel; i++) sb.Append('\t'); sb.Append('}'); } } else if (p is char) sb.Append((char) p); else if(p.GetType().IsPrimitive && p is IFormattable) sb.Append(p); else throw new FbxException(nodePath, j, "Invalid property type " + p.GetType()); first = false; } // Write child nodes if (node.Nodes.Count > 0) { sb.Append(" {\n"); foreach (var n in node.Nodes) { if (n == null) continue; BuildString(n, sb, writeArrayLength, indentLevel + 1); } for (int i = 0; i < indentLevel; i++) sb.Append('\t'); sb.Append('}'); } sb.Append('\n'); nodePath.Pop(); }
public NodeLink(FbxNodeList parent, FbxNode node) { this.parent = parent; this.node = node; }
/// <summary>Parse Node Property</summary> /// <param name="reader">Binary Reader</param> /// <param name="node">current node</param> private void ParseProperty(BinaryReader reader, FbxNode node) { #region local func GetArrayProperty byte[] GetArrayProperty(BinaryReader r, int typeSize) { var len = (int)r.ReadUInt32(); var encoded = r.ReadUInt32() != 0; var compressedSize = (int)r.ReadUInt32(); if (encoded) { var deflateMetaData = r.ReadInt16(); const int deflateMetaDataSize = 2; var byteArray = r.ReadBytes(compressedSize - deflateMetaDataSize); using (var ms = new MemoryStream(byteArray)) using (var ds = new DeflateStream(ms, CompressionMode.Decompress)) { try { var decompressed = new byte[len * typeSize]; if (ds.Read(decompressed, 0, decompressed.Length) != decompressed.Length) { throw new FormatException(); } return(decompressed); } catch (InvalidDataException ex) { throw new FormatException("Parse fail", ex); } } } else { var byteArray = r.ReadBytes(compressedSize); return(byteArray); } } #endregion var propertyType = reader.ReadByte(); switch (propertyType) { case INT16_PROPERTY: { var value = reader.ReadInt16(); node.Properties.Add(new FbxShortProperty() { Value = value }); break; } case BOOL_PROPERTY: { var tmp = reader.ReadByte(); // two types format exists. (Oh my gosh !! Fuuuuuuuu*k !!) // blender -> true/false = 0x01/0x00 // Autodesk production -> true/false = 'Y'/'T' = 0x59/0x54 var value = (tmp == 0x00 || tmp == 0x54) ? false : true; node.Properties.Add(new FbxBoolProperty() { Value = value }); break; } case INT32_PROPERTY: { var value = reader.ReadInt32(); node.Properties.Add(new FbxIntProperty() { Value = value }); break; } case FLOAT_PROPERTY: { var value = reader.ReadSingle(); node.Properties.Add(new FbxFloatProperty() { Value = value }); break; } case DOUBLE_PROPERTY: { var value = reader.ReadDouble(); node.Properties.Add(new FbxDoubleProperty() { Value = value }); break; } case INT64_PROPERTY: { var value = reader.ReadInt64(); node.Properties.Add(new FbxLongProperty() { Value = value }); break; } case STRING_PROPERTY: { var len = (int)reader.ReadUInt32(); var value = GetAsciiString(reader.ReadBytes(len)); node.Properties.Add(new FbxStringProperty() { Value = value }); break; } case BOOL_ARRAY_PROPERTY: { var byteArray = GetArrayProperty(reader, 1); var prop = new FbxBoolArrayProperty() { Value = GetBoolArray(byteArray) }; node.Properties.Add(prop); break; } case INT32_ARRAY_PROPERTY: { var byteArray = GetArrayProperty(reader, 4); var prop = new FbxIntArrayProperty() { Value = GetIntArray(byteArray) }; node.Properties.Add(prop); break; } case FLOAT_ARRAY_PROPERTY: { var byteArray = GetArrayProperty(reader, 4); var prop = new FbxFloatArrayProperty() { Value = GetFloatArray(byteArray) }; node.Properties.Add(prop); break; } case DOUBLE_ARRAY_PROPERTY: { var byteArray = GetArrayProperty(reader, 8); var prop = new FbxDoubleArrayProperty() { Value = GetDoubleArray(byteArray) }; node.Properties.Add(prop); break; } case INT64_ARRAY_PROPERTY: { var byteArray = GetArrayProperty(reader, 8); var prop = new FbxLongArrayProperty() { Value = GetLongArray(byteArray) }; node.Properties.Add(prop); break; } case RAW_BINARY_PROPERTY: { var len = (int)reader.ReadUInt32(); var value = reader.ReadBytes(len); var prop = new FbxBinaryProperty() { Value = value }; node.Properties.Add(prop); break; } default: { Debug.WriteLine($"[Skip Unknow Type Property] Position : {reader.BaseStream.Position}, type : {propertyType}"); break; } } }