// Adds the given node text to the string void BuildString(FbxNode node, LineStringBuilder sb, FbxVersion version, int indentLevel = 0) { // Write identifier sb.Indent(indentLevel); node.Identifier.WriteAscii(version, sb, indentLevel); // 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(" "); p.WriteAscii(version, sb, indentLevel); 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, version, indentLevel + 1); } sb.Indent(indentLevel); sb.Append("}"); } sb.Append("\n"); }
// Gets a single timestamp component static int GetTimestampVar(FbxNode timestamp, string element) { var elementNode = timestamp[element].FirstOrDefault(); if (elementNode != null && elementNode.Properties.Count > 0) { var prop = elementNode.Properties[0]; if (prop.TryGetAsLong(out var longValue)) { return((int)longValue); } } throw new FbxException(-1, "Timestamp has no " + element); }
/// <summary> /// Add node to node array /// </summary> /// <param name="node"></param> public void AddNode(FbxNode node) { Nodes.Add(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 { // 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 } node.Identifier.WriteBinary(document.Version, stream); // Write properties and length var propertyBegin = stream.BaseStream.Position; for (int i = 0; i < node.Properties.Count; i++) { WriteProperty(document.Version, node.Properties[i]); } var propertyEnd = stream.BaseStream.Position; stream.BaseStream.Position = propertyLengthPos; if (document.Version >= FbxVersion.v7_5) { stream.Write(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(dataEnd); } else { stream.Write((int)dataEnd); } stream.BaseStream.Position = dataEnd; } }
/// <summary> /// Reads the next node from the stream /// </summary> /// <returns>The read node, or <c>null</c></returns> public FbxNode ReadNode() { var first = ReadToken(); if (!(first is IdentifierToken id)) { if (first is Token tok && tok.TokenType == TokenType.EndOfStream) { return(null); } throw new FbxException(_fbxAsciiFileInfo, "Unexpected '" + first + "', expected an identifier"); } var node = new FbxNode(id); // Read properties Token token = ReadToken(); bool expectComma = false; while (token.TokenType != TokenType.OpenBrace && token.TokenType != TokenType.Identifier && token.TokenType != TokenType.CloseBrace) { if (expectComma) { if (token.TokenType != TokenType.Comma) { throw new FbxException(_fbxAsciiFileInfo, "Unexpected '" + token + "', expected a ','"); } expectComma = false; token = ReadToken(); continue; } if (token.TokenType == TokenType.Asterix) { token = ReadArray(); } else if (token.TokenType == TokenType.CloseBrace || token.TokenType == TokenType.Comma) { throw new FbxException(_fbxAsciiFileInfo, "Unexpected '" + token.TokenType + "' in property list"); } node.AddProperty(token); expectComma = true; // The final comma before the open brace isn't required token = ReadToken(); } // 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.TokenType == TokenType.Identifier || token.TokenType == TokenType.CloseBrace) { _tokenStack.Push(token); return(node); } // The while loop can't end unless we're at an open brace, so we can continue right on Token endBrace = ReadToken(); while (endBrace.TokenType != TokenType.CloseBrace) { _tokenStack.Push(endBrace); node.AddNode(ReadNode()); endBrace = ReadToken(); } if (node.Nodes.Count < 1) // If there's an open brace, we want that to be preserved { node.AddNode(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(FbxDocument document) { var endOffset = document.Version >= FbxVersion.v7_5 ? stream.ReadInt64() : stream.ReadInt32(); var numProperties = document.Version >= FbxVersion.v7_5 ? stream.ReadInt64() : stream.ReadInt32(); var propertyListLen = document.Version >= FbxVersion.v7_5 ? stream.ReadInt64() : 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(new IdentifierToken(name)); var propertyEnd = stream.BaseStream.Position + propertyListLen; // Read properties for (int i = 0; i < numProperties; i++) { node.AddProperty(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(document); node.AddNode(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); }