├── .gitignore ├── LICENSE ├── README.md └── src ├── CMakeLists.txt ├── fbxdocument.cpp ├── fbxdocument.h ├── fbxdump.cpp ├── fbxnode.cpp ├── fbxnode.h ├── fbxproperty.cpp ├── fbxproperty.h ├── fbxutil.cpp ├── fbxutil.h └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build* 2 | CMakeLists.txt.user 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Jakub Skořepa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C++ Library for reading and writing FBX files 2 | 3 | This library allows you to read and write fbx files. 4 | 5 | It currently supports full fbx binary format. It works even with larger files. 6 | 7 | Also includes fbxdump which allows you to inspect fbx files in json format. 8 | 9 | # References 10 | 11 | [FBX format description](https://code.blender.org/2013/08/fbx-binary-file-format-specification/) 12 | 13 | [FBX file structure](https://web.archive.org/web/20160605023014/https://wiki.blender.org/index.php/User:Mont29/Foundation/FBX_File_Structure) 14 | 15 | # Javascript port 16 | 17 | [fbx.js](https://github.com/jskorepa/fbx.js) 18 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(fbx-writer) 3 | 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -std=c++17 -lstdc++") 5 | 6 | find_package( ZLIB REQUIRED ) 7 | 8 | include_directories( ${ZLIB_INCLUDE_DIRS} ) 9 | 10 | set(SOURCE_FILES fbxdocument.cpp fbxnode.cpp fbxutil.cpp fbxproperty.cpp) 11 | 12 | add_executable(fbx-writer main.cpp ${SOURCE_FILES}) 13 | target_link_libraries(fbx-writer ${ZLIB_LIBRARIES}) 14 | 15 | add_executable(fbxdump fbxdump.cpp ${SOURCE_FILES}) 16 | target_link_libraries(fbxdump ${ZLIB_LIBRARIES}) 17 | -------------------------------------------------------------------------------- /src/fbxdocument.cpp: -------------------------------------------------------------------------------- 1 | #include "fbxdocument.h" 2 | #include "fbxutil.h" 3 | 4 | using std::string; 5 | using std::cout; 6 | using std::endl; 7 | using std::ifstream; 8 | using std::ofstream; 9 | using std::uint32_t; 10 | using std::uint8_t; 11 | 12 | namespace fbx { 13 | FBXDocument::FBXDocument() 14 | { 15 | version = 7400; 16 | } 17 | 18 | void FBXDocument::read(string fname) 19 | { 20 | ifstream file; 21 | 22 | // buffer 23 | int bufferSize = 1 << 16; 24 | char buffer[bufferSize]; 25 | file.rdbuf()->pubsetbuf(buffer, bufferSize); 26 | 27 | file.open(fname, std::ios::in | std::ios::binary); 28 | if (file.is_open()) { 29 | read(file); 30 | } else { 31 | throw std::string("Cannot read from file: \"" + fname + "\""); 32 | } 33 | file.close(); 34 | } 35 | 36 | void FBXDocument::write(string fname) 37 | { 38 | ofstream file; 39 | 40 | // buffer 41 | int bufferSize = 1 << 16; 42 | char buffer[bufferSize]; 43 | file.rdbuf()->pubsetbuf(buffer, bufferSize); 44 | 45 | file.open(fname, std::ios::out | std::ios::binary); 46 | if (file.is_open()) { 47 | write(file); 48 | } else { 49 | throw std::string("Cannot write to file: \"" + fname + "\""); 50 | } 51 | file.close(); 52 | } 53 | 54 | bool checkMagic(Reader &reader) 55 | { 56 | string magic("Kaydara FBX Binary "); 57 | for(char c : magic) { 58 | if(reader.readUint8() != c) return false; 59 | } 60 | if(reader.readUint8() != 0x00) return false; 61 | if(reader.readUint8() != 0x1A) return false; 62 | if(reader.readUint8() != 0x00) return false; 63 | return true; 64 | } 65 | 66 | void FBXDocument::read(std::ifstream &input) 67 | { 68 | Reader reader(&input); 69 | input >> std::noskipws; 70 | if(!checkMagic(reader)) throw std::string("Not a FBX file"); 71 | 72 | uint32_t version = reader.readUint32(); 73 | 74 | uint32_t maxVersion = 7400; 75 | if(version > maxVersion) throw "Unsupported FBX version "+std::to_string(version) 76 | + " latest supported version is "+std::to_string(maxVersion); 77 | 78 | uint32_t start_offset = 27; // magic: 21+2, version: 4 79 | do{ 80 | FBXNode node; 81 | start_offset += node.read(input, start_offset); 82 | if(node.isNull()) break; 83 | nodes.push_back(node); 84 | } while(true); 85 | } 86 | 87 | namespace { 88 | void writerFooter(Writer &writer) { 89 | uint8_t footer[] = { 90 | 0xfa, 0xbc, 0xab, 0x09, 91 | 0xd0, 0xc8, 0xd4, 0x66, 0xb1, 0x76, 0xfb, 0x83, 0x1c, 0xf7, 0x26, 0x7e, 0x00, 0x00, 0x00, 0x00, 92 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 93 | 0xe8, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 94 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 95 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 96 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 97 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 98 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 99 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 100 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x5a, 0x8c, 0x6a, 101 | 0xde, 0xf5, 0xd9, 0x7e, 0xec, 0xe9, 0x0c, 0xe3, 0x75, 0x8f, 0x29, 0x0b 102 | }; 103 | for(unsigned int i = 0; i < sizeof(footer); i++) { 104 | writer.write(footer[i]); 105 | } 106 | } 107 | 108 | } 109 | 110 | void FBXDocument::write(std::ofstream &output) 111 | { 112 | Writer writer(&output); 113 | writer.write("Kaydara FBX Binary "); 114 | writer.write((uint8_t) 0); 115 | writer.write((uint8_t) 0x1A); 116 | writer.write((uint8_t) 0); 117 | writer.write(version); 118 | 119 | uint32_t offset = 27; // magic: 21+2, version: 4 120 | for(FBXNode node : nodes) { 121 | offset += node.write(output, offset); 122 | } 123 | FBXNode nullNode; 124 | offset += nullNode.write(output, offset); 125 | writerFooter(writer); 126 | } 127 | 128 | void FBXDocument::createBasicStructure() 129 | { 130 | FBXNode headerExtension("FBXHeaderExtension"); 131 | headerExtension.addPropertyNode("FBXHeaderVersion", (int32_t) 1003); 132 | headerExtension.addPropertyNode("FBXVersion", (int32_t) getVersion()); 133 | headerExtension.addPropertyNode("EncryptionType", (int32_t) 0); 134 | { 135 | FBXNode creationTimeStamp("CreationTimeStamp"); 136 | creationTimeStamp.addPropertyNode("Version", (int32_t) 1000); 137 | creationTimeStamp.addPropertyNode("Year", (int32_t) 2017); 138 | creationTimeStamp.addPropertyNode("Month", (int32_t) 5); 139 | creationTimeStamp.addPropertyNode("Day", (int32_t) 2); 140 | creationTimeStamp.addPropertyNode("Hour", (int32_t) 14); 141 | creationTimeStamp.addPropertyNode("Minute", (int32_t) 11); 142 | creationTimeStamp.addPropertyNode("Second", (int32_t) 46); 143 | creationTimeStamp.addPropertyNode("Millisecond", (int32_t) 917); 144 | headerExtension.addChild(creationTimeStamp); 145 | } 146 | headerExtension.addPropertyNode("Creator", "Blender (stable FBX IO) - 2.78 (sub 0) - 3.7.7"); 147 | { 148 | FBXNode sceneInfo("SceneInfo"); 149 | sceneInfo.addProperty(std::vector({'G','l','o','b','a','l','I','n','f','o',0,1,'S','c','e','n','e','I','n','f','o'}), 'S'); 150 | sceneInfo.addProperty("UserData"); 151 | sceneInfo.addPropertyNode("Type", "UserData"); 152 | sceneInfo.addPropertyNode("Version", 100); 153 | { 154 | FBXNode metadata("MetaData"); 155 | metadata.addPropertyNode("Version", 100); 156 | metadata.addPropertyNode("Title", ""); 157 | metadata.addPropertyNode("Subject", ""); 158 | metadata.addPropertyNode("Author", ""); 159 | metadata.addPropertyNode("Keywords", ""); 160 | metadata.addPropertyNode("Revision", ""); 161 | metadata.addPropertyNode("Comment", ""); 162 | sceneInfo.addChild(metadata); 163 | } 164 | { 165 | FBXNode properties("Properties70"); 166 | { 167 | FBXNode p("P"); 168 | p.addProperty("DocumentUrl"); 169 | p.addProperty("KString"); 170 | p.addProperty("Url"); 171 | p.addProperty(""); 172 | p.addProperty("/foobar.fbx"); 173 | properties.addChild(p); 174 | } 175 | { 176 | FBXNode p("P"); 177 | p.addProperty("SrcDocumentUrl"); 178 | p.addProperty("KString"); 179 | p.addProperty("Url"); 180 | p.addProperty(""); 181 | p.addProperty("/foobar.fbx"); 182 | properties.addChild(p); 183 | } 184 | { 185 | FBXNode p("P"); 186 | p.addProperty("Original"); 187 | p.addProperty("Compound"); 188 | p.addProperty(""); 189 | p.addProperty(""); 190 | properties.addChild(p); 191 | } 192 | { 193 | FBXNode p("P"); 194 | p.addProperty("Original|ApplicationVendor"); 195 | p.addProperty("KString"); 196 | p.addProperty(""); 197 | p.addProperty(""); 198 | p.addProperty("Blender Foundation"); 199 | properties.addChild(p); 200 | } 201 | { 202 | FBXNode p("P"); 203 | p.addProperty("Original|ApplicationName"); 204 | p.addProperty("KString"); 205 | p.addProperty(""); 206 | p.addProperty(""); 207 | p.addProperty("Blender (stable FBX IO)"); 208 | properties.addChild(p); 209 | } 210 | { 211 | FBXNode p("P"); 212 | p.addProperty("Original|ApplicationVersion"); 213 | p.addProperty("KString"); 214 | p.addProperty(""); 215 | p.addProperty(""); 216 | p.addProperty("2.78 (sub 0)"); 217 | properties.addChild(p); 218 | } 219 | { 220 | FBXNode p("P"); 221 | p.addProperty("Original|DateTime_GMT"); 222 | p.addProperty("DateTime"); 223 | p.addProperty(""); 224 | p.addProperty(""); 225 | p.addProperty("01/01/1970 00:00:00.000"); 226 | properties.addChild(p); 227 | } 228 | { 229 | FBXNode p("P"); 230 | p.addProperty("Original|FileName"); 231 | p.addProperty("KString"); 232 | p.addProperty(""); 233 | p.addProperty(""); 234 | p.addProperty("/foobar.fbx"); 235 | properties.addChild(p); 236 | } 237 | { 238 | FBXNode p("P"); 239 | p.addProperty("LastSaved"); 240 | p.addProperty("Compound"); 241 | p.addProperty(""); 242 | p.addProperty(""); 243 | properties.addChild(p); 244 | } 245 | { 246 | FBXNode p("P"); 247 | p.addProperty("LastSaved|ApplicationVendor"); 248 | p.addProperty("KString"); 249 | p.addProperty(""); 250 | p.addProperty(""); 251 | p.addProperty("Blender Foundation"); 252 | properties.addChild(p); 253 | } 254 | { 255 | FBXNode p("P"); 256 | p.addProperty("LastSaved|ApplicationName"); 257 | p.addProperty("KString"); 258 | p.addProperty(""); 259 | p.addProperty(""); 260 | p.addProperty("Blender (stable FBX IO)"); 261 | properties.addChild(p); 262 | } 263 | { 264 | FBXNode p("P"); 265 | p.addProperty("LastSaved|DateTime_GMT"); 266 | p.addProperty("DateTime"); 267 | p.addProperty(""); 268 | p.addProperty(""); 269 | p.addProperty("01/01/1970 00:00:00.000"); 270 | properties.addChild(p); 271 | } 272 | sceneInfo.addChild(properties); 273 | } 274 | headerExtension.addChild(sceneInfo); 275 | } 276 | nodes.push_back(headerExtension); 277 | 278 | 279 | } 280 | 281 | std::uint32_t FBXDocument::getVersion() 282 | { 283 | return version; 284 | } 285 | 286 | void FBXDocument::print() 287 | { 288 | cout << "{\n"; 289 | cout << " \"version\": " << getVersion() << ",\n"; 290 | cout << " \"children\": [\n"; 291 | bool hasPrev = false; 292 | for(auto node : nodes) { 293 | if(hasPrev) cout << ",\n"; 294 | node.print(" "); 295 | hasPrev = true; 296 | } 297 | cout << "\n ]\n}" << endl; 298 | } 299 | 300 | } // namespace fbx 301 | -------------------------------------------------------------------------------- /src/fbxdocument.h: -------------------------------------------------------------------------------- 1 | #ifndef FBXDOCUMENT_H 2 | #define FBXDOCUMENT_H 3 | 4 | #include "fbxnode.h" 5 | 6 | namespace fbx { 7 | 8 | class FBXDocument 9 | { 10 | public: 11 | FBXDocument(); 12 | void read(std::ifstream &input); 13 | void read(std::string fname); 14 | void write(std::string fname); 15 | void write(std::ofstream &output); 16 | 17 | void createBasicStructure(); 18 | 19 | std::vector nodes; 20 | 21 | std::uint32_t getVersion(); 22 | void print(); 23 | 24 | private: 25 | std::uint32_t version; 26 | }; 27 | 28 | } // namespace fbx 29 | 30 | #endif // FBXDOCUMENT_H 31 | -------------------------------------------------------------------------------- /src/fbxdump.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "fbxdocument.h" 6 | using std::cout; 7 | using std::cerr; 8 | using std::endl; 9 | using std::string; 10 | using namespace fbx; 11 | 12 | bool findNode(std::string name, FBXNode where) { 13 | if(where.getName() == name) { 14 | where.print(); 15 | return true; 16 | } 17 | for(FBXNode n : where.getChildren()) { 18 | if(findNode(name, n)) { 19 | return true; 20 | } 21 | } 22 | return false; 23 | } 24 | 25 | int main(int argc, char** argv) { 26 | if(argc < 2) { 27 | cerr << "Specify file which you want to dump" << endl; 28 | return 1; 29 | } 30 | 31 | try { 32 | fbx::FBXDocument d; 33 | d.read(argv[1]); 34 | if(argc >= 3) { 35 | for(auto n : d.nodes) { 36 | if(findNode(argv[2], n)) break; 37 | } 38 | } else { 39 | d.print(); 40 | } 41 | 42 | } catch(string s) { 43 | cerr << "ERROR: " << s << endl; 44 | return 2; 45 | } 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /src/fbxnode.cpp: -------------------------------------------------------------------------------- 1 | #include "fbxnode.h" 2 | 3 | #include "fbxutil.h" 4 | using std::string; 5 | using std::cout; 6 | using std::endl; 7 | using std::ifstream; 8 | using std::uint32_t; 9 | using std::uint8_t; 10 | 11 | namespace fbx { 12 | 13 | FBXNode::FBXNode() 14 | { 15 | } 16 | 17 | FBXNode::FBXNode(std::string name):name(name) {} 18 | 19 | uint32_t FBXNode::read(std::ifstream &input, uint32_t start_offset) 20 | { 21 | Reader reader(&input); 22 | uint32_t bytes = 0; 23 | 24 | uint32_t endOffset = reader.readUint32(); 25 | uint32_t numProperties = reader.readUint32(); 26 | uint32_t propertyListLength = reader.readUint32(); 27 | uint8_t nameLength = reader.readUint8(); 28 | name = reader.readString(nameLength); 29 | bytes += 13 + nameLength; 30 | 31 | //std::cout << "so: " << start_offset 32 | // << "\tbytes: " << (endOffset == 0 ? 0 : (endOffset - start_offset)) 33 | // << "\tnumProp: " << numProperties 34 | // << "\tpropListLen: " << propertyListLength 35 | // << "\tnameLen: " << std::to_string(nameLength) 36 | // << "\tname: " << name << "\n"; 37 | 38 | for(uint32_t i = 0; i < numProperties; i++) { 39 | addProperty(FBXProperty(input)); 40 | } 41 | bytes += propertyListLength; 42 | 43 | while(start_offset + bytes < endOffset) { 44 | FBXNode child; 45 | bytes += child.read(input, start_offset + bytes); 46 | addChild(std::move(child)); 47 | } 48 | return bytes; 49 | } 50 | 51 | uint32_t FBXNode::write(std::ofstream &output, uint32_t start_offset) 52 | { 53 | Writer writer(&output); 54 | 55 | if(isNull()) { 56 | //std::cout << "so: " << start_offset 57 | // << "\tbytes: 0" 58 | // << "\tnumProp: 0" 59 | // << "\tpropListLen: 0" 60 | // << "\tnameLen: 0" 61 | // << "\tname: \n"; 62 | for(int i = 0; i < 13; i++) writer.write((uint8_t) 0); 63 | return 13; 64 | } 65 | 66 | uint32_t propertyListLength = 0; 67 | for(auto prop : properties) propertyListLength += prop.getBytes(); 68 | uint32_t bytes = 13 + name.length() + propertyListLength; 69 | for(auto child : children) bytes += child.getBytes(); 70 | 71 | if(bytes != getBytes()) throw std::string("bytes != getBytes()"); 72 | writer.write(start_offset + bytes); // endOffset 73 | writer.write((uint32_t) properties.size()); // numProperties 74 | writer.write(propertyListLength); // propertyListLength 75 | writer.write((uint8_t) name.length()); 76 | writer.write(name); 77 | 78 | //std::cout << "so: " << start_offset 79 | // << "\tbytes: " << bytes 80 | // << "\tnumProp: " << properties.size() 81 | // << "\tpropListLen: " << propertyListLength 82 | // << "\tnameLen: " << name.length() 83 | // << "\tname: " << name << "\n"; 84 | 85 | bytes = 13 + name.length() + propertyListLength; 86 | 87 | for(auto prop : properties) prop.write(output); 88 | for(auto child : children) bytes += child.write(output, start_offset + bytes); 89 | 90 | return bytes; 91 | } 92 | 93 | void FBXNode::print(std::string prefix) 94 | { 95 | cout << prefix << "{ \"name\": \"" << name << "\"" << (properties.size() + children.size() > 0 ? ",\n" : "\n"); 96 | if(properties.size() > 0) { 97 | cout << prefix << " \"properties\": [\n"; 98 | bool hasPrev = false; 99 | for(FBXProperty prop : properties) { 100 | if(hasPrev) cout << ",\n"; 101 | cout << prefix << " { \"type\": \"" << prop.getType() << "\", \"value\": " << prop.to_string() << " }"; 102 | hasPrev = true; 103 | } 104 | cout << "\n"; 105 | cout << prefix << " ]" << (children.size() > 0 ? ",\n" : "\n"); 106 | 107 | } 108 | 109 | if(children.size() > 0) { 110 | cout << prefix << " \"children\": [\n"; 111 | bool hasPrev = false; 112 | for(FBXNode node : children) { 113 | if(hasPrev) cout << ",\n"; 114 | node.print(prefix+" "); 115 | hasPrev = true; 116 | } 117 | cout << "\n"; 118 | cout << prefix << " ]\n"; 119 | } 120 | 121 | cout << prefix << "}"; 122 | 123 | } 124 | 125 | bool FBXNode::isNull() 126 | { 127 | return children.size() == 0 128 | && properties.size() == 0 129 | && name.length() == 0; 130 | } 131 | 132 | // primitive values 133 | void FBXNode::addProperty(int16_t v) { addProperty(FBXProperty(v)); } 134 | void FBXNode::addProperty(bool v) { addProperty(FBXProperty(v)); } 135 | void FBXNode::addProperty(int32_t v) { addProperty(FBXProperty(v)); } 136 | void FBXNode::addProperty(float v) { addProperty(FBXProperty(v)); } 137 | void FBXNode::addProperty(double v) { addProperty(FBXProperty(v)); } 138 | void FBXNode::addProperty(int64_t v) { addProperty(FBXProperty(v)); } 139 | // arrays 140 | void FBXNode::addProperty(const std::vector v) { addProperty(FBXProperty(v)); } 141 | void FBXNode::addProperty(const std::vector v) { addProperty(FBXProperty(v)); } 142 | void FBXNode::addProperty(const std::vector v) { addProperty(FBXProperty(v)); } 143 | void FBXNode::addProperty(const std::vector v) { addProperty(FBXProperty(v)); } 144 | void FBXNode::addProperty(const std::vector v) { addProperty(FBXProperty(v)); } 145 | // raw / string 146 | void FBXNode::addProperty(const std::vector v, uint8_t type) { addProperty(FBXProperty(v, type)); } 147 | void FBXNode::addProperty(const std::string v) { addProperty(FBXProperty(v)); } 148 | void FBXNode::addProperty(const char *v) { addProperty(FBXProperty(v)); } 149 | 150 | void FBXNode::addProperty(FBXProperty prop) { properties.push_back(prop); } 151 | 152 | 153 | void FBXNode::addPropertyNode(const std::string name, int16_t v) { FBXNode n(name); n.addProperty(v); addChild(n); } 154 | void FBXNode::addPropertyNode(const std::string name, bool v) { FBXNode n(name); n.addProperty(v); addChild(n); } 155 | void FBXNode::addPropertyNode(const std::string name, int32_t v) { FBXNode n(name); n.addProperty(v); addChild(n); } 156 | void FBXNode::addPropertyNode(const std::string name, float v) { FBXNode n(name); n.addProperty(v); addChild(n); } 157 | void FBXNode::addPropertyNode(const std::string name, double v) { FBXNode n(name); n.addProperty(v); addChild(n); } 158 | void FBXNode::addPropertyNode(const std::string name, int64_t v) { FBXNode n(name); n.addProperty(v); addChild(n); } 159 | void FBXNode::addPropertyNode(const std::string name, const std::vector v) { FBXNode n(name); n.addProperty(v); addChild(n); } 160 | void FBXNode::addPropertyNode(const std::string name, const std::vector v) { FBXNode n(name); n.addProperty(v); addChild(n); } 161 | void FBXNode::addPropertyNode(const std::string name, const std::vector v) { FBXNode n(name); n.addProperty(v); addChild(n); } 162 | void FBXNode::addPropertyNode(const std::string name, const std::vector v) { FBXNode n(name); n.addProperty(v); addChild(n); } 163 | void FBXNode::addPropertyNode(const std::string name, const std::vector v) { FBXNode n(name); n.addProperty(v); addChild(n); } 164 | void FBXNode::addPropertyNode(const std::string name, const std::vector v, uint8_t type) { FBXNode n(name); n.addProperty(v, type); addChild(n); } 165 | void FBXNode::addPropertyNode(const std::string name, const std::string v) { FBXNode n(name); n.addProperty(v); addChild(n); } 166 | void FBXNode::addPropertyNode(const std::string name, const char *v) { FBXNode n(name); n.addProperty(v); addChild(n); } 167 | 168 | void FBXNode::addChild(FBXNode child) { children.push_back(child); } 169 | 170 | uint32_t FBXNode::getBytes() { 171 | uint32_t bytes = 13 + name.length(); 172 | for(auto child : children) { 173 | bytes += child.getBytes(); 174 | } 175 | for(auto prop : properties) { 176 | bytes += prop.getBytes(); 177 | } 178 | return bytes; 179 | } 180 | 181 | const std::vector FBXNode::getChildren() 182 | { 183 | return children; 184 | } 185 | 186 | const std::string FBXNode::getName() 187 | { 188 | return name; 189 | } 190 | 191 | } // namespace fbx 192 | -------------------------------------------------------------------------------- /src/fbxnode.h: -------------------------------------------------------------------------------- 1 | #ifndef FBXNODE_H 2 | #define FBXNODE_H 3 | 4 | #include "fbxproperty.h" 5 | 6 | namespace fbx { 7 | 8 | class FBXNode 9 | { 10 | public: 11 | FBXNode(); 12 | FBXNode(std::string name); 13 | 14 | std::uint32_t read(std::ifstream &input, uint32_t start_offset); 15 | std::uint32_t write(std::ofstream &output, uint32_t start_offset); 16 | void print(std::string prefix=""); 17 | bool isNull(); 18 | 19 | void addProperty(int16_t); 20 | void addProperty(bool); 21 | void addProperty(int32_t); 22 | void addProperty(float); 23 | void addProperty(double); 24 | void addProperty(int64_t); 25 | void addProperty(const std::vector); 26 | void addProperty(const std::vector); 27 | void addProperty(const std::vector); 28 | void addProperty(const std::vector); 29 | void addProperty(const std::vector); 30 | void addProperty(const std::vector, uint8_t type); 31 | void addProperty(const std::string); 32 | void addProperty(const char*); 33 | void addProperty(FBXProperty); 34 | 35 | void addPropertyNode(const std::string name, int16_t); 36 | void addPropertyNode(const std::string name, bool); 37 | void addPropertyNode(const std::string name, int32_t); 38 | void addPropertyNode(const std::string name, float); 39 | void addPropertyNode(const std::string name, double); 40 | void addPropertyNode(const std::string name, int64_t); 41 | void addPropertyNode(const std::string name, const std::vector); 42 | void addPropertyNode(const std::string name, const std::vector); 43 | void addPropertyNode(const std::string name, const std::vector); 44 | void addPropertyNode(const std::string name, const std::vector); 45 | void addPropertyNode(const std::string name, const std::vector); 46 | void addPropertyNode(const std::string name, const std::vector, uint8_t type); 47 | void addPropertyNode(const std::string name, const std::string); 48 | void addPropertyNode(const std::string name, const char*); 49 | 50 | void addChild(FBXNode child); 51 | uint32_t getBytes(); 52 | 53 | const std::vector getChildren(); 54 | const std::string getName(); 55 | private: 56 | std::vector children; 57 | std::vector properties; 58 | std::string name; 59 | }; 60 | 61 | } // namespace fbx 62 | 63 | #endif // FBXNODE_H 64 | -------------------------------------------------------------------------------- /src/fbxproperty.cpp: -------------------------------------------------------------------------------- 1 | #include "fbxproperty.h" 2 | #include "fbxutil.h" 3 | #include 4 | #include 5 | 6 | using std::cout; 7 | using std::endl; 8 | using std::string; 9 | 10 | namespace fbx { 11 | 12 | namespace { // helpers for reading properties 13 | FBXPropertyValue readPrimitiveValue(Reader &reader, char type) 14 | { 15 | FBXPropertyValue value; 16 | if(type == 'Y') { // 2 byte signed integer 17 | value.i16 = reader.readInt16(); 18 | } else if(type == 'C' || type == 'B') { // 1 bit boolean (1: true, 0: false) encoded as the LSB of a 1 Byte value. 19 | value.boolean = reader.readUint8() != 0; 20 | } else if(type == 'I') { // 4 byte signed Integer 21 | value.i32 = reader.readInt32(); 22 | } else if(type == 'F') { // 4 byte single-precision IEEE 754 number 23 | value.f32 = reader.readFloat(); 24 | } else if(type == 'D') { // 8 byte double-precision IEEE 754 number 25 | value.f64 = reader.readDouble(); 26 | } else if(type == 'L') { // 8 byte signed Integer 27 | value.i64 = reader.readUint64(); 28 | } else { 29 | throw std::string("Unsupported property type ")+std::to_string(type); 30 | } 31 | return value; 32 | } 33 | 34 | uint32_t arrayElementSize(char type) 35 | { 36 | if(type == 'Y') { // 2 byte signed integer 37 | return 2; 38 | } else if(type == 'C' || type == 'B') { // 1 bit boolean (1: true, 0: false) encoded as the LSB of a 1 Byte value. 39 | return 1; 40 | } else if(type == 'I') { // 4 byte signed Integer 41 | return 4; 42 | } else if(type == 'F') { // 4 byte single-precision IEEE 754 number 43 | return 4; 44 | } else if(type == 'D') { // 8 byte double-precision IEEE 754 number 45 | return 8; 46 | } else if(type == 'L') { // 8 byte signed Integer 47 | return 8; 48 | } else { 49 | return 0; 50 | } 51 | } 52 | 53 | class STRMAutoCloser 54 | { 55 | public: 56 | z_stream *strm; 57 | STRMAutoCloser(z_stream& _strm):strm(&_strm) {} 58 | 59 | ~STRMAutoCloser() { 60 | (void)inflateEnd(strm); 61 | } 62 | }; 63 | 64 | class BufferAutoFree 65 | { 66 | public: 67 | uint8_t *buffer; 68 | BufferAutoFree(uint8_t *buf):buffer(buf) {} 69 | 70 | ~BufferAutoFree() { 71 | free(buffer); 72 | } 73 | }; 74 | } 75 | 76 | FBXProperty::FBXProperty(std::ifstream &input) 77 | { 78 | Reader reader(&input); 79 | type = reader.readUint8(); 80 | // std::cout << " " << type << "\n"; 81 | if(type == 'S' || type == 'R') { 82 | uint32_t length = reader.readUint32(); 83 | for(uint32_t i = 0; i < length; i++){ 84 | uint8_t v = reader.readUint8(); 85 | raw.push_back(v); 86 | } 87 | } else if(type < 'Z') { // primitive types 88 | value = readPrimitiveValue(reader, type); 89 | } else { 90 | uint32_t arrayLength = reader.readUint32(); // number of elements in array 91 | uint32_t encoding = reader.readUint32(); // 0 .. uncompressed, 1 .. zlib-compressed 92 | uint32_t compressedLength = reader.readUint32(); 93 | if(encoding) { 94 | uint64_t uncompressedLength = arrayElementSize(type - ('a'-'A')) * arrayLength; 95 | 96 | uint8_t *decompressedBuffer = (uint8_t*) malloc(uncompressedLength); 97 | if(decompressedBuffer == NULL) throw std::string("Malloc failed"); 98 | BufferAutoFree baf(decompressedBuffer); 99 | 100 | uint8_t compressedBuffer[compressedLength]; 101 | reader.read((char*)compressedBuffer, compressedLength); 102 | 103 | uint64_t destLen = uncompressedLength; 104 | uint64_t srcLen = compressedLength; 105 | uncompress2(decompressedBuffer, &destLen, compressedBuffer, &srcLen); 106 | 107 | if(srcLen != compressedLength) throw std::string("compressedLength does not match data"); 108 | if(destLen != uncompressedLength) throw std::string("uncompressedLength does not match data"); 109 | 110 | Reader r((char*)decompressedBuffer); 111 | 112 | for(uint32_t i = 0; i < arrayLength; i++) { 113 | values.push_back(readPrimitiveValue(r, type - ('a'-'A'))); 114 | } 115 | } else { 116 | for(uint32_t i = 0; i < arrayLength; i++) { 117 | values.push_back(readPrimitiveValue(reader, type - ('a'-'A'))); 118 | } 119 | } 120 | } 121 | } 122 | 123 | void FBXProperty::write(std::ofstream &output) 124 | { 125 | Writer writer(&output); 126 | 127 | writer.write(type); 128 | if(type == 'Y') { 129 | writer.write(value.i16); 130 | } else if(type == 'C') { 131 | writer.write((uint8_t)(value.boolean ? 1 : 0)); 132 | } else if(type == 'I') { 133 | writer.write(value.i32); 134 | } else if(type == 'F') { 135 | writer.write(value.f32); 136 | } else if(type == 'D') { 137 | writer.write(value.f64); 138 | } else if(type == 'L') { 139 | writer.write(value.i64); 140 | } else if(type == 'R' || type == 'S') { 141 | writer.write((uint32_t)raw.size()); 142 | for(char c : raw) { 143 | writer.write((uint8_t)c); 144 | } 145 | } else { 146 | writer.write((uint32_t) values.size()); // arrayLength 147 | writer.write((uint32_t) 0); // encoding // TODO: support compression 148 | uint32_t compressedLength = 0; 149 | if(type == 'f') compressedLength = values.size() * 4; 150 | else if(type == 'd') compressedLength = values.size() * 8; 151 | else if(type == 'l') compressedLength = values.size() * 8; 152 | else if(type == 'i') compressedLength = values.size() * 4; 153 | else if(type == 'b') compressedLength = values.size() * 1; 154 | else throw std::string("Invalid property"); 155 | writer.write(compressedLength); 156 | 157 | for(auto e : values) { 158 | if(type == 'f') writer.write(e.f32); 159 | else if(type == 'd') writer.write(e.f64); 160 | else if(type == 'l') writer.write((int64_t)(e.i64)); 161 | else if(type == 'i') writer.write(e.i32); 162 | else if(type == 'b') writer.write((uint8_t)(e.boolean ? 1 : 0)); 163 | else throw std::string("Invalid property"); 164 | } 165 | } 166 | } 167 | 168 | // primitive values 169 | FBXProperty::FBXProperty(int16_t a) { type = 'Y'; value.i16 = a; } 170 | FBXProperty::FBXProperty(bool a) { type = 'C'; value.boolean = a; } 171 | FBXProperty::FBXProperty(int32_t a) { type = 'I'; value.i32 = a; } 172 | FBXProperty::FBXProperty(float a) { type = 'F'; value.f32 = a; } 173 | FBXProperty::FBXProperty(double a) { type = 'D'; value.f64 = a; } 174 | FBXProperty::FBXProperty(int64_t a) { type = 'L'; value.i64 = a; } 175 | // arrays 176 | FBXProperty::FBXProperty(const std::vector a) : type('b'), values(a.size()) { 177 | for(auto el : a) { 178 | FBXPropertyValue v; 179 | v.boolean = el; 180 | values.push_back(v); 181 | } 182 | } 183 | FBXProperty::FBXProperty(const std::vector a) : type('i'), values(a.size()) { 184 | for(auto el : a) { 185 | FBXPropertyValue v; 186 | v.i32 = el; 187 | values.push_back(v); 188 | } 189 | } 190 | FBXProperty::FBXProperty(const std::vector a) : type('f'), values(a.size()) { 191 | for(auto el : a) { 192 | FBXPropertyValue v; 193 | v.f32 = el; 194 | values.push_back(v); 195 | } 196 | } 197 | FBXProperty::FBXProperty(const std::vector a) : type('d'), values(a.size()) { 198 | for(auto el : a) { 199 | FBXPropertyValue v; 200 | v.f64 = el; 201 | values.push_back(v); 202 | } 203 | } 204 | FBXProperty::FBXProperty(const std::vector a) : type('l'), values(a.size()) { 205 | for(auto el : a) { 206 | FBXPropertyValue v; 207 | v.i64 = el; 208 | values.push_back(v); 209 | } 210 | } 211 | // raw / string 212 | FBXProperty::FBXProperty(const std::vector a, uint8_t type): raw(a) { 213 | if(type != 'R' && type != 'S') { 214 | throw std::string("Bad argument to FBXProperty constructor"); 215 | } 216 | this->type = type; 217 | } 218 | // string 219 | FBXProperty::FBXProperty(const std::string a){ 220 | for(uint8_t v : a) { 221 | raw.push_back(v); 222 | } 223 | this->type = 'S'; 224 | } 225 | FBXProperty::FBXProperty(const char *a){ 226 | for(;*a != 0; a++) { 227 | raw.push_back(*a); 228 | } 229 | this->type = 'S'; 230 | } 231 | 232 | namespace { 233 | char base16Letter(uint8_t n) { 234 | n %= 16; 235 | if(n <= 9) return n + '0'; 236 | return n + 'a' - 10; 237 | } 238 | std::string base16Number(uint8_t n) { 239 | return std::string() + base16Letter(n >> 4) + base16Letter(n); 240 | } 241 | } 242 | 243 | char FBXProperty::getType() 244 | { 245 | return type; 246 | } 247 | 248 | string FBXProperty::to_string() 249 | { 250 | if(type == 'Y') return std::to_string(value.i16); 251 | else if(type == 'C') return value.boolean ? "true" : "false"; 252 | else if(type == 'I') return std::to_string(value.i32); 253 | else if(type == 'F') return std::to_string(value.f32); 254 | else if(type == 'D') return std::to_string(value.f64); 255 | else if(type == 'L') return std::to_string(value.i64); 256 | else if(type == 'R') { 257 | string s("\""); 258 | for(char c : raw) { 259 | s += std::to_string(c) + " "; 260 | } 261 | return s + "\""; 262 | } else if(type == 'S') { 263 | string s("\""); 264 | for(uint8_t c : raw) { 265 | if(c == '\\') s += "\\\\"; 266 | else if(c >= 32 && c <= 126) s += c; 267 | else s = s + "\\u00" + base16Number(c); 268 | } 269 | return s + "\""; 270 | } else { 271 | string s("["); 272 | bool hasPrev = false; 273 | for(auto e : values) { 274 | if(hasPrev) s += ", "; 275 | if(type == 'f') s += std::to_string(e.f32); 276 | else if(type == 'd') s += std::to_string(e.f64); 277 | else if(type == 'l') s += std::to_string(e.i64); 278 | else if(type == 'i') s += std::to_string(e.i32); 279 | else if(type == 'b') s += (e.boolean ? "true" : "false"); 280 | hasPrev = true; 281 | } 282 | return s+"]"; 283 | } 284 | throw std::string("Invalid property"); 285 | } 286 | 287 | uint32_t FBXProperty::getBytes() 288 | { 289 | if(type == 'Y') return 2 + 1; // 2 for int16, 1 for type spec 290 | else if(type == 'C') return 1 + 1; 291 | else if(type == 'I') return 4 + 1; 292 | else if(type == 'F') return 4 + 1; 293 | else if(type == 'D') return 8 + 1; 294 | else if(type == 'L') return 8 + 1; 295 | else if(type == 'R') return raw.size() + 5; 296 | else if(type == 'S') return raw.size() + 5; 297 | else if(type == 'f') return values.size() * 4 + 13; 298 | else if(type == 'd') return values.size() * 8 + 13; 299 | else if(type == 'l') return values.size() * 8 + 13; 300 | else if(type == 'i') return values.size() * 4 + 13; 301 | else if(type == 'b') return values.size() * 1 + 13; 302 | throw std::string("Invalid property"); 303 | } 304 | 305 | } // namespace fbx 306 | -------------------------------------------------------------------------------- /src/fbxproperty.h: -------------------------------------------------------------------------------- 1 | #ifndef FBXPROPERTY_H 2 | #define FBXPROPERTY_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace fbx { 9 | 10 | // WARNING: (copied from fbxutil.h) 11 | // this assumes that float is 32bit and double is 64bit 12 | // both conforming to IEEE 754, it does not assume endianness 13 | // it also assumes that signed integers are two's complement 14 | union FBXPropertyValue { 15 | int16_t i16; // Y 16 | bool boolean; // C, b 17 | int32_t i32; // I, i 18 | float f32; // F, f 19 | double f64; // D, d 20 | int64_t i64; // L, l 21 | }; 22 | 23 | class FBXProperty 24 | { 25 | public: 26 | FBXProperty(std::ifstream &input); 27 | // primitive values 28 | FBXProperty(int16_t); 29 | FBXProperty(bool); 30 | FBXProperty(int32_t); 31 | FBXProperty(float); 32 | FBXProperty(double); 33 | FBXProperty(int64_t); 34 | // arrays 35 | FBXProperty(const std::vector); 36 | FBXProperty(const std::vector); 37 | FBXProperty(const std::vector); 38 | FBXProperty(const std::vector); 39 | FBXProperty(const std::vector); 40 | // raw / string 41 | FBXProperty(const std::vector, uint8_t type); 42 | FBXProperty(const std::string); 43 | FBXProperty(const char *); 44 | 45 | void write(std::ofstream &output); 46 | 47 | std::string to_string(); 48 | char getType(); 49 | 50 | bool is_array(); 51 | uint32_t getBytes(); 52 | private: 53 | uint8_t type; 54 | FBXPropertyValue value; 55 | std::vector raw; 56 | std::vector values; 57 | }; 58 | 59 | } // namespace fbx 60 | 61 | #endif // FBXPROPERTY_H 62 | -------------------------------------------------------------------------------- /src/fbxutil.cpp: -------------------------------------------------------------------------------- 1 | #include "fbxutil.h" 2 | 3 | namespace fbx { 4 | 5 | namespace { 6 | bool isLittleEndian() 7 | { 8 | uint16_t number = 0x1; 9 | char *numPtr = (char*)&number; 10 | return (numPtr[0] == 1); 11 | } 12 | } 13 | 14 | uint8_t Reader::readUint8() 15 | { 16 | return getc(); 17 | } 18 | 19 | int8_t Reader::readInt8() 20 | { 21 | return getc(); 22 | } 23 | 24 | uint16_t Reader::readUint16() 25 | { 26 | uint16_t i; 27 | char *c = (char *)(&i); 28 | if(isLittleEndian()) { 29 | read(c, 2); 30 | } else { 31 | c[1] = getc(); 32 | c[0] = getc(); 33 | } 34 | return i; 35 | } 36 | 37 | int16_t Reader::readInt16() 38 | { 39 | int16_t i; 40 | char *c = (char *)(&i); 41 | if(isLittleEndian()) { 42 | read(c, 2); 43 | } else { 44 | c[1] = getc(); 45 | c[0] = getc(); 46 | } 47 | return i; 48 | } 49 | 50 | uint32_t Reader::readUint32() 51 | { 52 | uint32_t i; 53 | char *c = (char *)(&i); 54 | if(isLittleEndian()) { 55 | read(c, 4); 56 | } else { 57 | c[3] = getc(); 58 | c[2] = getc(); 59 | c[1] = getc(); 60 | c[0] = getc(); 61 | } 62 | return i; 63 | } 64 | 65 | int32_t Reader::readInt32() 66 | { 67 | int32_t i; 68 | char *c = (char *)(&i); 69 | if(isLittleEndian()) { 70 | read(c, 4); 71 | } else { 72 | c[3] = getc(); 73 | c[2] = getc(); 74 | c[1] = getc(); 75 | c[0] = getc(); 76 | } 77 | return i; 78 | } 79 | 80 | uint64_t Reader::readUint64() 81 | { 82 | uint64_t i; 83 | char *c = (char *)(&i); 84 | if(isLittleEndian()) { 85 | read(c, 8); 86 | } else { 87 | c[7] = getc(); 88 | c[6] = getc(); 89 | c[5] = getc(); 90 | c[4] = getc(); 91 | c[3] = getc(); 92 | c[2] = getc(); 93 | c[1] = getc(); 94 | c[0] = getc(); 95 | } 96 | return i; 97 | } 98 | 99 | 100 | std::string Reader::readString(uint32_t length) 101 | { 102 | char buffer[length + 1]; 103 | buffer[length] = 0; 104 | if(length) read(buffer, length); 105 | return std::string(buffer); 106 | } 107 | 108 | float Reader::readFloat() 109 | { 110 | float f; 111 | char *c = (char *)(&f); 112 | if(isLittleEndian()) { 113 | read(c, 4); 114 | } else { 115 | c[3] = getc(); 116 | c[2] = getc(); 117 | c[1] = getc(); 118 | c[0] = getc(); 119 | } 120 | return f; 121 | } 122 | 123 | double Reader::readDouble() 124 | { 125 | double f; 126 | char *c = (char *)(&f); 127 | if(isLittleEndian()) { 128 | read(c, 8); 129 | } else { 130 | c[7] = getc(); 131 | c[6] = getc(); 132 | c[5] = getc(); 133 | c[4] = getc(); 134 | c[3] = getc(); 135 | c[2] = getc(); 136 | c[1] = getc(); 137 | c[0] = getc(); 138 | } 139 | return f; 140 | } 141 | 142 | Reader::Reader(std::ifstream *input) 143 | :ifstream(input),buffer(NULL),i(0) 144 | {} 145 | 146 | Reader::Reader(char *input) 147 | :ifstream(NULL),buffer(input),i(0) 148 | {} 149 | 150 | uint8_t Reader::getc() 151 | { 152 | uint8_t tmp; 153 | if(ifstream != NULL) (*ifstream) >> tmp; 154 | else tmp = buffer[i++]; 155 | return tmp; 156 | } 157 | 158 | void Reader::read(char *s, uint32_t n) 159 | { 160 | if(ifstream != NULL) { 161 | ifstream->read(s, n); 162 | } else for(uint32_t a = 0; a < n; a++) { 163 | s[a] = buffer[i++]; 164 | } 165 | } 166 | 167 | Writer::Writer(std::ofstream *output):ofstream(output){} 168 | 169 | void Writer::putc(uint8_t c) 170 | { 171 | (*ofstream) << c; 172 | } 173 | 174 | void Writer::write(std::uint8_t a) 175 | { 176 | putc(a); 177 | } 178 | 179 | void Writer::write(std::int8_t a) 180 | { 181 | putc(a); 182 | } 183 | 184 | void Writer::write(std::uint16_t a) 185 | { 186 | putc(a); 187 | putc(a >> 8); 188 | } 189 | 190 | void Writer::write(std::int16_t _a) 191 | { 192 | uint16_t a = *(int16_t*)((char*) &_a); 193 | putc(a); 194 | putc(a >> 8); 195 | } 196 | 197 | void Writer::write(std::uint32_t a) 198 | { 199 | putc(a); 200 | putc(a >> 8); 201 | putc(a >> 16); 202 | putc(a >> 24); 203 | } 204 | 205 | void Writer::write(std::int32_t _a) 206 | { 207 | uint32_t a = *(uint32_t*)((char*) &_a); 208 | putc(a); 209 | putc(a >> 8); 210 | putc(a >> 16); 211 | putc(a >> 24); 212 | } 213 | 214 | void Writer::write(std::uint64_t a) 215 | { 216 | putc(a); 217 | putc(a >> 8); 218 | putc(a >> 16); 219 | putc(a >> 24); 220 | putc(a >> 32); 221 | putc(a >> 40); 222 | putc(a >> 48); 223 | putc(a >> 56); 224 | } 225 | 226 | void Writer::write(std::int64_t _a) 227 | { 228 | uint64_t a = *(uint64_t*)((char*) &_a); 229 | putc(a); 230 | putc(a >> 8); 231 | putc(a >> 16); 232 | putc(a >> 24); 233 | putc(a >> 32); 234 | putc(a >> 40); 235 | putc(a >> 48); 236 | putc(a >> 56); 237 | } 238 | 239 | void Writer::write(std::string a) 240 | { 241 | for(char c : a) { 242 | putc(c); 243 | } 244 | } 245 | 246 | void Writer::write(float a) 247 | { 248 | char *c = (char *)(&a); 249 | if(isLittleEndian()) { 250 | for(int i = 0; i < 4; i++) { 251 | putc(c[i]); 252 | } 253 | } else { 254 | for(int i = 3; i >= 0; i--) { 255 | putc(c[i]); 256 | } 257 | } 258 | } 259 | 260 | void Writer::write(double a) 261 | { 262 | char *c = (char *)(&a); 263 | if(isLittleEndian()) { 264 | for(int i = 0; i < 8; i++) { 265 | putc(c[i]); 266 | } 267 | } else { 268 | for(int i = 7; i >= 0; i--) { 269 | putc(c[i]); 270 | } 271 | } 272 | } 273 | 274 | } // namespace fbx 275 | -------------------------------------------------------------------------------- /src/fbxutil.h: -------------------------------------------------------------------------------- 1 | #ifndef FBXUTIL_H 2 | #define FBXUTIL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace fbx { 12 | // WARNING: 13 | // this assumes that float is 32bit and double is 64bit 14 | // both conforming to IEEE 754, it does not assume endianness 15 | // it also assumes that signed integers are two's complement 16 | class Reader { 17 | public: 18 | Reader(std::ifstream *input); 19 | Reader(char *input); 20 | 21 | std::uint8_t readUint8(); 22 | std::int8_t readInt8(); 23 | std::uint16_t readUint16(); 24 | std::int16_t readInt16(); 25 | std::uint32_t readUint32(); 26 | std::int32_t readInt32(); 27 | std::uint64_t readUint64(); 28 | std::string readString(uint32_t length); 29 | float readFloat(); 30 | double readDouble(); 31 | 32 | void read(char*, uint32_t); 33 | private: 34 | uint8_t getc(); 35 | std::ifstream *ifstream; 36 | char *buffer; 37 | uint32_t i; 38 | }; 39 | class Writer { 40 | public: 41 | Writer(std::ofstream *output); 42 | 43 | void write(std::uint8_t); 44 | void write(std::int8_t); 45 | void write(std::uint16_t); 46 | void write(std::int16_t); 47 | void write(std::uint32_t); 48 | void write(std::int32_t); 49 | void write(std::uint64_t); 50 | void write(std::int64_t); 51 | void write(std::string); 52 | void write(float); 53 | void write(double); 54 | private: 55 | void putc(uint8_t); 56 | std::ofstream *ofstream; 57 | }; 58 | } 59 | 60 | #endif // FBXUTIL_H 61 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "fbxdocument.h" 5 | 6 | using std::cout; 7 | using std::endl; 8 | using namespace fbx; 9 | 10 | int main(int argc, char** argv) 11 | { 12 | if(argc < 2) { 13 | std::cout << "Specify file which you want to \"copy\"" << std::endl; 14 | return 1; 15 | } 16 | 17 | try { 18 | fbx::FBXDocument doc; 19 | std::cout << "Reading " << argv[1] << std::endl; 20 | doc.read(argv[1]); 21 | 22 | //doc.print(); 23 | std::cout << "Writing test.fbx" << std::endl; 24 | doc.write("test.fbx"); 25 | } catch(std::string e) { 26 | std::cout << e << std::endl; 27 | return 2; 28 | } 29 | 30 | return 0; 31 | } 32 | --------------------------------------------------------------------------------