1#include "FileArchive.h"
3#include "ArchiveExtractor.h"
4#include "CrossPlatform.h"
5#include "MiscInfoFile.h"
7#include "../invalid_odb_error.h"
10#include <system_error>
14#include "../../Text/Utf8Sanitizer.h"
17using namespace std::filesystem;
19namespace Odb::Lib::FileModel::Design
21 FileArchive::FileArchive()
26 FileArchive::FileArchive(
const std::string& path)
31 FileArchive::~FileArchive()
33 m_stepsByName.clear();
34 m_symbolsDirectoriesByName.clear();
37 std::string FileArchive::GetRootDir()
const
42 std::string FileArchive::GetProductName()
const
47 std::string FileArchive::GetFilePath()
const
52 std::string FileArchive::GetFilename()
const
55 return path(m_filePath).filename().string();
58 const StepDirectory::StringMap& FileArchive::GetStepsByName()
const
63 const SymbolsDirectory::StringMap& FileArchive::GetSymbolsDirectoriesByName()
const
65 return m_symbolsDirectoriesByName;
68 bool FileArchive::ParseFileModel()
72 StopWatch timer(
true);
74 if (!exists(m_filePath))
return false;
76 if (is_regular_file(m_filePath))
79 if (!ExtractDesignArchive(m_filePath, extractedPath))
81 logerror(
"failed to extract archive: (" + m_filePath +
")");
85 m_rootDir = findRootDir(extractedPath);
88 if (is_directory(m_rootDir))
90 loginfo(
"Parsing... ");
92 if (ParseDesignDirectory(m_rootDir))
95 auto s = timer.getElapsedSecondsString();
96 loginfo(
"Successfully parsed. (" + s +
"s)");
102 logerror(
"Parsing failed.");
103 throw std::runtime_error(
"Parsing failed.");
108 logerror(
"Failed to find root directory");
120 bool FileArchive::SaveFileModel(
const path& directory)
122 return SaveFileModel(directory, m_productName);
125 bool FileArchive::SaveFileModel(
const path& directory,
const std::string& archiveName)
132 std::string tempName;
133 if (!CrossPlatform::tmpnam_safe(tempName))
return false;
135 auto tempPath = temp_directory_path() / tempName;
136 if (!create_directory(tempPath))
return false;
138 auto rootPath = tempPath / archiveName;
139 if (!create_directory(rootPath))
return false;
140 if (!Save(rootPath))
return false;
143 std::string createdArchivePath;
144 if (! Utils::ArchiveExtractor::CompressDir(rootPath.string(), tempPath.string(), archiveName, createdArchivePath))
return false;
145 if (createdArchivePath.empty())
return false;
148 path archiveFilename = path(createdArchivePath).filename();
149 path destPath = directory / archiveFilename;
151 Utils::fastmove_file(createdArchivePath, destPath,
true, ec);
152 if (ec.value() != 0)
return false;
157 bool FileArchive::Save(
const path& directory)
160 auto miscPath = directory /
"misc";
161 if (!create_directory(miscPath))
return false;
163 std::ofstream ofs1(miscPath /
"info", std::ios::out);
164 if (!m_miscInfoFile.Save(ofs1))
return false;
168 std::ofstream ofs2(miscPath /
"sysattr");
169 if (!m_miscAttrListFile.Save(ofs2))
return false;
173 auto fontsPath = directory /
"fonts";
174 if (!create_directory(fontsPath))
return false;
175 std::ofstream ofs3(fontsPath /
"standard");
176 if (!m_standardFontsFile.Save(ofs3))
return false;
180 auto matrixPath = directory /
"matrix";
181 if (!create_directory(matrixPath))
return false;
182 std::ofstream ofs4(matrixPath /
"matrix");
183 if (!m_matrixFile.Save(ofs4))
return false;
187 const auto stepsDirectory = directory /
"steps";
188 if (!create_directory(stepsDirectory))
return false;
189 for (
auto& kvStepDirectory : m_stepsByName)
191 if (!kvStepDirectory.second->Save(stepsDirectory))
return false;
195 const auto symbolsDirectory = directory /
"symbols";
196 if (!create_directory(symbolsDirectory))
return false;
197 for (
auto& kvSymbolsDirectory : m_symbolsDirectoriesByName)
199 if (!kvSymbolsDirectory.second->Save(symbolsDirectory))
return false;
205 bool FileArchive::ExtractDesignArchive(
const path& archivePath, path& extractedPath)
207 loginfo(
"Extracting... ");
209 if (!Utils::ArchiveExtractor::IsArchiveTypeSupported(archivePath))
211 logerror(
"Unsupported archive type: (" + archivePath.string() +
")");
215 Utils::ArchiveExtractor extractor(archivePath.string());
216 if (!extractor.Extract())
return false;
218 auto extracted = path(extractor.GetExtractionDirectory());
219 if (!exists(extracted))
return false;
221 extractedPath = extracted;
223 loginfo(
"Successfully extracted.");
229 std::string FileArchive::findRootDir(
const path& extractedPath)
231 if (pathContainsTopLevelDesignDirs(extractedPath))
233 return extractedPath.string();
237 for (
const auto& p : directory_iterator(extractedPath))
241 if (pathContainsTopLevelDesignDirs(p.path()))
243 return p.path().string();
252 bool FileArchive::pathContainsTopLevelDesignDirs(
const path& path)
254 for (
const auto& topLevelRootDirName : TOPLEVEL_DESIGN_DIR_NAMES)
256 auto rootLevelDirPath = path / topLevelRootDirName;
257 if (!exists(rootLevelDirPath))
return false;
262 std::unique_ptr<Protobuf::FileArchive> FileArchive::to_protobuf()
const
264 std::unique_ptr<Protobuf::FileArchive> pFileArchiveMessage(
new Protobuf::FileArchive);
265 pFileArchiveMessage->set_productname(Odb::Lib::Text::ToUtf8(m_productName));
266 pFileArchiveMessage->set_filename(Odb::Lib::Text::ToUtf8(m_filename));
268 pFileArchiveMessage->mutable_matrixfile()->CopyFrom(*m_matrixFile.to_protobuf());
269 pFileArchiveMessage->mutable_miscinfofile()->CopyFrom(*m_miscInfoFile.to_protobuf());
270 pFileArchiveMessage->mutable_standardfontsfile()->CopyFrom(*m_standardFontsFile.to_protobuf());
271 pFileArchiveMessage->mutable_miscattrlistfile()->CopyFrom(*m_miscAttrListFile.to_protobuf());
273 for (
const auto& kvStepDirectoryRecord : m_stepsByName)
275 (*pFileArchiveMessage->mutable_stepsbyname())[Odb::Lib::Text::ToUtf8(kvStepDirectoryRecord.first)] = *kvStepDirectoryRecord.second->to_protobuf();
278 for (
const auto& kvSymbolsDirectory : m_symbolsDirectoriesByName)
280 (*pFileArchiveMessage->mutable_symbolsdirectoriesbyname())[Odb::Lib::Text::ToUtf8(kvSymbolsDirectory.first)] = *kvSymbolsDirectory.second->to_protobuf();
288 return pFileArchiveMessage;
291 void FileArchive::from_protobuf(
const Protobuf::FileArchive& message)
293 m_productName = message.productname();
294 m_filename = message.filename();
296 m_matrixFile.from_protobuf(message.matrixfile());
297 m_miscInfoFile.from_protobuf(message.miscinfofile());
298 m_standardFontsFile.from_protobuf(message.standardfontsfile());
299 m_miscAttrListFile.from_protobuf(message.miscattrlistfile());
301 for (
const auto& kvStepDirectoryRecord : message.stepsbyname())
303 auto pStepDirectory = std::make_shared<StepDirectory>(
"");
304 pStepDirectory->from_protobuf(kvStepDirectoryRecord.second);
305 m_stepsByName[kvStepDirectoryRecord.first] = pStepDirectory;
308 for (
const auto& kvSymbolsDirectory : message.symbolsdirectoriesbyname())
310 auto pSymbolsDirectory = std::make_shared<SymbolsDirectory>(
"");
311 pSymbolsDirectory->from_protobuf(kvSymbolsDirectory.second);
312 m_symbolsDirectoriesByName[kvSymbolsDirectory.first] = pSymbolsDirectory;
316 bool FileArchive::ParseDesignDirectory(
const path& path)
318 if (!exists(path))
return false;
319 else if (!is_directory(path))
return false;
321 m_productName = path.stem().string();
323 if (! ParseStepDirectories(path))
return false;
324 if (! ParseMiscInfoFile(path))
return false;
325 if (! ParseMatrixFile(path))
return false;
326 if (! ParseStandardFontsFile(path))
return false;
327 if (! ParseSymbolsDirectories(path))
return false;
328 if (! ParseMiscAttrListFile(path))
return false;
333 bool FileArchive::ParseStepDirectories(
const path& path)
335 loginfo(
"Parsing steps...");
337 auto stepsPath = path /
"steps";
338 for (
auto& d : directory_iterator(stepsPath))
342 auto pStep = std::make_shared<StepDirectory>(d.path());
345 m_stepsByName[pStep->GetName()] = pStep;
349 logwarn(
"Failed to parse step: " + pStep->GetName());
355 loginfo(
"Parsing steps complete");
360 bool FileArchive::ParseMiscInfoFile(
const path& path)
362 loginfo(
"Parsing misc/info file...");
364 auto miscDirectory = path /
"misc";
365 if (!exists(miscDirectory))
367 auto message =
"misc directory does not exist: [" + miscDirectory.string() +
"]";
368 throw invalid_odb_error(message);
370 if (!is_directory(miscDirectory))
372 auto message =
"misc path is not a directory: [" + miscDirectory.string() +
"]";
373 throw invalid_odb_error(message);
376 if (!m_miscInfoFile.Parse(miscDirectory))
return false;
378 loginfo(
"Parsing misc/info file complete");
383 bool FileArchive::ParseMiscAttrListFile(
const path& path)
385 loginfo(
"Parsing misc/attrlist file...");
387 auto miscDirectory = path /
"misc";
388 if (!exists(miscDirectory))
390 auto message =
"misc directory does not exist: [" + miscDirectory.string() +
"]";
391 throw invalid_odb_error(message);
393 if (!is_directory(miscDirectory))
395 auto message =
"misc path is not a directory: [" + miscDirectory.string() +
"]";
396 throw invalid_odb_error(message);
399 if (!m_miscAttrListFile.Parse(miscDirectory))
return false;
401 loginfo(
"Parsing misc/attrlist file complete");
406 bool FileArchive::ParseMatrixFile(
const path& path)
408 loginfo(
"Parsing matrix/matrix file...");
410 auto matrixDir = path /
"matrix";
411 if (!exists(matrixDir))
413 auto message =
"matrix directory does not exist: [" + matrixDir.string() +
"]";
414 throw invalid_odb_error(message);
416 if (!is_directory(matrixDir))
418 auto message =
"matrix path is not a directory: [" + matrixDir.string() +
"]";
419 throw invalid_odb_error(message);
422 if (!m_matrixFile.Parse(matrixDir))
return false;
424 loginfo(
"Parsing matrix/matrix file complete");
428 bool FileArchive::ParseStandardFontsFile(
const path& path)
430 loginfo(
"Parsing fonts/standard file...");
432 auto fontsDir = path /
"fonts";
433 if (!exists(fontsDir))
435 auto message =
"fonts directory does not exist: [" + fontsDir.string() +
"]";
436 throw invalid_odb_error(message);
438 if (!is_directory(fontsDir))
440 auto message =
"fonts path is not a directory: [" + fontsDir.string() +
"]";
441 throw invalid_odb_error(message);
444 if (!m_standardFontsFile.Parse(fontsDir))
return false;
446 loginfo(
"Parsing fonts/standard file complete");
451 bool FileArchive::ParseSymbolsDirectories(
const path& path)
453 loginfo(
"Parsing symbols root directory...");
455 auto symbolsDirectory = path /
"symbols";
457 if (!exists(symbolsDirectory))
459 logwarn(
"symbols root directory does not exist (" + symbolsDirectory.string() +
")");
462 else if (!is_directory(symbolsDirectory))
464 logerror(
"symbols root directory exists but is a regular file/not a directory (" + symbolsDirectory.string() +
")");
468 for (
auto& d : directory_iterator(symbolsDirectory))
472 auto pSymbolsDirectory = std::make_shared<SymbolsDirectory>(d.path());
473 if (pSymbolsDirectory->Parse())
477 m_symbolsDirectoriesByName[pSymbolsDirectory->GetName()] = pSymbolsDirectory;
481 logerror(
"Parsing symbol directory: " + pSymbolsDirectory->GetName() +
" failed");
487 loginfo(
"Parsing symbols root directory complete");
492 const MiscInfoFile &FileArchive::GetMiscInfoFile()
const
494 return m_miscInfoFile;
497 const MatrixFile& FileArchive::GetMatrixFile()
const
502 const StandardFontsFile& FileArchive::GetStandardFontsFile()
const
504 return m_standardFontsFile;
507 const AttrListFile& FileArchive::GetMiscAttrListFile()
const
509 return m_miscAttrListFile;
512 std::shared_ptr<StepDirectory> FileArchive::GetStepDirectory(
const std::string& stepName )
const
514 std::shared_ptr<FileModel::Design::StepDirectory> pStepDirectory;
516 const auto& steps = GetStepsByName();
519 if (stepName.empty())
522 pStepDirectory = steps.begin()->second;
526 auto findIt = steps.find(stepName);
527 if (findIt != steps.end())
529 pStepDirectory = findIt->second;
534 return pStepDirectory;
void AssertAllStringFieldsAreValidUtf8(const google::protobuf::Message &msg, std::string_view msgName)
Debug-only assertion to verify all string fields in a message are valid UTF-8.