OdbDesignLib
OdbDesign ODB++ Parsing Library
 
Loading...
Searching...
No Matches
FileArchive.cpp
1#include "FileArchive.h"
2#include <filesystem>
3#include "ArchiveExtractor.h"
4#include "CrossPlatform.h"
5#include "MiscInfoFile.h"
6#include "Logger.h"
7#include "../invalid_odb_error.h"
8#include "StopWatch.h"
9#include "fastmove.h"
10#include <system_error>
11#include <cstdio>
12#include <string>
13#include <memory>
14
15using namespace Utils;
16using namespace std::filesystem;
17
18namespace Odb::Lib::FileModel::Design
19{
20 FileArchive::FileArchive()
21 : m_filePath()
22 {
23 }
24
25 FileArchive::FileArchive(const std::string& path)
26 : m_filePath(path)
27 {
28 }
29
30 FileArchive::~FileArchive()
31 {
32 m_stepsByName.clear();
33 m_symbolsDirectoriesByName.clear();
34 }
35
36 std::string FileArchive::GetRootDir() const
37 {
38 return m_rootDir;
39 }
40
41 std::string FileArchive::GetProductName() const
42 {
43 return m_productName;
44 }
45
46 std::string FileArchive::GetFilePath() const
47 {
48 return m_filePath;
49 }
50
51 std::string FileArchive::GetFilename() const
52 {
53 //return m_filename;
54 return path(m_filePath).filename().string();
55 }
56
57 const StepDirectory::StringMap& FileArchive::GetStepsByName() const
58 {
59 return m_stepsByName;
60 }
61
62 const SymbolsDirectory::StringMap& FileArchive::GetSymbolsDirectoriesByName() const
63 {
64 return m_symbolsDirectoriesByName;
65 }
66
67 bool FileArchive::ParseFileModel()
68 {
69 //try
70 {
71 StopWatch timer(true);
72
73 if (!exists(m_filePath)) return false;
74
75 if (is_regular_file(m_filePath))
76 {
77 path extractedPath;
78 if (!ExtractDesignArchive(m_filePath, extractedPath))
79 {
80 logerror("failed to extract archive: (" + m_filePath + ")");
81 return false;
82 }
83
84 m_rootDir = findRootDir(extractedPath);
85 }
86
87 if (is_directory(m_rootDir))
88 {
89 loginfo("Parsing... ");
90
91 if (ParseDesignDirectory(m_rootDir))
92 {
93 timer.stop();
94 auto s = timer.getElapsedSecondsString();
95 loginfo("Successfully parsed. (" + s + "s)");
96
97 return true;
98 }
99 else
100 {
101 logerror("Parsing failed.");
102 throw std::runtime_error("Parsing failed.");
103 }
104 }
105 else
106 {
107 logerror("Failed to find root directory");
108 }
109 }
110 //catch (std::exception& e)
111 //{
112 // logexception(e);
113 // throw e;
114 //}
115
116 return false;
117 }
118
119 bool FileArchive::SaveFileModel(const path& directory)
120 {
121 return SaveFileModel(directory, m_productName);
122 }
123
124 bool FileArchive::SaveFileModel(const path& directory, const std::string& archiveName)
125 {
126 // create directory in /tmp
127 // write dir structure and files to it
128 // gzip with archiveName
129 // move archive to directory
130
131 std::string tempName;
132 if (!CrossPlatform::tmpnam_safe(tempName)) return false;
133
134 auto tempPath = temp_directory_path() / tempName;
135 if (!create_directory(tempPath)) return false;
136
137 auto rootPath = tempPath / archiveName;
138 if (!create_directory(rootPath)) return false;
139 if (!Save(rootPath)) return false;
140
141 // compress the written file structure
142 std::string createdArchivePath;
143 if (! Utils::ArchiveExtractor::CompressDir(rootPath.string(), tempPath.string(), archiveName, createdArchivePath)) return false;
144 if (createdArchivePath.empty()) return false;
145
146 // move the compressed file to the requested save directory
147 path archiveFilename = path(createdArchivePath).filename();
148 path destPath = directory / archiveFilename;
149 std::error_code ec;
150 Utils::fastmove_file(createdArchivePath, destPath, true, ec);
151 if (ec.value() != 0) return false;
152
153 return true;
154 }
155
156 bool FileArchive::Save(const path& directory)
157 {
158 // MiscInfoFile
159 auto miscPath = directory / "misc";
160 if (!create_directory(miscPath)) return false;
161
162 std::ofstream ofs1(miscPath / "info", std::ios::out);
163 if (!m_miscInfoFile.Save(ofs1)) return false;
164 ofs1.close();
165
166 // MiscAttrFile
167 std::ofstream ofs2(miscPath / "sysattr");
168 if (!m_miscAttrListFile.Save(ofs2)) return false;
169 ofs2.close();
170
171 // StandardFontsFile
172 auto fontsPath = directory / "fonts";
173 if (!create_directory(fontsPath)) return false;
174 std::ofstream ofs3(fontsPath / "standard");
175 if (!m_standardFontsFile.Save(ofs3)) return false;
176 ofs3.close();
177
178 // MatrixFile
179 auto matrixPath = directory / "matrix";
180 if (!create_directory(matrixPath)) return false;
181 std::ofstream ofs4(matrixPath / "matrix");
182 if (!m_matrixFile.Save(ofs4)) return false;
183 ofs4.close();
184
185 // Steps
186 const auto stepsDirectory = directory / "steps";
187 if (!create_directory(stepsDirectory)) return false;
188 for (auto& kvStepDirectory : m_stepsByName)
189 {
190 if (!kvStepDirectory.second->Save(stepsDirectory)) return false;
191 }
192
193 // Symbols
194 const auto symbolsDirectory = directory / "symbols";
195 if (!create_directory(symbolsDirectory)) return false;
196 for (auto& kvSymbolsDirectory : m_symbolsDirectoriesByName)
197 {
198 if (!kvSymbolsDirectory.second->Save(symbolsDirectory)) return false;
199 }
200
201 return true;
202 }
203
204 bool FileArchive::ExtractDesignArchive(const path& archivePath, path& extractedPath)
205 {
206 loginfo("Extracting... ");
207
208 if (!Utils::ArchiveExtractor::IsArchiveTypeSupported(archivePath))
209 {
210 logerror("Unsupported archive type: (" + archivePath.string() + ")");
211 return false;
212 }
213
214 Utils::ArchiveExtractor extractor(archivePath.string());
215 if (!extractor.Extract()) return false;
216
217 auto extracted = path(extractor.GetExtractionDirectory());
218 if (!exists(extracted)) return false;
219
220 extractedPath = extracted;
221
222 loginfo("Successfully extracted.");
223
224 return true;
225
226 }
227
228 /*static*/ std::string FileArchive::findRootDir(const path& extractedPath)
229 {
230 if (pathContainsTopLevelDesignDirs(extractedPath))
231 {
232 return extractedPath.string();
233 }
234 else
235 {
236 for (const auto& p : directory_iterator(extractedPath))
237 {
238 if (is_directory(p))
239 {
240 if (pathContainsTopLevelDesignDirs(p.path()))
241 {
242 return p.path().string();
243 }
244 }
245 }
246
247 return "";
248 }
249 }
250
251 /*static*/ bool FileArchive::pathContainsTopLevelDesignDirs(const path& path)
252 {
253 for (const auto& topLevelRootDirName : TOPLEVEL_DESIGN_DIR_NAMES)
254 {
255 auto rootLevelDirPath = path / topLevelRootDirName;
256 if (!exists(rootLevelDirPath)) return false;
257 }
258 return true;
259 }
260
261 std::unique_ptr<Protobuf::FileArchive> FileArchive::to_protobuf() const
262 {
263 std::unique_ptr<Protobuf::FileArchive> pFileArchiveMessage(new Protobuf::FileArchive);
264 pFileArchiveMessage->set_productname(m_productName);
265 pFileArchiveMessage->set_filename(m_filename);
266 //pFileArchiveMessage->set_filepath(m_filePath);
267 pFileArchiveMessage->mutable_matrixfile()->CopyFrom(*m_matrixFile.to_protobuf());
268 pFileArchiveMessage->mutable_miscinfofile()->CopyFrom(*m_miscInfoFile.to_protobuf());
269 pFileArchiveMessage->mutable_standardfontsfile()->CopyFrom(*m_standardFontsFile.to_protobuf());
270 pFileArchiveMessage->mutable_miscattrlistfile()->CopyFrom(*m_miscAttrListFile.to_protobuf());
271
272 for (const auto& kvStepDirectoryRecord : m_stepsByName)
273 {
274 (*pFileArchiveMessage->mutable_stepsbyname())[kvStepDirectoryRecord.first] = *kvStepDirectoryRecord.second->to_protobuf();
275 }
276
277 for (const auto& kvSymbolsDirectory : m_symbolsDirectoriesByName)
278 {
279 (*pFileArchiveMessage->mutable_symbolsdirectoriesbyname())[kvSymbolsDirectory.first] = *kvSymbolsDirectory.second->to_protobuf();
280 }
281
282 return pFileArchiveMessage;
283 }
284
285 void FileArchive::from_protobuf(const Protobuf::FileArchive& message)
286 {
287 m_productName = message.productname();
288 m_filename = message.filename();
289 //m_filePath = message.filepath();
290 m_matrixFile.from_protobuf(message.matrixfile());
291 m_miscInfoFile.from_protobuf(message.miscinfofile());
292 m_standardFontsFile.from_protobuf(message.standardfontsfile());
293 m_miscAttrListFile.from_protobuf(message.miscattrlistfile());
294
295 for (const auto& kvStepDirectoryRecord : message.stepsbyname())
296 {
297 auto pStepDirectory = std::make_shared<StepDirectory>("");
298 pStepDirectory->from_protobuf(kvStepDirectoryRecord.second);
299 m_stepsByName[kvStepDirectoryRecord.first] = pStepDirectory;
300 }
301
302 for (const auto& kvSymbolsDirectory : message.symbolsdirectoriesbyname())
303 {
304 auto pSymbolsDirectory = std::make_shared<SymbolsDirectory>("");
305 pSymbolsDirectory->from_protobuf(kvSymbolsDirectory.second);
306 m_symbolsDirectoriesByName[kvSymbolsDirectory.first] = pSymbolsDirectory;
307 }
308 }
309
310 bool FileArchive::ParseDesignDirectory(const path& path)
311 {
312 if (!exists(path)) return false;
313 else if (!is_directory(path)) return false;
314
315 m_productName = path.stem().string();
316
317 if (! ParseStepDirectories(path)) return false;
318 if (! ParseMiscInfoFile(path)) return false;
319 if (! ParseMatrixFile(path)) return false;
320 if (! ParseStandardFontsFile(path)) return false;
321 if (! ParseSymbolsDirectories(path)) return false;
322 if (! ParseMiscAttrListFile(path)) return false;
323
324 return true;
325 }
326
327 bool FileArchive::ParseStepDirectories(const path& path)
328 {
329 loginfo("Parsing steps...");
330
331 auto stepsPath = path / "steps";
332 for (auto& d : directory_iterator(stepsPath))
333 {
334 if (is_directory(d))
335 {
336 auto pStep = std::make_shared<StepDirectory>(d.path());
337 if (pStep->Parse())
338 {
339 m_stepsByName[pStep->GetName()] = pStep;
340 }
341 else
342 {
343 logwarn("Failed to parse step: " + pStep->GetName());
344 //return false;
345 }
346 }
347 }
348
349 loginfo("Parsing steps complete");
350
351 return true;
352 }
353
354 bool FileArchive::ParseMiscInfoFile(const path& path)
355 {
356 loginfo("Parsing misc/info file...");
357
358 auto miscDirectory = path / "misc";
359 if (!exists(miscDirectory))
360 {
361 auto message = "misc directory does not exist: [" + miscDirectory.string() + "]";
362 throw invalid_odb_error(message);
363 }
364 if (!is_directory(miscDirectory))
365 {
366 auto message = "misc path is not a directory: [" + miscDirectory.string() + "]";
367 throw invalid_odb_error(message);
368 }
369
370 if (!m_miscInfoFile.Parse(miscDirectory)) return false;
371
372 loginfo("Parsing misc/info file complete");
373
374 return true;
375 }
376
377 bool FileArchive::ParseMiscAttrListFile(const path& path)
378 {
379 loginfo("Parsing misc/attrlist file...");
380
381 auto miscDirectory = path / "misc";
382 if (!exists(miscDirectory))
383 {
384 auto message = "misc directory does not exist: [" + miscDirectory.string() + "]";
385 throw invalid_odb_error(message);
386 }
387 if (!is_directory(miscDirectory))
388 {
389 auto message = "misc path is not a directory: [" + miscDirectory.string() + "]";
390 throw invalid_odb_error(message);
391 }
392
393 if (!m_miscAttrListFile.Parse(miscDirectory)) return false;
394
395 loginfo("Parsing misc/attrlist file complete");
396
397 return true;
398 }
399
400 bool FileArchive::ParseMatrixFile(const path& path)
401 {
402 loginfo("Parsing matrix/matrix file...");
403
404 auto matrixDir = path / "matrix";
405 if (!exists(matrixDir))
406 {
407 auto message = "matrix directory does not exist: [" + matrixDir.string() + "]";
408 throw invalid_odb_error(message);
409 }
410 if (!is_directory(matrixDir))
411 {
412 auto message = "matrix path is not a directory: [" + matrixDir.string() + "]";
413 throw invalid_odb_error(message);
414 }
415
416 if (!m_matrixFile.Parse(matrixDir)) return false;
417
418 loginfo("Parsing matrix/matrix file complete");
419
420 return true;
421 }
422 bool FileArchive::ParseStandardFontsFile(const path& path)
423 {
424 loginfo("Parsing fonts/standard file...");
425
426 auto fontsDir = path / "fonts";
427 if (!exists(fontsDir))
428 {
429 auto message = "fonts directory does not exist: [" + fontsDir.string() + "]";
430 throw invalid_odb_error(message);
431 }
432 if (!is_directory(fontsDir))
433 {
434 auto message = "fonts path is not a directory: [" + fontsDir.string() + "]";
435 throw invalid_odb_error(message);
436 }
437
438 if (!m_standardFontsFile.Parse(fontsDir)) return false;
439
440 loginfo("Parsing fonts/standard file complete");
441
442 return true;
443 }
444
445 bool FileArchive::ParseSymbolsDirectories(const path& path)
446 {
447 loginfo("Parsing symbols root directory...");
448
449 auto symbolsDirectory = path / "symbols";
450
451 if (!exists(symbolsDirectory))
452 {
453 logwarn("symbols root directory does not exist (" + symbolsDirectory.string() + ")");
454 return true;
455 }
456 else if (!is_directory(symbolsDirectory))
457 {
458 logerror("symbols root directory exists but is a regular file/not a directory (" + symbolsDirectory.string() + ")");
459 return false;
460 }
461
462 for (auto& d : directory_iterator(symbolsDirectory))
463 {
464 if (is_directory(d))
465 {
466 auto pSymbolsDirectory = std::make_shared<SymbolsDirectory>(d.path());
467 if (pSymbolsDirectory->Parse())
468 {
469 //loginfo("Parsing symbols directory: " + pSymbolsDirectory->GetName() + " complete");
470
471 m_symbolsDirectoriesByName[pSymbolsDirectory->GetName()] = pSymbolsDirectory;
472 }
473 else
474 {
475 logerror("Parsing symbol directory: " + pSymbolsDirectory->GetName() + " failed");
476 return false;
477 }
478 }
479 }
480
481 loginfo("Parsing symbols root directory complete");
482
483 return true;
484 }
485
486 const MiscInfoFile &FileArchive::GetMiscInfoFile() const
487 {
488 return m_miscInfoFile;
489 }
490
491 const MatrixFile& FileArchive::GetMatrixFile() const
492 {
493 return m_matrixFile;
494 }
495
496 const StandardFontsFile& FileArchive::GetStandardFontsFile() const
497 {
498 return m_standardFontsFile;
499 }
500
501 const AttrListFile& FileArchive::GetMiscAttrListFile() const
502 {
503 return m_miscAttrListFile;
504 }
505
506 std::shared_ptr<StepDirectory> FileArchive::GetStepDirectory(const std::string& stepName /*= ""*/) const
507 {
508 std::shared_ptr<FileModel::Design::StepDirectory> pStepDirectory;
509
510 const auto& steps = GetStepsByName();
511 if (!steps.empty())
512 {
513 if (stepName.empty())
514 {
515 // return first step
516 pStepDirectory = steps.begin()->second;
517 }
518 else
519 {
520 auto findIt = steps.find(stepName);
521 if (findIt != steps.end())
522 {
523 pStepDirectory = findIt->second;
524 }
525 }
526 }
527
528 return pStepDirectory;
529 }
530}