// Copyright Epic Games, Inc. All Rights Reserved. #include "GEO/GEO.h" #if WITH_EDITOR #include "GEO/IFileStream.h" #include "HAL/PlatformFile.h" #include "HAL/PlatformFileManager.h" #ifdef USE_ZLIB #include "ChaosFlesh/ZIP.h" #endif #include "Containers/StringConv.h" #include #include #include #include #include #include namespace ChaosFlesh { using namespace std; //! Reads "abc 123", consuming quotes but returns unquoted. //! \p delim denotes the character to read to (and is discarded) //! \p bracketed specifies if the string is deliminated at the beginning and the end. bool Read(std::istream &inref, std::string &buffer, const char delim='"', const bool bracketed=true) { buffer.clear(); bool open=!bracketed; while(inref.good()) { char ch; inref.get(ch); // consume next char if(ch == delim) { if(!open) open = true; else return true; } else { buffer.append(1, ch); } } return false; } //! Given "[...]..." returns "[...]". Given "[...[...]...]..." returns "[...[...]...]". bool ReadMatching( std::istream &inref, std::string &buffer, const char openDelim='[', const char closeDelim=']', bool stripBrackets=false) { buffer.clear(); int open = 0; inref >> std::ws; // discard white space bool inQuote = false; const bool sameChar = openDelim == closeDelim; while(inref.good()) { if(!inQuote) // if not currently parsing a quoted string... inref >> std::ws; // discard white space char ch; inref.get(ch); // consume next char if(sameChar) { if(ch == openDelim) { if(!open) { open++; if(!stripBrackets) buffer.append(1, ch); } else { open--; if(!stripBrackets) buffer.append(1, ch); } } else buffer.append(1, ch); } else { if(ch == openDelim) { open++; if(!stripBrackets || (stripBrackets && open > 1)) buffer.append(1, ch); } else if(ch == closeDelim) { if(!stripBrackets || (stripBrackets && open > 1)) buffer.append(1, ch); open--; } else buffer.append(1, ch); } if(ch == '"') inQuote=!inQuote; if(!open) return true; } return false; } //! Read numeric characters from \p inref storing them in \p buffer, until a //! non-numeric character is encountered. Doesn't try to parse or validate. bool ReadNumeric(std::istream &inref, std::string &buffer) { buffer.clear(); inref >> std::ws; // discard white space while(inref.good()) { inref >> std::ws; // discard white space const char nch = inref.peek(); switch(nch) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'e': case 'E': case '.': case '-': buffer.append(1, nch); inref.ignore(); break; default: return !buffer.empty(); } } return !buffer.empty(); } std::string ParseString(const std::string& str) { if (str.empty()) return str; if (str.substr(0, 2) == "\\\"" && str.substr(str.size() - 2, 2) == "\\\"") return str.substr(2, str.size() - 4); else if (str.front() == '"' && str.back() == '"') return str.substr(1, str.size() - 2); return str; } bool ParseGeoKVPairs( std::istream &inref, TMap &kvp, const char *filename=nullptr, std::ostream *errorStream=nullptr, TArray *allValues=nullptr) { std::string key; key.reserve(128); std::string buffer; buffer.reserve(2048); bool globalOpenBracket = false; while(inref.good()) { inref >> std::ws; // discard white space // // Read key // if(key.empty()) { const char nch = inref.peek(); switch(nch) { case '[': // document and section (nv pair list) case '{': // dictionary parsing // Aside from the global opening bracket, we should not encounter brackets on the key side. if(globalOpenBracket) goto error; globalOpenBracket=true; inref.ignore(); // Skip this char continue; // Try again for key case ']': case '}': // Closing bracket must match the opening bracket. if(!globalOpenBracket) goto error; globalOpenBracket=false; inref.ignore(); // Skip this char break; // Stop parsing case '"': // Keys should be quoted strings. if(!Read(inref, buffer)) goto error; key = buffer; break; } if(key.empty()) { if(!globalOpenBracket) { // done reading break; } if(errorStream) *errorStream << "Failed to determine key at file position: " << inref.tellg() << "." << std::endl; goto error; } continue; } // // Read value // buffer.clear(); const char nch = inref.peek(); switch(nch) { case ',': // "", case ':': // "": inref.ignore(); // Skip this char continue; // Try again for value case '"': // ""," if(!Read(inref, buffer)) goto error; break; case '{': // "",{ if(!ReadMatching(inref, buffer, '{', '}')) goto error; break; case '[': // "",[ if(!ReadMatching(inref, buffer, '[', ']')) goto error; break; case 't': // "",true buffer = "true"; inref.ignore(4); if(!inref.good()) goto error; break; case 'f': // "",false buffer = "false"; inref.ignore(5); if(!inref.good()) goto error; break; default: // "", if(!ReadNumeric(inref, buffer)) goto error; break; } if(inref.peek()==',') // consume trailing ',' inref.ignore(); kvp.Add(FString(key.c_str()), buffer); if (allValues) { allValues->Add(key); allValues->Add(buffer); } key.clear(); buffer.clear(); } // end while(inref.good()) return true; error: if(errorStream && filename) *errorStream << "ParseGeoKVPairs(): parse error in file '" << filename << "' at position: " << inref.tellg() << "." << std::endl; return false; } bool ParseKVPairs( const std::string &str, TMap &kvp, const char *filename=nullptr, std::ostream *errorStream=nullptr, TArray* allValues=nullptr) { std::stringstream ss(str); return ParseGeoKVPairs(ss, kvp, filename, errorStream, allValues); } std::string FormatStringArray(const TArray &values, const char openBracket='[', const char closeBracket=']') { std::string retval; retval += openBracket; for(size_t i=0; i < values.Num(); i++) { if(i > 0) retval += ","; retval += '"' + values[i] + '"'; } retval += closeBracket; return retval; } /* //! Prints \p kvp by sorted keys. Values longer than \p maxLen are truncated in the middle: "abcd<...>wxyz". void DebugPrint(const TMap &kvp, const std::string &prefix=" ", const size_t maxLen=128) { //std::vector keys; //for(auto it=kvp.begin(), itEnd=kvp.end(); it != itEnd; ++it) // keys.push_back(it->first); //std::sort(keys.begin(), keys.end()); TArray keys; kvp.GetKeys(keys); keys.Sort(); for(const auto &key : keys) { //const std::string &value = kvp.find(key)->second; const std::string& value = *kvp.Find(key); if(value.size() > maxLen) { std::cout << prefix << key << " = " << value.substr(0,maxLen/2) << "<...>" << value.substr(value.size()-(maxLen/2)) << std::endl; } else { std::cout << prefix << key << " = " << value << std::endl; } } } void DebugPrint(const TMap> &kvp, const std::string &prefix=" ") { for(auto it=kvp.begin(), itEnd=kvp.end(); it != itEnd; ++it) DebugPrint(it->second, prefix+'.'+it->first+'.'); } */ //! Array format: //! [ [...],[...],... ] bool ParseArray(const std::string &strIn, TArray &values, const char openBracket='[', const char closeBracket=']', const bool stripInternalBrackets=false) { values.Empty(); bool open = false; std::string buffer; std::stringstream ss(strIn); ss >> std::ws; // discard white space while(ss.good()) { ss >> std::ws; // discard white space char ch = ss.peek(); if(ch == openBracket) { if(!open) { ss.ignore(); // consume this char open = true; continue; } else { if(ReadMatching(ss, buffer, openBracket, closeBracket, stripInternalBrackets)) values.Add(buffer); else return false; } } else if(ch == '"') // array of strings { if(ReadMatching(ss, buffer, '"', '"', stripInternalBrackets)) values.Add(buffer); else return false; } else if (isdigit(ch)) { buffer.resize(0); do { buffer.push_back(ch); ss.ignore(); // consume this char if (!ss.good()) break; ch = ss.peek(); } while (isdigit(ch)); values.Add(buffer); } else if(ch == ',') { ss.ignore(); // consume this char continue; } else if(ch == closeBracket) { break; } else { std::cout << "ParseArray() - parse failure at position " << ss.tellg() << ", expected '[],', got '" << ch << "', " << " input string: '" << strIn << "'" << std::endl; return false; } } return true; } #if defined(__GNUC__) // The gnu stdlib has issues with the floating point versions of std::from_chars() (their // implementations allocate memory, which the standard forbids), and they hide them behind // a macro through v20. Lame. See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100146 bool ParseNum(const std::string &strIn, double &value) { const char* str = strIn.c_str(); char* strEnd = nullptr; value = std::strtod(str, &strEnd); if(value == 0. && strEnd == str) return false; return true; } bool ParseNum(const std::string &strIn, float &value) { const char* str = strIn.c_str(); char* strEnd = nullptr; value = std::strtof(str, &strEnd); if(value == 0. && strEnd == str) return false; return true; } #endif //! Parse a numeric value of type \p T from \p strIn. Returns \c false on error. template #if defined(__GNUC__) // The gnu stdlib has issues with the floating point versions of std::from_chars() (their // implementations allocate memory, which the standard forbids), and they hide them behind // a macro through v20. Lame. See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100146 // // Enable if T is integral on gnu. typename std::enable_if::value, bool>::type #else bool #endif ParseNum(const std::string &strIn, T &value) { //if(std::from_chars(&strIn[0], &strIn[0]+strIn.size(), value).ec != std::errc()) if(std::from_chars(strIn.c_str(), strIn.c_str()+strIn.size(), value).ec != std::errc()) return false; return true; } //! Parses numeric values of type \p T from \p strIn in array format demarked by //! \p openBracket and \p closeBracket, with values delinated by commas. //! //! TArray values; //! ParseNum("[1, 2, 3, 4, 5, 6, 7, 8, 9]", values); //! <\code> template bool ParseNumArray(const std::string &strIn, TArray &values, const char openBracket='[', const char closeBracket=']', const bool debug=false) { //values.clear(); values.Empty(); // We're parsing an ascii format, and so there may be line breaks within str for formatting reasons. // Also, std::from_chars() fails with leading white space. Just remove all ws. std::string str; str.reserve(strIn.size()); size_t num = 0; for(char ch : strIn) { if(!std::isspace(ch)) str.push_back(ch); if(ch == ',') num++; } values.Reserve(num+1); size_t i = str.find_first_of(openBracket); if(i == std::string::npos) i = 0; // Didn't find openBracket, start at 0. else i++; // Found openBracket, skip it. std::string delim(","); delim+=closeBracket; delim+=openBracket; while(i < str.size()) { // advance i = str.find_first_not_of(delim, i); if(i == std::string::npos) break; size_t j = i+1 //! TArray values; //! ParseVector3Array("[[1, 2, 3], [4, 5, 6], [7, 8, 9]]", values); //! <\code> template bool ParseVector3Array(const std::string &strIn, TArray &values, const char openBracket='[', const char closeBracket=']') { values.clear(); std::string substr; substr.reserve(1024); TArray tmp; tmp.Reserve(3); size_t i = strIn.find(openBracket, 0); size_t iEnd = strIn.find_last_of(closeBracket, 0); if(i == std::string::npos || iEnd == std::string::npos) return false; while(i < iEnd) { i = strIn.find_first_of(openBracket, i+1); if(i == std::string::npos) break; size_t j = strIn.find_first_of(closeBracket, i+1); if(j == std::string::npos) break; substr = strIn.substr(i, j+1-i); // [i, j] if(!ParseNumArray(substr, tmp, openBracket, closeBracket) || tmp.Num()!=3) return false; size_t curr = values.size(); values.SetNum(curr+tmp.Num()); for(size_t k=0; k < tmp.Num(); k++) values[curr+k] = tmp[k]; i = j; } return true; } bool IsVector3(const std::string &str) { TArray vec; if(ParseNumArray(str, vec) && vec.Num()==3) return true; return false; } bool ParsePrimitives( const std::string &str, TMap &flattenedKvp) { /* "primitives", 1 [ 2 [ 3 ["type","Tetrahedron_run"],["startvertex",0,"nprimitives",8895] -2 ], 2 [ 3 ["type","Polygon_run"],["startvertex",35580,"nprimitives",162,"nvertices_rle",[3,162]] -2 ] -1 ] */ std::string l1str; { std::stringstream ss(str); if (!ReadMatching(ss, l1str, '[', ']', true)) // strip outer brackets 1 exit(1); } // Get L2 string std::stringstream ss(l1str); while(ss.good()) { ss >> std::ws; // discard white space if (!ss.good()) break; char ch = ss.peek(); std::string l2str; switch (ch) { case '[': ReadMatching(ss, l2str, '[', ']', true); // strip outer brackets 2, advances ss break; case ',': ss.get(ch); // consume char break; default: //exit(1); continue; } if (l2str.empty()) continue; // Parse L3 arrays std::string prefix; std::stringstream l2ss(l2str); while (l2ss.good()) { l2ss >> std::ws; // discard white space if (!l2ss.good()) break; char l2ch = l2ss.peek(); std::string l3str; switch (l2ch) { case '[': ReadMatching(l2ss, l3str, '[', ']', false); // keep brackets, advances l2ss break; case ',': l2ss.get(l2ch); break; default: //exit(1); continue; } if (l3str.empty()) continue; TArray values; if (ParseArray(l3str, values)) { for (int i = 0; i < values.Num() - 1; i++) { const std::string key = ParseString(values[i]); const std::string value = ParseString(values[++i]); if (key == "type") prefix = value + ":"; else { std::string k = prefix + key; flattenedKvp.Add(FString(k.c_str()), value); } } } } } return true; } bool ParseTopology( const std::string &str, TMap &flattenedKvp) { /* "topology",[ "pointref",[ "indices",[71500,25743,43157,...,88117,89363,81072] ] ], */ TMap level1kvp; if(!ParseKVPairs(str, level1kvp)) { std::cout << "ParseTopology() - ParseKVPairs() failed at level 1 with str: '" << str << "'" << std::endl; exit(1); } for(const auto & [l1key, l1value] : level1kvp) { TMap level2kvp; if(!ParseKVPairs(l1value, level2kvp)) { std::cout << "ParseTopology() - ParseKVPairs() failed at level 2 with str: '" << l1value << "'" << std::endl; exit(1); } TMap level3kvp; if(!ParseKVPairs(l1value, level3kvp)) { std::cout << "ParseTopology() - ParseKVPairs() failed at level 3 with str: '" << l1value << "'" << std::endl; exit(1); } for (const auto& [l3key, l3value] : level3kvp) { std::string tmp = std::string(TCHAR_TO_UTF8(*l1key)) + "." + std::string(TCHAR_TO_UTF8(*l3key)); flattenedKvp.Add(FString(tmp.c_str()), l3value); } } return true; } bool ParseAttributes( const std::string &str, TMap> &categorizedKvp) { // "vertexattributes",[...], // "pointattributes",[...], // "primitiveattributes", TMap attrCategories; ParseKVPairs(str, attrCategories); for(auto it = attrCategories.CreateConstIterator(); it; ++it) { const FString& category = it.Key(); const std::string& catScope = it.Value(); TMap &kvp = categorizedKvp.Add(category); TArray attrNames; TArray attrTuples; if(!ParseArray(catScope, attrTuples)) { std::cout << "ParseAttributes() - failed to parse attr tuples." << std::endl; return false; } for(const std::string &attrStr : attrTuples) { TArray attrBlocks; if(!ParseArray(attrStr, attrBlocks)) { std::cout << "ParseAttributes() - failed to parse attr blocks from str: '" << (attrStr.size()>1024?attrStr.substr(0,1024):attrStr) << "'" << std::endl; break; } std::string attrName; for(const std::string &attrBlock : attrBlocks) { TMap attrVars; if(!ParseKVPairs(attrBlock, attrVars)) { std::cout << "ParseAttributes() - failed to parse attrVars from str: '" << attrStr << "'" << std::endl; break; } // Attribute name should be in the first block if(attrName.empty()) { const auto nameIt=attrVars.Find("name"); if(nameIt == nullptr) { std::cout << "ParseAttributes() - failed to find attr name!" << std::endl; break; } attrName = *nameIt; attrNames.Add(attrName); } //std::string attrPath = "attributes." + category + "." + attrName + "."; std::string attrPath = attrName + "."; for(const auto & [k, v] : attrVars) { if(k == "values" || k == "indices") { TMap valuesKvp; if(!ParseKVPairs(v, valuesKvp)) { std::cout << "ParseAttributes() - failed to parse values for attr: '" << std::string(TCHAR_TO_UTF8(*k)) << "' from str: '" << v << "'" << std::endl; break; } for(const auto & [valuesK, valuesV] : valuesKvp) { if(valuesK == "arrays" && valuesV.substr(0,2) == "[[") { // Strip off extra brackets from "arrays" entries. std::string tmp = attrPath + std::string(TCHAR_TO_UTF8(*k)) + '.' + std::string(TCHAR_TO_UTF8(*valuesK)); kvp.Add(FString(tmp.c_str()), valuesV.substr(1, valuesV.size() - 2)); } else { std::string tmp = attrPath + std::string(TCHAR_TO_UTF8(*k)) + '.' + std::string(TCHAR_TO_UTF8(*valuesK)); kvp.Add(FString(tmp.c_str()), valuesV); } } } else { std::string tmp = attrPath + std::string(TCHAR_TO_UTF8(*k)); kvp.Add(FString(tmp.c_str()), v); } } } // end for attrBlocks } // end for attrTuples kvp.Add("entries", FormatStringArray(attrNames)); } // end for categories return true; } bool FlattenDictionaries( TMap &kvpIn, TMap &kvpOut) { kvpOut.Empty(); for(auto it=kvpIn.CreateIterator(); it; ++it) { const FString& key = it.Key(); std::string &value = it.Value(); if(key.IsEmpty() || value.empty()) continue; if(value[0] == '{') { TMap kvp; if(!ParseKVPairs(value, kvp)) { std::cout << "FlattenKVPairs() - ParseKVPairs returned false on key '" << std::string(TCHAR_TO_UTF8(*key)) << "' value: '" << value << "'" << std::endl; continue; } for (auto& [l2k, l2v] : kvp) { std::string tmp = std::string(TCHAR_TO_UTF8(*key)) + '.' + std::string(TCHAR_TO_UTF8(*l2k)); kvpOut.Add(FString(tmp.c_str()), l2v); } } else { kvpOut.Add(key, value); } } kvpIn.Empty(); return true; } void ProcessInfo( TMap& IntVars, const TMap &kvp) { int count=0; auto it=kvp.Find("pointcount"); if(it != nullptr) { const std::string &value = *it; if(ParseNum(value, count) && count > 0) { IntVars.Add("pointcount", count); } } /* if(!count) { //it=kvp.find("pointcount"); it=kvp.find("vertexcount"); if(it != itEnd) { const std::string &value = it->second; if(ParseNum(value, count) && count > 0) { pdm->addParticles(count); kvp.erase(it); } } } */ /* for(it=kvp.begin(), itEnd=kvp.end(); it != itEnd; ++it) { const std::string &key = it->first; const std::string &value = it->second; FixedAttribute attr = pdm->addFixedAttribute(key.c_str(), ParticleAttributeType::INDEXEDSTR, 1); const int id = pdm->registerFixedIndexedStr(attr, value.c_str()); pdm->setFixed(attr, &id); } */ } /* * IntVars: * { ["Tetrahedron_run:startvertex",0], ["Tetrahedron_run:nprimitives",8895], * ["Polygon_run:startvertex",35580], ["Polygon_run:nprimitives", 162] } */ void ProcessPrimitivesAndTopology( TMap& IntVars, TMap>& IntVectorVars, const TMap &prim, const TMap &topo) { // primitives: std::string type; for(auto it=prim.CreateConstIterator(); it; ++it) { const FString key=it.Key(); const std::string value=it.Value(); int vi; if (ParseNum(value, vi)) { std::string tmp = std::string(TCHAR_TO_UTF8(*key)); IntVars.Add(FString(tmp.c_str()), vi); } } // topo: // pointref.indices = int array for(auto it=topo.CreateConstIterator(); it; ++it) { const FString key=it.Key(); const std::string value=it.Value(); TArray iv; if(ParseNumArray(value, iv)) { IntVectorVars.Add(key, iv); } else { //UE_LOG(LogChaosFlesh, Display, TEXT("Unsupported topology attribute '%s' = '%s'."), // *key, // (value.size() > 128 ? value.substr(0, 64) + "<...>" + value.substr(value.size() - 64) : value).c_str()); std::cerr << "Unsupported topology attribute '" << std::string(TCHAR_TO_UTF8(*key)) << "' = '" << (value.size()>128?value.substr(0,64)+"<...>"+value.substr(value.size()-64):value) << "'." << std::endl; } } } void ProcessAttributes( TMap& IntVars, TMap>& IntVectorVars, TMap>& FloatVectorVars, TMap, TArray>>& IndexedStringVars, const TMap> &catToAttrs) { for(auto cIt=catToAttrs.CreateConstIterator(); cIt; ++cIt) { const FString &category = cIt.Key(); // vertexattributes, pointattributes, primitiveattributes const auto &attrs = cIt.Value(); auto it = attrs.Find("entries"); if(it == nullptr) continue; const std::string &attrNamesStr = *it; TArray attrNames; ParseArray(attrNamesStr, attrNames, '[', ']', true); //for(auto nIt=attrNames.begin(), nItEnd=attrNames.end(); nIt != nItEnd; ++nIt) for(const std::string &name : attrNames) { //const std::string &name = *nIt; it = attrs.Find(FString(std::string(name+".type").c_str())); std::string type = it != nullptr ? *it : ""; if(type == "numeric") { it = attrs.Find(FString(std::string(name+".values.size").c_str())); std::string sizeStr = it != nullptr ? *it : ""; int size=0; if(!ParseNum(sizeStr, size)) { std::cout << "ProcessAttributes() - failed to parse size string '" << sizeStr << "' for attribute: '" << std::string(TCHAR_TO_UTF8(*category)) << "." << name << "'." << std::endl; continue; } std::string dataAttrName = size==1 ? name+".values.arrays" : name+".values.tuples"; it = attrs.Find(FString(dataAttrName.c_str())); const std::string &dataStr = it != nullptr ? *it : ""; it = attrs.Find(FString(std::string(name+".values.storage").c_str())); std::string storage = it != nullptr ? *it : ""; if(storage == "int32") { TArray values; if(!ParseNumArray(dataStr, values)) { std::cout << "ProcessAttributes() - failed to parse int array attribute '" << dataAttrName << "' from string: '" << (dataStr.size()>128 ? dataStr.substr(0,64)+"<...>"+dataStr.substr(dataStr.size()-64) : dataStr) << "'." << std::endl; continue; } IntVectorVars.Add(FString(name.c_str()), values); } else if(storage == "fpreal32") { TArray values; if(!ParseNumArray(dataStr, values)) { std::cout << "ProcessAttributes() - failed to parse float array attribute '" << dataAttrName << "' from string: '" << (dataStr.size()>128 ? dataStr.substr(0,64)+"<...>"+dataStr.substr(dataStr.size()-64) : dataStr) << "'." << std::endl; continue; } FloatVectorVars.Add(FString(name.c_str()), values); } else { std::cout << "Unsupported storage format '" << storage << "' for attribute: '" << dataAttrName << "'." << std::endl; continue; } } else if(type == "string") { it = attrs.Find(FString(std::string(name+".strings").c_str())); const std::string &stringsStr = it != nullptr ? *it : ""; TArray strings; if(!ParseArray(stringsStr, strings)) { std::cout << "ProcessAttributes() - failed to parse string array attribute: '" << name << ".strings'" << std::endl; continue; } it = attrs.Find(FString(std::string(name+".indices.arrays").c_str())); const std::string &indicesStr = it != nullptr ? *it : ""; TArray indices; if(!ParseNumArray(indicesStr, indices)) { std::cout << "ProcessAttributes() - failed to parse int array attribute '" << name << ".indices.arrays' from string: '" << (indicesStr.size()>128 ? indicesStr.substr(0,64)+"<...>"+indicesStr.substr(indicesStr.size()-64) : indicesStr) << "'." << std::endl; continue; } IndexedStringVars.Add( FString(name.c_str()), TPair, TArray>(strings, indices)); } else { std::cout << "ProcessAttributes() - unsupported attr type: '" << type << "'." << std::endl; continue; } }// end for attrNames }// and for catToAttrs } bool ReadGEO_v19( const std::string &filename, TMap& IntVars, TMap>& IntVectorVars, TMap>& FloatVectorVars, TMap, TArray>>& IndexedStringVars, std::ostream *errorStream); std::unique_ptr unzip(const std::string& filename, std::unique_ptr& infile, std::ios::openmode mode=std::ios::in) { std::unique_ptr stream; FPlatformFileManager& FileManager = FPlatformFileManager::Get(); IPlatformFile& PlatformFile = FileManager.GetPlatformFile(); infile.reset(PlatformFile.OpenRead(*FString(filename.c_str()), false)); FString errmsg; #ifdef USE_ZLIB if(GZIP_FILE_HEADER header; header.Read(infile.get(), errmsg)) { infile->Seek(0); stream.reset(new ZIP_FILE_ISTREAM(infile.get(), false)); } else #endif // USE_ZLIB { infile->Seek(0); stream.reset(new IFileStream(infile.get())); } if(stream) stream->imbue(std::locale::classic()); return stream; } bool ReadGEO( const std::string &filename, TMap& IntVars, TMap>& IntVectorVars, TMap>& FloatVectorVars, TMap, TArray>>& IndexedStringVars, std::ostream *errorStream) { std::unique_ptr infile = nullptr; std::unique_ptr input(unzip(filename, infile)); if(!*input) { if(errorStream) *errorStream<<"ReadGEO(): Can't open GEO file: '"<> std::ws; std::string buffer; buffer.reserve(1024); inref >> buffer; if(buffer == "[") // [ "fileversion","19.0.518", { inref >> std::ws; inref >> buffer; if(buffer.substr(0,13) == "\"fileversion\"") { input.reset(); // close file if(buffer.substr(15, 3) == "19.") // match major version return ReadGEO_v19(filename, IntVars, IntVectorVars, FloatVectorVars, IndexedStringVars, errorStream); else { if(errorStream) (*errorStream) << "Warning: using v19 GEO parser for file: '" << filename << "'." << std::endl; return ReadGEO_v19(filename, IntVars, IntVectorVars, FloatVectorVars, IndexedStringVars, errorStream); } } } else if(buffer == "PGEOMETRY") // PGEOMETRY V5 { #ifdef READ_GEO_LEGACY inref >> std::ws; inref >> buffer; input.reset(); // close file if(buffer == "V5") return readGEO_Legacy(filename, headersOnly, errorStream); else { if(errorStream) (*errorStream) << "Warning: using legacy V5 GEO parser for file: '" << filename << "'." << std::endl; return readGEO_Legacy(filename, headersOnly, errorStream); } #else return false; #endif } infile.reset(); input.reset(); // close file #ifdef READ_GEO_LEGACY if(errorStream) (*errorStream) << "Warning: Failed to determine GEO file version; using legacy V5 GEO parser for file: '" << filename << "'." << std::endl; return readGEO_Legacy(filename, headersOnly, errorStream); #else return false; #endif } bool ReadGEO_v19( const std::string& filename, TMap& IntVars, TMap>& IntVectorVars, TMap>& FloatVectorVars, TMap, TArray>>& IndexedStringVars, std::ostream* errorStream) { std::unique_ptr infile = nullptr; std::unique_ptr input(unzip(filename, infile)); if(!*input) { if(errorStream) *errorStream<<"Partio: Can't open GEO file: '"< kvp; if(!ParseGeoKVPairs(inref, kvp, filename.c_str(), errorStream)) { if(errorStream) *errorStream<<"Partio: Failed to parse GEO file: '" << filename << "'" << std::endl; return false; } TMap> attributes; const auto *attributesIt = kvp.Find("attributes"); if(attributesIt != nullptr) { if(!ParseAttributes(*attributesIt, attributes)) return false; kvp.Remove("attributes"); } TMap topology; const auto topologyIt = kvp.Find("topology"); if(topologyIt != nullptr) { if(!ParseTopology(*topologyIt, topology)) return false; kvp.Remove("topology"); } TMap primitives; const auto primitivesIt = kvp.Find("primitives"); if(primitivesIt != nullptr) { if(!ParsePrimitives(*primitivesIt, primitives)) return false; kvp.Remove("primitives"); } TMap info; if(!FlattenDictionaries(kvp, info)) { if(errorStream) *errorStream<<"ReadGEO_v19(): Failed to flatten GEO file: '" << filename << "'" << std::endl; return false; } /* std::cout << "readGEO() - flattened kvp dump:" << std::endl; DebugPrint(info); std::cout << std::endl; std::cout << "readGEO() - primitives:" << std::endl; DebugPrint(primitives); std::cout << std::endl; std::cout << "readGEO() - topology:" << std::endl; DebugPrint(topology); std::cout << std::endl; std::cout << "readGEO() - attributes:" << std::endl; DebugPrint(attributes, " attributes"); std::cout << std::endl; */ ProcessInfo(IntVars, info); ProcessPrimitivesAndTopology(IntVars, IntVectorVars, primitives, topology); ProcessAttributes(IntVars, IntVectorVars, FloatVectorVars, IndexedStringVars, attributes); return true; } #ifdef READ_GEO_LEGACY ParticlesDataMutable* readGEO_Legacy(const char* filename,const bool headersOnly,std::ostream* errorStream) { unique_ptr input(io::unzip(filename)); if(!*input){ if(errorStream) *errorStream<<"Partio: Can't open particle data file: "<good()){ *input>>word; if(word=="NPoints"||word=="pointcount") *input>>NPoints; else if(word=="vertexcount") *input>>NIndices; else if(word=="NPointAttrib") { *input>>NPointAttrib; break; } } // skip until PointAttrib while(input->good()){ *input>>word; if(word=="PointAttrib") break; } // read attribute descriptions int attrInfoRead = 0; ParticleAttribute positionAttr=simple->addAttribute("position",VECTOR,3); ParticleAccessor positionAccessor(positionAttr); vector attrs; vector accessors; while (input->good() && attrInfoRead < NPointAttrib) { string attrName, attrType; int nvals = 0; *input >> attrName >> nvals >> attrType; if(attrType=="index"){ if(errorStream) *errorStream<<"Partio: attr '"<>nIndices; ParticleAttribute attribute=simple->addAttribute(attrName.c_str(),INDEXEDSTR,1); attrs.push_back(attribute); for(int j=0;j>indexName; indexName=scanString(*input); if (!headersOnly) { int id=simple->registerIndexedStr(attribute,indexName.c_str()); if(id != j){ if(errorStream) *errorStream<<"Partio: error on read, expected registerIndexStr to return index "<>defval; } ParticleAttributeType type; // TODO: fix for other attribute types if(attrType=="float") type=FLOAT; else if(attrType=="vector") type=VECTOR; else if(attrType=="int") type=INT; else{ if(errorStream) *errorStream<<"Partio: unknown attribute "<addAttribute(attrName.c_str(),type,nvals)); accessors.push_back(ParticleAccessor(attrs.back())); attrInfoRead++; } } simple->addParticles(NPoints); ParticlesDataMutable::iterator iterator=simple->begin(); iterator.addAccessor(positionAccessor); for(size_t i=0;iend();iterator!=end && input->good();++iterator){ float* posInternal=positionAccessor.raw(iterator); for(int i=0;i<3;i++) *input>>posInternal[i]; *input>>fval; //cout<<"saw "<> paren; if (paren != '(') break; // read additional attribute values for (unsigned int i=0;i(*input,attrs[i],accessors[i],iterator);break; case VECTOR: readGeoAttr(*input,attrs[i],accessors[i],iterator);break; case INT: readGeoAttr(*input,attrs[i],accessors[i],iterator);break; case INDEXEDSTR: readGeoAttr(*input,attrs[i],accessors[i],iterator);break; } } // skip closing parenthes *input >> paren; if (paren != ')') break; } return simple; } #endif //READ_GEO_LEGACY } // namespace ChaosFlesh #endif // WITH_EDITOR