/// <summary> /// Writes an FBX document /// </summary> /// <param name="document">The top level document node</param> /// <param name="stream"></param> public static void WriteAscii(FbxDocument document, Stream stream) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } var writer = new FbxAsciiWriter(stream); writer.Write(document); }
/// <summary> /// Writes an FBX document /// </summary> /// <param name="document">The top level document node</param> /// <param name="path"></param> public static void WriteAscii(FbxDocument document, string path) { if (path == null) { throw new ArgumentNullException(nameof(path)); } using (var stream = new FileStream(path, FileMode.Create)) { WriteAscii(document, stream); } }
/// <summary> /// Reads an FBX document from the stream /// </summary> /// <returns>The top-level node</returns> /// <exception cref="FbxException">The FBX data was malformed /// for the reader's error level</exception> public FbxDocument Read() { // Read header bool validHeader = ReadHeader(stream.BaseStream); if (errorLevel >= ErrorLevel.Strict && !validHeader) { throw new FbxException(stream.BaseStream.Position, "Invalid header string"); } var document = new FbxDocument { Version = (FbxVersion)stream.ReadInt32() }; // Read nodes FbxNode nested; do { nested = ReadNode(document); if (nested != null) { document.AddNode(nested); } } while (nested != null); // Read footer code var footerCode = new byte[footerCodeSize]; stream.BaseStream.Read(footerCode, 0, footerCode.Length); if (errorLevel >= ErrorLevel.Strict) { var validCode = GenerateFooterCode(document); if (!CheckEqual(footerCode, validCode)) { throw new FbxException(stream.BaseStream.Position - footerCodeSize, "Incorrect footer code"); } } // Read footer extension var dataPos = stream.BaseStream.Position; var validFooterExtension = CheckFooter(stream, document.Version); if (errorLevel >= ErrorLevel.Strict && !validFooterExtension) { throw new FbxException(dataPos, "Invalid footer"); } return(document); }
/// <summary> /// Writes an FBX file to the output /// </summary> /// <param name="document"></param> public void Write(FbxDocument document) { stream.BaseStream.Position = 0; WriteHeader(stream.BaseStream); stream.Write((int)document.Version); // TODO: Do we write a top level node or not? Maybe check the version? foreach (var node in document.Nodes) { WriteNode(document, node); } WriteNode(document, null); stream.Write(GenerateFooterCode(document)); WriteFooter(stream, (int)document.Version); output.Write(memory.ToArray(), 0, (int)memory.Position); }
/// <summary> /// Reads a full document from the stream /// </summary> /// <returns>The complete document object</returns> public FbxDocument Read() { var ret = new FbxDocument(); // Read version string const string versionString = @"; FBX (\d)\.(\d)\.(\d) project file"; AsciiTokenParser.TryConsumeWhiteSpace(_fbxAsciiFileInfo); bool hasVersionString = false; if (AsciiTokenParser.TryParseCommentToken(_fbxAsciiFileInfo, out var commentToken)) { var match = Regex.Match(commentToken.Value, versionString); hasVersionString = match.Success; if (hasVersionString) { ret.Version = (FbxVersion)( int.Parse(match.Groups[1].Value) * 1000 + int.Parse(match.Groups[2].Value) * 100 + int.Parse(match.Groups[3].Value) * 10 ); } } if (!hasVersionString && _errorLevel >= ErrorLevel.Strict) { throw new FbxException(_fbxAsciiFileInfo, "Invalid version string; first line must match \"" + versionString + "\""); } FbxNode node; while ((node = ReadNode()) != null) { ret.AddNode(node); } return(ret); }
// 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 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); }