| File: | libraries/libdxfrw/src/intern/dwgreader.cpp |
| Warning: | line 8164, column 14 Value stored to 'hasExplicitLink' during its initialization is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /****************************************************************************** |
| 2 | ** libDXFrw - Library to read/write DXF files (ascii & binary) ** |
| 3 | ** ** |
| 4 | ** Copyright (C) 2011-2015 José F. Soriano, rallazz@gmail.com ** |
| 5 | ** Copyright (C) 2026 LibreCAD (librecad.org) ** |
| 6 | ** ** |
| 7 | ** This library is free software, licensed under the terms of the GNU ** |
| 8 | ** General Public License as published by the Free Software Foundation, ** |
| 9 | ** either version 2 of the License, or (at your option) any later version. ** |
| 10 | ** You should have received a copy of the GNU General Public License ** |
| 11 | ** along with this program. If not, see <http://www.gnu.org/licenses/>. ** |
| 12 | ******************************************************************************/ |
| 13 | |
| 14 | #include "dwgreader.h" |
| 15 | #include "drw_dbg.h" |
| 16 | #include "drw_reserve.h" |
| 17 | #include "drw_textcodec.h" |
| 18 | #include "dwgobjectframe.h" |
| 19 | #include "dwgsafety.h" |
| 20 | #include "proxygraphicdecoder.h" |
| 21 | #include <algorithm> |
| 22 | #include <array> |
| 23 | #include <cctype> |
| 24 | #include <cstdio> |
| 25 | #include <cstdlib> |
| 26 | #include <fstream> |
| 27 | #include <functional> |
| 28 | #include <limits> |
| 29 | #include <sstream> |
| 30 | #include <stdexcept> |
| 31 | #include <string> |
| 32 | #include <type_traits> |
| 33 | #include <utility> |
| 34 | |
| 35 | namespace { |
| 36 | bool isSpaceBlockRecordName(const std::string &name); |
| 37 | |
| 38 | bool requiresAggregateDelivery(std::int16_t type) { |
| 39 | switch (type) { |
| 40 | case dwgType::ATTRIB: |
| 41 | case dwgType::SEQEND: |
| 42 | case dwgType::INSERT: |
| 43 | case dwgType::MINSERT: |
| 44 | case dwgType::VERTEX_2D: |
| 45 | case dwgType::VERTEX_3D: |
| 46 | case dwgType::VERTEX_MESH: |
| 47 | case dwgType::VERTEX_PFACE: |
| 48 | case dwgType::VERTEX_PFACE_FACE: |
| 49 | case dwgType::POLYLINE_2D: |
| 50 | case dwgType::POLYLINE_3D: |
| 51 | case dwgType::POLYLINE_PFACE: |
| 52 | case dwgType::POLYLINE_MESH: |
| 53 | return true; |
| 54 | default: |
| 55 | return false; |
| 56 | } |
| 57 | } |
| 58 | } // namespace |
| 59 | |
| 60 | dwgReader::DwgEntityOutput::~DwgEntityOutput() = default; |
| 61 | |
| 62 | dwgReader::DwgEntityOutput::SourceScope::SourceScope( |
| 63 | DwgEntityOutput &output, const DwgSourceFrameId &source) noexcept |
| 64 | : m_output(output), m_previous(output.m_activeSource) { |
| 65 | m_output.m_activeSource = source; |
| 66 | } |
| 67 | |
| 68 | dwgReader::DwgEntityOutput::SourceScope::~SourceScope() { |
| 69 | m_output.m_activeSource = std::move(m_previous); |
| 70 | } |
| 71 | |
| 72 | dwgReader::DwgEntityOutput::SourceScope dwgReader::DwgEntityOutput::bindSource( |
| 73 | const DwgSourceFrameId &source) noexcept { |
| 74 | return SourceScope(*this, source); |
| 75 | } |
| 76 | |
| 77 | template <typename T, typename Callback> |
| 78 | void dwgReader::DwgEntityOutput::appendValue(const T &value, |
| 79 | Callback callback) { |
| 80 | if constexpr (std::is_invocable_v<Callback, DRW_Interface &, const T &>) { |
| 81 | class ReferenceEvent final : public Event { |
| 82 | public: |
| 83 | ReferenceEvent(const T &eventValue, Callback eventCallback, |
| 84 | const std::optional<DwgSourceFrameId> &source) |
| 85 | : m_value(eventValue), m_callback(eventCallback), m_source(source) {} |
| 86 | |
| 87 | bool replay(dwgReader &, DRW_Interface &target) const override { |
| 88 | std::invoke(m_callback, target, m_value); |
| 89 | return true; |
| 90 | } |
| 91 | |
| 92 | bool hasSource() const noexcept override { return m_source.has_value(); } |
| 93 | |
| 94 | DwgSourceFrameId source() const noexcept override { |
| 95 | return m_source.value_or(DwgSourceFrameId{}); |
| 96 | } |
| 97 | |
| 98 | bool completesSource() const noexcept override { return false; } |
| 99 | |
| 100 | private: |
| 101 | T m_value; |
| 102 | Callback m_callback; |
| 103 | std::optional<DwgSourceFrameId> m_source; |
| 104 | }; |
| 105 | append(std::make_unique<ReferenceEvent>(value, callback, m_activeSource)); |
| 106 | } else { |
| 107 | static_assert(std::is_invocable_v<Callback, DRW_Interface &, const T *>, |
| 108 | "DWG entity output callback must accept a copied value"); |
| 109 | class PointerEvent final : public Event { |
| 110 | public: |
| 111 | PointerEvent(const T &eventValue, Callback eventCallback, |
| 112 | const std::optional<DwgSourceFrameId> &source) |
| 113 | : m_value(eventValue), m_callback(eventCallback), m_source(source) {} |
| 114 | |
| 115 | bool replay(dwgReader &, DRW_Interface &target) const override { |
| 116 | std::invoke(m_callback, target, &m_value); |
| 117 | return true; |
| 118 | } |
| 119 | |
| 120 | bool hasSource() const noexcept override { return m_source.has_value(); } |
| 121 | |
| 122 | DwgSourceFrameId source() const noexcept override { |
| 123 | return m_source.value_or(DwgSourceFrameId{}); |
| 124 | } |
| 125 | |
| 126 | bool completesSource() const noexcept override { return false; } |
| 127 | |
| 128 | private: |
| 129 | T m_value; |
| 130 | Callback m_callback; |
| 131 | std::optional<DwgSourceFrameId> m_source; |
| 132 | }; |
| 133 | append(std::make_unique<PointerEvent>(value, callback, m_activeSource)); |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | bool dwgReader::DwgEntityOutput::appendEndBlock() { |
| 138 | class EndBlockEvent final : public Event { |
| 139 | public: |
| 140 | explicit EndBlockEvent(const std::optional<DwgSourceFrameId> &source) |
| 141 | : m_source(source) {} |
| 142 | |
| 143 | bool replay(dwgReader &, DRW_Interface &target) const override { |
| 144 | target.endBlock(); |
| 145 | return true; |
| 146 | } |
| 147 | |
| 148 | bool hasSource() const noexcept override { return m_source.has_value(); } |
| 149 | |
| 150 | DwgSourceFrameId source() const noexcept override { |
| 151 | return m_source.value_or(DwgSourceFrameId{}); |
| 152 | } |
| 153 | |
| 154 | bool completesSource() const noexcept override { return false; } |
| 155 | |
| 156 | private: |
| 157 | std::optional<DwgSourceFrameId> m_source; |
| 158 | }; |
| 159 | return append(std::make_unique<EndBlockEvent>(m_activeSource)); |
| 160 | } |
| 161 | |
| 162 | bool dwgReader::DwgEntityOutput::appendFramePublication( |
| 163 | dwgReader &reader, const DRW_DwgFramePublication &publication, |
| 164 | DwgFramePublicationArtifacts artifacts) { |
| 165 | class FramePublicationEvent final : public Event { |
| 166 | public: |
| 167 | explicit FramePublicationEvent( |
| 168 | const DRW_DwgFramePublication &eventPublication, |
| 169 | DwgFramePublicationArtifacts eventArtifacts) |
| 170 | : m_publication(eventPublication) { |
| 171 | if (eventArtifacts.dictionaryMembership != nullptr) |
| 172 | m_dictionaryMembership.emplace(*eventArtifacts.dictionaryMembership); |
| 173 | if (eventArtifacts.typedReference != nullptr) |
| 174 | m_typedReference.emplace(*eventArtifacts.typedReference); |
| 175 | if (eventArtifacts.blockReachability != nullptr) |
| 176 | m_blockReachability.emplace(*eventArtifacts.blockReachability); |
| 177 | if (eventArtifacts.groupMembership != nullptr) |
| 178 | m_groupMembership.emplace(*eventArtifacts.groupMembership); |
| 179 | if (eventArtifacts.sortEntsMembership != nullptr) |
| 180 | m_sortEntsMembership.emplace(*eventArtifacts.sortEntsMembership); |
| 181 | if (eventArtifacts.fieldListMembership != nullptr) |
| 182 | m_fieldListMembership.emplace(*eventArtifacts.fieldListMembership); |
| 183 | if (eventArtifacts.dictionaryWithDefaultMembership != nullptr) |
| 184 | m_dictionaryWithDefaultMembership.emplace( |
| 185 | *eventArtifacts.dictionaryWithDefaultMembership); |
| 186 | } |
| 187 | |
| 188 | bool replay(dwgReader &eventReader, DRW_Interface &target) const override { |
| 189 | return eventReader.publishDwgFramePublication( |
| 190 | target, m_publication, |
| 191 | {m_dictionaryMembership.has_value() ? &*m_dictionaryMembership |
| 192 | : nullptr, |
| 193 | m_typedReference.has_value() ? &*m_typedReference : nullptr, |
| 194 | m_blockReachability.has_value() ? &*m_blockReachability : nullptr, |
| 195 | m_groupMembership.has_value() ? &*m_groupMembership : nullptr, |
| 196 | m_sortEntsMembership.has_value() ? &*m_sortEntsMembership : nullptr, |
| 197 | m_fieldListMembership.has_value() ? &*m_fieldListMembership |
| 198 | : nullptr, |
| 199 | m_dictionaryWithDefaultMembership.has_value() |
| 200 | ? &*m_dictionaryWithDefaultMembership |
| 201 | : nullptr}); |
| 202 | } |
| 203 | |
| 204 | bool hasSource() const noexcept override { return true; } |
| 205 | |
| 206 | DwgSourceFrameId source() const noexcept override { |
| 207 | return DwgSourceFrameId{ |
| 208 | m_publication.m_handle, m_publication.m_sourceOffset, |
| 209 | m_publication.m_sourceMapOrdinal, m_publication.m_sourceOffsetSpace}; |
| 210 | } |
| 211 | |
| 212 | bool completesSource() const noexcept override { return true; } |
| 213 | |
| 214 | private: |
| 215 | DRW_DwgFramePublication m_publication; |
| 216 | std::optional<DRW_DwgDictionaryMembership> m_dictionaryMembership; |
| 217 | std::optional<DRW_DwgTypedReference> m_typedReference; |
| 218 | std::optional<DRW_DwgBlockReachability> m_blockReachability; |
| 219 | std::optional<DRW_DwgGroupMembership> m_groupMembership; |
| 220 | std::optional<DRW_DwgSortEntsMembership> m_sortEntsMembership; |
| 221 | std::optional<DRW_DwgFieldListMembership> m_fieldListMembership; |
| 222 | std::optional<DRW_DwgDictionaryWithDefaultMembership> |
| 223 | m_dictionaryWithDefaultMembership; |
| 224 | }; |
| 225 | (void)reader; |
| 226 | return append( |
| 227 | std::make_unique<FramePublicationEvent>(publication, artifacts)); |
| 228 | } |
| 229 | |
| 230 | bool dwgReader::DwgEntityOutput::appendFrameCompletion() { |
| 231 | class FrameCompletionEvent final : public Event { |
| 232 | public: |
| 233 | explicit FrameCompletionEvent(const std::optional<DwgSourceFrameId> &source) |
| 234 | : m_source(source) {} |
| 235 | |
| 236 | bool replay(dwgReader &, DRW_Interface &) const override { return true; } |
| 237 | |
| 238 | bool hasSource() const noexcept override { return m_source.has_value(); } |
| 239 | |
| 240 | DwgSourceFrameId source() const noexcept override { |
| 241 | return m_source.value_or(DwgSourceFrameId{}); |
| 242 | } |
| 243 | |
| 244 | bool completesSource() const noexcept override { return true; } |
| 245 | |
| 246 | private: |
| 247 | std::optional<DwgSourceFrameId> m_source; |
| 248 | }; |
| 249 | return append(std::make_unique<FrameCompletionEvent>(m_activeSource)); |
| 250 | } |
| 251 | |
| 252 | class dwgReader::DwgImmediateEntityOutput final : public DwgEntityOutput { |
| 253 | public: |
| 254 | DwgImmediateEntityOutput(dwgReader &reader, DRW_Interface &target) |
| 255 | : m_reader(reader), m_target(target) {} |
| 256 | |
| 257 | private: |
| 258 | bool append(std::unique_ptr<Event> event) override { |
| 259 | return event->replay(m_reader, m_target); |
| 260 | } |
| 261 | |
| 262 | dwgReader &m_reader; |
| 263 | DRW_Interface &m_target; |
| 264 | }; |
| 265 | |
| 266 | dwgReader::DwgBlockJournalOutput::DwgBlockJournalOutput(dwgReader &reader) |
| 267 | : m_reader(reader) {} |
| 268 | |
| 269 | bool dwgReader::DwgBlockJournalOutput::replay(DRW_Interface &target, |
| 270 | DwgSourceFrameId *activeSource) { |
| 271 | for (std::size_t index = 0; index < m_events.size(); ++index) { |
| 272 | if (!replayEvent(index, target, activeSource, nullptr)) |
| 273 | return false; |
| 274 | } |
| 275 | return true; |
| 276 | } |
| 277 | |
| 278 | bool dwgReader::DwgBlockJournalOutput::replayEvent( |
| 279 | std::size_t index, DRW_Interface &target, DwgSourceFrameId *activeSource, |
| 280 | bool *completesSource) { |
| 281 | if (index >= m_events.size()) |
| 282 | return false; |
| 283 | const Event &event = *m_events[index]; |
| 284 | if (activeSource != nullptr && event.hasSource()) |
| 285 | *activeSource = event.source(); |
| 286 | if (completesSource != nullptr) |
| 287 | *completesSource = event.completesSource(); |
| 288 | return event.replay(m_reader, target); |
| 289 | } |
| 290 | |
| 291 | bool dwgReader::DwgBlockJournalOutput::reserve(std::size_t eventCount) { |
| 292 | if (eventCount > |
| 293 | static_cast<std::size_t>(dwgSafety::MaxBlockJournalEventCount) - |
| 294 | m_events.size()) { |
| 295 | return false; |
| 296 | } |
| 297 | try { |
| 298 | m_events.reserve(m_events.size() + eventCount); |
| 299 | } catch (...) { |
| 300 | return false; |
| 301 | } |
| 302 | return true; |
| 303 | } |
| 304 | |
| 305 | void dwgReader::DwgBlockJournalOutput::truncate( |
| 306 | std::size_t eventCount) noexcept { |
| 307 | if (eventCount <= m_events.size()) |
| 308 | m_events.resize(eventCount); |
| 309 | } |
| 310 | |
| 311 | bool dwgReader::DwgBlockJournalOutput::empty() const noexcept { |
| 312 | return m_events.empty(); |
| 313 | } |
| 314 | |
| 315 | std::size_t dwgReader::DwgBlockJournalOutput::size() const noexcept { |
| 316 | return m_events.size(); |
| 317 | } |
| 318 | |
| 319 | bool dwgReader::DwgBlockJournalOutput::append(std::unique_ptr<Event> event) { |
| 320 | if (event == nullptr || |
| 321 | m_events.size() >= |
| 322 | static_cast<std::size_t>(dwgSafety::MaxBlockJournalEventCount)) { |
| 323 | throw std::length_error("DWG block journal event limit exceeded"); |
| 324 | } |
| 325 | m_events.push_back(std::move(event)); |
| 326 | return true; |
| 327 | } |
| 328 | |
| 329 | dwgReader::DwgBlockScopeTransaction::DwgBlockScopeTransaction(dwgReader &reader) |
| 330 | : m_reader(reader), m_output(reader) {} |
| 331 | |
| 332 | dwgReader::DwgBlockScopeTransaction::~DwgBlockScopeTransaction() { |
| 333 | if (!m_finished) |
| 334 | (void)abort(); |
| 335 | } |
| 336 | |
| 337 | dwgReader::DwgBlockJournalOutput & |
| 338 | dwgReader::DwgBlockScopeTransaction::output() noexcept { |
| 339 | return m_output; |
| 340 | } |
| 341 | |
| 342 | bool dwgReader::DwgBlockScopeTransaction::reserveAdmission( |
| 343 | std::size_t sourceCount, std::size_t eventCount, |
| 344 | std::uint64_t bodyByteCount) { |
| 345 | return reserveAdmissionImpl(sourceCount, eventCount, bodyByteCount, true); |
| 346 | } |
| 347 | |
| 348 | bool dwgReader::DwgBlockScopeTransaction::reserveAdmissionImpl( |
| 349 | std::size_t sourceCount, std::size_t eventCount, |
| 350 | std::uint64_t bodyByteCount, bool consumeFailurePoint) { |
| 351 | const std::size_t maxSources = |
| 352 | static_cast<std::size_t>(dwgSafety::MaxOwnedObjectCount) + 2u; |
| 353 | if (m_finished || |
| 354 | (consumeFailurePoint && |
| 355 | m_reader.consumeBlockJournalReservationFailurePointForTest()) || |
| 356 | sourceCount > maxSources - m_leases.size() || |
| 357 | bodyByteCount > dwgSafety::MaxBufferSize - m_validatedBodyBytes || |
| 358 | !m_output.reserve(eventCount)) { |
| 359 | return false; |
| 360 | } |
| 361 | try { |
| 362 | m_leases.reserve(m_leases.size() + sourceCount); |
| 363 | m_leaseIndexes.reserve(m_leaseIndexes.size() + sourceCount); |
| 364 | } catch (...) { |
| 365 | return false; |
| 366 | } |
| 367 | return true; |
| 368 | } |
| 369 | |
| 370 | bool dwgReader::DwgBlockScopeTransaction::adopt(DwgFrameMapLease &lease) { |
| 371 | if (m_finished || !lease.isDetached() || |
| 372 | lease.object.handle != lease.source.handle || |
| 373 | lease.source.handle == DRW::NoHandle || |
| 374 | m_leaseIndexes.find(lease.source.handle) != m_leaseIndexes.end()) { |
| 375 | return false; |
| 376 | } |
| 377 | const std::uint64_t bodyByteCount = lease.classification.has_value() |
| 378 | ? lease.classification->bodyByteSize |
| 379 | : lease.bodyByteSize; |
| 380 | if (!reserveAdmissionImpl(1u, 0u, bodyByteCount, false)) |
| 381 | return false; |
| 382 | if (lease.hasCoverage) { |
| 383 | const auto sourceIt = |
| 384 | m_reader.m_dwgSourceFrameIndexes.find(lease.source.handle); |
| 385 | if (sourceIt == m_reader.m_dwgSourceFrameIndexes.end() || |
| 386 | sourceIt->second >= m_reader.m_dwgSourceFrameLedger.size()) { |
| 387 | return false; |
| 388 | } |
| 389 | const DRW_DwgFrameCoverageEntry &entry = |
| 390 | m_reader.m_dwgSourceFrameLedger[sourceIt->second]; |
| 391 | const DwgSourceFrameId expected{entry.m_handle, entry.m_sourceOffset, |
| 392 | entry.m_sourceMapOrdinal, |
| 393 | entry.m_sourceOffsetSpace}; |
| 394 | if (!(lease.source == expected) || |
| 395 | entry.m_disposition != DRW_DwgFrameDisposition::Staged || |
| 396 | entry.m_publicationCount != 0) { |
| 397 | return false; |
| 398 | } |
| 399 | } |
| 400 | if (m_reader.consumeBlockJournalAdoptFailurePointForTest()) |
| 401 | return false; |
| 402 | const std::uint32_t sourceHandle = lease.source.handle; |
| 403 | try { |
| 404 | const auto inserted = m_leaseIndexes.emplace(sourceHandle, m_leases.size()); |
| 405 | if (!inserted.second) |
| 406 | return false; |
| 407 | m_leases.emplace_back(std::move(lease)); |
| 408 | } catch (...) { |
| 409 | m_leaseIndexes.erase(sourceHandle); |
| 410 | return false; |
| 411 | } |
| 412 | m_validatedBodyBytes += bodyByteCount; |
| 413 | return true; |
| 414 | } |
| 415 | |
| 416 | bool dwgReader::DwgBlockScopeTransaction::replay(DRW_Interface &target) { |
| 417 | if (m_finished) |
| 418 | return false; |
| 419 | |
| 420 | for (std::size_t index = 0; index < m_output.size(); ++index) { |
| 421 | DwgSourceFrameId activeSource; |
| 422 | bool completesSource = false; |
| 423 | bool replayed = false; |
| 424 | try { |
| 425 | replayed = |
| 426 | m_output.replayEvent(index, target, &activeSource, &completesSource); |
| 427 | } catch (...) { |
| 428 | (void)failLease(activeSource); |
| 429 | (void)abort(); |
| 430 | return false; |
| 431 | } |
| 432 | if (!replayed || activeSource.handle == DRW::NoHandle || |
| 433 | findLease(activeSource) == nullptr) { |
| 434 | (void)abort(); |
| 435 | return false; |
| 436 | } |
| 437 | if (completesSource && !retireLease(activeSource)) { |
| 438 | (void)abort(); |
| 439 | return false; |
| 440 | } |
| 441 | } |
| 442 | |
| 443 | for (const DwgFrameMapLease &lease : m_leases) { |
| 444 | if (lease.isDetached()) { |
| 445 | (void)abort(); |
| 446 | return false; |
| 447 | } |
| 448 | } |
| 449 | m_finished = true; |
| 450 | return true; |
| 451 | } |
| 452 | |
| 453 | bool dwgReader::DwgBlockScopeTransaction::abort() noexcept { |
| 454 | if (m_finished) |
| 455 | return false; |
| 456 | |
| 457 | bool success = true; |
| 458 | try { |
| 459 | for (DwgFrameMapLease &lease : m_leases) { |
| 460 | if (!lease.isDetached()) |
| 461 | continue; |
| 462 | if (lease.hasCoverage) { |
| 463 | const auto sourceIt = |
| 464 | m_reader.m_dwgSourceFrameIndexes.find(lease.source.handle); |
| 465 | if (sourceIt == m_reader.m_dwgSourceFrameIndexes.end() || |
| 466 | sourceIt->second >= m_reader.m_dwgSourceFrameLedger.size()) { |
| 467 | success = false; |
| 468 | } else { |
| 469 | const DRW_DwgFrameDisposition disposition = |
| 470 | m_reader.m_dwgSourceFrameLedger[sourceIt->second].m_disposition; |
| 471 | if (disposition == DRW_DwgFrameDisposition::Pending || |
| 472 | disposition == DRW_DwgFrameDisposition::Deferred || |
| 473 | disposition == DRW_DwgFrameDisposition::Staged) { |
| 474 | success = m_reader.quarantineDwgFrame(lease.source) && success; |
| 475 | } |
| 476 | } |
| 477 | } else { |
| 478 | success = m_reader.suppressDwgFrame(lease.source, false) && success; |
| 479 | } |
| 480 | success = m_reader.discardDetachedDwgSourceFrame(lease) && success; |
| 481 | } |
| 482 | } catch (...) { |
| 483 | success = false; |
| 484 | } |
| 485 | m_finished = true; |
| 486 | return success; |
| 487 | } |
| 488 | |
| 489 | std::size_t dwgReader::DwgBlockScopeTransaction::sourceCount() const noexcept { |
| 490 | return m_leases.size(); |
| 491 | } |
| 492 | |
| 493 | dwgReader::DwgFrameMapLease *dwgReader::DwgBlockScopeTransaction::findLease( |
| 494 | const DwgSourceFrameId &source) noexcept { |
| 495 | const auto index = m_leaseIndexes.find(source.handle); |
| 496 | if (index == m_leaseIndexes.end() || index->second >= m_leases.size()) |
| 497 | return nullptr; |
| 498 | DwgFrameMapLease &lease = m_leases[index->second]; |
| 499 | return lease.source == source ? &lease : nullptr; |
| 500 | } |
| 501 | |
| 502 | bool dwgReader::DwgBlockScopeTransaction::retireLease( |
| 503 | const DwgSourceFrameId &source) noexcept { |
| 504 | DwgFrameMapLease *const lease = findLease(source); |
| 505 | return lease != nullptr && lease->isDetached() && |
| 506 | m_reader.discardDetachedDwgSourceFrame(*lease); |
| 507 | } |
| 508 | |
| 509 | bool dwgReader::DwgBlockScopeTransaction::releaseLast( |
| 510 | const DwgSourceFrameId &expected, DwgFrameMapLease &lease) noexcept { |
| 511 | if (m_finished || m_leases.empty()) |
| 512 | return false; |
| 513 | DwgFrameMapLease &last = m_leases.back(); |
| 514 | if (!last.isDetached() || !(last.source == expected) || |
| 515 | last.object.handle != last.source.handle) { |
| 516 | return false; |
| 517 | } |
| 518 | const std::uint64_t bodyByteCount = last.classification.has_value() |
| 519 | ? last.classification->bodyByteSize |
| 520 | : last.bodyByteSize; |
| 521 | if (bodyByteCount > m_validatedBodyBytes) |
| 522 | return false; |
| 523 | const auto index = m_leaseIndexes.find(last.source.handle); |
| 524 | if (index == m_leaseIndexes.end() || index->second + 1u != m_leases.size()) |
| 525 | return false; |
| 526 | lease = std::move(last); |
| 527 | m_leases.pop_back(); |
| 528 | m_leaseIndexes.erase(index); |
| 529 | m_validatedBodyBytes -= bodyByteCount; |
| 530 | return true; |
| 531 | } |
| 532 | |
| 533 | bool dwgReader::DwgBlockScopeTransaction::failLease( |
| 534 | const DwgSourceFrameId &source) noexcept { |
| 535 | DwgFrameMapLease *const lease = findLease(source); |
| 536 | if (lease == nullptr || !lease->isDetached()) |
| 537 | return false; |
| 538 | if (!lease->hasCoverage) |
| 539 | return m_reader.suppressDwgFrame(lease->source, false); |
| 540 | return m_reader.markDwgFrameOutcome( |
| 541 | lease->source, DRW_DwgFrameDisposition::Failed, |
| 542 | DRW_DwgFrameCoverageReason::CallbackException); |
| 543 | } |
| 544 | |
| 545 | void dwgReader::addIntegrityDiagnostic( |
| 546 | DwgIntegrityDiagnostic diagnostic) noexcept { |
| 547 | if (diagnostic.version == DRW::UNKNOWNV) |
| 548 | diagnostic.version = version; |
| 549 | if (m_integrityDiagnostics.size() >= dwgSafety::MaxIntegrityDiagnostics) { |
| 550 | if (m_integrityDiagnosticsDropped != |
| 551 | std::numeric_limits<std::size_t>::max()) |
| 552 | ++m_integrityDiagnosticsDropped; |
| 553 | return; |
| 554 | } |
| 555 | try { |
| 556 | m_integrityDiagnostics.push_back(std::move(diagnostic)); |
| 557 | } catch (...) { |
| 558 | // Diagnostics must never change the established read result when the |
| 559 | // process cannot allocate the optional reporting record. |
| 560 | if (m_integrityDiagnosticsDropped != |
| 561 | std::numeric_limits<std::size_t>::max()) |
| 562 | ++m_integrityDiagnosticsDropped; |
| 563 | } |
| 564 | } |
| 565 | |
| 566 | void dwgReader::recordIntegrityDiagnostic( |
| 567 | DwgIntegritySeverity severity, DwgIntegrityAddressSpace offsetSpace, |
| 568 | DwgIntegrityPhase phase, DwgIntegrityCheckKind kind, |
| 569 | std::int32_t logicalSectionId, std::int32_t sectionDescriptorId, |
| 570 | const char *sectionName, std::uint64_t pageId, bool hasPageId, |
| 571 | std::uint64_t offset, bool hasOffset, std::uint32_t logicalHandle, |
| 572 | bool hasHandle, std::uint64_t expected, std::uint64_t observed, |
| 573 | bool hasValues) noexcept { |
| 574 | try { |
| 575 | DwgIntegrityDiagnostic diagnostic; |
| 576 | diagnostic.severity = severity; |
| 577 | diagnostic.offsetSpace = offsetSpace; |
| 578 | diagnostic.phase = phase; |
| 579 | diagnostic.kind = kind; |
| 580 | diagnostic.logicalSectionId = logicalSectionId; |
| 581 | diagnostic.sectionDescriptorId = sectionDescriptorId; |
| 582 | if (sectionName != nullptr) |
| 583 | diagnostic.sectionName = sectionName; |
| 584 | diagnostic.pageId = pageId; |
| 585 | diagnostic.hasPageId = hasPageId; |
| 586 | diagnostic.fileOffset = offset; |
| 587 | diagnostic.hasFileOffset = hasOffset; |
| 588 | diagnostic.logicalHandle = logicalHandle; |
| 589 | diagnostic.hasLogicalHandle = hasHandle; |
| 590 | diagnostic.expected = expected; |
| 591 | diagnostic.observed = observed; |
| 592 | diagnostic.hasExpected = hasValues; |
| 593 | diagnostic.hasObserved = hasValues; |
| 594 | addIntegrityDiagnostic(std::move(diagnostic)); |
| 595 | } catch (...) { |
| 596 | // Integrity reporting is optional and must never alter parsing. |
| 597 | } |
| 598 | } |
| 599 | |
| 600 | void dwgReader::recordObjectFrameFailure( |
| 601 | const objHandle &object, DwgIntegrityAddressSpace offsetSpace) noexcept { |
| 602 | recordIntegrityDiagnostic( |
| 603 | DwgIntegritySeverity::Error, offsetSpace, DwgIntegrityPhase::ObjectFrame, |
| 604 | DwgIntegrityCheckKind::ObjectFrameBounds, secEnum::OBJECTS, -1, nullptr, |
| 605 | 0, false, object.loc, true, object.handle, |
| 606 | object.handle != DRW::NoHandle); |
| 607 | } |
| 608 | |
| 609 | void dwgReader::recordEntityFailure(const objHandle &object, std::int16_t type, |
| 610 | DwgEntityFailurePhase phase, |
| 611 | std::uint32_t blockRecordHandle) noexcept { |
| 612 | if (phase == DwgEntityFailurePhase::None || |
| 613 | m_entityFailureDiagnostics.size() >= |
| 614 | dwgSafety::MaxEntityFailureDiagnostics) { |
| 615 | return; |
| 616 | } |
| 617 | try { |
| 618 | m_entityFailureDiagnostics.push_back({object.handle, type, |
| 619 | blockRecordHandle != DRW::NoHandle |
| 620 | ? blockRecordHandle |
| 621 | : rawBlockEntityOwner, |
| 622 | phase}); |
| 623 | } catch (...) { |
| 624 | // Fixture attribution must never turn an existing parse failure into |
| 625 | // a different observable result when reporting cannot allocate. |
| 626 | } |
| 627 | } |
| 628 | |
| 629 | bool dwgReader::classifyDwgSourceFrame(dwgBuffer *dbuf, const objHandle &object, |
| 630 | DwgFrameClassification &classification) { |
| 631 | classification = {}; |
| 632 | if (dbuf == nullptr) |
| 633 | return false; |
| 634 | |
| 635 | DwgObjectFrame frame; |
| 636 | if (!frame.readAt(*dbuf, version, object.loc)) |
| 637 | return false; |
| 638 | if (frame.body().size() > std::numeric_limits<std::uint32_t>::max()) |
| 639 | return false; |
| 640 | classification.bodyByteSize = static_cast<std::uint32_t>(frame.body().size()); |
| 641 | |
| 642 | std::vector<std::uint8_t> &body = frame.body(); |
| 643 | dwgBuffer buffer(body.data(), body.size(), &decoder); |
| 644 | const std::int16_t encodedType = buffer.getObjType(version); |
| 645 | if (!buffer.isGood() || encodedType < 0) |
| 646 | return false; |
| 647 | |
| 648 | classification.encodedType = encodedType; |
| 649 | classification.resolvedType = encodedType; |
| 650 | if (encodedType == dwgType::BLOCK || encodedType == dwgType::ENDBLK) { |
| 651 | classification.route = DwgFrameClassification::Route::BlockDelimiter; |
| 652 | return true; |
| 653 | } |
| 654 | |
| 655 | classification.fixedObjectShell = |
| 656 | version >= DRW::AC1021 && |
| 657 | DRW_UnsupportedObject::isFixedObjectShellType(encodedType); |
| 658 | const bool fixedEntityShell = |
| 659 | version >= DRW::AC1021 && |
| 660 | DRW_UnsupportedObject::isFixedEntityShellType(encodedType); |
| 661 | const bool fixedObject = dwgObjType::isFixedObject(encodedType); |
| 662 | if (encodedType > dwgObjType::PROXY_OBJECT && !fixedObject && |
| 663 | !classification.fixedObjectShell && !fixedEntityShell && |
| 664 | encodedType != dwgType::WIPEOUT) { |
| 665 | const auto classIt = classesmap.find(encodedType); |
| 666 | if (classIt != classesmap.end() && classIt->second != nullptr) { |
| 667 | classification.resolvedClass = classIt->second; |
| 668 | if (classIt->second->dwgType != 0) { |
| 669 | classification.resolvedType = |
| 670 | static_cast<std::int16_t>(classIt->second->dwgType); |
| 671 | } |
| 672 | } |
| 673 | } |
| 674 | |
| 675 | if (classification.fixedObjectShell || fixedObject) { |
| 676 | classification.route = DwgFrameClassification::Route::Object; |
| 677 | } else if (classification.resolvedClass != nullptr) { |
| 678 | classification.route = classification.resolvedClass->entityFlag == 0 |
| 679 | ? DwgFrameClassification::Route::Object |
| 680 | : DwgFrameClassification::Route::Entity; |
| 681 | } else if (encodedType > dwgObjType::PROXY_OBJECT && !fixedEntityShell && |
| 682 | encodedType != dwgType::WIPEOUT) { |
| 683 | // Unregistered class ids have no on-disk entity/object discriminator. |
| 684 | // The entity pass only defers ownerless instances, which are preserved |
| 685 | // as opaque OBJECTS records here. |
| 686 | classification.route = DwgFrameClassification::Route::Object; |
| 687 | } else { |
| 688 | classification.route = DwgFrameClassification::Route::Entity; |
| 689 | } |
| 690 | return true; |
| 691 | } |
| 692 | |
| 693 | bool dwgReader::classificationsMatch( |
| 694 | const DwgFrameClassification &expected, |
| 695 | const DwgFrameClassification &observed) noexcept { |
| 696 | return expected.encodedType == observed.encodedType && |
| 697 | expected.resolvedType == observed.resolvedType && |
| 698 | expected.resolvedClass == observed.resolvedClass && |
| 699 | expected.bodyByteSize == observed.bodyByteSize && |
| 700 | expected.fixedObjectShell == observed.fixedObjectShell && |
| 701 | expected.route == observed.route; |
| 702 | } |
| 703 | |
| 704 | void dwgReader::recordDwgFramePhaseSnapshot( |
| 705 | const DwgFrameMapLease &lease, |
| 706 | DwgFramePhaseSnapshot::Destination destination, |
| 707 | DRW_DwgFrameDisposition disposition) noexcept { |
| 708 | if (!lease.hasCoverage || !lease.classification.has_value()) |
| 709 | return; |
| 710 | try { |
| 711 | m_dwgFramePhaseSnapshots.push_back({lease.source, *lease.classification, |
| 712 | lease.origin, destination, |
| 713 | disposition}); |
| 714 | } catch (...) { |
| 715 | // Routing evidence is optional and must never influence parsing. |
| 716 | } |
| 717 | } |
| 718 | |
| 719 | bool dwgReader::reportDwgFrameTransitionFailure(const DwgSourceFrameId &source, |
| 720 | std::uint64_t offset, |
| 721 | bool hasOffset) noexcept { |
| 722 | m_dwgFrameCoverageIntegrityViolation = true; |
| 723 | recordIntegrityDiagnostic( |
| 724 | DwgIntegritySeverity::Error, DwgIntegrityAddressSpace::None, |
| 725 | DwgIntegrityPhase::ObjectMap, |
| 726 | DwgIntegrityCheckKind::FrameLedgerTransition, secEnum::HANDLES, -1, |
| 727 | nullptr, 0, false, offset, hasOffset, source.handle, |
| 728 | source.handle != DRW::NoHandle); |
| 729 | return false; |
| 730 | } |
| 731 | |
| 732 | DwgSourceFrameId |
| 733 | dwgReader::sourceFrameIdForHandle(std::uint32_t handle) const noexcept { |
| 734 | DwgSourceFrameId source; |
| 735 | source.handle = handle; |
| 736 | const auto it = m_dwgSourceFrameIndexes.find(handle); |
| 737 | if (it == m_dwgSourceFrameIndexes.end() || |
| 738 | it->second >= m_dwgSourceFrameLedger.size()) { |
| 739 | return source; |
| 740 | } |
| 741 | const DRW_DwgFrameCoverageEntry &entry = m_dwgSourceFrameLedger[it->second]; |
| 742 | source.offset = entry.m_sourceOffset; |
| 743 | source.ordinal = entry.m_sourceMapOrdinal; |
| 744 | source.offsetSpace = entry.m_sourceOffsetSpace; |
| 745 | return source; |
| 746 | } |
| 747 | |
| 748 | bool dwgReader::borrowDwgSourceFrame(DwgObjectMap &map, |
| 749 | DwgObjectMap::iterator it, |
| 750 | DwgSourceFrameLease &lease) { |
| 751 | if (it == map.end()) |
| 752 | return false; |
| 753 | |
| 754 | const objHandle object = it->second; |
| 755 | const DwgSourceFrameId source = sourceFrameId(object); |
| 756 | if (object.handle != it->first) |
| 757 | return reportDwgFrameTransitionFailure(source, object.loc, true); |
| 758 | if ((&map == &ObjectMap && |
| 759 | objObjectMap.find(object.handle) != objObjectMap.end()) || |
| 760 | (&map == &objObjectMap && |
| 761 | ObjectMap.find(object.handle) != ObjectMap.end())) { |
| 762 | return reportDwgFrameTransitionFailure(source, object.loc, true); |
| 763 | } |
| 764 | |
| 765 | const bool hasCoverage = |
| 766 | m_dwgFrameCoverageStatus != DRW_DwgFrameCoverageStatus::NotAvailable; |
| 767 | if (hasCoverage) { |
| 768 | const auto sourceIt = m_dwgSourceFrameIndexes.find(object.handle); |
| 769 | if (sourceIt == m_dwgSourceFrameIndexes.end() || |
| 770 | sourceIt->second >= m_dwgSourceFrameLedger.size()) { |
| 771 | return reportDwgFrameTransitionFailure(source, object.loc, true); |
| 772 | } |
| 773 | DRW_DwgFrameCoverageEntry &entry = m_dwgSourceFrameLedger[sourceIt->second]; |
| 774 | const DwgSourceFrameId expected{entry.m_handle, entry.m_sourceOffset, |
| 775 | entry.m_sourceMapOrdinal, |
| 776 | entry.m_sourceOffsetSpace}; |
| 777 | if (!(source == expected) || |
| 778 | (entry.m_disposition != DRW_DwgFrameDisposition::Pending && |
| 779 | entry.m_disposition != DRW_DwgFrameDisposition::Deferred) || |
| 780 | entry.m_publicationCount != 0) { |
| 781 | return reportDwgFrameTransitionFailure(source, entry.m_sourceOffset, |
| 782 | true); |
| 783 | } |
| 784 | } |
| 785 | |
| 786 | lease.object = object; |
| 787 | lease.source = source; |
| 788 | lease.hasCoverage = hasCoverage; |
| 789 | return true; |
| 790 | } |
| 791 | |
| 792 | bool dwgReader::takeDwgSourceFrame(DwgObjectMap &map, DwgObjectMap::iterator it, |
| 793 | DwgSourceFrameLease &lease) { |
| 794 | if (!borrowDwgSourceFrame(map, it, lease)) |
| 795 | return false; |
| 796 | map.erase(it); |
| 797 | return true; |
| 798 | } |
| 799 | |
| 800 | bool dwgReader::detachDwgSourceFrame(DwgObjectMap &map, |
| 801 | DwgObjectMap::iterator it, |
| 802 | DwgFrameMapLease &lease) { |
| 803 | if (lease.isDetached()) |
| 804 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 805 | true); |
| 806 | |
| 807 | DwgSourceFrameLease borrowed; |
| 808 | if (!borrowDwgSourceFrame(map, it, borrowed)) |
| 809 | return false; |
| 810 | |
| 811 | DwgFrameMapLease::Origin origin = DwgFrameMapLease::Origin::None; |
| 812 | if (&map == &ObjectMap) |
| 813 | origin = DwgFrameMapLease::Origin::ObjectMap; |
| 814 | else if (&map == &objObjectMap) |
| 815 | origin = DwgFrameMapLease::Origin::DeferredObjectMap; |
| 816 | else |
| 817 | return reportDwgFrameTransitionFailure(borrowed.source, borrowed.object.loc, |
| 818 | true); |
| 819 | |
| 820 | DwgObjectMap::node_type node = map.extract(it); |
| 821 | if (node.empty()) |
| 822 | return reportDwgFrameTransitionFailure(borrowed.source, borrowed.object.loc, |
| 823 | true); |
| 824 | |
| 825 | lease.object = borrowed.object; |
| 826 | lease.source = borrowed.source; |
| 827 | lease.origin = origin; |
| 828 | lease.hasCoverage = borrowed.hasCoverage; |
| 829 | lease.classification.reset(); |
| 830 | if (origin == DwgFrameMapLease::Origin::DeferredObjectMap) { |
| 831 | const auto classificationIt = |
| 832 | m_deferredFrameClassifications.find(lease.object.handle); |
| 833 | if (classificationIt != m_deferredFrameClassifications.end()) |
| 834 | lease.classification.emplace(classificationIt->second); |
| 835 | } |
| 836 | lease.node.emplace(std::move(node)); |
| 837 | return true; |
| 838 | } |
| 839 | |
| 840 | bool dwgReader::stageDetachedDwgSourceFrame(DwgFrameMapLease &lease) { |
| 841 | if (!lease.isDetached() || lease.object.handle != lease.source.handle || |
| 842 | lease.origin == DwgFrameMapLease::Origin::None) { |
| 843 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 844 | true); |
| 845 | } |
| 846 | if (ObjectMap.find(lease.object.handle) != ObjectMap.end() || |
| 847 | objObjectMap.find(lease.object.handle) != objObjectMap.end()) { |
| 848 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 849 | true); |
| 850 | } |
| 851 | if (!lease.hasCoverage) |
| 852 | return true; |
| 853 | |
| 854 | const auto sourceIt = m_dwgSourceFrameIndexes.find(lease.source.handle); |
| 855 | if (sourceIt == m_dwgSourceFrameIndexes.end() || |
| 856 | sourceIt->second >= m_dwgSourceFrameLedger.size()) { |
| 857 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 858 | true); |
| 859 | } |
| 860 | const DRW_DwgFrameCoverageEntry &entry = |
| 861 | m_dwgSourceFrameLedger[sourceIt->second]; |
| 862 | const DwgSourceFrameId expected{entry.m_handle, entry.m_sourceOffset, |
| 863 | entry.m_sourceMapOrdinal, |
| 864 | entry.m_sourceOffsetSpace}; |
| 865 | if (!(lease.source == expected) || |
| 866 | entry.m_disposition != DRW_DwgFrameDisposition::Pending || |
| 867 | entry.m_publicationCount != 0) { |
| 868 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 869 | true); |
| 870 | } |
| 871 | return markDwgFrameOutcome(lease.source, DRW_DwgFrameDisposition::Staged, |
| 872 | DRW_DwgFrameCoverageReason::CompoundStaged); |
| 873 | } |
| 874 | |
| 875 | bool dwgReader::restoreDwgSourceFrame(DwgFrameMapLease &lease) { |
| 876 | if (!lease.isDetached() || lease.object.handle != lease.source.handle) { |
| 877 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 878 | true); |
| 879 | } |
| 880 | |
| 881 | DwgObjectMap *sourceMap = nullptr; |
| 882 | DwgObjectMap *otherMap = nullptr; |
| 883 | if (lease.origin == DwgFrameMapLease::Origin::ObjectMap) { |
| 884 | sourceMap = &ObjectMap; |
| 885 | otherMap = &objObjectMap; |
| 886 | } else if (lease.origin == DwgFrameMapLease::Origin::DeferredObjectMap) { |
| 887 | sourceMap = &objObjectMap; |
| 888 | otherMap = &ObjectMap; |
| 889 | } else { |
| 890 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 891 | true); |
| 892 | } |
| 893 | |
| 894 | if (sourceMap->find(lease.object.handle) != sourceMap->end() || |
| 895 | otherMap->find(lease.object.handle) != otherMap->end()) { |
| 896 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 897 | true); |
| 898 | } |
| 899 | DRW_DwgFrameCoverageEntry *stagedEntry = nullptr; |
| 900 | if (lease.hasCoverage) { |
| 901 | const auto sourceIt = m_dwgSourceFrameIndexes.find(lease.source.handle); |
| 902 | if (sourceIt == m_dwgSourceFrameIndexes.end() || |
| 903 | sourceIt->second >= m_dwgSourceFrameLedger.size()) { |
| 904 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 905 | true); |
| 906 | } |
| 907 | DRW_DwgFrameCoverageEntry &entry = m_dwgSourceFrameLedger[sourceIt->second]; |
| 908 | const DwgSourceFrameId expected{entry.m_handle, entry.m_sourceOffset, |
| 909 | entry.m_sourceMapOrdinal, |
| 910 | entry.m_sourceOffsetSpace}; |
| 911 | if (!(lease.source == expected) || |
| 912 | (entry.m_disposition != DRW_DwgFrameDisposition::Pending && |
| 913 | entry.m_disposition != DRW_DwgFrameDisposition::Deferred && |
| 914 | entry.m_disposition != DRW_DwgFrameDisposition::Staged) || |
| 915 | entry.m_publicationCount != 0) { |
| 916 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 917 | true); |
| 918 | } |
| 919 | if (entry.m_disposition == DRW_DwgFrameDisposition::Staged) |
| 920 | stagedEntry = &entry; |
| 921 | } |
| 922 | |
| 923 | DwgObjectMap::insert_return_type inserted; |
| 924 | try { |
| 925 | inserted = sourceMap->insert(std::move(*lease.node)); |
| 926 | } catch (...) { |
| 927 | return false; |
| 928 | } |
| 929 | if (!inserted.inserted) { |
| 930 | lease.node.reset(); |
| 931 | lease.node.emplace(std::move(inserted.node)); |
| 932 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 933 | true); |
| 934 | } |
| 935 | lease.node.reset(); |
| 936 | if (lease.origin == DwgFrameMapLease::Origin::DeferredObjectMap) |
| 937 | m_deferredFrameClassifications.erase(lease.object.handle); |
| 938 | lease.origin = DwgFrameMapLease::Origin::None; |
| 939 | if (stagedEntry != nullptr) { |
| 940 | stagedEntry->m_disposition = DRW_DwgFrameDisposition::Pending; |
| 941 | stagedEntry->m_reason = DRW_DwgFrameCoverageReason::None; |
| 942 | } |
| 943 | return true; |
| 944 | } |
| 945 | |
| 946 | bool dwgReader::deferDetachedDwgSourceFrame(DwgFrameMapLease &lease, |
| 947 | DwgObjectMap &target) { |
| 948 | if (!lease.isDetached() || |
| 949 | lease.origin != DwgFrameMapLease::Origin::ObjectMap || |
| 950 | &target != &objObjectMap || lease.object.handle != lease.source.handle) { |
| 951 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 952 | true); |
| 953 | } |
| 954 | if (ObjectMap.find(lease.object.handle) != ObjectMap.end() || |
| 955 | objObjectMap.find(lease.object.handle) != objObjectMap.end()) { |
| 956 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 957 | true); |
| 958 | } |
| 959 | if (lease.hasCoverage) { |
| 960 | const auto sourceIt = m_dwgSourceFrameIndexes.find(lease.source.handle); |
| 961 | if (sourceIt == m_dwgSourceFrameIndexes.end() || |
| 962 | sourceIt->second >= m_dwgSourceFrameLedger.size()) { |
| 963 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 964 | true); |
| 965 | } |
| 966 | const DRW_DwgFrameCoverageEntry &entry = |
| 967 | m_dwgSourceFrameLedger[sourceIt->second]; |
| 968 | const DwgSourceFrameId expected{entry.m_handle, entry.m_sourceOffset, |
| 969 | entry.m_sourceMapOrdinal, |
| 970 | entry.m_sourceOffsetSpace}; |
| 971 | if (!(lease.source == expected) || |
| 972 | (entry.m_disposition != DRW_DwgFrameDisposition::Pending && |
| 973 | entry.m_disposition != DRW_DwgFrameDisposition::Deferred) || |
| 974 | entry.m_publicationCount != 0) { |
| 975 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 976 | true); |
| 977 | } |
| 978 | } |
| 979 | bool storedClassification = false; |
| 980 | if (lease.classification.has_value()) { |
| 981 | if (m_deferredFrameClassifications.find(lease.object.handle) != |
| 982 | m_deferredFrameClassifications.end()) { |
| 983 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 984 | true); |
| 985 | } |
| 986 | try { |
| 987 | const auto inserted = m_deferredFrameClassifications.emplace( |
| 988 | lease.object.handle, *lease.classification); |
| 989 | if (!inserted.second) { |
| 990 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 991 | true); |
| 992 | } |
| 993 | storedClassification = true; |
| 994 | } catch (...) { |
| 995 | return false; |
| 996 | } |
| 997 | } |
| 998 | try { |
| 999 | target.reserve(target.size() + 1u); |
| 1000 | } catch (...) { |
| 1001 | if (storedClassification) |
| 1002 | m_deferredFrameClassifications.erase(lease.object.handle); |
| 1003 | return false; |
| 1004 | } |
| 1005 | |
| 1006 | DwgObjectMap::node_type node = std::move(*lease.node); |
| 1007 | lease.node.reset(); |
| 1008 | DwgObjectMap::insert_return_type inserted; |
| 1009 | try { |
| 1010 | inserted = target.insert(std::move(node)); |
| 1011 | } catch (...) { |
| 1012 | if (storedClassification) |
| 1013 | m_deferredFrameClassifications.erase(lease.object.handle); |
| 1014 | lease.node.emplace(std::move(node)); |
| 1015 | return false; |
| 1016 | } |
| 1017 | if (!inserted.inserted) { |
| 1018 | if (storedClassification) |
| 1019 | m_deferredFrameClassifications.erase(lease.object.handle); |
| 1020 | lease.node.emplace(std::move(inserted.node)); |
| 1021 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 1022 | true); |
| 1023 | } |
| 1024 | if (!lease.hasCoverage || |
| 1025 | markDwgFrameOutcome(lease.source, DRW_DwgFrameDisposition::Deferred)) { |
| 1026 | recordDwgFramePhaseSnapshot( |
| 1027 | lease, DwgFramePhaseSnapshot::Destination::DeferredObjectMap, |
| 1028 | DRW_DwgFrameDisposition::Deferred); |
| 1029 | lease.origin = DwgFrameMapLease::Origin::None; |
| 1030 | return true; |
| 1031 | } |
| 1032 | |
| 1033 | lease.node.emplace(target.extract(lease.object.handle)); |
| 1034 | if (storedClassification) |
| 1035 | m_deferredFrameClassifications.erase(lease.object.handle); |
| 1036 | return false; |
| 1037 | } |
| 1038 | |
| 1039 | bool dwgReader::discardDetachedDwgSourceFrame(DwgFrameMapLease &lease) { |
| 1040 | if (!lease.isDetached() || lease.object.handle != lease.source.handle || |
| 1041 | lease.origin == DwgFrameMapLease::Origin::None) { |
| 1042 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 1043 | true); |
| 1044 | } |
| 1045 | if (ObjectMap.find(lease.object.handle) != ObjectMap.end() || |
| 1046 | objObjectMap.find(lease.object.handle) != objObjectMap.end()) { |
| 1047 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 1048 | true); |
| 1049 | } |
| 1050 | if (lease.hasCoverage) { |
| 1051 | const auto sourceIt = m_dwgSourceFrameIndexes.find(lease.source.handle); |
| 1052 | if (sourceIt == m_dwgSourceFrameIndexes.end() || |
| 1053 | sourceIt->second >= m_dwgSourceFrameLedger.size()) { |
| 1054 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 1055 | true); |
| 1056 | } |
| 1057 | const DRW_DwgFrameCoverageEntry &entry = |
| 1058 | m_dwgSourceFrameLedger[sourceIt->second]; |
| 1059 | const DwgSourceFrameId expected{entry.m_handle, entry.m_sourceOffset, |
| 1060 | entry.m_sourceMapOrdinal, |
| 1061 | entry.m_sourceOffsetSpace}; |
| 1062 | if (!(lease.source == expected) || entry.m_publicationCount > 1 || |
| 1063 | (entry.m_disposition != DRW_DwgFrameDisposition::Published && |
| 1064 | entry.m_disposition != DRW_DwgFrameDisposition::Failed && |
| 1065 | entry.m_disposition != DRW_DwgFrameDisposition::Quarantined && |
| 1066 | entry.m_disposition != DRW_DwgFrameDisposition::Unresolved)) { |
| 1067 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 1068 | true); |
| 1069 | } |
| 1070 | } |
| 1071 | lease.node.reset(); |
| 1072 | if (lease.origin == DwgFrameMapLease::Origin::DeferredObjectMap) |
| 1073 | m_deferredFrameClassifications.erase(lease.object.handle); |
| 1074 | lease.origin = DwgFrameMapLease::Origin::None; |
| 1075 | return true; |
| 1076 | } |
| 1077 | |
| 1078 | bool dwgReader::deferDwgSourceFrame(const DwgSourceFrameLease &lease, |
| 1079 | DwgObjectMap &target) { |
| 1080 | if (lease.object.handle != lease.source.handle) |
| 1081 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 1082 | true); |
| 1083 | |
| 1084 | const auto validateLease = [&]() { |
| 1085 | if (!lease.hasCoverage) |
| 1086 | return true; |
| 1087 | const auto sourceIt = m_dwgSourceFrameIndexes.find(lease.source.handle); |
| 1088 | if (sourceIt == m_dwgSourceFrameIndexes.end() || |
| 1089 | sourceIt->second >= m_dwgSourceFrameLedger.size()) { |
| 1090 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 1091 | true); |
| 1092 | } |
| 1093 | const DRW_DwgFrameCoverageEntry &entry = |
| 1094 | m_dwgSourceFrameLedger[sourceIt->second]; |
| 1095 | const DwgSourceFrameId expected{entry.m_handle, entry.m_sourceOffset, |
| 1096 | entry.m_sourceMapOrdinal, |
| 1097 | entry.m_sourceOffsetSpace}; |
| 1098 | if (!(lease.source == expected) || |
| 1099 | (entry.m_disposition != DRW_DwgFrameDisposition::Pending && |
| 1100 | entry.m_disposition != DRW_DwgFrameDisposition::Deferred) || |
| 1101 | entry.m_publicationCount != 0) { |
| 1102 | return reportDwgFrameTransitionFailure(lease.source, entry.m_sourceOffset, |
| 1103 | true); |
| 1104 | } |
| 1105 | return true; |
| 1106 | }; |
| 1107 | if (!validateLease()) |
| 1108 | return false; |
| 1109 | |
| 1110 | const auto sourceObjectIt = ObjectMap.find(lease.object.handle); |
| 1111 | const auto sourceDeferredIt = objObjectMap.find(lease.object.handle); |
| 1112 | const bool inObjectMap = sourceObjectIt != ObjectMap.end(); |
| 1113 | const bool inDeferredMap = sourceDeferredIt != objObjectMap.end(); |
| 1114 | if (inObjectMap && inDeferredMap) { |
| 1115 | return reportDwgFrameTransitionFailure( |
| 1116 | sourceFrameId(sourceObjectIt->second), sourceObjectIt->second.loc, |
| 1117 | true); |
| 1118 | } |
| 1119 | |
| 1120 | const auto matchesLease = [&](const objHandle &object) { |
| 1121 | return object.handle == lease.object.handle && |
| 1122 | sourceFrameId(object) == lease.source; |
| 1123 | }; |
| 1124 | if ((inObjectMap && !matchesLease(sourceObjectIt->second)) || |
| 1125 | (inDeferredMap && !matchesLease(sourceDeferredIt->second))) { |
| 1126 | const objHandle &mapped = |
| 1127 | inObjectMap ? sourceObjectIt->second : sourceDeferredIt->second; |
| 1128 | return reportDwgFrameTransitionFailure(sourceFrameId(mapped), mapped.loc, |
| 1129 | true); |
| 1130 | } |
| 1131 | |
| 1132 | if (target.find(lease.object.handle) != target.end()) { |
| 1133 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 1134 | true); |
| 1135 | } |
| 1136 | |
| 1137 | // Reserve before extracting a source node so allocation failure cannot |
| 1138 | // leave a covered frame detached from both parser maps. |
| 1139 | try { |
| 1140 | target.reserve(target.size() + 1u); |
| 1141 | } catch (...) { |
| 1142 | return false; |
| 1143 | } |
| 1144 | |
| 1145 | DwgObjectMap *sourceMap = nullptr; |
| 1146 | DwgObjectMap::iterator sourceIt; |
| 1147 | if (inObjectMap) { |
| 1148 | sourceMap = &ObjectMap; |
| 1149 | sourceIt = sourceObjectIt; |
| 1150 | } else if (inDeferredMap) { |
| 1151 | sourceMap = &objObjectMap; |
| 1152 | sourceIt = sourceDeferredIt; |
| 1153 | } |
| 1154 | |
| 1155 | if (sourceMap != nullptr) { |
| 1156 | if (sourceMap == &target) { |
| 1157 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 1158 | true); |
| 1159 | } |
| 1160 | |
| 1161 | DwgObjectMap::node_type node = sourceMap->extract(sourceIt); |
| 1162 | DwgObjectMap::insert_return_type inserted; |
| 1163 | try { |
| 1164 | inserted = target.insert(std::move(node)); |
| 1165 | } catch (...) { |
| 1166 | sourceMap->insert(std::move(node)); |
| 1167 | return false; |
| 1168 | } |
| 1169 | if (!inserted.inserted) { |
| 1170 | sourceMap->insert(std::move(inserted.node)); |
| 1171 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 1172 | true); |
| 1173 | } |
| 1174 | if (!lease.hasCoverage || |
| 1175 | markDwgFrameOutcome(lease.source, DRW_DwgFrameDisposition::Deferred)) { |
| 1176 | return true; |
| 1177 | } |
| 1178 | |
| 1179 | DwgObjectMap::node_type rollback = target.extract(lease.object.handle); |
| 1180 | sourceMap->insert(std::move(rollback)); |
| 1181 | return false; |
| 1182 | } |
| 1183 | |
| 1184 | try { |
| 1185 | const auto inserted = target.emplace(lease.object.handle, lease.object); |
| 1186 | if (!inserted.second) { |
| 1187 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 1188 | true); |
| 1189 | } |
| 1190 | } catch (...) { |
| 1191 | return false; |
| 1192 | } |
| 1193 | |
| 1194 | if (!lease.hasCoverage || |
| 1195 | markDwgFrameOutcome(lease.source, DRW_DwgFrameDisposition::Deferred)) { |
| 1196 | return true; |
| 1197 | } |
| 1198 | |
| 1199 | target.erase(lease.object.handle); |
| 1200 | return false; |
| 1201 | } |
| 1202 | |
| 1203 | bool dwgReader::discardDwgSourceFrame(DwgObjectMap &map, |
| 1204 | DwgObjectMap::iterator it) { |
| 1205 | if (it == map.end()) |
| 1206 | return false; |
| 1207 | |
| 1208 | const objHandle object = it->second; |
| 1209 | const DwgSourceFrameId source = sourceFrameId(object); |
| 1210 | if (object.handle != it->first) |
| 1211 | return reportDwgFrameTransitionFailure(source, object.loc, true); |
| 1212 | if ((&map == &ObjectMap && |
| 1213 | objObjectMap.find(object.handle) != objObjectMap.end()) || |
| 1214 | (&map == &objObjectMap && |
| 1215 | ObjectMap.find(object.handle) != ObjectMap.end())) { |
| 1216 | return reportDwgFrameTransitionFailure(source, object.loc, true); |
| 1217 | } |
| 1218 | |
| 1219 | if (&map == &ObjectMap && |
| 1220 | objObjectMap.find(object.handle) != objObjectMap.end()) { |
| 1221 | return reportDwgFrameTransitionFailure(source, object.loc, true); |
| 1222 | } |
| 1223 | if (&map == &objObjectMap && |
| 1224 | ObjectMap.find(object.handle) != ObjectMap.end()) { |
| 1225 | return reportDwgFrameTransitionFailure(source, object.loc, true); |
| 1226 | } |
| 1227 | |
| 1228 | if (m_dwgFrameCoverageStatus == DRW_DwgFrameCoverageStatus::NotAvailable) { |
| 1229 | try { |
| 1230 | m_quarantinedEntityHandles.insert(object.handle); |
| 1231 | } catch (...) { |
| 1232 | // The source-less marker only suppresses later recovery sweeps. |
| 1233 | } |
| 1234 | map.erase(it); |
| 1235 | return true; |
| 1236 | } |
| 1237 | |
| 1238 | if (!quarantineDwgFrame(source)) |
| 1239 | return false; |
| 1240 | map.erase(it); |
| 1241 | return true; |
| 1242 | } |
| 1243 | |
| 1244 | bool dwgReader::quarantineMappedDwgSourceFrame(std::uint32_t handle) { |
| 1245 | const auto objectIt = ObjectMap.find(handle); |
| 1246 | const auto deferredIt = objObjectMap.find(handle); |
| 1247 | if (objectIt != ObjectMap.end() && deferredIt != objObjectMap.end()) { |
| 1248 | return reportDwgFrameTransitionFailure(sourceFrameId(objectIt->second), |
| 1249 | objectIt->second.loc, true); |
| 1250 | } |
| 1251 | |
| 1252 | if (m_dwgFrameCoverageStatus == DRW_DwgFrameCoverageStatus::NotAvailable) { |
| 1253 | if (objectIt == ObjectMap.end() && deferredIt == objObjectMap.end()) { |
| 1254 | return false; |
| 1255 | } |
| 1256 | try { |
| 1257 | m_quarantinedEntityHandles.insert(handle); |
| 1258 | } catch (...) { |
| 1259 | // Source-less recovery has no ledger. The marker is a best-effort |
| 1260 | // guard against later standalone child dispatch. |
| 1261 | } |
| 1262 | return true; |
| 1263 | } |
| 1264 | |
| 1265 | if (objectIt != ObjectMap.end()) |
| 1266 | return quarantineDwgFrame(sourceFrameId(objectIt->second)); |
| 1267 | |
| 1268 | if (deferredIt != objObjectMap.end()) |
| 1269 | return quarantineDwgFrame(sourceFrameId(deferredIt->second)); |
| 1270 | |
| 1271 | return false; |
| 1272 | } |
| 1273 | |
| 1274 | bool dwgReader::stageCurrentEntityFrame( |
| 1275 | DwgStagedFrame &frame, const DRW_DwgFramePublication &publication) { |
| 1276 | if (frame.publication.has_value()) |
| 1277 | return reportDwgFrameTransitionFailure( |
| 1278 | DwgSourceFrameId{publication.m_handle}); |
| 1279 | if (!frame.lease.has_value()) |
| 1280 | return reportDwgFrameTransitionFailure( |
| 1281 | DwgSourceFrameId{publication.m_handle}); |
| 1282 | if (!frame.hasDetachedLease()) |
| 1283 | return reportDwgFrameTransitionFailure(frame.lease->source, |
| 1284 | frame.lease->object.loc, true); |
| 1285 | |
| 1286 | const DwgFrameMapLease &lease = *frame.lease; |
| 1287 | const DwgSourceFrameId source{ |
| 1288 | publication.m_handle, publication.m_sourceOffset, |
| 1289 | publication.m_sourceMapOrdinal, publication.m_sourceOffsetSpace}; |
| 1290 | if (!lease.hasCoverage || !publication.m_hasSourceLocation || |
| 1291 | !(lease.source == source)) { |
| 1292 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 1293 | true); |
| 1294 | } |
| 1295 | |
| 1296 | try { |
| 1297 | frame.publication.emplace(publication); |
| 1298 | } catch (...) { |
| 1299 | return false; |
| 1300 | } |
| 1301 | return true; |
| 1302 | } |
| 1303 | |
| 1304 | bool dwgReader::validateStagedFrame(const DwgStagedFrame &frame) { |
| 1305 | if (!frame.lease.has_value()) { |
| 1306 | if (!frame.publication.has_value()) |
| 1307 | return true; |
| 1308 | return reportDwgFrameTransitionFailure( |
| 1309 | DwgSourceFrameId{frame.publication->m_handle}); |
| 1310 | } |
| 1311 | |
| 1312 | const DwgFrameMapLease &lease = *frame.lease; |
| 1313 | if (!lease.isDetached() || lease.origin == DwgFrameMapLease::Origin::None || |
| 1314 | lease.object.handle != lease.source.handle) { |
| 1315 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 1316 | true); |
| 1317 | } |
| 1318 | if (ObjectMap.find(lease.object.handle) != ObjectMap.end() || |
| 1319 | objObjectMap.find(lease.object.handle) != objObjectMap.end()) { |
| 1320 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 1321 | true); |
| 1322 | } |
| 1323 | if (!lease.hasCoverage) { |
| 1324 | if (!frame.publication.has_value()) |
| 1325 | return true; |
| 1326 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 1327 | true); |
| 1328 | } |
| 1329 | if (!frame.publication.has_value() || |
| 1330 | m_dwgFrameCoverageStatus == DRW_DwgFrameCoverageStatus::NotAvailable) { |
| 1331 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 1332 | true); |
| 1333 | } |
| 1334 | |
| 1335 | const DRW_DwgFramePublication &publication = *frame.publication; |
| 1336 | const DwgSourceFrameId publicationSource{ |
| 1337 | publication.m_handle, publication.m_sourceOffset, |
| 1338 | publication.m_sourceMapOrdinal, publication.m_sourceOffsetSpace}; |
| 1339 | if (!publication.m_hasSourceLocation || |
| 1340 | !(lease.source == publicationSource)) { |
| 1341 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 1342 | true); |
| 1343 | } |
| 1344 | const auto sourceIt = m_dwgSourceFrameIndexes.find(lease.source.handle); |
| 1345 | if (sourceIt == m_dwgSourceFrameIndexes.end() || |
| 1346 | sourceIt->second >= m_dwgSourceFrameLedger.size()) { |
| 1347 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 1348 | true); |
| 1349 | } |
| 1350 | const DRW_DwgFrameCoverageEntry &entry = |
| 1351 | m_dwgSourceFrameLedger[sourceIt->second]; |
| 1352 | const DwgSourceFrameId expected{entry.m_handle, entry.m_sourceOffset, |
| 1353 | entry.m_sourceMapOrdinal, |
| 1354 | entry.m_sourceOffsetSpace}; |
| 1355 | if (!(lease.source == expected) || |
| 1356 | entry.m_disposition != DRW_DwgFrameDisposition::Staged || |
| 1357 | entry.m_publicationCount != 0) { |
| 1358 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 1359 | true); |
| 1360 | } |
| 1361 | return true; |
| 1362 | } |
| 1363 | |
| 1364 | bool dwgReader::validateStagedCompoundState() { |
| 1365 | if (m_pendingInsertStates.empty() && m_orphanAttribStates.empty() && |
| 1366 | m_pendingPolylineStates.empty() && m_orphanPolylineVertexStates.empty() && |
| 1367 | m_stagedSeqEnds.empty()) { |
| 1368 | return true; |
| 1369 | } |
| 1370 | |
| 1371 | const auto fail = [this](std::uint32_t handle) { |
| 1372 | const DwgSourceFrameId source = sourceFrameIdForHandle(handle); |
| 1373 | return reportDwgFrameTransitionFailure(source, source.offset, true); |
| 1374 | }; |
| 1375 | std::vector<std::uint32_t> attributeHandles; |
| 1376 | for (const auto &item : m_pendingInsertStates) { |
| 1377 | const std::uint32_t handle = item.first; |
| 1378 | const PendingInsertState &pending = item.second; |
| 1379 | if (handle == DRW::NoHandle || pending.entity.handle != handle || |
| 1380 | pending.entity.attlist.size() != pending.attributes.size() || |
| 1381 | !validateStagedFrame(pending.frame)) { |
| 1382 | return fail(handle); |
| 1383 | } |
| 1384 | for (std::size_t index = 0; index < pending.attributes.size(); ++index) { |
| 1385 | const StagedAttribState &attribute = pending.attributes[index]; |
| 1386 | if (attribute.entity == nullptr || |
| 1387 | attribute.entity->parentHandle != handle || |
| 1388 | pending.entity.attlist[index] != attribute.entity || |
| 1389 | std::find(attributeHandles.cbegin(), attributeHandles.cend(), |
| 1390 | attribute.entity->handle) != attributeHandles.cend() || |
| 1391 | !validateStagedFrame(attribute.frame)) { |
| 1392 | return fail(handle); |
| 1393 | } |
| 1394 | attributeHandles.push_back(attribute.entity->handle); |
| 1395 | } |
| 1396 | } |
| 1397 | for (const auto &item : m_orphanAttribStates) { |
| 1398 | const std::uint32_t owner = item.first; |
| 1399 | if (owner == DRW::NoHandle || |
| 1400 | m_pendingInsertStates.find(owner) != m_pendingInsertStates.end()) { |
| 1401 | return fail(owner); |
| 1402 | } |
| 1403 | for (const StagedAttribState &attribute : item.second.attributes) { |
| 1404 | if (attribute.entity == nullptr || |
| 1405 | attribute.entity->parentHandle != owner || |
| 1406 | std::find(attributeHandles.cbegin(), attributeHandles.cend(), |
| 1407 | attribute.entity->handle) != attributeHandles.cend() || |
| 1408 | !validateStagedFrame(attribute.frame)) { |
| 1409 | return fail(owner); |
| 1410 | } |
| 1411 | attributeHandles.push_back(attribute.entity->handle); |
| 1412 | } |
| 1413 | } |
| 1414 | std::vector<std::uint32_t> vertexHandles; |
| 1415 | for (const auto &item : m_pendingPolylineStates) { |
| 1416 | const std::uint32_t handle = item.first; |
| 1417 | const PendingPolylineState &pending = item.second; |
| 1418 | if (handle == DRW::NoHandle || pending.entity.handle != handle || |
| 1419 | m_invalidPolylineOwners.find(handle) != m_invalidPolylineOwners.end() || |
| 1420 | !validateStagedFrame(pending.frame)) { |
| 1421 | return fail(handle); |
| 1422 | } |
| 1423 | for (const StagedVertexState &vertex : pending.vertices) { |
| 1424 | if (vertex.entity.handle == DRW::NoHandle || |
| 1425 | vertex.entity.parentHandle != handle || |
| 1426 | std::find(vertexHandles.cbegin(), vertexHandles.cend(), |
| 1427 | vertex.entity.handle) != vertexHandles.cend() || |
| 1428 | !validateStagedFrame(vertex.frame)) { |
| 1429 | return fail(handle); |
| 1430 | } |
| 1431 | vertexHandles.push_back(vertex.entity.handle); |
| 1432 | } |
| 1433 | } |
| 1434 | for (const auto &item : m_orphanPolylineVertexStates) { |
| 1435 | const std::uint32_t owner = item.first; |
| 1436 | if (owner == DRW::NoHandle || |
| 1437 | m_pendingPolylineStates.find(owner) != m_pendingPolylineStates.end() || |
| 1438 | m_invalidPolylineOwners.find(owner) != m_invalidPolylineOwners.end()) { |
| 1439 | return fail(owner); |
| 1440 | } |
| 1441 | for (const StagedVertexState &vertex : item.second.vertices) { |
| 1442 | if (vertex.entity.handle == DRW::NoHandle || |
| 1443 | vertex.entity.parentHandle != owner || |
| 1444 | std::find(vertexHandles.cbegin(), vertexHandles.cend(), |
| 1445 | vertex.entity.handle) != vertexHandles.cend() || |
| 1446 | !validateStagedFrame(vertex.frame)) { |
| 1447 | return fail(owner); |
| 1448 | } |
| 1449 | vertexHandles.push_back(vertex.entity.handle); |
| 1450 | } |
| 1451 | } |
| 1452 | for (const auto &item : m_stagedSeqEnds) { |
| 1453 | const std::uint32_t handle = item.first; |
| 1454 | const StagedSeqEndState &sequenceEnd = item.second; |
| 1455 | if (handle == DRW::NoHandle || sequenceEnd.owner == DRW::NoHandle || |
| 1456 | m_invalidSeqEndHandles.find(handle) != m_invalidSeqEndHandles.end() || |
| 1457 | m_consumedSeqEndHandles.find(handle) != m_consumedSeqEndHandles.end() || |
| 1458 | !validateStagedFrame(sequenceEnd.frame)) { |
| 1459 | return fail(handle); |
| 1460 | } |
| 1461 | } |
| 1462 | return true; |
| 1463 | } |
| 1464 | |
| 1465 | bool dwgReader::hasPendingCompoundState() const noexcept { |
| 1466 | return !m_pendingInsertStates.empty() || !m_orphanAttribStates.empty() || |
| 1467 | !m_pendingPolylineStates.empty() || |
| 1468 | !m_orphanPolylineVertexStates.empty() || !m_stagedSeqEnds.empty(); |
| 1469 | } |
| 1470 | |
| 1471 | bool dwgReader::abandonStagedCompoundState() { |
| 1472 | std::vector<std::uint32_t> pendingHandles; |
| 1473 | std::vector<std::uint32_t> orphanOwners; |
| 1474 | std::vector<std::uint32_t> polylineHandles; |
| 1475 | std::vector<std::uint32_t> polylineOrphanOwners; |
| 1476 | std::vector<std::uint32_t> sequenceHandles; |
| 1477 | try { |
| 1478 | pendingHandles.reserve(m_pendingInsertStates.size()); |
| 1479 | orphanOwners.reserve(m_orphanAttribStates.size()); |
| 1480 | polylineHandles.reserve(m_pendingPolylineStates.size()); |
| 1481 | polylineOrphanOwners.reserve(m_orphanPolylineVertexStates.size()); |
| 1482 | sequenceHandles.reserve(m_stagedSeqEnds.size()); |
| 1483 | for (const auto &item : m_pendingInsertStates) |
| 1484 | pendingHandles.push_back(item.first); |
| 1485 | for (const auto &item : m_orphanAttribStates) |
| 1486 | orphanOwners.push_back(item.first); |
| 1487 | for (const auto &item : m_pendingPolylineStates) |
| 1488 | polylineHandles.push_back(item.first); |
| 1489 | for (const auto &item : m_orphanPolylineVertexStates) |
| 1490 | polylineOrphanOwners.push_back(item.first); |
| 1491 | for (const auto &item : m_stagedSeqEnds) |
| 1492 | sequenceHandles.push_back(item.first); |
| 1493 | } catch (...) { |
| 1494 | return false; |
| 1495 | } |
| 1496 | std::sort(pendingHandles.begin(), pendingHandles.end()); |
| 1497 | std::sort(orphanOwners.begin(), orphanOwners.end()); |
| 1498 | std::sort(polylineHandles.begin(), polylineHandles.end()); |
| 1499 | std::sort(polylineOrphanOwners.begin(), polylineOrphanOwners.end()); |
| 1500 | std::sort(sequenceHandles.begin(), sequenceHandles.end()); |
| 1501 | |
| 1502 | for (const std::uint32_t handle : pendingHandles) { |
| 1503 | abandonPendingInsertState(handle); |
| 1504 | if (m_pendingInsertStates.find(handle) != m_pendingInsertStates.end()) |
| 1505 | return false; |
| 1506 | } |
| 1507 | for (const std::uint32_t owner : orphanOwners) { |
| 1508 | terminalizeOrphanAttribOwner(owner); |
| 1509 | if (m_orphanAttribStates.find(owner) != m_orphanAttribStates.end()) |
| 1510 | return false; |
| 1511 | } |
| 1512 | for (const std::uint32_t handle : polylineHandles) { |
| 1513 | terminalizePendingPolylineState(handle, |
| 1514 | DwgInsertTerminalReason::MalformedGroup); |
| 1515 | if (m_pendingPolylineStates.find(handle) != m_pendingPolylineStates.end()) { |
| 1516 | return false; |
| 1517 | } |
| 1518 | } |
| 1519 | for (const std::uint32_t owner : polylineOrphanOwners) { |
| 1520 | terminalizeOrphanPolylineVertexOwner(owner); |
| 1521 | if (m_orphanPolylineVertexStates.find(owner) != |
| 1522 | m_orphanPolylineVertexStates.end()) { |
| 1523 | return false; |
| 1524 | } |
| 1525 | } |
| 1526 | for (const std::uint32_t handle : sequenceHandles) { |
| 1527 | const auto sequenceIt = m_stagedSeqEnds.find(handle); |
| 1528 | if (sequenceIt == m_stagedSeqEnds.end()) |
| 1529 | continue; |
| 1530 | bool insertedMarker = false; |
| 1531 | if (!claimInvalidSeqEndTerminalizerMarker(handle, insertedMarker)) |
| 1532 | return false; |
| 1533 | if (!abandonStagedFrame(sequenceIt->second.frame)) |
| 1534 | return false; |
| 1535 | m_stagedSeqEnds.erase(sequenceIt); |
| 1536 | } |
| 1537 | return m_pendingInsertStates.empty() && m_orphanAttribStates.empty() && |
| 1538 | m_pendingPolylineStates.empty() && |
| 1539 | m_orphanPolylineVertexStates.empty() && m_stagedSeqEnds.empty(); |
| 1540 | } |
| 1541 | |
| 1542 | bool dwgReader::restoreStagedFrame(DwgStagedFrame &frame) { |
| 1543 | if (!frame.lease.has_value()) { |
| 1544 | if (frame.publication.has_value()) |
| 1545 | return reportDwgFrameTransitionFailure( |
| 1546 | DwgSourceFrameId{frame.publication->m_handle}); |
| 1547 | return true; |
| 1548 | } |
| 1549 | if (!frame.hasDetachedLease()) |
| 1550 | return reportDwgFrameTransitionFailure(frame.lease->source, |
| 1551 | frame.lease->object.loc, true); |
| 1552 | if (frame.hasDetachedLease() && !restoreDwgSourceFrame(*frame.lease)) { |
| 1553 | return false; |
| 1554 | } |
| 1555 | frame.lease.reset(); |
| 1556 | frame.publication.reset(); |
| 1557 | return true; |
| 1558 | } |
| 1559 | |
| 1560 | bool dwgReader::abandonStagedFrame(DwgStagedFrame &frame) { |
| 1561 | if (!frame.lease.has_value()) { |
| 1562 | if (frame.publication.has_value()) |
| 1563 | return reportDwgFrameTransitionFailure( |
| 1564 | DwgSourceFrameId{frame.publication->m_handle}); |
| 1565 | return true; |
| 1566 | } |
| 1567 | if (!frame.hasDetachedLease()) |
| 1568 | return reportDwgFrameTransitionFailure(frame.lease->source, |
| 1569 | frame.lease->object.loc, true); |
| 1570 | if (frame.hasDetachedLease()) { |
| 1571 | DwgFrameMapLease &lease = *frame.lease; |
| 1572 | if (lease.hasCoverage) { |
| 1573 | const auto sourceIt = m_dwgSourceFrameIndexes.find(lease.source.handle); |
| 1574 | if (sourceIt == m_dwgSourceFrameIndexes.end() || |
| 1575 | sourceIt->second >= m_dwgSourceFrameLedger.size()) { |
| 1576 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 1577 | true); |
| 1578 | } |
| 1579 | const DRW_DwgFrameDisposition disposition = |
| 1580 | m_dwgSourceFrameLedger[sourceIt->second].m_disposition; |
| 1581 | if (disposition == DRW_DwgFrameDisposition::Staged && |
| 1582 | !markDwgFrameOutcome(lease.source, |
| 1583 | DRW_DwgFrameDisposition::Quarantined)) { |
| 1584 | return false; |
| 1585 | } |
| 1586 | if (disposition != DRW_DwgFrameDisposition::Staged && |
| 1587 | disposition != DRW_DwgFrameDisposition::Failed && |
| 1588 | disposition != DRW_DwgFrameDisposition::Quarantined) { |
| 1589 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 1590 | true); |
| 1591 | } |
| 1592 | if (!discardDetachedDwgSourceFrame(lease)) { |
| 1593 | return false; |
| 1594 | } |
| 1595 | } else { |
| 1596 | try { |
| 1597 | m_quarantinedEntityHandles.insert(lease.object.handle); |
| 1598 | } catch (...) { |
| 1599 | // Source-less duplicate suppression is best effort. |
| 1600 | } |
| 1601 | lease.node.reset(); |
| 1602 | lease.origin = DwgFrameMapLease::Origin::None; |
| 1603 | } |
| 1604 | } |
| 1605 | frame.lease.reset(); |
| 1606 | frame.publication.reset(); |
| 1607 | return true; |
| 1608 | } |
| 1609 | |
| 1610 | bool dwgReader::markDwgFrameOutcome( |
| 1611 | std::uint32_t handle, DRW_DwgFrameDisposition disposition, |
| 1612 | DRW_DwgFrameCoverageReason reason) noexcept { |
| 1613 | return markDwgFrameOutcome(sourceFrameIdForHandle(handle), disposition, |
| 1614 | reason); |
| 1615 | } |
| 1616 | |
| 1617 | bool dwgReader::markDwgFrameOutcome( |
| 1618 | const DwgSourceFrameId &source, DRW_DwgFrameDisposition disposition, |
| 1619 | DRW_DwgFrameCoverageReason reason) noexcept { |
| 1620 | const auto it = m_dwgSourceFrameIndexes.find(source.handle); |
| 1621 | if (it == m_dwgSourceFrameIndexes.end() || |
| 1622 | it->second >= m_dwgSourceFrameLedger.size()) { |
| 1623 | return reportDwgFrameTransitionFailure(source); |
| 1624 | } |
| 1625 | DRW_DwgFrameCoverageEntry &entry = m_dwgSourceFrameLedger[it->second]; |
| 1626 | const DwgSourceFrameId expected{entry.m_handle, entry.m_sourceOffset, |
| 1627 | entry.m_sourceMapOrdinal, |
| 1628 | entry.m_sourceOffsetSpace}; |
| 1629 | if (!(source == expected)) { |
| 1630 | return reportDwgFrameTransitionFailure(source, entry.m_sourceOffset, true); |
| 1631 | } |
| 1632 | if (reason == DRW_DwgFrameCoverageReason::None) { |
| 1633 | switch (disposition) { |
| 1634 | case DRW_DwgFrameDisposition::Published: |
| 1635 | reason = DRW_DwgFrameCoverageReason::ReceiptPublished; |
| 1636 | break; |
| 1637 | case DRW_DwgFrameDisposition::Deferred: |
| 1638 | reason = DRW_DwgFrameCoverageReason::CompoundDeferred; |
| 1639 | break; |
| 1640 | case DRW_DwgFrameDisposition::Staged: |
| 1641 | reason = DRW_DwgFrameCoverageReason::CompoundStaged; |
| 1642 | break; |
| 1643 | case DRW_DwgFrameDisposition::Quarantined: |
| 1644 | reason = DRW_DwgFrameCoverageReason::Quarantined; |
| 1645 | break; |
| 1646 | case DRW_DwgFrameDisposition::Failed: |
| 1647 | reason = DRW_DwgFrameCoverageReason::ParseFailure; |
| 1648 | break; |
| 1649 | case DRW_DwgFrameDisposition::Unresolved: |
| 1650 | reason = DRW_DwgFrameCoverageReason::FinalizationUnresolved; |
| 1651 | break; |
| 1652 | case DRW_DwgFrameDisposition::Pending: |
| 1653 | default: |
| 1654 | break; |
| 1655 | } |
| 1656 | } |
| 1657 | |
| 1658 | const DRW_DwgFrameDisposition current = entry.m_disposition; |
| 1659 | if (current == disposition) { |
| 1660 | if (disposition == DRW_DwgFrameDisposition::Published && |
| 1661 | entry.m_publicationCount != 1) { |
| 1662 | return reportDwgFrameTransitionFailure(source, entry.m_sourceOffset, |
| 1663 | true); |
| 1664 | } |
| 1665 | return true; |
| 1666 | } |
| 1667 | |
| 1668 | const bool allowed = |
| 1669 | current == DRW_DwgFrameDisposition::Pending |
| 1670 | ? disposition == DRW_DwgFrameDisposition::Deferred || |
| 1671 | disposition == DRW_DwgFrameDisposition::Staged || |
| 1672 | disposition == DRW_DwgFrameDisposition::Published || |
| 1673 | disposition == DRW_DwgFrameDisposition::Quarantined || |
| 1674 | disposition == DRW_DwgFrameDisposition::Failed |
| 1675 | : (current == DRW_DwgFrameDisposition::Deferred || |
| 1676 | current == DRW_DwgFrameDisposition::Staged) && |
| 1677 | (disposition == DRW_DwgFrameDisposition::Published || |
| 1678 | disposition == DRW_DwgFrameDisposition::Quarantined || |
| 1679 | disposition == DRW_DwgFrameDisposition::Failed); |
| 1680 | if (!allowed) |
| 1681 | return reportDwgFrameTransitionFailure(source, entry.m_sourceOffset, true); |
| 1682 | |
| 1683 | if (disposition == DRW_DwgFrameDisposition::Published) { |
| 1684 | if (entry.m_publicationCount != 0) |
| 1685 | return reportDwgFrameTransitionFailure(source, entry.m_sourceOffset, |
| 1686 | true); |
| 1687 | entry.m_publicationCount = 1; |
| 1688 | } |
| 1689 | entry.m_disposition = disposition; |
| 1690 | entry.m_reason = reason; |
| 1691 | return true; |
| 1692 | } |
| 1693 | |
| 1694 | bool dwgReader::quarantineDwgFrame(std::uint32_t handle) { |
| 1695 | if (handle == DRW::NoHandle) |
| 1696 | return false; |
| 1697 | return quarantineDwgFrame(sourceFrameIdForHandle(handle)); |
| 1698 | } |
| 1699 | |
| 1700 | bool dwgReader::quarantineDwgFrame(const DwgSourceFrameId &source) { |
| 1701 | if (source.handle == DRW::NoHandle) |
| 1702 | return false; |
| 1703 | const auto sourceIt = m_dwgSourceFrameIndexes.find(source.handle); |
| 1704 | if (sourceIt == m_dwgSourceFrameIndexes.end() || |
| 1705 | sourceIt->second >= m_dwgSourceFrameLedger.size()) { |
| 1706 | (void)reportDwgFrameTransitionFailure(source); |
| 1707 | return false; |
| 1708 | } |
| 1709 | |
| 1710 | const DRW_DwgFrameDisposition disposition = |
| 1711 | m_dwgSourceFrameLedger[sourceIt->second].m_disposition; |
| 1712 | if (disposition == DRW_DwgFrameDisposition::Published || |
| 1713 | disposition == DRW_DwgFrameDisposition::Unresolved) { |
| 1714 | (void)reportDwgFrameTransitionFailure( |
| 1715 | source, m_dwgSourceFrameLedger[sourceIt->second].m_sourceOffset, true); |
| 1716 | return false; |
| 1717 | } |
| 1718 | if (disposition != DRW_DwgFrameDisposition::Failed && |
| 1719 | disposition != DRW_DwgFrameDisposition::Quarantined && |
| 1720 | !markDwgFrameOutcome(source, DRW_DwgFrameDisposition::Quarantined)) { |
| 1721 | return false; |
| 1722 | } |
| 1723 | try { |
| 1724 | m_quarantinedEntityHandles.insert(source.handle); |
| 1725 | } catch (...) { |
| 1726 | // The ledger state remains authoritative if optional parser cleanup |
| 1727 | // cannot allocate its duplicate-suppression marker. |
| 1728 | } |
| 1729 | return true; |
| 1730 | } |
| 1731 | |
| 1732 | bool dwgReader::suppressDwgFrame(const DwgSourceFrameId &source, |
| 1733 | bool hasCoverage) { |
| 1734 | if (source.handle == DRW::NoHandle) |
| 1735 | return false; |
| 1736 | if (hasCoverage) |
| 1737 | return quarantineDwgFrame(source); |
| 1738 | try { |
| 1739 | m_quarantinedEntityHandles.insert(source.handle); |
| 1740 | } catch (...) { |
| 1741 | // Source-less recovery has no coverage state to corrupt. The marker |
| 1742 | // is best effort and only prevents a later sweep from republishing it. |
| 1743 | } |
| 1744 | return true; |
| 1745 | } |
| 1746 | |
| 1747 | bool dwgReader::suppressDwgFramePublication( |
| 1748 | const DRW_DwgFramePublication &publication) { |
| 1749 | const DwgSourceFrameId source{ |
| 1750 | publication.m_handle, publication.m_sourceOffset, |
| 1751 | publication.m_sourceMapOrdinal, publication.m_sourceOffsetSpace}; |
| 1752 | const bool hasCoverage = |
| 1753 | m_dwgFrameCoverageStatus != DRW_DwgFrameCoverageStatus::NotAvailable; |
| 1754 | if (hasCoverage && !publication.m_hasSourceLocation) |
| 1755 | return reportDwgFrameTransitionFailure(source); |
| 1756 | return suppressDwgFrame(source, hasCoverage); |
| 1757 | } |
| 1758 | |
| 1759 | void dwgReader::normalizeDwgFramePublication( |
| 1760 | DRW_DwgFramePublication &publication) const { |
| 1761 | if (!publication.m_isCustomClass || |
| 1762 | publication.m_classStreamOrdinal.has_value()) { |
| 1763 | return; |
| 1764 | } |
| 1765 | const auto classOrdinal = m_dwgClassNumberOrdinals.find( |
| 1766 | static_cast<std::uint32_t>(publication.m_encodedType)); |
| 1767 | if (classOrdinal != m_dwgClassNumberOrdinals.end()) |
| 1768 | publication.m_classStreamOrdinal = classOrdinal->second; |
| 1769 | } |
| 1770 | |
| 1771 | bool dwgReader::validateDwgFramePublicationStaticArtifacts( |
| 1772 | const DRW_DwgFramePublication &publication, |
| 1773 | DwgFramePublicationArtifacts artifacts) const noexcept { |
| 1774 | const DRW_DwgDictionaryMembership *const dictionaryMembership = |
| 1775 | artifacts.dictionaryMembership; |
| 1776 | if (dictionaryMembership != nullptr && |
| 1777 | (!dictionaryMembership->m_complete || |
| 1778 | !dictionaryMembership->m_hasSourceLocation || |
| 1779 | dictionaryMembership->m_version != publication.m_version || |
| 1780 | dictionaryMembership->m_dictionaryHandle != publication.m_handle || |
| 1781 | dictionaryMembership->m_sourceOffset != publication.m_sourceOffset || |
| 1782 | dictionaryMembership->m_sourceMapOrdinal != |
| 1783 | publication.m_sourceMapOrdinal || |
| 1784 | dictionaryMembership->m_sourceOffsetSpace != |
| 1785 | publication.m_sourceOffsetSpace || |
| 1786 | publication.m_resolvedType != dwgObjType::DICTIONARY)) { |
| 1787 | return false; |
| 1788 | } |
| 1789 | |
| 1790 | const DRW_DwgGroupMembership *const groupMembership = |
| 1791 | artifacts.groupMembership; |
| 1792 | if (groupMembership != nullptr) { |
| 1793 | if (!groupMembership->m_complete || !groupMembership->m_hasSourceLocation || |
| 1794 | groupMembership->m_version != publication.m_version || |
| 1795 | groupMembership->m_groupHandle != publication.m_handle || |
| 1796 | groupMembership->m_sourceOffset != publication.m_sourceOffset || |
| 1797 | groupMembership->m_sourceMapOrdinal != publication.m_sourceMapOrdinal || |
| 1798 | groupMembership->m_sourceOffsetSpace != |
| 1799 | publication.m_sourceOffsetSpace || |
| 1800 | publication.m_resolvedType != DRW_Group::kDwgFixedType || |
| 1801 | publication.m_isEntity || |
| 1802 | groupMembership->m_entries.size() > DRW_Group::kMaxEntityHandles) { |
| 1803 | return false; |
| 1804 | } |
| 1805 | for (std::size_t index = 0; index < groupMembership->m_entries.size(); |
| 1806 | ++index) { |
| 1807 | const DRW_DwgGroupMembership::Entry &entry = |
| 1808 | groupMembership->m_entries[index]; |
| 1809 | if (entry.m_handle == DRW::NoHandle || |
| 1810 | entry.m_ordinal != static_cast<std::uint32_t>(index)) { |
| 1811 | return false; |
| 1812 | } |
| 1813 | } |
| 1814 | } |
| 1815 | |
| 1816 | const DRW_DwgSortEntsMembership *const sortEntsMembership = |
| 1817 | artifacts.sortEntsMembership; |
| 1818 | const bool sortEntsTable = publication.m_isCustomClass && |
| 1819 | (publication.m_recordName == "SORTENTSTABLE" || |
| 1820 | publication.m_className == "AcDbSortentsTable"); |
| 1821 | if (sortEntsMembership != nullptr) { |
| 1822 | if (!sortEntsMembership->m_complete || |
| 1823 | !sortEntsMembership->m_hasSourceLocation || |
| 1824 | sortEntsMembership->m_version != publication.m_version || |
| 1825 | sortEntsMembership->m_tableHandle != publication.m_handle || |
| 1826 | sortEntsMembership->m_sourceOffset != publication.m_sourceOffset || |
| 1827 | sortEntsMembership->m_sourceMapOrdinal != |
| 1828 | publication.m_sourceMapOrdinal || |
| 1829 | sortEntsMembership->m_sourceOffsetSpace != |
| 1830 | publication.m_sourceOffsetSpace || |
| 1831 | sortEntsMembership->m_encodedType != publication.m_encodedType || |
| 1832 | sortEntsMembership->m_resolvedType != publication.m_resolvedType || |
| 1833 | sortEntsMembership->m_recordName != publication.m_recordName || |
| 1834 | sortEntsMembership->m_className != publication.m_className || |
| 1835 | !sortEntsMembership->m_classStreamOrdinal.has_value() || |
| 1836 | !publication.m_classStreamOrdinal.has_value() || |
| 1837 | sortEntsMembership->m_classStreamOrdinal != |
| 1838 | publication.m_classStreamOrdinal || |
| 1839 | sortEntsMembership->m_blockOwnerHandle == DRW::NoHandle || |
| 1840 | sortEntsMembership->m_entries.size() > DRW_SortEntsTable::kMaxEntries || |
| 1841 | publication.m_isEntity || !sortEntsTable) { |
| 1842 | return false; |
| 1843 | } |
| 1844 | for (std::size_t index = 0; index < sortEntsMembership->m_entries.size(); |
| 1845 | ++index) { |
| 1846 | const DRW_DwgSortEntsMembership::Entry &entry = |
| 1847 | sortEntsMembership->m_entries[index]; |
| 1848 | if (entry.m_entityHandle == DRW::NoHandle || |
| 1849 | entry.m_ordinal != static_cast<std::uint32_t>(index) || |
| 1850 | entry.m_sortFallsBackToEntity != |
| 1851 | (entry.m_sortHandle == DRW::NoHandle)) { |
| 1852 | return false; |
| 1853 | } |
| 1854 | } |
| 1855 | } |
| 1856 | |
| 1857 | const DRW_DwgFieldListMembership *const fieldListMembership = |
| 1858 | artifacts.fieldListMembership; |
| 1859 | const bool fieldList = publication.m_isCustomClass && |
| 1860 | !publication.m_isEntity && |
| 1861 | (publication.m_recordName == "FIELDLIST" || |
| 1862 | publication.m_className == "AcDbFieldList"); |
| 1863 | if (fieldListMembership != nullptr) { |
| 1864 | if (!fieldListMembership->m_complete || |
| 1865 | !fieldListMembership->m_hasSourceLocation || |
| 1866 | fieldListMembership->m_version != publication.m_version || |
| 1867 | fieldListMembership->m_listHandle != publication.m_handle || |
| 1868 | fieldListMembership->m_sourceOffset != publication.m_sourceOffset || |
| 1869 | fieldListMembership->m_sourceMapOrdinal != |
| 1870 | publication.m_sourceMapOrdinal || |
| 1871 | fieldListMembership->m_sourceOffsetSpace != |
| 1872 | publication.m_sourceOffsetSpace || |
| 1873 | fieldListMembership->m_encodedType != publication.m_encodedType || |
| 1874 | fieldListMembership->m_resolvedType != publication.m_resolvedType || |
| 1875 | fieldListMembership->m_recordName != publication.m_recordName || |
| 1876 | fieldListMembership->m_className != publication.m_className || |
| 1877 | !fieldListMembership->m_classStreamOrdinal.has_value() || |
| 1878 | !publication.m_classStreamOrdinal.has_value() || |
| 1879 | fieldListMembership->m_classStreamOrdinal != |
| 1880 | publication.m_classStreamOrdinal || |
| 1881 | fieldListMembership->m_entries.size() > DRW_Field::kMaxItems || |
| 1882 | publication.m_version < DRW::AC1015 || !fieldList) { |
| 1883 | return false; |
| 1884 | } |
| 1885 | for (std::size_t index = 0; index < fieldListMembership->m_entries.size(); |
| 1886 | ++index) { |
| 1887 | if (fieldListMembership->m_entries[index].m_ordinal != |
| 1888 | static_cast<std::uint32_t>(index)) { |
| 1889 | return false; |
| 1890 | } |
| 1891 | } |
| 1892 | } |
| 1893 | |
| 1894 | const bool dictionaryWithDefault = |
| 1895 | publication.m_isCustomClass && !publication.m_isEntity && |
| 1896 | publication.m_className == "AcDbDictionaryWithDefault"; |
| 1897 | const DRW_DwgDictionaryWithDefaultMembership *const |
| 1898 | dictionaryWithDefaultMembership = |
| 1899 | artifacts.dictionaryWithDefaultMembership; |
| 1900 | if (dictionaryWithDefaultMembership != nullptr) { |
| 1901 | if (!dictionaryWithDefaultMembership->m_complete || |
| 1902 | !dictionaryWithDefaultMembership->m_hasSourceLocation || |
| 1903 | dictionaryWithDefaultMembership->m_version != publication.m_version || |
| 1904 | dictionaryWithDefaultMembership->m_dictionaryHandle != |
| 1905 | publication.m_handle || |
| 1906 | dictionaryWithDefaultMembership->m_sourceOffset != |
| 1907 | publication.m_sourceOffset || |
| 1908 | dictionaryWithDefaultMembership->m_sourceMapOrdinal != |
| 1909 | publication.m_sourceMapOrdinal || |
| 1910 | dictionaryWithDefaultMembership->m_sourceOffsetSpace != |
| 1911 | publication.m_sourceOffsetSpace || |
| 1912 | dictionaryWithDefaultMembership->m_encodedType != |
| 1913 | publication.m_encodedType || |
| 1914 | dictionaryWithDefaultMembership->m_resolvedType != |
| 1915 | publication.m_resolvedType || |
| 1916 | dictionaryWithDefaultMembership->m_recordName != |
| 1917 | publication.m_recordName || |
| 1918 | dictionaryWithDefaultMembership->m_className != |
| 1919 | publication.m_className || |
| 1920 | !dictionaryWithDefaultMembership->m_classStreamOrdinal.has_value() || |
| 1921 | !publication.m_classStreamOrdinal.has_value() || |
| 1922 | dictionaryWithDefaultMembership->m_classStreamOrdinal != |
| 1923 | publication.m_classStreamOrdinal || |
| 1924 | dictionaryWithDefaultMembership->m_hardOwner < 0 || |
| 1925 | dictionaryWithDefaultMembership->m_hardOwner > 1 || |
| 1926 | dictionaryWithDefaultMembership->m_defaultEntryHandle == |
| 1927 | DRW::NoHandle || |
| 1928 | dictionaryWithDefaultMembership->m_entries.size() > |
| 1929 | DRW_Dictionary::kMaxEntries || |
| 1930 | publication.m_version < DRW::AC1015 || !dictionaryWithDefault) { |
| 1931 | return false; |
| 1932 | } |
| 1933 | for (const DRW_DwgDictionaryWithDefaultMembership::Entry &entry : |
| 1934 | dictionaryWithDefaultMembership->m_entries) { |
| 1935 | if (entry.m_name.empty() || entry.m_handle == DRW::NoHandle) |
| 1936 | return false; |
| 1937 | } |
| 1938 | } |
| 1939 | |
| 1940 | const DRW_DwgFieldPayloadReceipt *const fieldPayloadReceipt = |
| 1941 | artifacts.fieldPayloadReceipt; |
| 1942 | if (fieldPayloadReceipt != nullptr) { |
| 1943 | const DRW_Field &field = fieldPayloadReceipt->m_field; |
| 1944 | const bool fieldObject = publication.m_isCustomClass && |
| 1945 | !publication.m_isEntity && |
| 1946 | publication.m_recordName == "FIELD" && |
| 1947 | publication.m_className == "AcDbField"; |
| 1948 | if (!fieldPayloadReceipt->m_complete || |
| 1949 | !fieldPayloadReceipt->m_hasSourceLocation || |
| 1950 | fieldPayloadReceipt->m_version != publication.m_version || |
| 1951 | fieldPayloadReceipt->m_fieldHandle != publication.m_handle || |
| 1952 | fieldPayloadReceipt->m_sourceOffset != publication.m_sourceOffset || |
| 1953 | fieldPayloadReceipt->m_sourceMapOrdinal != |
| 1954 | publication.m_sourceMapOrdinal || |
| 1955 | fieldPayloadReceipt->m_sourceOffsetSpace != |
| 1956 | publication.m_sourceOffsetSpace || |
| 1957 | fieldPayloadReceipt->m_encodedType != publication.m_encodedType || |
| 1958 | fieldPayloadReceipt->m_resolvedType != publication.m_resolvedType || |
| 1959 | fieldPayloadReceipt->m_recordName != publication.m_recordName || |
| 1960 | fieldPayloadReceipt->m_className != publication.m_className || |
| 1961 | !fieldPayloadReceipt->m_classStreamOrdinal.has_value() || |
| 1962 | !publication.m_classStreamOrdinal.has_value() || |
| 1963 | fieldPayloadReceipt->m_classStreamOrdinal != |
| 1964 | publication.m_classStreamOrdinal || |
| 1965 | field.handle != publication.m_handle || |
| 1966 | !field.hasCompleteDwgPayload() || |
| 1967 | !field.isDwgPayloadValid(publication.m_version) || |
| 1968 | publication.m_version < DRW::AC1015 || !fieldObject) { |
| 1969 | return false; |
| 1970 | } |
| 1971 | } |
| 1972 | |
| 1973 | const DRW_DwgTypedReference *const typedReference = artifacts.typedReference; |
| 1974 | return typedReference == nullptr || |
| 1975 | (typedReference->m_complete && typedReference->m_hasSourceLocation && |
| 1976 | typedReference->m_version == publication.m_version && |
| 1977 | typedReference->m_sourceHandle == publication.m_handle && |
| 1978 | typedReference->m_sourceOffset == publication.m_sourceOffset && |
| 1979 | typedReference->m_sourceMapOrdinal == |
| 1980 | publication.m_sourceMapOrdinal && |
| 1981 | typedReference->m_sourceOffsetSpace == |
| 1982 | publication.m_sourceOffsetSpace && |
| 1983 | typedReference->m_encodedType == publication.m_encodedType && |
| 1984 | typedReference->m_resolvedType == publication.m_resolvedType && |
| 1985 | typedReference->m_classStreamOrdinal.has_value() && |
| 1986 | publication.m_classStreamOrdinal.has_value() && |
| 1987 | typedReference->m_classStreamOrdinal == |
| 1988 | publication.m_classStreamOrdinal && |
| 1989 | typedReference->m_field == |
| 1990 | DRW_DwgTypedReferenceField::DictionaryDefault && |
| 1991 | typedReference->m_referenceCode == DRW::DwgHardPointer && |
| 1992 | typedReference->m_targetHandle != DRW::NoHandle && |
| 1993 | (dictionaryWithDefaultMembership == nullptr || |
| 1994 | typedReference->m_targetHandle == |
| 1995 | dictionaryWithDefaultMembership->m_defaultEntryHandle) && |
| 1996 | dictionaryWithDefault); |
| 1997 | } |
| 1998 | |
| 1999 | bool dwgReader::publishDwgFramePublication( |
| 2000 | DRW_Interface &intfa, DRW_DwgFramePublication publication, |
| 2001 | DwgFramePublicationArtifacts artifacts, |
| 2002 | DwgFieldFamilyPublicationOutputs fieldOutputs) { |
| 2003 | if (m_dwgFrameCoverageStatus == DRW_DwgFrameCoverageStatus::NotAvailable) { |
| 2004 | try { |
| 2005 | if (fieldOutputs.field != nullptr) |
| 2006 | intfa.addField(*fieldOutputs.field); |
| 2007 | if (fieldOutputs.fieldList != nullptr) |
| 2008 | intfa.addFieldList(*fieldOutputs.fieldList); |
| 2009 | if (fieldOutputs.rawObject != nullptr) |
| 2010 | intfa.addUnsupportedObject(*fieldOutputs.rawObject); |
| 2011 | } catch (...) { |
| 2012 | return false; |
| 2013 | } |
| 2014 | return true; |
| 2015 | } |
| 2016 | if (!publication.m_hasSourceLocation) { |
| 2017 | return reportDwgFrameTransitionFailure( |
| 2018 | DwgSourceFrameId{publication.m_handle}); |
| 2019 | } |
| 2020 | const DwgSourceFrameId frameSource{ |
| 2021 | publication.m_handle, publication.m_sourceOffset, |
| 2022 | publication.m_sourceMapOrdinal, publication.m_sourceOffsetSpace}; |
| 2023 | const auto sourceIt = m_dwgSourceFrameIndexes.find(publication.m_handle); |
| 2024 | if (sourceIt == m_dwgSourceFrameIndexes.end() || |
| 2025 | sourceIt->second >= m_dwgSourceFrameLedger.size()) { |
| 2026 | return reportDwgFrameTransitionFailure(frameSource); |
| 2027 | } |
| 2028 | const DRW_DwgFrameCoverageEntry &entry = |
| 2029 | m_dwgSourceFrameLedger[sourceIt->second]; |
| 2030 | const DwgSourceFrameId expected{entry.m_handle, entry.m_sourceOffset, |
| 2031 | entry.m_sourceMapOrdinal, |
| 2032 | entry.m_sourceOffsetSpace}; |
| 2033 | if (!(frameSource == expected) || |
| 2034 | (entry.m_disposition != DRW_DwgFrameDisposition::Pending && |
| 2035 | entry.m_disposition != DRW_DwgFrameDisposition::Deferred && |
| 2036 | entry.m_disposition != DRW_DwgFrameDisposition::Staged) || |
| 2037 | entry.m_publicationCount != 0) { |
| 2038 | return reportDwgFrameTransitionFailure(frameSource, entry.m_sourceOffset, |
| 2039 | true); |
| 2040 | } |
| 2041 | normalizeDwgFramePublication(publication); |
| 2042 | const DRW_DwgDictionaryMembership *const dictionaryMembership = |
| 2043 | artifacts.dictionaryMembership; |
| 2044 | const DRW_DwgTypedReference *const typedReference = artifacts.typedReference; |
| 2045 | const DRW_DwgGroupMembership *const groupMembership = |
| 2046 | artifacts.groupMembership; |
| 2047 | const DRW_DwgSortEntsMembership *const sortEntsMembership = |
| 2048 | artifacts.sortEntsMembership; |
| 2049 | const DRW_DwgFieldListMembership *const fieldListMembership = |
| 2050 | artifacts.fieldListMembership; |
| 2051 | const DRW_DwgDictionaryWithDefaultMembership *const |
| 2052 | dictionaryWithDefaultMembership = |
| 2053 | artifacts.dictionaryWithDefaultMembership; |
| 2054 | const DRW_DwgFieldPayloadReceipt *const fieldPayloadReceipt = |
| 2055 | artifacts.fieldPayloadReceipt; |
| 2056 | if (!validateDwgFramePublicationStaticArtifacts(publication, artifacts)) { |
| 2057 | (void)markDwgFrameOutcome(frameSource, DRW_DwgFrameDisposition::Failed, |
| 2058 | DRW_DwgFrameCoverageReason::ReceiptFailure); |
| 2059 | return false; |
| 2060 | } |
| 2061 | const auto matchesFieldListOutput = [&]() { |
| 2062 | if ((fieldOutputs.fieldList == nullptr) != |
| 2063 | (fieldListMembership == nullptr)) { |
| 2064 | return false; |
| 2065 | } |
| 2066 | if (fieldOutputs.fieldList == nullptr) |
| 2067 | return true; |
| 2068 | const DRW_FieldList &fieldList = *fieldOutputs.fieldList; |
| 2069 | if (fieldList.handle != publication.m_handle || |
| 2070 | !fieldList.hasCompleteDwgEntries() || |
| 2071 | !fieldList.isDwgPayloadValid(publication.m_version) || |
| 2072 | fieldList.m_fieldHandles.size() != fieldListMembership->m_entries.size()) { |
| 2073 | return false; |
| 2074 | } |
| 2075 | for (std::size_t index = 0; index < fieldList.m_fieldHandles.size(); |
| 2076 | ++index) { |
| 2077 | const DRW_DwgFieldListMembership::Entry &entry = |
| 2078 | fieldListMembership->m_entries[index]; |
| 2079 | if (entry.m_ordinal != static_cast<std::uint32_t>(index) || |
| 2080 | entry.m_fieldHandle != fieldList.m_fieldHandles[index]) { |
| 2081 | return false; |
| 2082 | } |
| 2083 | } |
| 2084 | return true; |
| 2085 | }; |
| 2086 | if ((fieldOutputs.field != nullptr && |
| 2087 | (fieldPayloadReceipt == nullptr || |
| 2088 | fieldOutputs.field->handle != publication.m_handle || |
| 2089 | fieldOutputs.field->handle != fieldPayloadReceipt->m_field.handle)) || |
| 2090 | !matchesFieldListOutput() || |
| 2091 | (fieldOutputs.rawObject != nullptr && |
| 2092 | (fieldOutputs.rawObject->m_version != publication.m_version || |
| 2093 | fieldOutputs.rawObject->m_handle != publication.m_handle || |
| 2094 | fieldOutputs.rawObject->m_objectType != publication.m_resolvedType || |
| 2095 | !fieldOutputs.rawObject->m_isCustomClass || |
| 2096 | fieldOutputs.rawObject->m_recordName != publication.m_recordName || |
| 2097 | fieldOutputs.rawObject->m_className != publication.m_className))) { |
| 2098 | (void)markDwgFrameOutcome(frameSource, DRW_DwgFrameDisposition::Failed, |
| 2099 | DRW_DwgFrameCoverageReason::ReceiptFailure); |
| 2100 | return false; |
| 2101 | } |
| 2102 | const DRW_DwgBlockReachability *const blockReachability = |
| 2103 | artifacts.blockReachability; |
| 2104 | const auto matchesLedgerSource = |
| 2105 | [this](const DRW_DwgSourceFrame &receiptSource) { |
| 2106 | if (receiptSource.m_handle == DRW::NoHandle || |
| 2107 | !receiptSource.m_hasSourceLocation) { |
| 2108 | return false; |
| 2109 | } |
| 2110 | const auto source = |
| 2111 | m_dwgSourceFrameIndexes.find(receiptSource.m_handle); |
| 2112 | if (source == m_dwgSourceFrameIndexes.cend() || |
| 2113 | source->second >= m_dwgSourceFrameLedger.size()) { |
| 2114 | return false; |
| 2115 | } |
| 2116 | const DRW_DwgFrameCoverageEntry &ledger = |
| 2117 | m_dwgSourceFrameLedger[source->second]; |
| 2118 | return ledger.m_handle == receiptSource.m_handle && |
| 2119 | ledger.m_sourceOffset == receiptSource.m_sourceOffset && |
| 2120 | ledger.m_sourceMapOrdinal == receiptSource.m_sourceMapOrdinal && |
| 2121 | ledger.m_sourceOffsetSpace == receiptSource.m_sourceOffsetSpace; |
| 2122 | }; |
| 2123 | const auto sameReceiptSource = [](const DRW_DwgSourceFrame &source, |
| 2124 | const DRW_DwgFramePublication &receipt) { |
| 2125 | return source.m_handle == receipt.m_handle && |
| 2126 | source.m_sourceOffset == receipt.m_sourceOffset && |
| 2127 | source.m_sourceMapOrdinal == receipt.m_sourceMapOrdinal && |
| 2128 | source.m_sourceOffsetSpace == receipt.m_sourceOffsetSpace && |
| 2129 | source.m_hasSourceLocation == receipt.m_hasSourceLocation; |
| 2130 | }; |
| 2131 | const auto receiptDisposition = |
| 2132 | [this](const DRW_DwgSourceFrame &receiptSource) |
| 2133 | -> std::optional<DRW_DwgFrameDisposition> { |
| 2134 | const auto source = m_dwgSourceFrameIndexes.find(receiptSource.m_handle); |
| 2135 | if (source == m_dwgSourceFrameIndexes.cend() || |
| 2136 | source->second >= m_dwgSourceFrameLedger.size()) { |
| 2137 | return std::nullopt; |
| 2138 | } |
| 2139 | return m_dwgSourceFrameLedger[source->second].m_disposition; |
| 2140 | }; |
| 2141 | const auto receiptPublished = [this]( |
| 2142 | const DRW_DwgSourceFrame &receiptSource) { |
| 2143 | const auto source = m_dwgSourceFrameIndexes.find(receiptSource.m_handle); |
| 2144 | return source != m_dwgSourceFrameIndexes.cend() && |
| 2145 | source->second < m_dwgSourceFrameLedger.size() && |
| 2146 | m_dwgSourceFrameLedger[source->second].m_disposition == |
| 2147 | DRW_DwgFrameDisposition::Published && |
| 2148 | m_dwgSourceFrameLedger[source->second].m_publicationCount == 1u; |
| 2149 | }; |
| 2150 | if (blockReachability != nullptr) { |
| 2151 | bool validReachability = |
| 2152 | blockReachability->m_complete && |
| 2153 | blockReachability->m_version == publication.m_version && |
| 2154 | publication.m_version >= DRW::AC1018 && |
| 2155 | publication.m_resolvedType == dwgType::BLOCK && |
| 2156 | publication.m_isEntity && |
| 2157 | sameReceiptSource(blockReachability->m_block, publication) && |
| 2158 | matchesLedgerSource(blockReachability->m_blockRecord) && |
| 2159 | matchesLedgerSource(blockReachability->m_block) && |
| 2160 | matchesLedgerSource(blockReachability->m_endBlock) && |
| 2161 | receiptPublished(blockReachability->m_blockRecord) && |
| 2162 | receiptDisposition(blockReachability->m_block) == |
| 2163 | DRW_DwgFrameDisposition::Staged && |
| 2164 | receiptDisposition(blockReachability->m_endBlock) == |
| 2165 | DRW_DwgFrameDisposition::Staged && |
| 2166 | blockReachability->m_blockRecord.m_handle != publication.m_handle && |
| 2167 | blockReachability->m_endBlock.m_handle != publication.m_handle && |
| 2168 | blockReachability->m_endBlock.m_handle != |
| 2169 | blockReachability->m_blockRecord.m_handle; |
| 2170 | std::unordered_set<std::uint32_t> entityHandles; |
| 2171 | if (validReachability) { |
| 2172 | try { |
| 2173 | entityHandles.reserve(blockReachability->m_entities.size()); |
| 2174 | for (const DRW_DwgSourceFrame &entity : blockReachability->m_entities) { |
| 2175 | if (!matchesLedgerSource(entity) || !receiptPublished(entity) || |
| 2176 | entity.m_handle == publication.m_handle || |
| 2177 | entity.m_handle == blockReachability->m_blockRecord.m_handle || |
| 2178 | entity.m_handle == blockReachability->m_endBlock.m_handle || |
| 2179 | !entityHandles.insert(entity.m_handle).second) { |
| 2180 | validReachability = false; |
| 2181 | break; |
| 2182 | } |
| 2183 | } |
| 2184 | } catch (...) { |
| 2185 | validReachability = false; |
| 2186 | } |
| 2187 | } |
| 2188 | if (!validReachability) { |
| 2189 | (void)markDwgFrameOutcome(frameSource, DRW_DwgFrameDisposition::Failed, |
| 2190 | DRW_DwgFrameCoverageReason::ReceiptFailure); |
| 2191 | return false; |
| 2192 | } |
| 2193 | } |
| 2194 | try { |
| 2195 | intfa.addDwgFramePublication(publication); |
| 2196 | if (dictionaryMembership != nullptr) |
| 2197 | intfa.addDwgDictionaryMembership(*dictionaryMembership); |
| 2198 | if (dictionaryWithDefaultMembership != nullptr) |
| 2199 | intfa.addDwgDictionaryWithDefaultMembership( |
| 2200 | *dictionaryWithDefaultMembership); |
| 2201 | if (typedReference != nullptr) |
| 2202 | intfa.addDwgTypedReference(*typedReference); |
| 2203 | if (blockReachability != nullptr) |
| 2204 | intfa.addDwgBlockReachability(*blockReachability); |
| 2205 | if (groupMembership != nullptr) |
| 2206 | intfa.addDwgGroupMembership(*groupMembership); |
| 2207 | if (sortEntsMembership != nullptr) |
| 2208 | intfa.addDwgSortEntsMembership(*sortEntsMembership); |
| 2209 | if (fieldListMembership != nullptr) |
| 2210 | intfa.addDwgFieldListMembership(*fieldListMembership); |
| 2211 | if (fieldPayloadReceipt != nullptr) |
| 2212 | intfa.addDwgFieldPayloadReceipt(*fieldPayloadReceipt); |
| 2213 | if (fieldOutputs.field != nullptr) |
| 2214 | intfa.addField(*fieldOutputs.field); |
| 2215 | if (fieldOutputs.fieldList != nullptr) |
| 2216 | intfa.addFieldList(*fieldOutputs.fieldList); |
| 2217 | if (fieldOutputs.rawObject != nullptr) |
| 2218 | intfa.addUnsupportedObject(*fieldOutputs.rawObject); |
| 2219 | } catch (...) { |
| 2220 | (void)markDwgFrameOutcome(frameSource, DRW_DwgFrameDisposition::Failed, |
| 2221 | DRW_DwgFrameCoverageReason::CallbackException); |
| 2222 | return false; |
| 2223 | } |
| 2224 | return markDwgFrameOutcome(frameSource, DRW_DwgFrameDisposition::Published, |
| 2225 | DRW_DwgFrameCoverageReason::ReceiptPublished); |
| 2226 | } |
| 2227 | |
| 2228 | void dwgReader::finalizeDwgFrameCoverage(DRW_Interface &intfa, |
| 2229 | bool readCompleted) { |
| 2230 | if (m_dwgFrameCoverageStatus == DRW_DwgFrameCoverageStatus::NotAvailable || |
| 2231 | m_dwgFrameCoveragePublished) { |
| 2232 | return; |
| 2233 | } |
| 2234 | |
| 2235 | for (DRW_DwgFrameCoverageEntry &entry : m_dwgSourceFrameLedger) { |
| 2236 | if (entry.m_disposition == DRW_DwgFrameDisposition::Pending || |
| 2237 | entry.m_disposition == DRW_DwgFrameDisposition::Deferred || |
| 2238 | entry.m_disposition == DRW_DwgFrameDisposition::Staged) { |
| 2239 | entry.m_disposition = DRW_DwgFrameDisposition::Unresolved; |
| 2240 | entry.m_reason = readCompleted |
| 2241 | ? DRW_DwgFrameCoverageReason::FinalizationUnresolved |
| 2242 | : DRW_DwgFrameCoverageReason::PhaseAborted; |
| 2243 | } |
| 2244 | } |
| 2245 | |
| 2246 | DRW_DwgFrameCoverageReport report; |
| 2247 | report.m_entries = m_dwgSourceFrameLedger; |
| 2248 | report.m_complete = |
| 2249 | readCompleted && !m_dwgFrameCoverageIntegrityViolation && |
| 2250 | std::all_of(report.m_entries.cbegin(), report.m_entries.cend(), |
| 2251 | [](const DRW_DwgFrameCoverageEntry &entry) { |
| 2252 | return entry.m_disposition == |
| 2253 | DRW_DwgFrameDisposition::Published && |
| 2254 | entry.m_publicationCount == 1; |
| 2255 | }); |
| 2256 | report.m_status = report.m_complete |
| 2257 | ? DRW_DwgFrameCoverageStatus::FinalizedComplete |
| 2258 | : DRW_DwgFrameCoverageStatus::FinalizedPartial; |
| 2259 | m_dwgFrameCoverageStatus = report.m_status; |
| 2260 | m_dwgFrameCoveragePublished = true; |
| 2261 | intfa.addDwgFrameCoverageReport(report); |
| 2262 | } |
| 2263 | |
| 2264 | void dwgReader::finalizeDwgFrameCoverageNoThrow(DRW_Interface &intfa, |
| 2265 | bool readCompleted) noexcept { |
| 2266 | try { |
| 2267 | finalizeDwgFrameCoverage(intfa, readCompleted); |
| 2268 | } catch (...) { |
| 2269 | if (m_dwgFrameCoverageStatus != DRW_DwgFrameCoverageStatus::NotAvailable) { |
| 2270 | m_dwgFrameCoverageIntegrityViolation = true; |
| 2271 | m_dwgFrameCoverageStatus = DRW_DwgFrameCoverageStatus::FinalizedPartial; |
| 2272 | m_dwgFrameCoveragePublished = true; |
| 2273 | } |
| 2274 | } |
| 2275 | } |
| 2276 | |
| 2277 | namespace { |
| 2278 | |
| 2279 | DRW_DwgFrameOffsetSpace |
| 2280 | frameOffsetSpace(DwgIntegrityAddressSpace offsetSpace) noexcept { |
| 2281 | switch (offsetSpace) { |
| 2282 | case DwgIntegrityAddressSpace::PhysicalFile: |
| 2283 | return DRW_DwgFrameOffsetSpace::PhysicalFile; |
| 2284 | case DwgIntegrityAddressSpace::DecodedBuffer: |
| 2285 | return DRW_DwgFrameOffsetSpace::DecodedBuffer; |
| 2286 | case DwgIntegrityAddressSpace::None: |
| 2287 | default: |
| 2288 | return DRW_DwgFrameOffsetSpace::Unknown; |
| 2289 | } |
| 2290 | } |
| 2291 | |
| 2292 | // DWG control-object layouts do not use one common count field. The |
| 2293 | // listed controls encode numEntries as FIELD_BS; the remaining controls |
| 2294 | // use FIELD_BL (dwg.spec control-object definitions). |
| 2295 | bool controlEntryCountUsesBitShort(std::int16_t objectType) { |
| 2296 | switch (objectType) { |
| 2297 | case DRW::DwgLTypeControlObjectType: |
| 2298 | case DRW::DwgUcsControlObjectType: |
| 2299 | case DRW::DwgVPortControlObjectType: |
| 2300 | case DRW::DwgAppIdControlObjectType: |
| 2301 | case DRW::DwgDimStyleControlObjectType: |
| 2302 | return true; |
| 2303 | default: |
| 2304 | return false; |
| 2305 | } |
| 2306 | } |
| 2307 | |
| 2308 | bool controlHasPhantomEntries(std::int16_t objectType) { |
| 2309 | return objectType == DRW::DwgBlockControlObjectType || |
| 2310 | objectType == DRW::DwgLTypeControlObjectType; |
| 2311 | } |
| 2312 | |
| 2313 | struct DwgTableDescriptor { |
| 2314 | std::int16_t controlType; |
| 2315 | std::int16_t recordType; |
| 2316 | const char *controlReceiptName; |
| 2317 | }; |
| 2318 | |
| 2319 | constexpr DwgTableDescriptor kLTypeTable{ |
| 2320 | DRW::DwgLTypeControlObjectType, DRW::DwgLTypeObjectType, "LTYPE_CONTROL"}; |
| 2321 | constexpr DwgTableDescriptor kLayerTable{ |
| 2322 | DRW::DwgLayerControlObjectType, DRW::DwgLayerObjectType, "LAYER_CONTROL"}; |
| 2323 | constexpr DwgTableDescriptor kStyleTable{ |
| 2324 | DRW::DwgStyleControlObjectType, DRW::DwgStyleObjectType, "STYLE_CONTROL"}; |
| 2325 | constexpr DwgTableDescriptor kDimStyleTable{DRW::DwgDimStyleControlObjectType, |
| 2326 | DRW::DwgDimStyleObjectType, |
| 2327 | "DIMSTYLE_CONTROL"}; |
| 2328 | constexpr DwgTableDescriptor kVPortTable{ |
| 2329 | DRW::DwgVPortControlObjectType, DRW::DwgVPortObjectType, "VPORT_CONTROL"}; |
| 2330 | constexpr DwgTableDescriptor kBlockTable{DRW::DwgBlockControlObjectType, |
| 2331 | DRW::DwgBlockRecordObjectType, |
| 2332 | "BLOCK_CONTROL"}; |
| 2333 | constexpr DwgTableDescriptor kAppIdTable{ |
| 2334 | DRW::DwgAppIdControlObjectType, DRW::DwgAppIdObjectType, "APPID_CONTROL"}; |
| 2335 | constexpr DwgTableDescriptor kViewTable{DRW::DwgViewControlObjectType, |
| 2336 | DRW::DwgViewObjectType, "VIEW_CONTROL"}; |
| 2337 | constexpr DwgTableDescriptor kUcsTable{DRW::DwgUcsControlObjectType, |
| 2338 | DRW::DwgUcsObjectType, "UCS_CONTROL"}; |
| 2339 | |
| 2340 | bool controlCurrentBit(const dwgBuffer &buffer, std::uint64_t &value) { |
| 2341 | std::uint64_t byteBits = 0; |
| 2342 | return dwgSafety::multiply(buffer.getPosition(), 8, byteBits) && |
| 2343 | dwgSafety::add(byteBits, buffer.getBitPos(), value); |
| 2344 | } |
| 2345 | |
| 2346 | template <typename Value, typename Reader> |
| 2347 | bool readControlValue(dwgBuffer &buffer, std::uint64_t endBit, Reader reader, |
| 2348 | Value &value) { |
| 2349 | std::uint64_t currentBit = 0; |
| 2350 | if (!buffer.isGood() || !controlCurrentBit(buffer, currentBit) || |
| 2351 | currentBit > endBit) |
| 2352 | return false; |
| 2353 | |
| 2354 | dwgBuffer probe = buffer.forkIndependent(); |
| 2355 | const Value parsed = reader(probe); |
| 2356 | std::uint64_t parsedBit = 0; |
| 2357 | if (!probe.isGood() || !controlCurrentBit(probe, parsedBit) || |
| 2358 | parsedBit > endBit) |
| 2359 | return false; |
| 2360 | buffer = probe; |
| 2361 | value = parsed; |
| 2362 | return true; |
| 2363 | } |
| 2364 | |
| 2365 | bool readControlBitShort(dwgBuffer &buffer, std::uint64_t endBit, |
| 2366 | std::int32_t &value) { |
| 2367 | return readControlValue<std::int32_t>( |
| 2368 | buffer, endBit, |
| 2369 | [](dwgBuffer &probe) { |
| 2370 | return static_cast<std::int32_t>(probe.getBitShort()); |
| 2371 | }, |
| 2372 | value); |
| 2373 | } |
| 2374 | |
| 2375 | bool readControlBitLong(dwgBuffer &buffer, std::uint64_t endBit, |
| 2376 | std::int32_t &value) { |
| 2377 | return readControlValue<std::int32_t>( |
| 2378 | buffer, endBit, |
| 2379 | [](dwgBuffer &probe) { |
| 2380 | return static_cast<std::int32_t>(probe.getBitLong()); |
| 2381 | }, |
| 2382 | value); |
| 2383 | } |
| 2384 | |
| 2385 | bool readControlRawChar(dwgBuffer &buffer, std::uint64_t endBit, |
| 2386 | std::uint8_t &value) { |
| 2387 | return readControlValue<std::uint8_t>( |
| 2388 | buffer, endBit, [](dwgBuffer &probe) { return probe.getRawChar8(); }, |
| 2389 | value); |
| 2390 | } |
| 2391 | |
| 2392 | bool readControlBit(dwgBuffer &buffer, std::uint64_t endBit, bool &value) { |
| 2393 | return readControlValue<bool>( |
| 2394 | buffer, endBit, [](dwgBuffer &probe) { return probe.getBit() != 0; }, |
| 2395 | value); |
| 2396 | } |
| 2397 | |
| 2398 | bool isSpaceBlockRecordName(const std::string &name) { |
| 2399 | std::string normalized = name; |
| 2400 | std::transform(normalized.begin(), normalized.end(), normalized.begin(), |
| 2401 | [](unsigned char value) { |
| 2402 | return static_cast<char>(std::toupper(value)); |
| 2403 | }); |
| 2404 | return normalized == "*MODEL_SPACE" || normalized == "*PAPER_SPACE"; |
| 2405 | } |
| 2406 | |
| 2407 | // helper function to cleanup pointers in Look Up Tables |
| 2408 | template <typename T> |
| 2409 | void mapCleanUp(std::unordered_map<std::uint32_t, T *> &table) { |
| 2410 | for (auto &item : table) |
| 2411 | delete item.second; |
| 2412 | } |
| 2413 | |
| 2414 | DRW_DwgFramePublication |
| 2415 | makeTypedEntityFramePublication(DRW::Version version, const objHandle &object, |
| 2416 | int type, const DRW_Entity &entity) { |
| 2417 | DRW_DwgFramePublication publication; |
| 2418 | publication.m_version = version; |
| 2419 | publication.m_handle = object.handle; |
| 2420 | publication.m_sourceOffset = object.loc; |
| 2421 | publication.m_sourceMapOrdinal = object.sourceOrdinal; |
| 2422 | publication.m_sourceOffsetSpace = object.sourceOffsetSpace; |
| 2423 | publication.m_hasSourceLocation = true; |
| 2424 | publication.m_encodedType = type; |
| 2425 | publication.m_resolvedType = type; |
| 2426 | publication.m_isEntity = true; |
| 2427 | publication.setCommonLinkEvidence(drwDwgCommonLinkEvidenceForLinks( |
| 2428 | entity.hasDwgCommonLinkTail(), entity.parentHandle, entity.reactorHandles, |
| 2429 | entity.dwgReactorCount(), entity.xDictHandle)); |
| 2430 | publication.m_parentHandle = entity.parentHandle; |
| 2431 | publication.m_reactorHandles = entity.reactorHandles; |
| 2432 | publication.m_xDictHandle = entity.xDictHandle; |
| 2433 | publication.m_numReactors = entity.dwgReactorCount(); |
| 2434 | publication.m_xDictFlag = entity.dwgXDictionaryFlag(); |
| 2435 | publication.m_carrier = DRW_DwgFramePublication::Carrier::Typed; |
| 2436 | return publication; |
| 2437 | } |
| 2438 | |
| 2439 | bool isExpectedAttribute(const DRW_Insert &insert, std::uint32_t handle, |
| 2440 | DRW::Version version) { |
| 2441 | if (version < DRW::AC1018 && insert.attribHandles.size() == 2) { |
| 2442 | // R13-R2000 carries only the first and last ATTRIB handles. The |
| 2443 | // owner handle remains the authoritative membership check for any |
| 2444 | // attributes between those two boundaries. |
| 2445 | const std::uint32_t first = insert.attribHandles.front().ref; |
| 2446 | const std::uint32_t last = insert.attribHandles.back().ref; |
| 2447 | if (first == DRW::NoHandle || last == DRW::NoHandle) |
| 2448 | return false; |
| 2449 | if (first == last) |
| 2450 | return handle == first; |
| 2451 | return handle != DRW::NoHandle; |
| 2452 | } |
| 2453 | for (const dwgHandle &expected : insert.attribHandles) { |
| 2454 | if (expected.ref == handle) |
| 2455 | return true; |
| 2456 | } |
| 2457 | return false; |
| 2458 | } |
| 2459 | |
| 2460 | bool isExpectedAttribute(const DRW_Insert &insert, const DRW_Attrib &attribute, |
| 2461 | DRW::Version version) { |
| 2462 | return attribute.parentHandle == insert.handle && |
| 2463 | isExpectedAttribute(insert, attribute.handle, version); |
| 2464 | } |
| 2465 | |
| 2466 | bool hasCompleteAttributeList(const DRW_Insert &insert, DRW::Version version) { |
| 2467 | if (version < DRW::AC1018 && insert.attribHandles.size() == 2) { |
| 2468 | const std::uint32_t first = insert.attribHandles.front().ref; |
| 2469 | const std::uint32_t last = insert.attribHandles.back().ref; |
| 2470 | if (first == DRW::NoHandle || last == DRW::NoHandle) |
| 2471 | return first == last && insert.attlist.empty(); |
| 2472 | if (first == last) { |
| 2473 | return insert.attlist.size() == 1 && insert.attlist.front() && |
| 2474 | insert.attlist.front()->handle == first; |
| 2475 | } |
| 2476 | |
| 2477 | bool hasFirst = false; |
| 2478 | bool hasLast = false; |
| 2479 | for (std::size_t index = 0; index < insert.attlist.size(); ++index) { |
| 2480 | const auto &attrib = insert.attlist[index]; |
| 2481 | if (!attrib) |
| 2482 | return false; |
| 2483 | for (std::size_t previous = 0; previous < index; ++previous) { |
| 2484 | if (insert.attlist[previous] && |
| 2485 | insert.attlist[previous]->handle == attrib->handle) { |
| 2486 | return false; |
| 2487 | } |
| 2488 | } |
| 2489 | hasFirst = hasFirst || attrib->handle == first; |
| 2490 | hasLast = hasLast || attrib->handle == last; |
| 2491 | } |
| 2492 | return hasFirst && hasLast; |
| 2493 | } |
| 2494 | if (insert.attlist.size() != insert.attribHandles.size()) |
| 2495 | return false; |
| 2496 | |
| 2497 | for (const dwgHandle &handle : insert.attribHandles) { |
| 2498 | if (handle.ref == DRW::NoHandle) |
| 2499 | return false; |
| 2500 | std::size_t matches = 0; |
| 2501 | for (const auto &attrib : insert.attlist) { |
| 2502 | if (!attrib) |
| 2503 | return false; |
| 2504 | if (attrib->handle == handle.ref) |
| 2505 | ++matches; |
| 2506 | } |
| 2507 | if (matches != 1u) |
| 2508 | return false; |
| 2509 | } |
| 2510 | return true; |
| 2511 | } |
| 2512 | |
| 2513 | // Minimal concrete entity whose only job is to run DRW_Entity's |
| 2514 | // class-agnostic common-prologue parser (handle/EED/graphData/layer/…). |
| 2515 | // Raw-net custom entities (STDPART2D, AEC_*) are emitted byte-for-byte by |
| 2516 | // makeRawEntity and never parsed, so their proxyGraphics is empty; this host |
| 2517 | // lets us lift the cached graphData bytes without modelling the unknown |
| 2518 | // class body. parseDwg runs only the common DATA prologue (it does NOT read |
| 2519 | // the handle stream), which is exactly the section that carries graphData. |
| 2520 | struct ProxyHostEntity : public DRW_Entity { |
| 2521 | void applyExtrusion() override {} |
| 2522 | bool parseDwg(DRW::Version v, dwgBuffer *b, std::uint32_t bsz = 0) override { |
| 2523 | return DRW_Entity::parseDwg(v, b, nullptr, bsz) && b != nullptr && |
| 2524 | b->isGood(); |
| 2525 | } |
| 2526 | bool hasOwnerHandle() const noexcept { return ownerHandle; } |
| 2527 | }; |
| 2528 | |
| 2529 | // The R2007 dynamic-block parameter/grip range has no typed body parser, |
| 2530 | // but each record still has the standard entity prologue and handle tail. |
| 2531 | // Validate both before publishing its raw bytes for same-version replay. |
| 2532 | struct RawEntityShell final : public DRW_Entity { |
| 2533 | void applyExtrusion() override {} |
| 2534 | bool parseDwg(DRW::Version v, dwgBuffer *b, std::uint32_t bsz = 0) override { |
| 2535 | return DRW_Entity::parseDwg(v, b, nullptr, bsz) && |
| 2536 | DRW_Entity::parseDwgEntHandle(v, b); |
| 2537 | } |
| 2538 | }; |
| 2539 | |
| 2540 | // Run only the common OBJECTS header parser for an opaque carrier. The |
| 2541 | // AC1027 DataStorage bit belongs to that header, so keeping this probe on |
| 2542 | // DRW_TableEntry avoids a second, drift-prone bit layout implementation. |
| 2543 | struct RawObjectHeaderProbe final : public DRW_TableEntry { |
| 2544 | bool parseCommon(DRW::Version version, dwgBuffer *buffer, |
| 2545 | std::uint32_t bodyBitSize) { |
| 2546 | return DRW_TableEntry::parseDwg(version, buffer, nullptr, bodyBitSize); |
| 2547 | } |
| 2548 | |
| 2549 | protected: |
| 2550 | bool parseDwg(DRW::Version version, dwgBuffer *buffer, |
| 2551 | std::uint32_t bodyBitSize = 0) override { |
| 2552 | return parseCommon(version, buffer, bodyBitSize); |
| 2553 | } |
| 2554 | }; |
| 2555 | |
| 2556 | // Fixed opaque OBJECTS records still require the standard header and |
| 2557 | // handle tail. Preserve only records that satisfy both framing contracts. |
| 2558 | struct RawObjectShell final : public DRW_TableEntry { |
| 2559 | bool parseDwg(DRW::Version v, dwgBuffer *b, std::uint32_t bsz = 0) override { |
| 2560 | if (b == nullptr) |
| 2561 | return false; |
| 2562 | dwgBuffer strings = b->forkIndependent(); |
| 2563 | return DRW_TableEntry::parseDwg(v, b, v > DRW::AC1018 ? &strings : nullptr, |
| 2564 | bsz) && |
| 2565 | DRW_TableEntry::parseDwgCommonHandleData(v, b); |
| 2566 | } |
| 2567 | }; |
| 2568 | |
| 2569 | bool rawObjectHasDataStorage(DRW::Version version, |
| 2570 | std::vector<std::uint8_t> &bytes, |
| 2571 | std::uint32_t bodyBitSize, |
| 2572 | DRW_TextCodec *decoder) { |
| 2573 | if (version <= DRW::AC1024 || bytes.empty() || decoder == nullptr) |
| 2574 | return false; |
| 2575 | |
| 2576 | dwgBuffer probeBuffer(bytes.data(), bytes.size(), decoder); |
| 2577 | RawObjectHeaderProbe probe; |
| 2578 | if (!probe.parseCommon(version, &probeBuffer, bodyBitSize) || |
| 2579 | !probeBuffer.isGood()) |
| 2580 | return false; |
| 2581 | return probe.hasDataStorageBinaryData(); |
| 2582 | } |
| 2583 | |
| 2584 | std::string normalizeDwgClassToken(const std::string &value) { |
| 2585 | std::string token; |
| 2586 | token.reserve(value.size()); |
| 2587 | for (unsigned char ch : value) { |
| 2588 | if (std::isalnum(ch)) |
| 2589 | token.push_back(static_cast<char>(std::toupper(ch))); |
| 2590 | } |
| 2591 | if (token.rfind("ACDB", 0) == 0) |
| 2592 | token.erase(0, 4); |
| 2593 | const std::string suffix = "CLASS"; |
| 2594 | if (token.size() >= suffix.size() && |
| 2595 | token.compare(token.size() - suffix.size(), suffix.size(), suffix) == 0) { |
| 2596 | token.erase(token.size() - suffix.size()); |
| 2597 | } |
| 2598 | return token; |
| 2599 | } |
| 2600 | |
| 2601 | bool isValidatedRawCustomObjectShell(const DRW_Class *objectClass) { |
| 2602 | if (objectClass == nullptr) |
| 2603 | return false; |
| 2604 | const std::string recordName = normalizeDwgClassToken(objectClass->recName); |
| 2605 | const std::string className = normalizeDwgClassToken(objectClass->className); |
| 2606 | return recordName == "LINERES" || recordName == "CIRCARCRES" || |
| 2607 | recordName == "TABLETEMPLATE" || className == "LINERES" || |
| 2608 | className == "CIRCARCRES" || className == "TABLETEMPLATE"; |
| 2609 | } |
| 2610 | |
| 2611 | bool isCenterLineActionBodyClass(const DRW_Class *objectClass) { |
| 2612 | if (objectClass == nullptr) |
| 2613 | return false; |
| 2614 | return normalizeDwgClassToken(objectClass->recName) == |
| 2615 | "CENTERLINEACTIONBODY" || |
| 2616 | normalizeDwgClassToken(objectClass->className) == |
| 2617 | "CENTERLINEACTIONBODY"; |
| 2618 | } |
| 2619 | |
| 2620 | bool objectContextKindFromClassNames(const std::string &recName, |
| 2621 | const std::string &className, |
| 2622 | DRW_ObjectContextData::Kind &kind) { |
| 2623 | const std::string rn = normalizeDwgClassToken(recName); |
| 2624 | const std::string cn = normalizeDwgClassToken(className); |
| 2625 | const auto matches = [&](const char *compact, const char *verbose = nullptr) { |
| 2626 | return rn == compact || cn == compact || |
| 2627 | (verbose != nullptr && (rn == verbose || cn == verbose)); |
| 2628 | }; |
| 2629 | |
| 2630 | if (matches("ANNOTSCALEOBJECTCONTEXTDATA", |
| 2631 | "ANNOTATIONSCALEOBJECTCONTEXTDATA")) { |
| 2632 | kind = DRW_ObjectContextData::Kind::AnnotScale; |
| 2633 | return true; |
| 2634 | } |
| 2635 | if (matches("TEXTOBJECTCONTEXTDATA")) { |
| 2636 | kind = DRW_ObjectContextData::Kind::Text; |
| 2637 | return true; |
| 2638 | } |
| 2639 | if (matches("MTEXTOBJECTCONTEXTDATA")) { |
| 2640 | kind = DRW_ObjectContextData::Kind::MText; |
| 2641 | return true; |
| 2642 | } |
| 2643 | if (matches("MTEXTATTRIBUTEOBJECTCONTEXTDATA")) { |
| 2644 | kind = DRW_ObjectContextData::Kind::MTextAttribute; |
| 2645 | return true; |
| 2646 | } |
| 2647 | if (matches("ORDDIMOBJECTCONTEXTDATA", |
| 2648 | "ORDINATEDIMENSIONOBJECTCONTEXTDATA")) { |
| 2649 | kind = DRW_ObjectContextData::Kind::OrdinateDimension; |
| 2650 | return true; |
| 2651 | } |
| 2652 | if (matches("ALDIMOBJECTCONTEXTDATA", "ALIGNEDDIMENSIONOBJECTCONTEXTDATA")) { |
| 2653 | kind = DRW_ObjectContextData::Kind::AlignedDimension; |
| 2654 | return true; |
| 2655 | } |
| 2656 | if (matches("ANGDIMOBJECTCONTEXTDATA", "ANGULARDIMENSIONOBJECTCONTEXTDATA")) { |
| 2657 | kind = DRW_ObjectContextData::Kind::AngularDimension; |
| 2658 | return true; |
| 2659 | } |
| 2660 | if (matches("RADIMOBJECTCONTEXTDATA", "RADIALDIMENSIONOBJECTCONTEXTDATA")) { |
| 2661 | kind = DRW_ObjectContextData::Kind::RadialDimension; |
| 2662 | return true; |
| 2663 | } |
| 2664 | if (matches("RADIMLGOBJECTCONTEXTDATA", |
| 2665 | "LARGERADIALDIMENSIONOBJECTCONTEXTDATA")) { |
| 2666 | kind = DRW_ObjectContextData::Kind::LargeRadialDimension; |
| 2667 | return true; |
| 2668 | } |
| 2669 | if (matches("DMDIMOBJECTCONTEXTDATA", |
| 2670 | "DIAMETRICDIMENSIONOBJECTCONTEXTDATA")) { |
| 2671 | kind = DRW_ObjectContextData::Kind::DiameterDimension; |
| 2672 | return true; |
| 2673 | } |
| 2674 | if (matches("LEADEROBJECTCONTEXTDATA")) { |
| 2675 | kind = DRW_ObjectContextData::Kind::Leader; |
| 2676 | return true; |
| 2677 | } |
| 2678 | if (matches("BLKREFOBJECTCONTEXTDATA", "BLOCKREFERENCEOBJECTCONTEXTDATA")) { |
| 2679 | kind = DRW_ObjectContextData::Kind::BlockReference; |
| 2680 | return true; |
| 2681 | } |
| 2682 | if (matches("FCFOBJECTCONTEXTDATA")) { |
| 2683 | kind = DRW_ObjectContextData::Kind::Fcf; |
| 2684 | return true; |
| 2685 | } |
| 2686 | if (matches("MLEADEROBJECTCONTEXTDATA")) { |
| 2687 | kind = DRW_ObjectContextData::Kind::MLeader; |
| 2688 | return true; |
| 2689 | } |
| 2690 | |
| 2691 | return false; |
| 2692 | } |
| 2693 | } // namespace |
| 2694 | |
| 2695 | bool dwgReader::hasPendingCompoundStateForBlock( |
| 2696 | const DRW_Block_Record &block) const { |
| 2697 | const auto containsEntity = [&block](std::uint32_t handle) { |
| 2698 | return std::find(block.entMap.cbegin(), block.entMap.cend(), handle) != |
| 2699 | block.entMap.cend(); |
| 2700 | }; |
| 2701 | const auto belongsToBlock = [&block](const DRW_Entity &entity) { |
| 2702 | return std::find(block.entMap.cbegin(), block.entMap.cend(), |
| 2703 | entity.handle) != block.entMap.cend() || |
| 2704 | entity.parentHandle == block.handle || |
| 2705 | (entity.parentHandle == DRW::NoHandle && |
| 2706 | isSpaceBlockRecordName(block.name)); |
| 2707 | }; |
| 2708 | return std::any_of(m_pendingInsertStates.cbegin(), |
| 2709 | m_pendingInsertStates.cend(), |
| 2710 | [&belongsToBlock](const auto &item) { |
| 2711 | return belongsToBlock(item.second.entity); |
| 2712 | }) || |
| 2713 | std::any_of(m_pendingPolylineStates.cbegin(), |
| 2714 | m_pendingPolylineStates.cend(), |
| 2715 | [&belongsToBlock](const auto &item) { |
| 2716 | return belongsToBlock(item.second.entity); |
| 2717 | }) || |
| 2718 | std::any_of( |
| 2719 | m_orphanAttribStates.cbegin(), m_orphanAttribStates.cend(), |
| 2720 | [&containsEntity](const auto &item) { |
| 2721 | return std::any_of( |
| 2722 | item.second.attributes.cbegin(), |
| 2723 | item.second.attributes.cend(), |
| 2724 | [&containsEntity](const StagedAttribState &attribute) { |
| 2725 | return attribute.entity != nullptr && |
| 2726 | containsEntity(attribute.entity->handle); |
| 2727 | }); |
| 2728 | }) || |
| 2729 | std::any_of(m_orphanPolylineVertexStates.cbegin(), |
| 2730 | m_orphanPolylineVertexStates.cend(), |
| 2731 | [&containsEntity](const auto &item) { |
| 2732 | return std::any_of( |
| 2733 | item.second.vertices.cbegin(), |
| 2734 | item.second.vertices.cend(), |
| 2735 | [&containsEntity](const StagedVertexState &vertex) { |
| 2736 | return containsEntity(vertex.entity.handle); |
| 2737 | }); |
| 2738 | }) || |
| 2739 | std::any_of(m_stagedSeqEnds.cbegin(), m_stagedSeqEnds.cend(), |
| 2740 | [&containsEntity](const auto &item) { |
| 2741 | return containsEntity(item.first); |
| 2742 | }); |
| 2743 | } |
| 2744 | |
| 2745 | bool readDwgClassStringFooter(dwgBuffer &buffer, std::uint64_t footerEndBit, |
| 2746 | std::uint64_t &stringStartBit, |
| 2747 | std::uint64_t &stringSize) { |
| 2748 | const std::uint64_t savedPosition = buffer.getPosition(); |
| 2749 | const std::uint8_t savedBitPos = buffer.getBitPos(); |
| 2750 | std::uint64_t totalBits = 0; |
| 2751 | if (!dwgSafety::multiply(buffer.size(), 8, totalBits) || |
| 2752 | footerEndBit >= totalBits) |
| 2753 | return false; |
| 2754 | |
| 2755 | auto restore = [&]() { |
| 2756 | buffer.setPosition(savedPosition); |
| 2757 | buffer.setBitPos(savedBitPos); |
| 2758 | }; |
| 2759 | auto seekBits = [&](std::uint64_t bitPosition) { |
| 2760 | if (bitPosition >= totalBits || !buffer.setPosition(bitPosition >> 3)) |
| 2761 | return false; |
| 2762 | const auto bitPos = static_cast<std::uint8_t>(bitPosition & 7); |
| 2763 | buffer.setBitPos(bitPos); |
| 2764 | return buffer.isGood() && buffer.getPosition() == (bitPosition >> 3) && |
| 2765 | buffer.getBitPos() == bitPos; |
| 2766 | }; |
| 2767 | |
| 2768 | std::uint64_t cursor = footerEndBit; |
| 2769 | if (!seekBits(cursor)) { |
| 2770 | restore(); |
| 2771 | return false; |
| 2772 | } |
| 2773 | buffer.getBit(); // end-bit field |
| 2774 | if (!buffer.isGood() || cursor < 16) { |
| 2775 | restore(); |
| 2776 | return false; |
| 2777 | } |
| 2778 | cursor -= 16; |
| 2779 | if (!seekBits(cursor)) { |
| 2780 | restore(); |
| 2781 | return false; |
| 2782 | } |
| 2783 | std::uint64_t encodedSize = buffer.getRawShort16(); |
| 2784 | if (!buffer.isGood()) { |
| 2785 | restore(); |
| 2786 | return false; |
| 2787 | } |
| 2788 | if ((encodedSize & 0x8000U) != 0) { |
| 2789 | encodedSize &= 0x7FFFU; |
| 2790 | if (cursor < 16) { |
| 2791 | restore(); |
| 2792 | return false; |
| 2793 | } |
| 2794 | cursor -= 16; |
| 2795 | if (!seekBits(cursor)) { |
| 2796 | restore(); |
| 2797 | return false; |
| 2798 | } |
| 2799 | const std::uint64_t highSize = buffer.getRawShort16(); |
| 2800 | if (!buffer.isGood()) { |
| 2801 | restore(); |
| 2802 | return false; |
| 2803 | } |
| 2804 | encodedSize |= highSize << 15; |
| 2805 | } |
| 2806 | if (encodedSize > cursor) { |
| 2807 | restore(); |
| 2808 | return false; |
| 2809 | } |
| 2810 | cursor -= encodedSize; |
| 2811 | if (!seekBits(cursor)) { |
| 2812 | restore(); |
| 2813 | return false; |
| 2814 | } |
| 2815 | stringStartBit = cursor; |
| 2816 | stringSize = encodedSize; |
| 2817 | return true; |
| 2818 | } |
| 2819 | |
| 2820 | // DWG file-header codepage id -> DRW_TextCodec ANSI name (libreDWG |
| 2821 | // codepages.h:35-82). Only codec-recognized names are mapped; unknown/rare ids |
| 2822 | // (UTF-16, Johab, CP866, US-ASCII, ...) return nullptr so the caller keeps the |
| 2823 | // ANSI_1252 default. 31 (GB2312) maps to its CP936 superset. |
| 2824 | const char *dwgCodePageName(std::uint16_t cp) { |
| 2825 | switch (cp) { |
| 2826 | case dwgCP::ANSI_1250: |
| 2827 | return "ANSI_1250"; |
| 2828 | case dwgCP::ANSI_1251: |
| 2829 | return "ANSI_1251"; |
| 2830 | case dwgCP::ANSI_1252: |
| 2831 | return "ANSI_1252"; |
| 2832 | case dwgCP::GBK_CP936: |
| 2833 | return "ANSI_936"; |
| 2834 | case dwgCP::ANSI_1253: |
| 2835 | return "ANSI_1253"; |
| 2836 | case dwgCP::ANSI_1254: |
| 2837 | return "ANSI_1254"; |
| 2838 | case dwgCP::ANSI_1255: |
| 2839 | return "ANSI_1255"; |
| 2840 | case dwgCP::ANSI_1256: |
| 2841 | return "ANSI_1256"; |
| 2842 | case dwgCP::ANSI_1257: |
| 2843 | return "ANSI_1257"; |
| 2844 | case dwgCP::ANSI_874: |
| 2845 | return "ANSI_874"; |
| 2846 | case dwgCP::SHIFT_JIS: |
| 2847 | return "ANSI_932"; |
| 2848 | case dwgCP::GBK: |
| 2849 | return "ANSI_936"; |
| 2850 | case dwgCP::KOREAN_WANSUNG: |
| 2851 | return "ANSI_949"; |
| 2852 | case dwgCP::BIG5: |
| 2853 | return "ANSI_950"; |
| 2854 | case dwgCP::ANSI_1258: |
| 2855 | return "ANSI_1258"; |
| 2856 | default: |
| 2857 | return nullptr; |
| 2858 | } |
| 2859 | } |
| 2860 | |
| 2861 | std::uint16_t dwgCodePageId(const char *name) { |
| 2862 | if (name == nullptr) |
| 2863 | return 30; |
| 2864 | // Round-trip set: map back exactly the names dwgCodePageName() emits. |
| 2865 | // 31 (GB2312) and 39 both resolve to "ANSI_936"; pick 39 (Simplified |
| 2866 | // Chinese / GBK superset) for the inverse direction. |
| 2867 | const std::string n(name); |
| 2868 | if (n == "ANSI_1250") |
| 2869 | return 28; |
| 2870 | if (n == "ANSI_1251") |
| 2871 | return 29; |
| 2872 | if (n == "ANSI_1252") |
| 2873 | return 30; |
| 2874 | if (n == "ANSI_1253") |
| 2875 | return 32; |
| 2876 | if (n == "ANSI_1254") |
| 2877 | return 33; |
| 2878 | if (n == "ANSI_1255") |
| 2879 | return 34; |
| 2880 | if (n == "ANSI_1256") |
| 2881 | return 35; |
| 2882 | if (n == "ANSI_1257") |
| 2883 | return 36; |
| 2884 | if (n == "ANSI_874") |
| 2885 | return 37; |
| 2886 | if (n == "ANSI_932") |
| 2887 | return 38; |
| 2888 | if (n == "ANSI_936") |
| 2889 | return 39; |
| 2890 | if (n == "ANSI_949") |
| 2891 | return 40; |
| 2892 | if (n == "ANSI_950") |
| 2893 | return 41; |
| 2894 | if (n == "ANSI_1258") |
| 2895 | return 44; |
| 2896 | return 30; // fallback |
| 2897 | } |
| 2898 | |
| 2899 | std::string decodeEedString(std::uint16_t cp, const std::string &raw, |
| 2900 | DRW_TextCodec *fallback) { |
| 2901 | if (raw.empty()) |
| 2902 | return std::string{}; |
| 2903 | if (const char *name = dwgCodePageName(cp)) { |
| 2904 | // Build an AC1015-bound codec so setCodePage() selects the table |
| 2905 | // converter for `name` (the AC1021+ branch would pick UTF-16 instead). |
| 2906 | DRW_TextCodec codec; |
| 2907 | codec.setVersion(DRW::AC1015, /*dxfFormat=*/false); |
| 2908 | codec.setCodePage(name, /*dxfFormat=*/false); |
| 2909 | return codec.toUtf8(raw); |
| 2910 | } |
| 2911 | return fallback ? fallback->toUtf8(raw) : raw; |
| 2912 | } |
| 2913 | |
| 2914 | namespace { |
| 2915 | |
| 2916 | void appendUtf8(std::uint32_t codePoint, std::string &output) { |
| 2917 | if (codePoint <= 0x7FU) { |
| 2918 | output.push_back(static_cast<char>(codePoint)); |
| 2919 | } else if (codePoint <= 0x7FFU) { |
| 2920 | output.push_back(static_cast<char>(0xC0U | (codePoint >> 6))); |
| 2921 | output.push_back(static_cast<char>(0x80U | (codePoint & 0x3FU))); |
| 2922 | } else if (codePoint <= 0xFFFFU) { |
| 2923 | output.push_back(static_cast<char>(0xE0U | (codePoint >> 12))); |
| 2924 | output.push_back(static_cast<char>(0x80U | ((codePoint >> 6) & 0x3FU))); |
| 2925 | output.push_back(static_cast<char>(0x80U | (codePoint & 0x3FU))); |
| 2926 | } else { |
| 2927 | output.push_back(static_cast<char>(0xF0U | (codePoint >> 18))); |
| 2928 | output.push_back(static_cast<char>(0x80U | ((codePoint >> 12) & 0x3FU))); |
| 2929 | output.push_back(static_cast<char>(0x80U | ((codePoint >> 6) & 0x3FU))); |
| 2930 | output.push_back(static_cast<char>(0x80U | (codePoint & 0x3FU))); |
| 2931 | } |
| 2932 | } |
| 2933 | |
| 2934 | bool decodeEedUtf16(const std::vector<std::uint8_t> &bytes, |
| 2935 | std::string &output) { |
| 2936 | if ((bytes.size() & 1U) != 0) |
| 2937 | return false; |
| 2938 | |
| 2939 | std::string decoded; |
| 2940 | if (bytes.size() > |
| 2941 | static_cast<std::size_t>(std::numeric_limits<int>::max()) || |
| 2942 | !DRW::reserve(decoded, static_cast<int>(bytes.size()))) |
| 2943 | return false; |
| 2944 | for (std::size_t i = 0; i < bytes.size(); i += 2) { |
| 2945 | const std::uint16_t unit = static_cast<std::uint16_t>(bytes[i]) | |
| 2946 | (static_cast<std::uint16_t>(bytes[i + 1]) << 8); |
| 2947 | std::uint32_t codePoint = unit; |
| 2948 | if (unit >= 0xD800U && unit <= 0xDBFFU) { |
| 2949 | if (i + 3 >= bytes.size()) |
| 2950 | return false; |
| 2951 | const std::uint16_t low = static_cast<std::uint16_t>(bytes[i + 2]) | |
| 2952 | (static_cast<std::uint16_t>(bytes[i + 3]) << 8); |
| 2953 | if (low < 0xDC00U || low > 0xDFFFU) |
| 2954 | return false; |
| 2955 | codePoint = 0x10000U + |
| 2956 | ((static_cast<std::uint32_t>(unit) - 0xD800U) << 10) + |
| 2957 | (static_cast<std::uint32_t>(low) - 0xDC00U); |
| 2958 | i += 2; |
| 2959 | } else if (unit >= 0xDC00U && unit <= 0xDFFFU) { |
| 2960 | return false; |
| 2961 | } |
| 2962 | appendUtf8(codePoint, decoded); |
| 2963 | } |
| 2964 | output = std::move(decoded); |
| 2965 | return true; |
| 2966 | } |
| 2967 | |
| 2968 | bool readEedRawHandle(dwgBuffer &buffer, std::uint64_t &ref) { |
| 2969 | std::uint8_t bytes[8]{}; |
| 2970 | if (!buffer.getBytes(bytes, sizeof(bytes))) |
| 2971 | return false; |
| 2972 | ref = 0; |
| 2973 | for (const std::uint8_t byte : bytes) |
| 2974 | ref = (ref << 8) | byte; |
| 2975 | return true; |
| 2976 | } |
| 2977 | |
| 2978 | bool readEedRawHandleLE(dwgBuffer &buffer, std::uint64_t &ref) { |
| 2979 | std::uint8_t bytes[8]{}; |
| 2980 | if (!buffer.getBytes(bytes, sizeof(bytes))) |
| 2981 | return false; |
| 2982 | ref = 0; |
| 2983 | for (std::size_t index = 0; index < sizeof(bytes); ++index) |
| 2984 | ref |= static_cast<std::uint64_t>(bytes[index]) << (index * 8U); |
| 2985 | return true; |
| 2986 | } |
| 2987 | |
| 2988 | std::uint64_t eedCurrentBit(const dwgBuffer &buffer) { |
| 2989 | return buffer.getPosition() * 8u + buffer.getBitPos(); |
| 2990 | } |
| 2991 | |
| 2992 | bool eedHasBits(const dwgBuffer &buffer, std::uint64_t endBit, |
| 2993 | std::uint64_t count) { |
| 2994 | const std::uint64_t current = eedCurrentBit(buffer); |
| 2995 | return current <= endBit && count <= endBit - current; |
| 2996 | } |
| 2997 | |
| 2998 | bool readEedBitShort(dwgBuffer &buffer, std::uint64_t endBit, |
| 2999 | std::uint16_t &value) { |
| 3000 | if (!eedHasBits(buffer, endBit, 2)) |
| 3001 | return false; |
| 3002 | dwgBuffer probe = buffer.forkIndependent(); |
| 3003 | const std::uint8_t selector = probe.get2Bits(); |
| 3004 | if (!probe.isGood()) |
| 3005 | return false; |
| 3006 | const std::uint64_t bits = selector == 0 ? 18u : selector == 1 ? 10u : 2u; |
| 3007 | if (!eedHasBits(buffer, endBit, bits)) |
| 3008 | return false; |
| 3009 | value = buffer.getBitShort(); |
| 3010 | return buffer.isGood(); |
| 3011 | } |
| 3012 | |
| 3013 | bool readEedHandle(dwgBuffer &buffer, std::uint64_t endBit, dwgHandle &value) { |
| 3014 | if (!eedHasBits(buffer, endBit, 8)) |
| 3015 | return false; |
| 3016 | dwgBuffer probe = buffer.forkIndependent(); |
| 3017 | const dwgHandle parsed = probe.getHandle(); |
| 3018 | if (!probe.isGood() || eedCurrentBit(probe) > endBit) |
| 3019 | return false; |
| 3020 | buffer = probe; |
| 3021 | value = parsed; |
| 3022 | return true; |
| 3023 | } |
| 3024 | |
| 3025 | bool readEedBytes(dwgBuffer &buffer, std::uint64_t endBit, std::uint8_t *data, |
| 3026 | std::size_t size) { |
| 3027 | std::uint64_t bits = 0; |
| 3028 | if (!dwgSafety::multiply(static_cast<std::uint64_t>(size), 8, bits) || |
| 3029 | !eedHasBits(buffer, endBit, bits)) |
| 3030 | return false; |
| 3031 | return size == 0 || (buffer.getBytes(data, size) && buffer.isGood()); |
| 3032 | } |
| 3033 | |
| 3034 | } // namespace |
| 3035 | |
| 3036 | namespace { |
| 3037 | |
| 3038 | template <typename... Args> |
| 3039 | bool appendEedItem(DwgEedChunk &chunk, std::uint32_t &totalItems, |
| 3040 | Args &&...args) { |
| 3041 | if (chunk.items.size() >= dwgSafety::MaxEedItems || |
| 3042 | totalItems >= dwgSafety::MaxEedTotalItems) |
| 3043 | return false; |
| 3044 | try { |
| 3045 | chunk.items.emplace_back(std::forward<Args>(args)...); |
| 3046 | } catch (...) { |
| 3047 | return false; |
| 3048 | } |
| 3049 | ++totalItems; |
| 3050 | return true; |
| 3051 | } |
| 3052 | |
| 3053 | } // namespace |
| 3054 | |
| 3055 | bool readDwgEed(DRW::Version version, dwgBuffer &buffer, |
| 3056 | std::vector<DwgEedChunk> &chunks, std::uint64_t endBit) { |
| 3057 | try { |
| 3058 | std::vector<DwgEedChunk> staged; |
| 3059 | std::uint32_t totalItems = 0; |
| 3060 | while (true) { |
| 3061 | std::uint16_t dataSize = 0; |
| 3062 | if (!readEedBitShort(buffer, endBit, dataSize)) |
| 3063 | return false; |
| 3064 | if (dataSize == 0) |
| 3065 | break; |
| 3066 | if (staged.size() >= dwgSafety::MaxEedChunks) |
| 3067 | return false; |
| 3068 | |
| 3069 | dwgHandle appHandle; |
| 3070 | if (!readEedHandle(buffer, endBit, appHandle)) |
| 3071 | return false; |
| 3072 | std::vector<std::uint8_t> data; |
| 3073 | if (!DRW::resize(data, static_cast<int>(dataSize))) |
| 3074 | return false; |
| 3075 | if (!readEedBytes(buffer, endBit, data.data(), data.size())) |
| 3076 | return false; |
| 3077 | |
| 3078 | DwgEedChunk chunk; |
| 3079 | chunk.appHandle = appHandle.ref; |
| 3080 | dwgBuffer itemBuffer(data.data(), data.size(), buffer.decoder); |
| 3081 | while (itemBuffer.numRemainingBytes() > 0) { |
| 3082 | const std::uint8_t code = itemBuffer.getRawChar8(); |
| 3083 | if (!itemBuffer.isGood()) |
| 3084 | return false; |
| 3085 | |
| 3086 | switch (code) { |
| 3087 | case 0: { |
| 3088 | std::string value; |
| 3089 | if (version > DRW::AC1018) { |
| 3090 | if (itemBuffer.numRemainingBytes() < 2) |
| 3091 | return false; |
| 3092 | const std::uint16_t charCount = itemBuffer.getRawShort16(); |
| 3093 | const std::uint64_t byteCount = |
| 3094 | static_cast<std::uint64_t>(charCount) * 2U; |
| 3095 | if (!itemBuffer.isGood() || |
| 3096 | byteCount > |
| 3097 | static_cast<std::uint64_t>(itemBuffer.numRemainingBytes())) |
| 3098 | return false; |
| 3099 | std::vector<std::uint8_t> bytes; |
| 3100 | if (!DRW::resize(bytes, static_cast<int>(byteCount))) |
| 3101 | return false; |
| 3102 | if (!itemBuffer.getBytes(bytes.data(), bytes.size()) || |
| 3103 | !decodeEedUtf16(bytes, value)) |
| 3104 | return false; |
| 3105 | } else { |
| 3106 | if (itemBuffer.numRemainingBytes() < 3) |
| 3107 | return false; |
| 3108 | const std::uint8_t length = itemBuffer.getRawChar8(); |
| 3109 | const std::uint16_t codePage = itemBuffer.getBERawShort16(); |
| 3110 | if (!itemBuffer.isGood() || length > itemBuffer.numRemainingBytes()) |
| 3111 | return false; |
| 3112 | std::string raw; |
| 3113 | if (!DRW::resize(raw, static_cast<int>(length))) |
| 3114 | return false; |
| 3115 | if (length > 0 && |
| 3116 | !itemBuffer.getBytes( |
| 3117 | reinterpret_cast<std::uint8_t *>(raw.data()), length)) |
| 3118 | return false; |
| 3119 | value = decodeEedString(codePage, raw, itemBuffer.decoder); |
| 3120 | } |
| 3121 | if (!appendEedItem(chunk, totalItems, 1000, std::move(value))) |
| 3122 | return false; |
| 3123 | break; |
| 3124 | } |
| 3125 | case 2: { |
| 3126 | if (itemBuffer.numRemainingBytes() < 1) |
| 3127 | return false; |
| 3128 | const std::uint8_t control = itemBuffer.getRawChar8(); |
| 3129 | if (!itemBuffer.isGood() || control > 1) |
| 3130 | return false; |
| 3131 | if (!appendEedItem(chunk, totalItems, 1002, |
| 3132 | std::string(control == 0 ? "{" : "}"))) |
| 3133 | return false; |
| 3134 | break; |
| 3135 | } |
| 3136 | case 3: { |
| 3137 | if (itemBuffer.numRemainingBytes() < 8) |
| 3138 | return false; |
| 3139 | std::uint64_t ref = 0; |
| 3140 | if (!readEedRawHandleLE(itemBuffer, ref)) |
| 3141 | return false; |
| 3142 | const std::size_t index = chunk.items.size(); |
| 3143 | char text[24]{}; |
| 3144 | std::snprintf(text, sizeof(text), "%llX", |
| 3145 | static_cast<unsigned long long>(ref)); |
| 3146 | if (!appendEedItem(chunk, totalItems, 1003, std::string{text}, true)) |
| 3147 | return false; |
| 3148 | chunk.items.back().setDwgRawLayerReference(ref, version); |
| 3149 | // EED layer references are always eight bytes on disk. Keep |
| 3150 | // a valid wide reference even though the in-memory layer map |
| 3151 | // is currently keyed by 32-bit DWG handles. |
| 3152 | if (ref <= std::numeric_limits<std::uint32_t>::max()) |
| 3153 | chunk.layerRefs.push_back({index, static_cast<std::uint32_t>(ref)}); |
| 3154 | break; |
| 3155 | } |
| 3156 | case 4: { |
| 3157 | if (itemBuffer.numRemainingBytes() < 1) |
| 3158 | return false; |
| 3159 | const std::uint8_t length = itemBuffer.getRawChar8(); |
| 3160 | if (!itemBuffer.isGood() || length > itemBuffer.numRemainingBytes()) |
| 3161 | return false; |
| 3162 | std::vector<std::uint8_t> value; |
| 3163 | if (!DRW::resize(value, static_cast<int>(length))) |
| 3164 | return false; |
| 3165 | if (length > 0 && !itemBuffer.getBytes(value.data(), value.size())) |
| 3166 | return false; |
| 3167 | if (!appendEedItem(chunk, totalItems, 1004, std::move(value))) |
| 3168 | return false; |
| 3169 | break; |
| 3170 | } |
| 3171 | case 5: { |
| 3172 | if (itemBuffer.numRemainingBytes() < 8) |
| 3173 | return false; |
| 3174 | std::uint64_t ref = 0; |
| 3175 | if (!readEedRawHandle(itemBuffer, ref)) |
| 3176 | return false; |
| 3177 | char text[24]{}; |
| 3178 | std::snprintf(text, sizeof(text), "%llX", |
| 3179 | static_cast<unsigned long long>(ref)); |
| 3180 | if (!appendEedItem(chunk, totalItems, 1005, std::string{text})) |
| 3181 | return false; |
| 3182 | break; |
| 3183 | } |
| 3184 | case 10: |
| 3185 | case 11: |
| 3186 | case 12: |
| 3187 | case 13: { |
| 3188 | if (itemBuffer.numRemainingBytes() < 24) |
| 3189 | return false; |
| 3190 | DRW_Coord value; |
| 3191 | value.x = itemBuffer.getRawDouble(); |
| 3192 | value.y = itemBuffer.getRawDouble(); |
| 3193 | value.z = itemBuffer.getRawDouble(); |
| 3194 | if (!itemBuffer.isGood()) |
| 3195 | return false; |
| 3196 | if (!appendEedItem(chunk, totalItems, 1000 + code, value)) |
| 3197 | return false; |
| 3198 | break; |
| 3199 | } |
| 3200 | case 40: |
| 3201 | case 41: |
| 3202 | case 42: { |
| 3203 | if (itemBuffer.numRemainingBytes() < 8) |
| 3204 | return false; |
| 3205 | const double value = itemBuffer.getRawDouble(); |
| 3206 | if (!itemBuffer.isGood()) |
| 3207 | return false; |
| 3208 | if (!appendEedItem(chunk, totalItems, 1000 + code, value)) |
| 3209 | return false; |
| 3210 | break; |
| 3211 | } |
| 3212 | case 70: { |
| 3213 | if (itemBuffer.numRemainingBytes() < 2) |
| 3214 | return false; |
| 3215 | const auto value = |
| 3216 | static_cast<std::int16_t>(itemBuffer.getRawShort16()); |
| 3217 | if (!itemBuffer.isGood()) |
| 3218 | return false; |
| 3219 | if (!appendEedItem(chunk, totalItems, 1070, |
| 3220 | static_cast<std::int32_t>(value))) |
| 3221 | return false; |
| 3222 | break; |
| 3223 | } |
| 3224 | case 71: { |
| 3225 | if (itemBuffer.numRemainingBytes() < 4) |
| 3226 | return false; |
| 3227 | const auto value = |
| 3228 | static_cast<std::int32_t>(itemBuffer.getRawLong32()); |
| 3229 | if (!itemBuffer.isGood()) |
| 3230 | return false; |
| 3231 | if (!appendEedItem(chunk, totalItems, 1071, value)) |
| 3232 | return false; |
| 3233 | break; |
| 3234 | } |
| 3235 | default: |
| 3236 | return false; |
| 3237 | } |
| 3238 | } |
| 3239 | if (!itemBuffer.isGood() || itemBuffer.numRemainingBytes() != 0) |
| 3240 | return false; |
| 3241 | staged.push_back(std::move(chunk)); |
| 3242 | } |
| 3243 | chunks = std::move(staged); |
| 3244 | return true; |
| 3245 | } catch (...) { |
| 3246 | return false; |
| 3247 | } |
| 3248 | } |
| 3249 | |
| 3250 | bool readDwgHandleChecked(dwgBuffer &buffer, std::uint32_t baseHandle, |
| 3251 | bool offset, dwgHandle &handle) { |
| 3252 | if (!buffer.isGood()) |
| 3253 | return false; |
| 3254 | const dwgHandle value = |
| 3255 | offset ? buffer.getOffsetHandle(baseHandle) : buffer.getHandle(); |
| 3256 | if (!buffer.isGood()) |
| 3257 | return false; |
| 3258 | handle = value; |
| 3259 | return true; |
| 3260 | } |
| 3261 | |
| 3262 | bool readDwgHandleList(dwgBuffer &buffer, std::uint32_t baseHandle, |
| 3263 | std::int32_t count, bool offset, |
| 3264 | std::vector<std::uint32_t> *refs) { |
| 3265 | if (!dwgSafety::validReactorCount(count)) { |
| 3266 | buffer.invalidate(); |
| 3267 | return false; |
| 3268 | } |
| 3269 | const int remaining = buffer.numRemainingBytes(); |
| 3270 | if (remaining < 0 || static_cast<std::uint32_t>(count) > |
| 3271 | static_cast<std::uint32_t>(remaining)) { |
| 3272 | buffer.invalidate(); |
| 3273 | return false; |
| 3274 | } |
| 3275 | |
| 3276 | std::vector<std::uint32_t> staged; |
| 3277 | if (refs != nullptr && !DRW::reserve(staged, count)) |
| 3278 | return false; |
| 3279 | for (std::int32_t i = 0; i < count; ++i) { |
| 3280 | dwgHandle value; |
| 3281 | if (!readDwgHandleChecked(buffer, baseHandle, offset, value)) |
| 3282 | return false; |
| 3283 | if (refs != nullptr) |
| 3284 | staged.push_back(value.ref); |
| 3285 | } |
| 3286 | if (refs != nullptr) |
| 3287 | *refs = std::move(staged); |
| 3288 | return true; |
| 3289 | } |
| 3290 | |
| 3291 | bool dwgReader::stageActiveEntityFrame( |
| 3292 | DwgStagedFrame &frame, const DRW_DwgFramePublication &publication) { |
| 3293 | if (frame.lease.has_value() || frame.publication.has_value() || |
| 3294 | m_activeEntityFrameLease == nullptr) { |
| 3295 | return reportDwgFrameTransitionFailure( |
| 3296 | DwgSourceFrameId{publication.m_handle}); |
| 3297 | } |
| 3298 | |
| 3299 | DwgFrameMapLease &activeLease = *m_activeEntityFrameLease; |
| 3300 | if (!activeLease.isDetached() || |
| 3301 | activeLease.origin != DwgFrameMapLease::Origin::ObjectMap || |
| 3302 | activeLease.object.handle != activeLease.source.handle || |
| 3303 | publication.m_handle != activeLease.object.handle) { |
| 3304 | return reportDwgFrameTransitionFailure(activeLease.source, |
| 3305 | activeLease.object.loc, true); |
| 3306 | } |
| 3307 | if (activeLease.hasCoverage) { |
| 3308 | const DwgSourceFrameId publicationSource{ |
| 3309 | publication.m_handle, publication.m_sourceOffset, |
| 3310 | publication.m_sourceMapOrdinal, publication.m_sourceOffsetSpace}; |
| 3311 | if (!publication.m_hasSourceLocation || |
| 3312 | !(activeLease.source == publicationSource)) { |
| 3313 | return reportDwgFrameTransitionFailure(activeLease.source, |
| 3314 | activeLease.object.loc, true); |
| 3315 | } |
| 3316 | } |
| 3317 | if (!stageDetachedDwgSourceFrame(activeLease)) |
| 3318 | return false; |
| 3319 | |
| 3320 | frame.lease.emplace(std::move(activeLease)); |
| 3321 | if (!frame.lease->hasCoverage) |
| 3322 | return true; |
| 3323 | if (stageCurrentEntityFrame(frame, publication)) |
| 3324 | return true; |
| 3325 | |
| 3326 | // Attaching the receipt is the only allocation after the source node has |
| 3327 | // been staged. Preserve recoverability when it fails. |
| 3328 | if (!restoreStagedFrame(frame)) |
| 3329 | (void)abandonStagedFrame(frame); |
| 3330 | return false; |
| 3331 | } |
| 3332 | |
| 3333 | bool dwgReader::claimInvalidSeqEndTerminalizerMarker(std::uint32_t handle, |
| 3334 | bool &inserted) noexcept { |
| 3335 | inserted = false; |
| 3336 | if (handle == DRW::NoHandle || |
| 3337 | consumeTerminalizerFailurePointForTest( |
| 3338 | DwgTerminalizerFailurePoint::BeforeSeqEndMarker)) { |
| 3339 | return false; |
| 3340 | } |
| 3341 | try { |
| 3342 | inserted = m_invalidSeqEndHandles.insert(handle).second; |
| 3343 | return true; |
| 3344 | } catch (...) { |
| 3345 | return false; |
| 3346 | } |
| 3347 | } |
| 3348 | |
| 3349 | bool dwgReader::claimInvalidInsertOwnerTerminalizerMarker( |
| 3350 | std::uint32_t owner, DwgTerminalizerFailurePoint failurePoint, |
| 3351 | bool &inserted) noexcept { |
| 3352 | inserted = false; |
| 3353 | if (owner == DRW::NoHandle || |
| 3354 | consumeTerminalizerFailurePointForTest(failurePoint)) { |
| 3355 | return false; |
| 3356 | } |
| 3357 | try { |
| 3358 | inserted = m_invalidInsertOwners.insert(owner).second; |
| 3359 | return true; |
| 3360 | } catch (...) { |
| 3361 | return false; |
| 3362 | } |
| 3363 | } |
| 3364 | |
| 3365 | void dwgReader::terminalizeInsertGroup(std::uint32_t handle, |
| 3366 | DwgInsertTerminalReason reason) { |
| 3367 | const auto pendingIt = m_pendingInsertStates.find(handle); |
| 3368 | if (pendingIt == m_pendingInsertStates.end()) |
| 3369 | return; |
| 3370 | |
| 3371 | const auto coverageReason = [reason]() { |
| 3372 | switch (reason) { |
| 3373 | case DwgInsertTerminalReason::CallbackException: |
| 3374 | return DRW_DwgFrameCoverageReason::CallbackException; |
| 3375 | case DwgInsertTerminalReason::ReceiptFailure: |
| 3376 | return DRW_DwgFrameCoverageReason::ReceiptFailure; |
| 3377 | case DwgInsertTerminalReason::MalformedGroup: |
| 3378 | default: |
| 3379 | return DRW_DwgFrameCoverageReason::None; |
| 3380 | } |
| 3381 | }(); |
| 3382 | const auto terminalizeFrame = [this, coverageReason](DwgStagedFrame &frame) { |
| 3383 | if (coverageReason != DRW_DwgFrameCoverageReason::None && |
| 3384 | frame.lease.has_value() && frame.hasDetachedLease() && |
| 3385 | frame.lease->hasCoverage) { |
| 3386 | const auto sourceIt = |
| 3387 | m_dwgSourceFrameIndexes.find(frame.lease->source.handle); |
| 3388 | if (sourceIt != m_dwgSourceFrameIndexes.end() && |
| 3389 | sourceIt->second < m_dwgSourceFrameLedger.size() && |
| 3390 | m_dwgSourceFrameLedger[sourceIt->second].m_disposition == |
| 3391 | DRW_DwgFrameDisposition::Staged) { |
| 3392 | (void)markDwgFrameOutcome(frame.lease->source, |
| 3393 | DRW_DwgFrameDisposition::Failed, |
| 3394 | coverageReason); |
| 3395 | } |
| 3396 | } |
| 3397 | return abandonStagedFrame(frame); |
| 3398 | }; |
| 3399 | |
| 3400 | PendingInsertState &pending = pendingIt->second; |
| 3401 | const std::uint32_t sequenceHandle = pending.entity.seqendH.ref; |
| 3402 | auto sequenceIt = m_stagedSeqEnds.find(sequenceHandle); |
| 3403 | const bool hasOwnedSequence = |
| 3404 | sequenceIt != m_stagedSeqEnds.end() && sequenceIt->second.owner == handle; |
| 3405 | bool insertedSequenceMarker = false; |
| 3406 | if (hasOwnedSequence && !claimInvalidSeqEndTerminalizerMarker( |
| 3407 | sequenceHandle, insertedSequenceMarker)) { |
| 3408 | return; |
| 3409 | } |
| 3410 | bool insertedOwnerMarker = false; |
| 3411 | if (!claimInvalidInsertOwnerTerminalizerMarker( |
| 3412 | handle, DwgTerminalizerFailurePoint::BeforeInsertOwnerMarker, |
| 3413 | insertedOwnerMarker)) { |
| 3414 | if (insertedSequenceMarker) |
| 3415 | m_invalidSeqEndHandles.erase(sequenceHandle); |
| 3416 | return; |
| 3417 | } |
| 3418 | |
| 3419 | bool complete = true; |
| 3420 | for (StagedAttribState &attribute : pending.attributes) |
| 3421 | complete = terminalizeFrame(attribute.frame) && complete; |
| 3422 | |
| 3423 | if (hasOwnedSequence) { |
| 3424 | const bool sequenceComplete = terminalizeFrame(sequenceIt->second.frame); |
| 3425 | complete = sequenceComplete && complete; |
| 3426 | if (sequenceComplete) |
| 3427 | m_stagedSeqEnds.erase(sequenceIt); |
| 3428 | } |
| 3429 | |
| 3430 | complete = terminalizeFrame(pending.frame) && complete; |
| 3431 | if (!complete) |
| 3432 | return; |
| 3433 | ++m_entityParseFailures; |
| 3434 | m_pendingInsertStates.erase(pendingIt); |
| 3435 | } |
| 3436 | |
| 3437 | void dwgReader::abandonPendingInsertState(std::uint32_t handle) { |
| 3438 | terminalizeInsertGroup(handle, DwgInsertTerminalReason::MalformedGroup); |
| 3439 | } |
| 3440 | |
| 3441 | void dwgReader::terminalizeOrphanAttribOwner(std::uint32_t owner) { |
| 3442 | if (owner == DRW::NoHandle) |
| 3443 | return; |
| 3444 | |
| 3445 | const auto orphanIt = m_orphanAttribStates.find(owner); |
| 3446 | bool insertedOwnerMarker = false; |
| 3447 | if (!claimInvalidInsertOwnerTerminalizerMarker( |
| 3448 | owner, DwgTerminalizerFailurePoint::BeforeOrphanOwnerMarker, |
| 3449 | insertedOwnerMarker)) { |
| 3450 | return; |
| 3451 | } |
| 3452 | bool complete = true; |
| 3453 | if (orphanIt != m_orphanAttribStates.end()) { |
| 3454 | for (StagedAttribState &attribute : orphanIt->second.attributes) |
| 3455 | complete = abandonStagedFrame(attribute.frame) && complete; |
| 3456 | } |
| 3457 | if (!complete) |
| 3458 | return; |
| 3459 | if (orphanIt != m_orphanAttribStates.end()) { |
| 3460 | ++m_entityParseFailures; |
| 3461 | m_orphanAttribStates.erase(orphanIt); |
| 3462 | } |
| 3463 | } |
| 3464 | |
| 3465 | void dwgReader::terminalizePendingPolylineState( |
| 3466 | std::uint32_t handle, DwgInsertTerminalReason reason) { |
| 3467 | const auto pendingIt = m_pendingPolylineStates.find(handle); |
| 3468 | if (pendingIt == m_pendingPolylineStates.end()) |
| 3469 | return; |
| 3470 | |
| 3471 | bool markedInvalid = false; |
| 3472 | try { |
| 3473 | markedInvalid = m_invalidPolylineOwners.insert(handle).second; |
| 3474 | } catch (...) { |
| 3475 | return; |
| 3476 | } |
| 3477 | if (!markedInvalid) |
| 3478 | return; |
| 3479 | |
| 3480 | const auto coverageReason = [reason]() { |
| 3481 | switch (reason) { |
| 3482 | case DwgInsertTerminalReason::CallbackException: |
| 3483 | return DRW_DwgFrameCoverageReason::CallbackException; |
| 3484 | case DwgInsertTerminalReason::ReceiptFailure: |
| 3485 | return DRW_DwgFrameCoverageReason::ReceiptFailure; |
| 3486 | case DwgInsertTerminalReason::MalformedGroup: |
| 3487 | default: |
| 3488 | return DRW_DwgFrameCoverageReason::None; |
| 3489 | } |
| 3490 | }(); |
| 3491 | const auto terminalizeFrame = [this, coverageReason](DwgStagedFrame &frame) { |
| 3492 | if (frame.lease.has_value() && frame.lease->hasCoverage && |
| 3493 | coverageReason != DRW_DwgFrameCoverageReason::None) { |
| 3494 | (void)markDwgFrameOutcome( |
| 3495 | frame.lease->source, DRW_DwgFrameDisposition::Failed, coverageReason); |
| 3496 | } |
| 3497 | return abandonStagedFrame(frame); |
| 3498 | }; |
| 3499 | |
| 3500 | const auto orphanIt = m_orphanPolylineVertexStates.find(handle); |
| 3501 | bool complete = true; |
| 3502 | for (StagedVertexState &vertex : pendingIt->second.vertices) |
| 3503 | complete = terminalizeFrame(vertex.frame) && complete; |
| 3504 | if (orphanIt != m_orphanPolylineVertexStates.end()) { |
| 3505 | for (StagedVertexState &vertex : orphanIt->second.vertices) |
| 3506 | complete = terminalizeFrame(vertex.frame) && complete; |
| 3507 | } |
| 3508 | const auto sequenceIt = std::find_if( |
| 3509 | m_stagedSeqEnds.begin(), m_stagedSeqEnds.end(), |
| 3510 | [handle](const auto &item) { return item.second.owner == handle; }); |
| 3511 | if (sequenceIt != m_stagedSeqEnds.end()) { |
| 3512 | const bool sequenceComplete = terminalizeFrame(sequenceIt->second.frame); |
| 3513 | complete = sequenceComplete && complete; |
| 3514 | if (sequenceComplete) |
| 3515 | m_stagedSeqEnds.erase(sequenceIt); |
| 3516 | } |
| 3517 | complete = terminalizeFrame(pendingIt->second.frame) && complete; |
| 3518 | if (!complete) |
| 3519 | return; |
| 3520 | ++m_entityParseFailures; |
| 3521 | m_pendingPolylineStates.erase(pendingIt); |
| 3522 | if (orphanIt != m_orphanPolylineVertexStates.end()) |
| 3523 | m_orphanPolylineVertexStates.erase(orphanIt); |
| 3524 | } |
| 3525 | |
| 3526 | void dwgReader::terminalizeOrphanPolylineVertexOwner(std::uint32_t owner) { |
| 3527 | const auto orphanIt = m_orphanPolylineVertexStates.find(owner); |
| 3528 | if (orphanIt == m_orphanPolylineVertexStates.end()) |
| 3529 | return; |
| 3530 | try { |
| 3531 | if (!m_invalidPolylineOwners.insert(owner).second) |
| 3532 | return; |
| 3533 | } catch (...) { |
| 3534 | return; |
| 3535 | } |
| 3536 | bool complete = true; |
| 3537 | for (StagedVertexState &vertex : orphanIt->second.vertices) |
| 3538 | complete = abandonStagedFrame(vertex.frame) && complete; |
| 3539 | if (!complete) |
| 3540 | return; |
| 3541 | ++m_entityParseFailures; |
| 3542 | m_orphanPolylineVertexStates.erase(orphanIt); |
| 3543 | } |
| 3544 | |
| 3545 | dwgReader::DwgMappedEntityOutcome |
| 3546 | dwgReader::stagePendingInsert(DRW_Insert &&insert, |
| 3547 | const DRW_DwgFramePublication &publication, |
| 3548 | DRW_Interface &intfa) { |
| 3549 | if (insert.handle == DRW::NoHandle || !insert.attlist.empty() || |
| 3550 | m_invalidInsertOwners.find(insert.handle) != |
| 3551 | m_invalidInsertOwners.end() || |
| 3552 | m_pendingInsertStates.find(insert.handle) != |
| 3553 | m_pendingInsertStates.end()) { |
| 3554 | (void)reportDwgFrameTransitionFailure(DwgSourceFrameId{insert.handle}); |
| 3555 | return DwgMappedEntityOutcome::Rejected; |
| 3556 | } |
| 3557 | |
| 3558 | const auto orphanIt = m_orphanAttribStates.find(insert.handle); |
| 3559 | if (orphanIt != m_orphanAttribStates.end()) { |
| 3560 | std::vector<std::uint32_t> handles; |
| 3561 | try { |
| 3562 | handles.reserve(orphanIt->second.attributes.size()); |
| 3563 | } catch (...) { |
| 3564 | terminalizeOrphanAttribOwner(insert.handle); |
| 3565 | return DwgMappedEntityOutcome::Rejected; |
| 3566 | } |
| 3567 | for (const StagedAttribState &attribute : orphanIt->second.attributes) { |
| 3568 | if (attribute.entity == nullptr || |
| 3569 | !isExpectedAttribute(insert, *attribute.entity, version) || |
| 3570 | std::find(handles.cbegin(), handles.cend(), |
| 3571 | attribute.entity->handle) != handles.cend()) { |
| 3572 | terminalizeOrphanAttribOwner(insert.handle); |
| 3573 | (void)reportDwgFrameTransitionFailure(DwgSourceFrameId{insert.handle}); |
| 3574 | return DwgMappedEntityOutcome::Rejected; |
| 3575 | } |
| 3576 | handles.push_back(attribute.entity->handle); |
| 3577 | } |
| 3578 | } |
| 3579 | |
| 3580 | PendingInsertState pending; |
| 3581 | pending.entity = std::move(insert); |
| 3582 | const std::uint32_t insertHandle = pending.entity.handle; |
| 3583 | const std::size_t adoptedAttributeCount = |
| 3584 | orphanIt == m_orphanAttribStates.end() |
| 3585 | ? 0u |
| 3586 | : orphanIt->second.attributes.size(); |
| 3587 | try { |
| 3588 | m_pendingInsertStates.reserve(m_pendingInsertStates.size() + 1u); |
| 3589 | pending.entity.attlist.reserve(adoptedAttributeCount); |
| 3590 | pending.attributes.reserve(adoptedAttributeCount); |
| 3591 | } catch (...) { |
| 3592 | terminalizeOrphanAttribOwner(insertHandle); |
| 3593 | return DwgMappedEntityOutcome::Rejected; |
| 3594 | } |
| 3595 | if (!stageActiveEntityFrame(pending.frame, publication)) { |
| 3596 | terminalizeOrphanAttribOwner(insertHandle); |
| 3597 | return DwgMappedEntityOutcome::Rejected; |
| 3598 | } |
| 3599 | |
| 3600 | auto inserted = m_pendingInsertStates.end(); |
| 3601 | try { |
| 3602 | const auto result = m_pendingInsertStates.emplace(pending.entity.handle, |
| 3603 | std::move(pending)); |
| 3604 | if (!result.second) { |
| 3605 | (void)restoreStagedFrame(pending.frame); |
| 3606 | terminalizeOrphanAttribOwner(insertHandle); |
| 3607 | (void)reportDwgFrameTransitionFailure( |
| 3608 | DwgSourceFrameId{publication.m_handle}); |
| 3609 | return DwgMappedEntityOutcome::Rejected; |
| 3610 | } |
| 3611 | inserted = result.first; |
| 3612 | } catch (...) { |
| 3613 | (void)restoreStagedFrame(pending.frame); |
| 3614 | terminalizeOrphanAttribOwner(insertHandle); |
| 3615 | return DwgMappedEntityOutcome::Rejected; |
| 3616 | } |
| 3617 | |
| 3618 | if (orphanIt != m_orphanAttribStates.end()) { |
| 3619 | auto adoptedIt = m_orphanAttribStates.find(inserted->first); |
| 3620 | if (adoptedIt == m_orphanAttribStates.end()) { |
| 3621 | abandonPendingInsertState(inserted->first); |
| 3622 | return DwgMappedEntityOutcome::Rejected; |
| 3623 | } |
| 3624 | PendingInsertState &staged = inserted->second; |
| 3625 | for (StagedAttribState &attribute : adoptedIt->second.attributes) { |
| 3626 | staged.entity.attlist.push_back(attribute.entity); |
| 3627 | staged.attributes.push_back(std::move(attribute)); |
| 3628 | } |
| 3629 | m_orphanAttribStates.erase(adoptedIt); |
| 3630 | } |
| 3631 | return tryCommitPendingInsert(inserted->first, intfa); |
| 3632 | } |
| 3633 | |
| 3634 | dwgReader::DwgMappedEntityOutcome dwgReader::stageMappedInsertAggregate( |
| 3635 | DRW_Insert &&insert, const DRW_DwgFramePublication &publication, |
| 3636 | dwgBuffer *dbuf, DRW_Interface &intfa, |
| 3637 | DwgIntegrityAddressSpace offsetSpace) { |
| 3638 | if (version < DRW::AC1018 || dbuf == nullptr || |
| 3639 | m_activeEntityFrameLease == nullptr || |
| 3640 | !m_activeEntityFrameLease->isDetached() || |
| 3641 | m_activeEntityFrameLease->object.handle != insert.handle) { |
| 3642 | return DwgMappedEntityOutcome::Rejected; |
| 3643 | } |
| 3644 | |
| 3645 | const std::uint32_t insertHandle = insert.handle; |
| 3646 | std::vector<std::uint32_t> discoveredHandles; |
| 3647 | const auto reject = [this, insertHandle, &discoveredHandles]() { |
| 3648 | terminalizeInsertGroup(insertHandle, |
| 3649 | DwgInsertTerminalReason::MalformedGroup); |
| 3650 | for (const std::uint32_t handle : discoveredHandles) |
| 3651 | (void)quarantineMappedDwgSourceFrame(handle); |
| 3652 | return DwgMappedEntityOutcome::Rejected; |
| 3653 | }; |
| 3654 | |
| 3655 | try { |
| 3656 | discoveredHandles.reserve(insert.attribHandles.size() + 1u); |
| 3657 | std::vector<std::uint32_t> declaredAttributeHandles; |
| 3658 | declaredAttributeHandles.reserve(insert.attribHandles.size()); |
| 3659 | std::unordered_set<std::uint32_t> declaredAttributes; |
| 3660 | declaredAttributes.reserve(insert.attribHandles.size()); |
| 3661 | for (const dwgHandle &declared : insert.attribHandles) { |
| 3662 | const std::uint32_t handle = declared.ref; |
| 3663 | if (handle == DRW::NoHandle || |
| 3664 | !declaredAttributes.insert(handle).second) { |
| 3665 | return reject(); |
| 3666 | } |
| 3667 | declaredAttributeHandles.push_back(handle); |
| 3668 | |
| 3669 | const auto sourceIt = ObjectMap.find(handle); |
| 3670 | if (sourceIt == ObjectMap.end()) |
| 3671 | continue; |
| 3672 | discoveredHandles.push_back(handle); |
| 3673 | DwgFrameClassification classification; |
| 3674 | if (!classifyDwgSourceFrame(dbuf, sourceIt->second, classification) || |
| 3675 | classification.route != DwgFrameClassification::Route::Entity || |
| 3676 | classification.resolvedType != dwgType::ATTRIB) { |
| 3677 | return reject(); |
| 3678 | } |
| 3679 | } |
| 3680 | |
| 3681 | const std::uint32_t sequenceHandle = insert.seqendH.ref; |
| 3682 | if (sequenceHandle != DRW::NoHandle) { |
| 3683 | if (declaredAttributes.find(sequenceHandle) != declaredAttributes.end()) { |
| 3684 | return reject(); |
| 3685 | } |
| 3686 | const auto sourceIt = ObjectMap.find(sequenceHandle); |
| 3687 | if (sourceIt != ObjectMap.end()) { |
| 3688 | discoveredHandles.push_back(sequenceHandle); |
| 3689 | DwgFrameClassification classification; |
| 3690 | if (!classifyDwgSourceFrame(dbuf, sourceIt->second, classification) || |
| 3691 | classification.route != DwgFrameClassification::Route::Entity || |
| 3692 | classification.resolvedType != dwgType::SEQEND) { |
| 3693 | return reject(); |
| 3694 | } |
| 3695 | } |
| 3696 | } |
| 3697 | |
| 3698 | const DwgMappedEntityOutcome parentOutcome = |
| 3699 | stagePendingInsert(std::move(insert), publication, intfa); |
| 3700 | if (parentOutcome == DwgMappedEntityOutcome::Rejected) |
| 3701 | return reject(); |
| 3702 | if (parentOutcome == DwgMappedEntityOutcome::CommittedCompound) { |
| 3703 | // A declared child frame still in ObjectMap would duplicate an |
| 3704 | // already committed child-first sequence. |
| 3705 | return discoveredHandles.empty() ? parentOutcome : reject(); |
| 3706 | } |
| 3707 | |
| 3708 | const auto stageMappedChild = [this, dbuf, &intfa, |
| 3709 | offsetSpace](DwgFrameMapLease &lease) { |
| 3710 | DwgFrameMapLease *const previousLease = m_activeEntityFrameLease; |
| 3711 | m_activeEntityFrameLease = nullptr; |
| 3712 | bool frameFailure = false; |
| 3713 | bool read = false; |
| 3714 | try { |
| 3715 | read = |
| 3716 | readMappedDwgEntity(dbuf, lease, intfa, &frameFailure, offsetSpace); |
| 3717 | } catch (...) { |
| 3718 | read = false; |
| 3719 | frameFailure = true; |
| 3720 | } |
| 3721 | m_activeEntityFrameLease = previousLease; |
| 3722 | return read && !frameFailure; |
| 3723 | }; |
| 3724 | const auto restoreUnstagedLease = [this](DwgFrameMapLease &lease) { |
| 3725 | if (lease.isDetached()) |
| 3726 | (void)restoreDwgSourceFrame(lease); |
| 3727 | }; |
| 3728 | const auto stageDeclaredChild = |
| 3729 | [this, dbuf, &stageMappedChild, |
| 3730 | &restoreUnstagedLease](std::uint32_t handle) { |
| 3731 | const auto sourceIt = ObjectMap.find(handle); |
| 3732 | if (sourceIt == ObjectMap.end()) |
| 3733 | return true; |
| 3734 | DwgFrameClassification classification; |
| 3735 | if (!classifyDwgSourceFrame(dbuf, sourceIt->second, classification)) { |
| 3736 | return false; |
| 3737 | } |
| 3738 | DwgFrameMapLease lease; |
| 3739 | if (!detachDwgSourceFrame(ObjectMap, sourceIt, lease)) |
| 3740 | return false; |
| 3741 | lease.classification.emplace(std::move(classification)); |
| 3742 | if (stageMappedChild(lease)) |
| 3743 | return true; |
| 3744 | restoreUnstagedLease(lease); |
| 3745 | return false; |
| 3746 | }; |
| 3747 | |
| 3748 | // The INSERT handle list defines ATTRIB order. The set above is only |
| 3749 | // for duplicate detection; iterating it would reorder the callback. |
| 3750 | for (const std::uint32_t declared : declaredAttributeHandles) { |
| 3751 | if (!stageDeclaredChild(declared)) |
| 3752 | return reject(); |
| 3753 | } |
| 3754 | if (sequenceHandle != DRW::NoHandle && |
| 3755 | !stageDeclaredChild(sequenceHandle)) { |
| 3756 | return reject(); |
| 3757 | } |
| 3758 | |
| 3759 | // Staging the declared SEQEND may commit and remove the complete |
| 3760 | // group. A second commit probe would report it as merely unstaged. |
| 3761 | if (m_pendingInsertStates.find(insertHandle) == |
| 3762 | m_pendingInsertStates.end()) { |
| 3763 | return DwgMappedEntityOutcome::CommittedCompound; |
| 3764 | } |
| 3765 | const DwgMappedEntityOutcome outcome = |
| 3766 | tryCommitPendingInsert(insertHandle, intfa); |
| 3767 | return outcome == DwgMappedEntityOutcome::StagedCompound ? reject() |
| 3768 | : outcome; |
| 3769 | } catch (...) { |
| 3770 | return reject(); |
| 3771 | } |
| 3772 | } |
| 3773 | |
| 3774 | dwgReader::DwgMappedEntityOutcome dwgReader::stageLegacyInsertAggregate( |
| 3775 | DRW_Insert &&insert, const DRW_DwgFramePublication &publication, |
| 3776 | dwgBuffer *dbuf, DRW_Interface &intfa, |
| 3777 | DwgIntegrityAddressSpace offsetSpace) { |
| 3778 | if (version >= DRW::AC1018 || dbuf == nullptr || |
| 3779 | m_activeEntityFrameLease == nullptr || |
| 3780 | !m_activeEntityFrameLease->isDetached() || |
| 3781 | m_activeEntityFrameLease->object.handle != insert.handle) { |
| 3782 | return DwgMappedEntityOutcome::Rejected; |
| 3783 | } |
| 3784 | |
| 3785 | // R13-R2000 stores only the first and last ATTRIB handles. Those children |
| 3786 | // are outside the BLOCK_RECORD's normal next-entity chain, so follow their |
| 3787 | // own explicit (or implicit +1) chain before staging the parent. |
| 3788 | if (insert.attribHandles.empty()) |
| 3789 | return stagePendingInsert(std::move(insert), publication, intfa); |
| 3790 | if (insert.attribHandles.size() != 2u || |
| 3791 | insert.seqendH.ref == DRW::NoHandle) { |
| 3792 | return DwgMappedEntityOutcome::Rejected; |
| 3793 | } |
| 3794 | |
| 3795 | struct ParsedAttrib { |
| 3796 | objHandle object; |
| 3797 | std::shared_ptr<DRW_Attrib> entity; |
| 3798 | DRW_DwgFramePublication publication; |
| 3799 | }; |
| 3800 | |
| 3801 | const std::uint32_t insertHandle = insert.handle; |
| 3802 | const std::uint32_t firstHandle = insert.attribHandles.front().ref; |
| 3803 | const std::uint32_t lastHandle = insert.attribHandles.back().ref; |
| 3804 | const std::uint32_t sequenceHandle = insert.seqendH.ref; |
| 3805 | const bool emptyAttributeRange = firstHandle == DRW::NoHandle && |
| 3806 | lastHandle == DRW::NoHandle; |
| 3807 | if ((firstHandle == DRW::NoHandle) != (lastHandle == DRW::NoHandle)) |
| 3808 | return DwgMappedEntityOutcome::Rejected; |
| 3809 | std::vector<ParsedAttrib> attributes; |
| 3810 | std::vector<std::uint32_t> discoveredHandles; |
| 3811 | std::unordered_set<std::uint32_t> visitedHandles; |
| 3812 | const auto quarantineDiscovered = [this, &discoveredHandles]() { |
| 3813 | for (const std::uint32_t handle : discoveredHandles) |
| 3814 | (void)quarantineMappedDwgSourceFrame(handle); |
| 3815 | }; |
| 3816 | const auto reject = [this, insertHandle, &quarantineDiscovered]() { |
| 3817 | terminalizeInsertGroup(insertHandle, DwgInsertTerminalReason::MalformedGroup); |
| 3818 | quarantineDiscovered(); |
| 3819 | return DwgMappedEntityOutcome::Rejected; |
| 3820 | }; |
| 3821 | |
| 3822 | try { |
| 3823 | std::uint32_t nextHandle = firstHandle; |
| 3824 | bool reachedLast = emptyAttributeRange; |
| 3825 | while (nextHandle != DRW::NoHandle) { |
| 3826 | if (!visitedHandles.insert(nextHandle).second) |
| 3827 | return reject(); |
| 3828 | discoveredHandles.push_back(nextHandle); |
| 3829 | |
| 3830 | const auto sourceIt = ObjectMap.find(nextHandle); |
| 3831 | if (sourceIt == ObjectMap.end()) |
| 3832 | return reject(); |
| 3833 | DwgSourceFrameLease borrowed; |
| 3834 | if (!borrowDwgSourceFrame(ObjectMap, sourceIt, borrowed) || |
| 3835 | borrowed.object.handle != nextHandle) { |
| 3836 | return reject(); |
| 3837 | } |
| 3838 | |
| 3839 | DwgObjectFrame frame; |
| 3840 | if (!frame.readAt(*dbuf, version, borrowed.object.loc)) { |
| 3841 | recordObjectFrameFailure(borrowed.object, offsetSpace); |
| 3842 | return reject(); |
| 3843 | } |
| 3844 | std::vector<std::uint8_t> &body = frame.body(); |
| 3845 | dwgBuffer buffer(body.data(), body.size(), &decoder); |
| 3846 | if (buffer.getObjType(version) != dwgType::ATTRIB || !buffer.isGood()) |
| 3847 | return reject(); |
| 3848 | buffer.resetPosition(); |
| 3849 | |
| 3850 | auto attribute = std::make_shared<DRW_Attrib>(); |
| 3851 | if (!attribute->parseDwg(version, &buffer, frame.bodyBitSize()) || |
| 3852 | !buffer.isGood() || attribute->handle != nextHandle) { |
| 3853 | if (attribute->handle != nextHandle) |
| 3854 | parsedEntityHandleMismatch = true; |
| 3855 | return reject(); |
| 3856 | } |
| 3857 | if (attribute->parentHandle != insertHandle) { |
| 3858 | parsedEntityOwnerMismatch = true; |
| 3859 | return reject(); |
| 3860 | } |
| 3861 | parseAttribs(attribute.get()); |
| 3862 | |
| 3863 | ParsedAttrib parsed; |
| 3864 | parsed.object = borrowed.object; |
| 3865 | parsed.entity = std::move(attribute); |
| 3866 | parsed.publication = makeTypedEntityFramePublication( |
| 3867 | version, borrowed.object, dwgType::ATTRIB, *parsed.entity); |
| 3868 | attributes.push_back(std::move(parsed)); |
| 3869 | |
| 3870 | if (nextHandle == lastHandle) { |
| 3871 | reachedLast = true; |
| 3872 | break; |
| 3873 | } |
| 3874 | nextHandle = attributes.back().entity->nextEntLink; |
| 3875 | } |
| 3876 | if (!reachedLast) |
| 3877 | return reject(); |
| 3878 | |
| 3879 | // Handle maps are visited by handle, so a valid legacy SEQEND can be |
| 3880 | // staged before its INSERT. The empty-range form has no ATTRIB frames to |
| 3881 | // move, and the pending INSERT commit can consume that staged SEQEND |
| 3882 | // directly. |
| 3883 | const auto preStagedSequenceIt = m_stagedSeqEnds.find(sequenceHandle); |
| 3884 | if (preStagedSequenceIt != m_stagedSeqEnds.end()) { |
| 3885 | if (preStagedSequenceIt->second.owner != insertHandle || |
| 3886 | !attributes.empty()) |
| 3887 | return reject(); |
| 3888 | const DwgMappedEntityOutcome outcome = |
| 3889 | stagePendingInsert(std::move(insert), publication, intfa); |
| 3890 | return outcome == DwgMappedEntityOutcome::CommittedCompound |
| 3891 | ? outcome |
| 3892 | : reject(); |
| 3893 | } |
| 3894 | |
| 3895 | discoveredHandles.push_back(sequenceHandle); |
| 3896 | const auto sequenceIt = ObjectMap.find(sequenceHandle); |
| 3897 | if (sequenceIt == ObjectMap.end()) |
| 3898 | return reject(); |
| 3899 | DwgSourceFrameLease borrowedSequence; |
| 3900 | if (!borrowDwgSourceFrame(ObjectMap, sequenceIt, borrowedSequence) || |
| 3901 | borrowedSequence.object.handle != sequenceHandle) { |
| 3902 | return reject(); |
| 3903 | } |
| 3904 | DwgObjectFrame sequenceFrame; |
| 3905 | if (!sequenceFrame.readAt(*dbuf, version, borrowedSequence.object.loc)) { |
| 3906 | recordObjectFrameFailure(borrowedSequence.object, offsetSpace); |
| 3907 | return reject(); |
| 3908 | } |
| 3909 | std::vector<std::uint8_t> &sequenceBody = sequenceFrame.body(); |
| 3910 | dwgBuffer sequenceBuffer(sequenceBody.data(), sequenceBody.size(), |
| 3911 | &decoder); |
| 3912 | if (sequenceBuffer.getObjType(version) != dwgType::SEQEND || |
| 3913 | !sequenceBuffer.isGood()) { |
| 3914 | return reject(); |
| 3915 | } |
| 3916 | sequenceBuffer.resetPosition(); |
| 3917 | DRW_SeqEnd sequenceEnd; |
| 3918 | if (!sequenceEnd.parseDwg(version, &sequenceBuffer, |
| 3919 | sequenceFrame.bodyBitSize()) || |
| 3920 | !sequenceBuffer.isGood() || sequenceEnd.handle != sequenceHandle) { |
| 3921 | if (sequenceEnd.handle != sequenceHandle) |
| 3922 | parsedEntityHandleMismatch = true; |
| 3923 | return reject(); |
| 3924 | } |
| 3925 | if (sequenceEnd.parentHandle != insertHandle) { |
| 3926 | parsedEntityOwnerMismatch = true; |
| 3927 | return reject(); |
| 3928 | } |
| 3929 | const DRW_DwgFramePublication sequencePublication = |
| 3930 | makeTypedEntityFramePublication(version, borrowedSequence.object, |
| 3931 | dwgType::SEQEND, sequenceEnd); |
| 3932 | |
| 3933 | const DwgMappedEntityOutcome parentOutcome = |
| 3934 | stagePendingInsert(std::move(insert), publication, intfa); |
| 3935 | if (parentOutcome != DwgMappedEntityOutcome::StagedCompound) |
| 3936 | return reject(); |
| 3937 | |
| 3938 | const auto stageWithLease = [this](DwgFrameMapLease &lease, |
| 3939 | const auto &operation) { |
| 3940 | DwgFrameMapLease *const previousLease = m_activeEntityFrameLease; |
| 3941 | m_activeEntityFrameLease = &lease; |
| 3942 | DwgMappedEntityOutcome outcome = DwgMappedEntityOutcome::Rejected; |
| 3943 | try { |
| 3944 | outcome = operation(); |
| 3945 | } catch (...) { |
| 3946 | outcome = DwgMappedEntityOutcome::Rejected; |
| 3947 | } |
| 3948 | m_activeEntityFrameLease = previousLease; |
| 3949 | return outcome; |
| 3950 | }; |
| 3951 | const auto restoreUnstagedLease = [this](DwgFrameMapLease &lease) { |
| 3952 | if (lease.isDetached()) |
| 3953 | (void)restoreDwgSourceFrame(lease); |
| 3954 | }; |
| 3955 | |
| 3956 | for (ParsedAttrib &attribute : attributes) { |
| 3957 | const auto sourceIt = ObjectMap.find(attribute.object.handle); |
| 3958 | if (sourceIt == ObjectMap.end() || |
| 3959 | !(sourceFrameId(sourceIt->second) == sourceFrameId(attribute.object))) { |
| 3960 | return reject(); |
| 3961 | } |
| 3962 | DwgFrameMapLease lease; |
| 3963 | if (!detachDwgSourceFrame(ObjectMap, sourceIt, lease)) |
| 3964 | return reject(); |
| 3965 | const DwgMappedEntityOutcome outcome = |
| 3966 | stageWithLease(lease, [this, &attribute, &intfa]() { |
| 3967 | return stagePendingAttribute(std::move(attribute.entity), |
| 3968 | attribute.publication, intfa); |
| 3969 | }); |
| 3970 | if (outcome == DwgMappedEntityOutcome::Rejected) { |
| 3971 | restoreUnstagedLease(lease); |
| 3972 | return reject(); |
| 3973 | } |
| 3974 | } |
| 3975 | |
| 3976 | const auto stagedSequenceIt = ObjectMap.find(sequenceHandle); |
| 3977 | if (stagedSequenceIt == ObjectMap.end() || |
| 3978 | !(sourceFrameId(stagedSequenceIt->second) == |
| 3979 | sourceFrameId(borrowedSequence.object))) { |
| 3980 | return reject(); |
| 3981 | } |
| 3982 | DwgFrameMapLease sequenceLease; |
| 3983 | if (!detachDwgSourceFrame(ObjectMap, stagedSequenceIt, sequenceLease)) |
| 3984 | return reject(); |
| 3985 | const DwgMappedEntityOutcome outcome = |
| 3986 | stageWithLease(sequenceLease, |
| 3987 | [this, sequenceHandle, insertHandle, |
| 3988 | &sequencePublication, &intfa]() { |
| 3989 | return stagePendingSeqEnd( |
| 3990 | sequenceHandle, insertHandle, |
| 3991 | sequencePublication, intfa); |
| 3992 | }); |
| 3993 | if (outcome != DwgMappedEntityOutcome::CommittedCompound) { |
| 3994 | restoreUnstagedLease(sequenceLease); |
| 3995 | return reject(); |
| 3996 | } |
| 3997 | return outcome; |
| 3998 | } catch (...) { |
| 3999 | return reject(); |
| 4000 | } |
| 4001 | } |
| 4002 | |
| 4003 | dwgReader::DwgMappedEntityOutcome dwgReader::stageMappedPolylineAggregate( |
| 4004 | DRW_Polyline &&polyline, const DRW_DwgFramePublication &publication, |
| 4005 | dwgBuffer *dbuf, DRW_Interface &intfa, |
| 4006 | DwgIntegrityAddressSpace offsetSpace) { |
| 4007 | if (version < DRW::AC1018 || dbuf == nullptr || |
| 4008 | m_activeEntityFrameLease == nullptr || |
| 4009 | !m_activeEntityFrameLease->isDetached() || |
| 4010 | m_activeEntityFrameLease->object.handle != polyline.handle) { |
| 4011 | return DwgMappedEntityOutcome::Rejected; |
| 4012 | } |
| 4013 | |
| 4014 | const std::uint32_t polylineHandle = polyline.handle; |
| 4015 | std::vector<std::uint32_t> discoveredHandles; |
| 4016 | std::vector<std::uint32_t> declaredChildHandles; |
| 4017 | const auto reject = [this, polylineHandle, &discoveredHandles, |
| 4018 | &declaredChildHandles]() { |
| 4019 | const auto discardChild = [this](std::uint32_t handle) { |
| 4020 | const auto objectIt = ObjectMap.find(handle); |
| 4021 | if (objectIt != ObjectMap.end()) |
| 4022 | return discardDwgSourceFrame(ObjectMap, objectIt); |
| 4023 | const auto deferredIt = objObjectMap.find(handle); |
| 4024 | return deferredIt == objObjectMap.end() || |
| 4025 | discardDwgSourceFrame(objObjectMap, deferredIt); |
| 4026 | }; |
| 4027 | terminalizePendingPolylineState(polylineHandle, |
| 4028 | DwgInsertTerminalReason::MalformedGroup); |
| 4029 | try { |
| 4030 | m_invalidPolylineOwners.insert(polylineHandle); |
| 4031 | } catch (...) { |
| 4032 | // The source frames below still cannot be republished. |
| 4033 | } |
| 4034 | for (const std::uint32_t handle : discoveredHandles) |
| 4035 | (void)discardChild(handle); |
| 4036 | for (const std::uint32_t handle : declaredChildHandles) |
| 4037 | (void)discardChild(handle); |
| 4038 | return DwgMappedEntityOutcome::Rejected; |
| 4039 | }; |
| 4040 | const auto isVertexType = [](std::int16_t type) { |
| 4041 | return type == dwgType::VERTEX_2D || type == dwgType::VERTEX_3D || |
| 4042 | type == dwgType::VERTEX_MESH || type == dwgType::VERTEX_PFACE || |
| 4043 | type == dwgType::VERTEX_PFACE_FACE; |
| 4044 | }; |
| 4045 | |
| 4046 | try { |
| 4047 | const std::uint32_t sequenceHandle = polyline.seqEndH.ref; |
| 4048 | declaredChildHandles.reserve(polyline.hadlesList.size() + 1u); |
| 4049 | for (const std::uint32_t handle : polyline.hadlesList) { |
| 4050 | if (handle != DRW::NoHandle) |
| 4051 | declaredChildHandles.push_back(handle); |
| 4052 | } |
| 4053 | if (sequenceHandle != DRW::NoHandle) |
| 4054 | declaredChildHandles.push_back(sequenceHandle); |
| 4055 | |
| 4056 | discoveredHandles.reserve(polyline.hadlesList.size() + 1u); |
| 4057 | std::vector<std::uint32_t> declaredVertexHandles; |
| 4058 | declaredVertexHandles.reserve(polyline.hadlesList.size()); |
| 4059 | std::unordered_set<std::uint32_t> declaredVertices; |
| 4060 | declaredVertices.reserve(polyline.hadlesList.size()); |
| 4061 | for (const std::uint32_t handle : polyline.hadlesList) { |
| 4062 | if (handle == DRW::NoHandle || !declaredVertices.insert(handle).second) { |
| 4063 | return reject(); |
| 4064 | } |
| 4065 | declaredVertexHandles.push_back(handle); |
| 4066 | |
| 4067 | const auto sourceIt = ObjectMap.find(handle); |
| 4068 | if (sourceIt == ObjectMap.end()) |
| 4069 | continue; |
| 4070 | discoveredHandles.push_back(handle); |
| 4071 | DwgFrameClassification classification; |
| 4072 | if (!classifyDwgSourceFrame(dbuf, sourceIt->second, classification) || |
| 4073 | classification.route != DwgFrameClassification::Route::Entity || |
| 4074 | !isVertexType(classification.resolvedType)) { |
| 4075 | return reject(); |
| 4076 | } |
| 4077 | } |
| 4078 | |
| 4079 | if (sequenceHandle == DRW::NoHandle || |
| 4080 | declaredVertices.find(sequenceHandle) != declaredVertices.end()) { |
| 4081 | return reject(); |
| 4082 | } |
| 4083 | const auto sequenceIt = ObjectMap.find(sequenceHandle); |
| 4084 | if (sequenceIt != ObjectMap.end()) { |
| 4085 | discoveredHandles.push_back(sequenceHandle); |
| 4086 | DwgFrameClassification classification; |
| 4087 | if (!classifyDwgSourceFrame(dbuf, sequenceIt->second, classification) || |
| 4088 | classification.route != DwgFrameClassification::Route::Entity || |
| 4089 | classification.resolvedType != dwgType::SEQEND) { |
| 4090 | return reject(); |
| 4091 | } |
| 4092 | } |
| 4093 | |
| 4094 | const DwgMappedEntityOutcome parentOutcome = |
| 4095 | stagePendingPolyline(std::move(polyline), publication, intfa); |
| 4096 | if (parentOutcome == DwgMappedEntityOutcome::Rejected) |
| 4097 | return reject(); |
| 4098 | if (parentOutcome == DwgMappedEntityOutcome::CommittedCompound) { |
| 4099 | return discoveredHandles.empty() ? parentOutcome : reject(); |
| 4100 | } |
| 4101 | |
| 4102 | const auto stageMappedChild = [this, dbuf, &intfa, |
| 4103 | offsetSpace](DwgFrameMapLease &lease) { |
| 4104 | DwgFrameMapLease *const previousLease = m_activeEntityFrameLease; |
| 4105 | m_activeEntityFrameLease = nullptr; |
| 4106 | bool frameFailure = false; |
| 4107 | bool read = false; |
| 4108 | try { |
| 4109 | read = |
| 4110 | readMappedDwgEntity(dbuf, lease, intfa, &frameFailure, offsetSpace); |
| 4111 | } catch (...) { |
| 4112 | read = false; |
| 4113 | frameFailure = true; |
| 4114 | } |
| 4115 | m_activeEntityFrameLease = previousLease; |
| 4116 | return read && !frameFailure; |
| 4117 | }; |
| 4118 | const auto restoreUnstagedLease = [this](DwgFrameMapLease &lease) { |
| 4119 | if (lease.isDetached()) |
| 4120 | (void)restoreDwgSourceFrame(lease); |
| 4121 | }; |
| 4122 | const auto stageDeclaredChild = |
| 4123 | [this, dbuf, &stageMappedChild, |
| 4124 | &restoreUnstagedLease](std::uint32_t handle) { |
| 4125 | const auto sourceIt = ObjectMap.find(handle); |
| 4126 | if (sourceIt == ObjectMap.end()) |
| 4127 | return true; |
| 4128 | DwgFrameClassification classification; |
| 4129 | if (!classifyDwgSourceFrame(dbuf, sourceIt->second, classification)) { |
| 4130 | return false; |
| 4131 | } |
| 4132 | DwgFrameMapLease lease; |
| 4133 | if (!detachDwgSourceFrame(ObjectMap, sourceIt, lease)) |
| 4134 | return false; |
| 4135 | lease.classification.emplace(std::move(classification)); |
| 4136 | if (stageMappedChild(lease)) |
| 4137 | return true; |
| 4138 | restoreUnstagedLease(lease); |
| 4139 | return false; |
| 4140 | }; |
| 4141 | |
| 4142 | for (const std::uint32_t declared : declaredVertexHandles) { |
| 4143 | if (!stageDeclaredChild(declared)) |
| 4144 | return reject(); |
| 4145 | } |
| 4146 | if (!stageDeclaredChild(sequenceHandle)) |
| 4147 | return reject(); |
| 4148 | |
| 4149 | if (m_pendingPolylineStates.find(polylineHandle) == |
| 4150 | m_pendingPolylineStates.end()) { |
| 4151 | return DwgMappedEntityOutcome::CommittedCompound; |
| 4152 | } |
| 4153 | const DwgMappedEntityOutcome outcome = |
| 4154 | tryCommitPendingPolyline(polylineHandle, intfa); |
| 4155 | return outcome == DwgMappedEntityOutcome::StagedCompound ? reject() |
| 4156 | : outcome; |
| 4157 | } catch (...) { |
| 4158 | return reject(); |
| 4159 | } |
| 4160 | } |
| 4161 | |
| 4162 | dwgReader::DwgMappedEntityOutcome |
| 4163 | dwgReader::stagePendingAttribute(std::shared_ptr<DRW_Attrib> attribute, |
| 4164 | const DRW_DwgFramePublication &publication, |
| 4165 | DRW_Interface &intfa) { |
| 4166 | if (attribute == nullptr || attribute->handle == DRW::NoHandle || |
| 4167 | attribute->parentHandle == DRW::NoHandle || |
| 4168 | m_invalidInsertOwners.find(attribute->parentHandle) != |
| 4169 | m_invalidInsertOwners.end()) { |
| 4170 | (void)reportDwgFrameTransitionFailure( |
| 4171 | DwgSourceFrameId{attribute ? attribute->handle : DRW::NoHandle}); |
| 4172 | return DwgMappedEntityOutcome::Rejected; |
| 4173 | } |
| 4174 | |
| 4175 | const std::uint32_t owner = attribute->parentHandle; |
| 4176 | const auto pendingIt = m_pendingInsertStates.find(owner); |
| 4177 | if (pendingIt != m_pendingInsertStates.end()) { |
| 4178 | PendingInsertState &pending = pendingIt->second; |
| 4179 | if (!isExpectedAttribute(pending.entity, *attribute, version) || |
| 4180 | std::any_of(pending.attributes.cbegin(), pending.attributes.cend(), |
| 4181 | [&attribute](const StagedAttribState ¤t) { |
| 4182 | return current.entity != nullptr && |
| 4183 | current.entity->handle == attribute->handle; |
| 4184 | })) { |
| 4185 | terminalizeInsertGroup(owner, DwgInsertTerminalReason::MalformedGroup); |
| 4186 | (void)reportDwgFrameTransitionFailure( |
| 4187 | DwgSourceFrameId{attribute->handle}); |
| 4188 | return DwgMappedEntityOutcome::Rejected; |
| 4189 | } |
| 4190 | try { |
| 4191 | pending.entity.attlist.reserve(pending.entity.attlist.size() + 1u); |
| 4192 | pending.attributes.reserve(pending.attributes.size() + 1u); |
| 4193 | } catch (...) { |
| 4194 | terminalizeInsertGroup(owner, DwgInsertTerminalReason::MalformedGroup); |
| 4195 | return DwgMappedEntityOutcome::Rejected; |
| 4196 | } |
| 4197 | |
| 4198 | StagedAttribState staged; |
| 4199 | staged.entity = std::move(attribute); |
| 4200 | if (!stageActiveEntityFrame(staged.frame, publication)) { |
| 4201 | terminalizeInsertGroup(owner, DwgInsertTerminalReason::MalformedGroup); |
| 4202 | return DwgMappedEntityOutcome::Rejected; |
| 4203 | } |
| 4204 | pending.entity.attlist.push_back(staged.entity); |
| 4205 | pending.attributes.push_back(std::move(staged)); |
| 4206 | return tryCommitPendingInsert(owner, intfa); |
| 4207 | } |
| 4208 | |
| 4209 | auto orphanIt = m_orphanAttribStates.find(owner); |
| 4210 | if (orphanIt == m_orphanAttribStates.end()) { |
| 4211 | try { |
| 4212 | m_orphanAttribStates.reserve(m_orphanAttribStates.size() + 1u); |
| 4213 | orphanIt = m_orphanAttribStates.try_emplace(owner).first; |
| 4214 | } catch (...) { |
| 4215 | terminalizeOrphanAttribOwner(owner); |
| 4216 | return DwgMappedEntityOutcome::Rejected; |
| 4217 | } |
| 4218 | } |
| 4219 | if (std::any_of(orphanIt->second.attributes.cbegin(), |
| 4220 | orphanIt->second.attributes.cend(), |
| 4221 | [&attribute](const StagedAttribState ¤t) { |
| 4222 | return current.entity != nullptr && |
| 4223 | current.entity->handle == attribute->handle; |
| 4224 | })) { |
| 4225 | terminalizeOrphanAttribOwner(owner); |
| 4226 | (void)reportDwgFrameTransitionFailure(DwgSourceFrameId{attribute->handle}); |
| 4227 | return DwgMappedEntityOutcome::Rejected; |
| 4228 | } |
| 4229 | try { |
| 4230 | orphanIt->second.attributes.reserve(orphanIt->second.attributes.size() + |
| 4231 | 1u); |
| 4232 | } catch (...) { |
| 4233 | terminalizeOrphanAttribOwner(owner); |
| 4234 | return DwgMappedEntityOutcome::Rejected; |
| 4235 | } |
| 4236 | |
| 4237 | StagedAttribState staged; |
| 4238 | staged.entity = std::move(attribute); |
| 4239 | if (!stageActiveEntityFrame(staged.frame, publication)) { |
| 4240 | terminalizeOrphanAttribOwner(owner); |
| 4241 | return DwgMappedEntityOutcome::Rejected; |
| 4242 | } |
| 4243 | orphanIt->second.attributes.push_back(std::move(staged)); |
| 4244 | return DwgMappedEntityOutcome::StagedCompound; |
| 4245 | } |
| 4246 | |
| 4247 | dwgReader::DwgMappedEntityOutcome |
| 4248 | dwgReader::stagePendingSeqEnd(std::uint32_t handle, std::uint32_t owner, |
| 4249 | const DRW_DwgFramePublication &publication, |
| 4250 | DRW_Interface &intfa) { |
| 4251 | if (handle == DRW::NoHandle || owner == DRW::NoHandle || |
| 4252 | publication.m_handle != handle || |
| 4253 | m_invalidSeqEndHandles.find(handle) != m_invalidSeqEndHandles.end() || |
| 4254 | m_invalidPolylineOwners.find(owner) != m_invalidPolylineOwners.end() || |
| 4255 | m_consumedSeqEndHandles.find(handle) != m_consumedSeqEndHandles.end() || |
| 4256 | m_stagedSeqEnds.find(handle) != m_stagedSeqEnds.end()) { |
| 4257 | (void)reportDwgFrameTransitionFailure(DwgSourceFrameId{handle}); |
| 4258 | return DwgMappedEntityOutcome::Rejected; |
| 4259 | } |
| 4260 | const auto pendingIt = m_pendingInsertStates.find(owner); |
| 4261 | if (pendingIt != m_pendingInsertStates.end() && |
| 4262 | pendingIt->second.entity.seqendH.ref != handle) { |
| 4263 | terminalizeInsertGroup(owner, DwgInsertTerminalReason::MalformedGroup); |
| 4264 | (void)reportDwgFrameTransitionFailure(DwgSourceFrameId{handle}); |
| 4265 | return DwgMappedEntityOutcome::Rejected; |
| 4266 | } |
| 4267 | const auto polylineIt = m_pendingPolylineStates.find(owner); |
| 4268 | if (polylineIt != m_pendingPolylineStates.end() && |
| 4269 | polylineIt->second.entity.seqEndH.ref != handle) { |
| 4270 | terminalizePendingPolylineState(owner, |
| 4271 | DwgInsertTerminalReason::MalformedGroup); |
| 4272 | (void)reportDwgFrameTransitionFailure(DwgSourceFrameId{handle}); |
| 4273 | return DwgMappedEntityOutcome::Rejected; |
| 4274 | } |
| 4275 | try { |
| 4276 | m_stagedSeqEnds.reserve(m_stagedSeqEnds.size() + 1u); |
| 4277 | } catch (...) { |
| 4278 | if (pendingIt != m_pendingInsertStates.end()) { |
| 4279 | terminalizeInsertGroup(owner, DwgInsertTerminalReason::MalformedGroup); |
| 4280 | } |
| 4281 | return DwgMappedEntityOutcome::Rejected; |
| 4282 | } |
| 4283 | |
| 4284 | StagedSeqEndState staged; |
| 4285 | staged.owner = owner; |
| 4286 | if (!stageActiveEntityFrame(staged.frame, publication)) { |
| 4287 | if (pendingIt != m_pendingInsertStates.end()) { |
| 4288 | terminalizeInsertGroup(owner, DwgInsertTerminalReason::MalformedGroup); |
| 4289 | } |
| 4290 | return DwgMappedEntityOutcome::Rejected; |
| 4291 | } |
| 4292 | try { |
| 4293 | const auto inserted = m_stagedSeqEnds.emplace(handle, std::move(staged)); |
| 4294 | if (!inserted.second) { |
| 4295 | (void)restoreStagedFrame(staged.frame); |
| 4296 | if (pendingIt != m_pendingInsertStates.end()) { |
| 4297 | terminalizeInsertGroup(owner, DwgInsertTerminalReason::MalformedGroup); |
| 4298 | } |
| 4299 | (void)reportDwgFrameTransitionFailure(DwgSourceFrameId{handle}); |
| 4300 | return DwgMappedEntityOutcome::Rejected; |
| 4301 | } |
| 4302 | } catch (...) { |
| 4303 | (void)restoreStagedFrame(staged.frame); |
| 4304 | if (pendingIt != m_pendingInsertStates.end()) { |
| 4305 | terminalizeInsertGroup(owner, DwgInsertTerminalReason::MalformedGroup); |
| 4306 | } |
| 4307 | return DwgMappedEntityOutcome::Rejected; |
| 4308 | } |
| 4309 | const DwgMappedEntityOutcome insertOutcome = |
| 4310 | tryCommitPendingInsert(owner, intfa); |
| 4311 | if (insertOutcome == DwgMappedEntityOutcome::Rejected) |
| 4312 | return insertOutcome; |
| 4313 | const DwgMappedEntityOutcome polylineOutcome = |
| 4314 | tryCommitPendingPolyline(owner, intfa); |
| 4315 | if (polylineOutcome == DwgMappedEntityOutcome::Rejected || |
| 4316 | polylineOutcome == DwgMappedEntityOutcome::CommittedCompound) { |
| 4317 | return polylineOutcome; |
| 4318 | } |
| 4319 | return insertOutcome; |
| 4320 | } |
| 4321 | |
| 4322 | dwgReader::DwgMappedEntityOutcome |
| 4323 | dwgReader::stagePendingPolyline(DRW_Polyline &&polyline, |
| 4324 | const DRW_DwgFramePublication &publication, |
| 4325 | DRW_Interface &intfa) { |
| 4326 | const std::uint32_t handle = polyline.handle; |
| 4327 | if (handle == DRW::NoHandle || !polyline.vertlist.empty() || |
| 4328 | m_invalidPolylineOwners.find(handle) != m_invalidPolylineOwners.end() || |
| 4329 | m_pendingPolylineStates.find(handle) != m_pendingPolylineStates.end()) { |
| 4330 | (void)reportDwgFrameTransitionFailure(DwgSourceFrameId{handle}); |
| 4331 | return DwgMappedEntityOutcome::Rejected; |
| 4332 | } |
| 4333 | |
| 4334 | const auto orphanIt = m_orphanPolylineVertexStates.find(handle); |
| 4335 | if (orphanIt != m_orphanPolylineVertexStates.end()) { |
| 4336 | const auto isExpectedExactlyOnce = [&polyline]( |
| 4337 | const StagedVertexState &vertex) { |
| 4338 | return std::count(polyline.hadlesList.cbegin(), |
| 4339 | polyline.hadlesList.cend(), vertex.entity.handle) == 1; |
| 4340 | }; |
| 4341 | const bool invalidOrphan = std::any_of( |
| 4342 | orphanIt->second.vertices.cbegin(), orphanIt->second.vertices.cend(), |
| 4343 | [&polyline, &isExpectedExactlyOnce](const StagedVertexState &vertex) { |
| 4344 | return !isExpectedExactlyOnce(vertex) || |
| 4345 | !polyline.isDwgVertexCompatible(vertex.entity); |
| 4346 | }); |
| 4347 | if (invalidOrphan) { |
| 4348 | terminalizeOrphanPolylineVertexOwner(handle); |
| 4349 | (void)reportDwgFrameTransitionFailure(DwgSourceFrameId{handle}); |
| 4350 | return DwgMappedEntityOutcome::Rejected; |
| 4351 | } |
| 4352 | } |
| 4353 | if (consumePolylineStageFailurePointForTest( |
| 4354 | DwgPolylineStageFailurePoint::BeforeParentStateReserve)) { |
| 4355 | terminalizeOrphanPolylineVertexOwner(handle); |
| 4356 | return DwgMappedEntityOutcome::Rejected; |
| 4357 | } |
| 4358 | try { |
| 4359 | m_pendingPolylineStates.reserve(m_pendingPolylineStates.size() + 1u); |
| 4360 | } catch (...) { |
| 4361 | return DwgMappedEntityOutcome::Rejected; |
| 4362 | } |
| 4363 | |
| 4364 | if (consumePolylineStageFailurePointForTest( |
| 4365 | DwgPolylineStageFailurePoint::BeforeParentStateInsert)) { |
| 4366 | terminalizeOrphanPolylineVertexOwner(handle); |
| 4367 | return DwgMappedEntityOutcome::Rejected; |
| 4368 | } |
| 4369 | auto inserted = m_pendingPolylineStates.end(); |
| 4370 | try { |
| 4371 | const auto result = m_pendingPolylineStates.try_emplace(handle); |
| 4372 | if (!result.second) { |
| 4373 | (void)reportDwgFrameTransitionFailure(DwgSourceFrameId{handle}); |
| 4374 | return DwgMappedEntityOutcome::Rejected; |
| 4375 | } |
| 4376 | inserted = result.first; |
| 4377 | } catch (...) { |
| 4378 | return DwgMappedEntityOutcome::Rejected; |
| 4379 | } |
| 4380 | |
| 4381 | PendingPolylineState &pending = inserted->second; |
| 4382 | try { |
| 4383 | pending.entity = std::move(polyline); |
| 4384 | } catch (...) { |
| 4385 | m_pendingPolylineStates.erase(inserted); |
| 4386 | terminalizeOrphanPolylineVertexOwner(handle); |
| 4387 | return DwgMappedEntityOutcome::Rejected; |
| 4388 | } |
| 4389 | const std::size_t expectedVertexCount = pending.entity.hadlesList.size(); |
| 4390 | const std::size_t adoptedVertexCount = |
| 4391 | orphanIt == m_orphanPolylineVertexStates.end() |
| 4392 | ? 0u |
| 4393 | : orphanIt->second.vertices.size(); |
| 4394 | if (expectedVertexCount != 0u || adoptedVertexCount != 0u) { |
| 4395 | try { |
| 4396 | pending.vertices.reserve( |
| 4397 | std::max(expectedVertexCount, adoptedVertexCount)); |
| 4398 | } catch (...) { |
| 4399 | m_pendingPolylineStates.erase(inserted); |
| 4400 | terminalizeOrphanPolylineVertexOwner(handle); |
| 4401 | return DwgMappedEntityOutcome::Rejected; |
| 4402 | } |
| 4403 | } |
| 4404 | if (consumePolylineStageFailurePointForTest( |
| 4405 | DwgPolylineStageFailurePoint::BeforeFrameStaging)) { |
| 4406 | m_pendingPolylineStates.erase(inserted); |
| 4407 | terminalizeOrphanPolylineVertexOwner(handle); |
| 4408 | return DwgMappedEntityOutcome::Rejected; |
| 4409 | } |
| 4410 | if (!stageActiveEntityFrame(pending.frame, publication)) { |
| 4411 | m_pendingPolylineStates.erase(inserted); |
| 4412 | terminalizeOrphanPolylineVertexOwner(handle); |
| 4413 | return DwgMappedEntityOutcome::Rejected; |
| 4414 | } |
| 4415 | if (orphanIt != m_orphanPolylineVertexStates.end()) { |
| 4416 | if (consumePolylineStageFailurePointForTest( |
| 4417 | DwgPolylineStageFailurePoint::BeforeOrphanAdoption)) { |
| 4418 | terminalizePendingPolylineState(handle, |
| 4419 | DwgInsertTerminalReason::MalformedGroup); |
| 4420 | return DwgMappedEntityOutcome::Rejected; |
| 4421 | } |
| 4422 | try { |
| 4423 | for (StagedVertexState &vertex : orphanIt->second.vertices) { |
| 4424 | if (vertex.entity.dwgSubtype() == DRW_Vertex::DwgSubtype::Vertex2D) { |
| 4425 | vertex.entity.basePoint.z = pending.entity.basePoint.z; |
| 4426 | } |
| 4427 | pending.vertices.push_back(std::move(vertex)); |
| 4428 | } |
| 4429 | } catch (...) { |
| 4430 | terminalizePendingPolylineState(handle, |
| 4431 | DwgInsertTerminalReason::MalformedGroup); |
| 4432 | return DwgMappedEntityOutcome::Rejected; |
| 4433 | } |
| 4434 | m_orphanPolylineVertexStates.erase(orphanIt); |
| 4435 | } |
| 4436 | return tryCommitPendingPolyline(handle, intfa); |
| 4437 | } |
| 4438 | |
| 4439 | dwgReader::DwgMappedEntityOutcome |
| 4440 | dwgReader::stageLegacyPolylineChain(DRW_Polyline &&polyline, |
| 4441 | const DRW_DwgFramePublication &publication, |
| 4442 | dwgBuffer *dbuf, DRW_Interface &intfa, |
| 4443 | DwgIntegrityAddressSpace offsetSpace) { |
| 4444 | if (dbuf == nullptr || version >= DRW::AC1018 || |
| 4445 | m_activeEntityFrameLease == nullptr || |
| 4446 | !m_activeEntityFrameLease->isDetached() || |
| 4447 | m_activeEntityFrameLease->object.handle != polyline.handle) { |
| 4448 | return DwgMappedEntityOutcome::Rejected; |
| 4449 | } |
| 4450 | |
| 4451 | struct ParsedVertex { |
| 4452 | objHandle object; |
| 4453 | DRW_Vertex entity; |
| 4454 | DRW_DwgFramePublication publication; |
| 4455 | }; |
| 4456 | |
| 4457 | const std::uint32_t parentHandle = polyline.handle; |
| 4458 | const std::uint32_t sequenceHandle = polyline.seqEndH.ref; |
| 4459 | const bool haveNextLinks = polyline.haveNextLinks != 0; |
| 4460 | const std::uint32_t savedNext = nextEntLink; |
| 4461 | const std::uint32_t savedPrev = prevEntLink; |
| 4462 | const bool savedNextImplicit = nextEntLinkImplicit; |
| 4463 | const auto restoreLinks = [this, savedNext, savedPrev, savedNextImplicit]() { |
| 4464 | nextEntLink = savedNext; |
| 4465 | prevEntLink = savedPrev; |
| 4466 | nextEntLinkImplicit = savedNextImplicit; |
| 4467 | }; |
| 4468 | |
| 4469 | std::vector<ParsedVertex> vertices; |
| 4470 | std::vector<std::uint32_t> discoveredHandles; |
| 4471 | std::unordered_set<std::uint32_t> visitedHandles; |
| 4472 | const auto quarantineDiscovered = [this, &discoveredHandles]() { |
| 4473 | for (const std::uint32_t handle : discoveredHandles) |
| 4474 | (void)quarantineMappedDwgSourceFrame(handle); |
| 4475 | }; |
| 4476 | const auto reject = [this, parentHandle, &restoreLinks, |
| 4477 | &quarantineDiscovered]() { |
| 4478 | restoreLinks(); |
| 4479 | terminalizePendingPolylineState(parentHandle, |
| 4480 | DwgInsertTerminalReason::MalformedGroup); |
| 4481 | quarantineDiscovered(); |
| 4482 | return DwgMappedEntityOutcome::Rejected; |
| 4483 | }; |
| 4484 | |
| 4485 | try { |
| 4486 | if (parentHandle == DRW::NoHandle || sequenceHandle == DRW::NoHandle) |
| 4487 | return reject(); |
| 4488 | discoveredHandles.push_back(sequenceHandle); |
| 4489 | if (polyline.lastEH != DRW::NoHandle && |
| 4490 | polyline.lastEH != polyline.firstEH) { |
| 4491 | discoveredHandles.push_back(polyline.lastEH); |
| 4492 | } |
| 4493 | |
| 4494 | std::uint32_t nextHandle = polyline.firstEH; |
| 4495 | bool reachedLast = |
| 4496 | nextHandle == DRW::NoHandle && polyline.lastEH == DRW::NoHandle; |
| 4497 | while (nextHandle != DRW::NoHandle) { |
| 4498 | if (!visitedHandles.insert(nextHandle).second) |
| 4499 | return reject(); |
| 4500 | discoveredHandles.push_back(nextHandle); |
| 4501 | |
| 4502 | const auto sourceIt = ObjectMap.find(nextHandle); |
| 4503 | if (sourceIt == ObjectMap.end()) |
| 4504 | return reject(); |
| 4505 | DwgSourceFrameLease borrowed; |
| 4506 | if (!borrowDwgSourceFrame(ObjectMap, sourceIt, borrowed) || |
| 4507 | borrowed.object.handle != nextHandle) { |
| 4508 | return reject(); |
| 4509 | } |
| 4510 | |
| 4511 | DwgObjectFrame frame; |
| 4512 | if (!frame.readAt(*dbuf, version, borrowed.object.loc)) { |
| 4513 | recordObjectFrameFailure(borrowed.object, offsetSpace); |
| 4514 | return reject(); |
| 4515 | } |
| 4516 | std::vector<std::uint8_t> &body = frame.body(); |
| 4517 | dwgBuffer buffer(body.data(), body.size(), &decoder); |
| 4518 | const std::int16_t objectType = buffer.getObjType(version); |
| 4519 | buffer.resetPosition(); |
| 4520 | if (!buffer.isGood() || (objectType != dwgType::VERTEX_2D && |
| 4521 | objectType != dwgType::VERTEX_3D && |
| 4522 | objectType != dwgType::VERTEX_MESH && |
| 4523 | objectType != dwgType::VERTEX_PFACE && |
| 4524 | objectType != dwgType::VERTEX_PFACE_FACE)) { |
| 4525 | return reject(); |
| 4526 | } |
| 4527 | |
| 4528 | DRW_Vertex vertex; |
| 4529 | if (!vertex.parseDwg(version, &buffer, frame.bodyBitSize(), |
| 4530 | polyline.basePoint.z) || |
| 4531 | !buffer.isGood() || vertex.handle != nextHandle) { |
| 4532 | if (vertex.handle != nextHandle) |
| 4533 | parsedEntityHandleMismatch = true; |
| 4534 | return reject(); |
| 4535 | } |
| 4536 | if (vertex.parentHandle != DRW::NoHandle && |
| 4537 | vertex.parentHandle != parentHandle) { |
| 4538 | parsedEntityOwnerMismatch = true; |
| 4539 | return reject(); |
| 4540 | } |
| 4541 | // The chain walker bypasses entryParse() so it can preserve the |
| 4542 | // parent walk's next-link state. Keep its EED reference |
| 4543 | // resolution equivalent to the mapped VERTEX path before staging. |
| 4544 | parseAttribs(&vertex); |
| 4545 | |
| 4546 | ParsedVertex parsed; |
| 4547 | parsed.object = borrowed.object; |
| 4548 | parsed.entity = std::move(vertex); |
| 4549 | parsed.publication = makeTypedEntityFramePublication( |
| 4550 | version, borrowed.object, objectType, parsed.entity); |
| 4551 | vertices.push_back(std::move(parsed)); |
| 4552 | polyline.hadlesList.push_back(nextHandle); |
| 4553 | |
| 4554 | if (nextHandle == polyline.lastEH) { |
| 4555 | reachedLast = true; |
| 4556 | break; |
| 4557 | } |
| 4558 | nextHandle = vertices.back().entity.nextEntLink; |
| 4559 | } |
| 4560 | if (!reachedLast) |
| 4561 | return reject(); |
| 4562 | |
| 4563 | const auto sequenceIt = ObjectMap.find(sequenceHandle); |
| 4564 | if (sequenceIt == ObjectMap.end()) |
| 4565 | return reject(); |
| 4566 | DwgSourceFrameLease borrowedSequence; |
| 4567 | if (!borrowDwgSourceFrame(ObjectMap, sequenceIt, borrowedSequence) || |
| 4568 | borrowedSequence.object.handle != sequenceHandle) { |
| 4569 | return reject(); |
| 4570 | } |
| 4571 | DwgObjectFrame sequenceFrame; |
| 4572 | if (!sequenceFrame.readAt(*dbuf, version, borrowedSequence.object.loc)) { |
| 4573 | recordObjectFrameFailure(borrowedSequence.object, offsetSpace); |
| 4574 | return reject(); |
| 4575 | } |
| 4576 | std::vector<std::uint8_t> &sequenceBody = sequenceFrame.body(); |
| 4577 | dwgBuffer sequenceBuffer(sequenceBody.data(), sequenceBody.size(), |
| 4578 | &decoder); |
| 4579 | if (sequenceBuffer.getObjType(version) != dwgType::SEQEND || |
| 4580 | !sequenceBuffer.isGood()) { |
| 4581 | return reject(); |
| 4582 | } |
| 4583 | sequenceBuffer.resetPosition(); |
| 4584 | DRW_SeqEnd sequenceEnd; |
| 4585 | if (!sequenceEnd.parseDwg(version, &sequenceBuffer, |
| 4586 | sequenceFrame.bodyBitSize()) || |
| 4587 | !sequenceBuffer.isGood() || sequenceEnd.handle != sequenceHandle) { |
| 4588 | if (sequenceEnd.handle != sequenceHandle) |
| 4589 | parsedEntityHandleMismatch = true; |
| 4590 | return reject(); |
| 4591 | } |
| 4592 | if (sequenceEnd.parentHandle != parentHandle) { |
| 4593 | parsedEntityOwnerMismatch = true; |
| 4594 | return reject(); |
| 4595 | } |
| 4596 | const DRW_DwgFramePublication sequencePublication = |
| 4597 | makeTypedEntityFramePublication(version, borrowedSequence.object, |
| 4598 | dwgType::SEQEND, sequenceEnd); |
| 4599 | |
| 4600 | if (stagePendingPolyline(std::move(polyline), publication, intfa) == |
| 4601 | DwgMappedEntityOutcome::Rejected) { |
| 4602 | return reject(); |
| 4603 | } |
| 4604 | |
| 4605 | const auto stageWithLease = [this](DwgFrameMapLease &lease, |
| 4606 | const auto &operation) { |
| 4607 | DwgFrameMapLease *const previousLease = m_activeEntityFrameLease; |
| 4608 | m_activeEntityFrameLease = &lease; |
| 4609 | DwgMappedEntityOutcome outcome = DwgMappedEntityOutcome::Rejected; |
| 4610 | try { |
| 4611 | outcome = operation(); |
| 4612 | } catch (...) { |
| 4613 | outcome = DwgMappedEntityOutcome::Rejected; |
| 4614 | } |
| 4615 | m_activeEntityFrameLease = previousLease; |
| 4616 | return outcome; |
| 4617 | }; |
| 4618 | const auto restoreUnstagedLease = [this](DwgFrameMapLease &lease) { |
| 4619 | if (lease.isDetached()) |
| 4620 | (void)restoreDwgSourceFrame(lease); |
| 4621 | }; |
| 4622 | |
| 4623 | for (ParsedVertex &vertex : vertices) { |
| 4624 | const auto sourceIt = ObjectMap.find(vertex.object.handle); |
| 4625 | if (sourceIt == ObjectMap.end() || |
| 4626 | !(sourceFrameId(sourceIt->second) == sourceFrameId(vertex.object))) { |
| 4627 | return reject(); |
| 4628 | } |
| 4629 | DwgFrameMapLease lease; |
| 4630 | if (!detachDwgSourceFrame(ObjectMap, sourceIt, lease)) |
| 4631 | return reject(); |
| 4632 | const DwgMappedEntityOutcome outcome = |
| 4633 | stageWithLease(lease, [this, &vertex, &intfa]() { |
| 4634 | return stagePendingPolylineVertex(std::move(vertex.entity), |
| 4635 | vertex.publication, intfa); |
| 4636 | }); |
| 4637 | if (outcome == DwgMappedEntityOutcome::Rejected) { |
| 4638 | restoreUnstagedLease(lease); |
| 4639 | return reject(); |
| 4640 | } |
| 4641 | } |
| 4642 | |
| 4643 | const auto stagedSequenceIt = ObjectMap.find(sequenceHandle); |
| 4644 | if (stagedSequenceIt == ObjectMap.end() || |
| 4645 | !(sourceFrameId(stagedSequenceIt->second) == |
| 4646 | sourceFrameId(borrowedSequence.object))) { |
| 4647 | return reject(); |
| 4648 | } |
| 4649 | DwgFrameMapLease sequenceLease; |
| 4650 | if (!detachDwgSourceFrame(ObjectMap, stagedSequenceIt, sequenceLease)) { |
| 4651 | return reject(); |
| 4652 | } |
| 4653 | const DwgMappedEntityOutcome outcome = |
| 4654 | stageWithLease(sequenceLease, [this, sequenceHandle, parentHandle, |
| 4655 | &sequencePublication, &intfa]() { |
| 4656 | return stagePendingSeqEnd(sequenceHandle, parentHandle, |
| 4657 | sequencePublication, intfa); |
| 4658 | }); |
| 4659 | if (outcome != DwgMappedEntityOutcome::CommittedCompound) { |
| 4660 | restoreUnstagedLease(sequenceLease); |
| 4661 | return reject(); |
| 4662 | } |
| 4663 | |
| 4664 | restoreLinks(); |
| 4665 | if (haveNextLinks && |
| 4666 | sequenceHandle != std::numeric_limits<std::uint32_t>::max()) { |
| 4667 | nextEntLink = sequenceHandle + 1u; |
| 4668 | } |
| 4669 | return outcome; |
| 4670 | } catch (...) { |
| 4671 | return reject(); |
| 4672 | } |
| 4673 | } |
| 4674 | |
| 4675 | dwgReader::DwgMappedEntityOutcome dwgReader::stagePendingPolylineVertex( |
| 4676 | DRW_Vertex &&vertex, const DRW_DwgFramePublication &publication, |
| 4677 | DRW_Interface &intfa) { |
| 4678 | const std::uint32_t handle = vertex.handle; |
| 4679 | const std::uint32_t owner = vertex.parentHandle; |
| 4680 | if (handle == DRW::NoHandle || owner == DRW::NoHandle || |
| 4681 | m_invalidPolylineOwners.find(owner) != m_invalidPolylineOwners.end()) { |
| 4682 | (void)reportDwgFrameTransitionFailure(DwgSourceFrameId{handle}); |
| 4683 | return DwgMappedEntityOutcome::Rejected; |
| 4684 | } |
| 4685 | |
| 4686 | const auto pendingIt = m_pendingPolylineStates.find(owner); |
| 4687 | if (pendingIt != m_pendingPolylineStates.end()) { |
| 4688 | std::vector<std::uint32_t> conflictingOwners; |
| 4689 | try { |
| 4690 | for (const auto &candidate : m_pendingPolylineStates) { |
| 4691 | if (candidate.first != owner && |
| 4692 | std::find(candidate.second.entity.hadlesList.cbegin(), |
| 4693 | candidate.second.entity.hadlesList.cend(), |
| 4694 | handle) != candidate.second.entity.hadlesList.cend()) { |
| 4695 | conflictingOwners.push_back(candidate.first); |
| 4696 | } |
| 4697 | } |
| 4698 | } catch (...) { |
| 4699 | terminalizePendingPolylineState(owner, |
| 4700 | DwgInsertTerminalReason::MalformedGroup); |
| 4701 | return DwgMappedEntityOutcome::Rejected; |
| 4702 | } |
| 4703 | if (!conflictingOwners.empty()) { |
| 4704 | terminalizePendingPolylineState(owner, |
| 4705 | DwgInsertTerminalReason::MalformedGroup); |
| 4706 | for (const std::uint32_t conflictingOwner : conflictingOwners) { |
| 4707 | terminalizePendingPolylineState( |
| 4708 | conflictingOwner, DwgInsertTerminalReason::MalformedGroup); |
| 4709 | } |
| 4710 | parsedEntityOwnerMismatch = true; |
| 4711 | (void)reportDwgFrameTransitionFailure(DwgSourceFrameId{handle}); |
| 4712 | return DwgMappedEntityOutcome::Rejected; |
| 4713 | } |
| 4714 | |
| 4715 | PendingPolylineState &pending = pendingIt->second; |
| 4716 | const bool expected = std::find(pending.entity.hadlesList.cbegin(), |
| 4717 | pending.entity.hadlesList.cend(), |
| 4718 | handle) != pending.entity.hadlesList.cend(); |
| 4719 | const bool duplicate = |
| 4720 | std::any_of(pending.vertices.cbegin(), pending.vertices.cend(), |
| 4721 | [handle](const StagedVertexState ¤t) { |
| 4722 | return current.entity.handle == handle; |
| 4723 | }); |
| 4724 | if (!expected || duplicate || |
| 4725 | !pending.entity.isDwgVertexCompatible(vertex)) { |
| 4726 | terminalizePendingPolylineState(owner, |
| 4727 | DwgInsertTerminalReason::MalformedGroup); |
| 4728 | (void)reportDwgFrameTransitionFailure(DwgSourceFrameId{handle}); |
| 4729 | return DwgMappedEntityOutcome::Rejected; |
| 4730 | } |
| 4731 | try { |
| 4732 | pending.vertices.reserve(pending.vertices.size() + 1u); |
| 4733 | } catch (...) { |
| 4734 | terminalizePendingPolylineState(owner, |
| 4735 | DwgInsertTerminalReason::MalformedGroup); |
| 4736 | return DwgMappedEntityOutcome::Rejected; |
| 4737 | } |
| 4738 | StagedVertexState staged; |
| 4739 | staged.entity = std::move(vertex); |
| 4740 | if (staged.entity.dwgSubtype() == DRW_Vertex::DwgSubtype::Vertex2D) { |
| 4741 | staged.entity.basePoint.z = pending.entity.basePoint.z; |
| 4742 | } |
| 4743 | if (!stageActiveEntityFrame(staged.frame, publication)) { |
| 4744 | terminalizePendingPolylineState(owner, |
| 4745 | DwgInsertTerminalReason::MalformedGroup); |
| 4746 | return DwgMappedEntityOutcome::Rejected; |
| 4747 | } |
| 4748 | pending.vertices.push_back(std::move(staged)); |
| 4749 | return tryCommitPendingPolyline(owner, intfa); |
| 4750 | } |
| 4751 | |
| 4752 | const auto declaredParent = std::find_if( |
| 4753 | m_pendingPolylineStates.cbegin(), m_pendingPolylineStates.cend(), |
| 4754 | [handle](const auto &item) { |
| 4755 | return std::find(item.second.entity.hadlesList.cbegin(), |
| 4756 | item.second.entity.hadlesList.cend(), |
| 4757 | handle) != item.second.entity.hadlesList.cend(); |
| 4758 | }); |
| 4759 | if (declaredParent != m_pendingPolylineStates.cend()) { |
| 4760 | terminalizePendingPolylineState(declaredParent->first, |
| 4761 | DwgInsertTerminalReason::MalformedGroup); |
| 4762 | parsedEntityOwnerMismatch = true; |
| 4763 | (void)reportDwgFrameTransitionFailure(DwgSourceFrameId{handle}); |
| 4764 | return DwgMappedEntityOutcome::Rejected; |
| 4765 | } |
| 4766 | |
| 4767 | auto orphanIt = m_orphanPolylineVertexStates.find(owner); |
| 4768 | if (orphanIt == m_orphanPolylineVertexStates.end()) { |
| 4769 | try { |
| 4770 | m_orphanPolylineVertexStates.reserve(m_orphanPolylineVertexStates.size() + |
| 4771 | 1u); |
| 4772 | orphanIt = m_orphanPolylineVertexStates.try_emplace(owner).first; |
| 4773 | } catch (...) { |
| 4774 | return DwgMappedEntityOutcome::Rejected; |
| 4775 | } |
| 4776 | } |
| 4777 | const bool duplicate = std::any_of( |
| 4778 | orphanIt->second.vertices.cbegin(), orphanIt->second.vertices.cend(), |
| 4779 | [handle](const StagedVertexState ¤t) { |
| 4780 | return current.entity.handle == handle; |
| 4781 | }); |
| 4782 | if (duplicate) { |
| 4783 | terminalizeOrphanPolylineVertexOwner(owner); |
| 4784 | (void)reportDwgFrameTransitionFailure(DwgSourceFrameId{handle}); |
| 4785 | return DwgMappedEntityOutcome::Rejected; |
| 4786 | } |
| 4787 | try { |
| 4788 | orphanIt->second.vertices.reserve(orphanIt->second.vertices.size() + 1u); |
| 4789 | } catch (...) { |
| 4790 | terminalizeOrphanPolylineVertexOwner(owner); |
| 4791 | return DwgMappedEntityOutcome::Rejected; |
| 4792 | } |
| 4793 | StagedVertexState staged; |
| 4794 | staged.entity = std::move(vertex); |
| 4795 | if (!stageActiveEntityFrame(staged.frame, publication)) { |
| 4796 | terminalizeOrphanPolylineVertexOwner(owner); |
| 4797 | return DwgMappedEntityOutcome::Rejected; |
| 4798 | } |
| 4799 | orphanIt->second.vertices.push_back(std::move(staged)); |
| 4800 | return DwgMappedEntityOutcome::StagedCompound; |
| 4801 | } |
| 4802 | |
| 4803 | bool dwgReader::preparePolylineCommit(std::uint32_t handle, |
| 4804 | PendingPolylineState &pending, |
| 4805 | DwgPreparedPolylineCommit &prepared) { |
| 4806 | if (!validateStagedFrame(pending.frame) || pending.entity.handle != handle || |
| 4807 | !prepared.orderedVertices.empty() || |
| 4808 | !prepared.claimedChildHandles.empty() || |
| 4809 | prepared.sequenceEnd != nullptr || prepared.claimedSeqEnd || |
| 4810 | pending.vertices.size() != pending.entity.hadlesList.size()) { |
| 4811 | return false; |
| 4812 | } |
| 4813 | const std::uint32_t sequenceHandle = pending.entity.seqEndH.ref; |
| 4814 | const auto sequenceIt = m_stagedSeqEnds.find(sequenceHandle); |
| 4815 | if (sequenceHandle == DRW::NoHandle || sequenceIt == m_stagedSeqEnds.end() || |
| 4816 | sequenceIt->second.owner != handle || |
| 4817 | !validateStagedFrame(sequenceIt->second.frame)) { |
| 4818 | return false; |
| 4819 | } |
| 4820 | prepared.sequenceEnd = &sequenceIt->second; |
| 4821 | if (consumePolylinePrepareFailurePointForTest( |
| 4822 | DwgPolylinePrepareFailurePoint::BeforeReservation)) { |
| 4823 | return false; |
| 4824 | } |
| 4825 | try { |
| 4826 | prepared.orderedVertices.reserve(pending.vertices.size()); |
| 4827 | prepared.claimedChildHandles.reserve(pending.vertices.size()); |
| 4828 | } catch (...) { |
| 4829 | return false; |
| 4830 | } |
| 4831 | for (const std::uint32_t expected : pending.entity.hadlesList) { |
| 4832 | if (expected == DRW::NoHandle) |
| 4833 | return false; |
| 4834 | const auto vertexIt = |
| 4835 | std::find_if(pending.vertices.begin(), pending.vertices.end(), |
| 4836 | [expected, handle](const StagedVertexState &vertex) { |
| 4837 | return vertex.entity.handle == expected && |
| 4838 | vertex.entity.parentHandle == handle; |
| 4839 | }); |
| 4840 | if (vertexIt == pending.vertices.end() || |
| 4841 | !validateStagedFrame(vertexIt->frame) || |
| 4842 | std::find(prepared.orderedVertices.cbegin(), |
| 4843 | prepared.orderedVertices.cend(), |
| 4844 | &*vertexIt) != prepared.orderedVertices.cend()) { |
| 4845 | return false; |
| 4846 | } |
| 4847 | prepared.orderedVertices.push_back(&*vertexIt); |
| 4848 | } |
| 4849 | const auto rollbackMarkers = [this, &prepared, sequenceHandle]() { |
| 4850 | for (const std::uint32_t child : prepared.claimedChildHandles) |
| 4851 | m_consumedCompoundChildHandles.erase(child); |
| 4852 | prepared.claimedChildHandles.clear(); |
| 4853 | if (prepared.claimedSeqEnd) { |
| 4854 | m_consumedSeqEndHandles.erase(sequenceHandle); |
| 4855 | prepared.claimedSeqEnd = false; |
| 4856 | } |
| 4857 | }; |
| 4858 | try { |
| 4859 | for (const StagedVertexState *vertex : prepared.orderedVertices) { |
| 4860 | if (consumePolylinePrepareFailurePointForTest( |
| 4861 | DwgPolylinePrepareFailurePoint::BeforeChildMarker)) { |
| 4862 | rollbackMarkers(); |
| 4863 | return false; |
| 4864 | } |
| 4865 | if (!m_consumedCompoundChildHandles.insert(vertex->entity.handle) |
| 4866 | .second) { |
| 4867 | rollbackMarkers(); |
| 4868 | return false; |
| 4869 | } |
| 4870 | prepared.claimedChildHandles.push_back(vertex->entity.handle); |
| 4871 | } |
| 4872 | if (consumePolylinePrepareFailurePointForTest( |
| 4873 | DwgPolylinePrepareFailurePoint::BeforeSeqEndMarker)) { |
| 4874 | rollbackMarkers(); |
| 4875 | return false; |
| 4876 | } |
| 4877 | if (!m_consumedSeqEndHandles.insert(sequenceHandle).second) { |
| 4878 | rollbackMarkers(); |
| 4879 | return false; |
| 4880 | } |
| 4881 | prepared.claimedSeqEnd = true; |
| 4882 | } catch (...) { |
| 4883 | rollbackMarkers(); |
| 4884 | return false; |
| 4885 | } |
| 4886 | return true; |
| 4887 | } |
| 4888 | |
| 4889 | dwgReader::DwgMappedEntityOutcome |
| 4890 | dwgReader::journalPreparedPolylineCommit(std::uint32_t handle, |
| 4891 | PendingPolylineState &pending, |
| 4892 | DwgPreparedPolylineCommit &prepared) { |
| 4893 | if (m_activeBlockTransaction == nullptr || m_activeBlockOutput == nullptr || |
| 4894 | &m_activeBlockTransaction->output() != m_activeBlockOutput || |
| 4895 | pending.frame.lease == std::nullopt) { |
| 4896 | return DwgMappedEntityOutcome::Rejected; |
| 4897 | } |
| 4898 | |
| 4899 | DwgBlockScopeTransaction &transaction = *m_activeBlockTransaction; |
| 4900 | DwgEntityOutput &output = *m_activeBlockOutput; |
| 4901 | const std::uint32_t sequenceHandle = pending.entity.seqEndH.ref; |
| 4902 | const auto releasePreparedMarkers = [this, &prepared, sequenceHandle]() { |
| 4903 | for (const std::uint32_t child : prepared.claimedChildHandles) |
| 4904 | m_consumedCompoundChildHandles.erase(child); |
| 4905 | prepared.claimedChildHandles.clear(); |
| 4906 | if (prepared.claimedSeqEnd) { |
| 4907 | m_consumedSeqEndHandles.erase(sequenceHandle); |
| 4908 | prepared.claimedSeqEnd = false; |
| 4909 | } |
| 4910 | }; |
| 4911 | std::size_t sourceCount = 0; |
| 4912 | std::uint64_t bodyByteCount = 0; |
| 4913 | const auto chargeFrame = [this, &sourceCount, |
| 4914 | &bodyByteCount](const DwgStagedFrame &frame) { |
| 4915 | if (!validateStagedFrame(frame) || !frame.lease.has_value() || |
| 4916 | sourceCount == std::numeric_limits<std::size_t>::max()) { |
| 4917 | return false; |
| 4918 | } |
| 4919 | const std::uint64_t frameBytes = |
| 4920 | frame.lease->classification.has_value() |
| 4921 | ? frame.lease->classification->bodyByteSize |
| 4922 | : frame.lease->bodyByteSize; |
| 4923 | std::uint64_t total = 0; |
| 4924 | if (!dwgSafety::add(bodyByteCount, frameBytes, total)) |
| 4925 | return false; |
| 4926 | bodyByteCount = total; |
| 4927 | ++sourceCount; |
| 4928 | return true; |
| 4929 | }; |
| 4930 | if (!chargeFrame(pending.frame)) { |
| 4931 | releasePreparedMarkers(); |
| 4932 | return DwgMappedEntityOutcome::Rejected; |
| 4933 | } |
| 4934 | for (const StagedVertexState *vertex : prepared.orderedVertices) { |
| 4935 | if (vertex == nullptr || !chargeFrame(vertex->frame)) { |
| 4936 | releasePreparedMarkers(); |
| 4937 | return DwgMappedEntityOutcome::Rejected; |
| 4938 | } |
| 4939 | } |
| 4940 | if (prepared.sequenceEnd == nullptr || |
| 4941 | !chargeFrame(prepared.sequenceEnd->frame) || |
| 4942 | !transaction.reserveAdmission(sourceCount, sourceCount + 1u, |
| 4943 | bodyByteCount)) { |
| 4944 | releasePreparedMarkers(); |
| 4945 | return DwgMappedEntityOutcome::Rejected; |
| 4946 | } |
| 4947 | struct TransferredFrame { |
| 4948 | DwgStagedFrame *frame{nullptr}; |
| 4949 | DwgSourceFrameId source; |
| 4950 | }; |
| 4951 | std::vector<TransferredFrame> transferred; |
| 4952 | try { |
| 4953 | transferred.reserve(sourceCount); |
| 4954 | } catch (...) { |
| 4955 | releasePreparedMarkers(); |
| 4956 | return DwgMappedEntityOutcome::Rejected; |
| 4957 | } |
| 4958 | const std::size_t eventStart = transaction.output().size(); |
| 4959 | const auto rollback = [&]() { |
| 4960 | transaction.output().truncate(eventStart); |
| 4961 | bool restored = true; |
| 4962 | for (auto it = transferred.rbegin(); it != transferred.rend(); ++it) { |
| 4963 | DwgFrameMapLease restoredLease; |
| 4964 | if (it->frame == nullptr || |
| 4965 | !transaction.releaseLast(it->source, restoredLease)) { |
| 4966 | restored = false; |
| 4967 | continue; |
| 4968 | } |
| 4969 | it->frame->lease.emplace(std::move(restoredLease)); |
| 4970 | } |
| 4971 | releasePreparedMarkers(); |
| 4972 | return restored; |
| 4973 | }; |
| 4974 | const auto queueFrame = [this, &transaction, &output, |
| 4975 | &transferred](DwgStagedFrame &frame) { |
| 4976 | if (!validateStagedFrame(frame) || !frame.lease.has_value()) |
| 4977 | return false; |
| 4978 | const DwgSourceFrameId source = frame.lease->source; |
| 4979 | if (frame.publication.has_value()) { |
| 4980 | if (!output.appendFramePublication(*this, *frame.publication)) |
| 4981 | return false; |
| 4982 | } else { |
| 4983 | auto sourceScope = output.bindSource(source); |
| 4984 | if (!output.appendFrameCompletion()) |
| 4985 | return false; |
| 4986 | } |
| 4987 | if (!transaction.adopt(*frame.lease)) |
| 4988 | return false; |
| 4989 | transferred.push_back({&frame, source}); |
| 4990 | return true; |
| 4991 | }; |
| 4992 | |
| 4993 | DRW_Polyline delivery; |
| 4994 | { |
| 4995 | try { |
| 4996 | delivery = pending.entity; |
| 4997 | for (const StagedVertexState *vertex : prepared.orderedVertices) |
| 4998 | delivery.addVertex(vertex->entity); |
| 4999 | } catch (...) { |
| 5000 | releasePreparedMarkers(); |
| 5001 | return DwgMappedEntityOutcome::Rejected; |
| 5002 | } |
| 5003 | } |
| 5004 | bool committed = false; |
| 5005 | try { |
| 5006 | const DwgSourceFrameId parentSource = pending.frame.lease->source; |
| 5007 | { |
| 5008 | auto sourceScope = output.bindSource(parentSource); |
| 5009 | output.appendValue(delivery, &DRW_Interface::addPolyline); |
| 5010 | } |
| 5011 | committed = queueFrame(pending.frame); |
| 5012 | for (StagedVertexState *vertex : prepared.orderedVertices) |
| 5013 | committed = committed && queueFrame(vertex->frame); |
| 5014 | committed = committed && queueFrame(prepared.sequenceEnd->frame); |
| 5015 | } catch (...) { |
| 5016 | committed = false; |
| 5017 | } |
| 5018 | if (!committed) { |
| 5019 | (void)rollback(); |
| 5020 | return DwgMappedEntityOutcome::Rejected; |
| 5021 | } |
| 5022 | |
| 5023 | for (const TransferredFrame &frame : transferred) { |
| 5024 | frame.frame->lease.reset(); |
| 5025 | frame.frame->publication.reset(); |
| 5026 | } |
| 5027 | m_stagedSeqEnds.erase(sequenceHandle); |
| 5028 | m_pendingPolylineStates.erase(handle); |
| 5029 | return DwgMappedEntityOutcome::CommittedCompound; |
| 5030 | } |
| 5031 | |
| 5032 | dwgReader::DwgMappedEntityOutcome dwgReader::deliverPreparedPolylineCommit( |
| 5033 | std::uint32_t handle, PendingPolylineState &pending, |
| 5034 | DwgPreparedPolylineCommit &prepared, DRW_Interface &intfa) { |
| 5035 | if (m_activeBlockTransaction != nullptr || m_activeBlockOutput != nullptr) |
| 5036 | return journalPreparedPolylineCommit(handle, pending, prepared); |
| 5037 | |
| 5038 | const std::uint32_t sequenceHandle = pending.entity.seqEndH.ref; |
| 5039 | const auto publishFrame = [this, &intfa](DwgStagedFrame &frame) { |
| 5040 | if (!validateStagedFrame(frame)) |
| 5041 | return false; |
| 5042 | if (frame.lease->hasCoverage && |
| 5043 | (!frame.publication.has_value() || |
| 5044 | !publishDwgFramePublication(intfa, *frame.publication))) { |
| 5045 | return false; |
| 5046 | } |
| 5047 | if (!discardDetachedDwgSourceFrame(*frame.lease)) |
| 5048 | return false; |
| 5049 | frame.lease.reset(); |
| 5050 | frame.publication.reset(); |
| 5051 | return true; |
| 5052 | }; |
| 5053 | |
| 5054 | for (const StagedVertexState *vertex : prepared.orderedVertices) |
| 5055 | pending.entity.addVertex(vertex->entity); |
| 5056 | try { |
| 5057 | intfa.addPolyline(pending.entity); |
| 5058 | } catch (...) { |
| 5059 | terminalizePendingPolylineState(handle, |
| 5060 | DwgInsertTerminalReason::CallbackException); |
| 5061 | return DwgMappedEntityOutcome::Rejected; |
| 5062 | } |
| 5063 | if (!publishFrame(pending.frame)) { |
| 5064 | terminalizePendingPolylineState(handle, |
| 5065 | DwgInsertTerminalReason::ReceiptFailure); |
| 5066 | return DwgMappedEntityOutcome::Rejected; |
| 5067 | } |
| 5068 | for (StagedVertexState *vertex : prepared.orderedVertices) { |
| 5069 | if (!publishFrame(vertex->frame)) { |
| 5070 | terminalizePendingPolylineState(handle, |
| 5071 | DwgInsertTerminalReason::ReceiptFailure); |
| 5072 | return DwgMappedEntityOutcome::Rejected; |
| 5073 | } |
| 5074 | } |
| 5075 | if (!publishFrame(prepared.sequenceEnd->frame)) { |
| 5076 | terminalizePendingPolylineState(handle, |
| 5077 | DwgInsertTerminalReason::ReceiptFailure); |
| 5078 | return DwgMappedEntityOutcome::Rejected; |
| 5079 | } |
| 5080 | m_stagedSeqEnds.erase(sequenceHandle); |
| 5081 | m_pendingPolylineStates.erase(handle); |
| 5082 | return DwgMappedEntityOutcome::CommittedCompound; |
| 5083 | } |
| 5084 | |
| 5085 | dwgReader::DwgMappedEntityOutcome |
| 5086 | dwgReader::tryCommitPendingPolyline(std::uint32_t handle, |
| 5087 | DRW_Interface &intfa) { |
| 5088 | const auto pendingIt = m_pendingPolylineStates.find(handle); |
| 5089 | if (pendingIt == m_pendingPolylineStates.end()) |
| 5090 | return DwgMappedEntityOutcome::StagedCompound; |
| 5091 | PendingPolylineState &pending = pendingIt->second; |
| 5092 | if (!validateStagedFrame(pending.frame) || pending.entity.handle != handle) { |
| 5093 | terminalizePendingPolylineState(handle, |
| 5094 | DwgInsertTerminalReason::MalformedGroup); |
| 5095 | return DwgMappedEntityOutcome::Rejected; |
| 5096 | } |
| 5097 | if (pending.vertices.size() != pending.entity.hadlesList.size() || |
| 5098 | pending.entity.seqEndH.ref == DRW::NoHandle || |
| 5099 | m_stagedSeqEnds.find(pending.entity.seqEndH.ref) == |
| 5100 | m_stagedSeqEnds.end()) { |
| 5101 | return DwgMappedEntityOutcome::StagedCompound; |
| 5102 | } |
| 5103 | DwgPreparedPolylineCommit prepared; |
| 5104 | if (!preparePolylineCommit(handle, pending, prepared)) { |
| 5105 | terminalizePendingPolylineState(handle, |
| 5106 | DwgInsertTerminalReason::MalformedGroup); |
| 5107 | return DwgMappedEntityOutcome::Rejected; |
| 5108 | } |
| 5109 | return deliverPreparedPolylineCommit(handle, pending, prepared, intfa); |
| 5110 | } |
| 5111 | |
| 5112 | bool dwgReader::prepareInsertCommit(std::uint32_t handle, |
| 5113 | PendingInsertState &pending, |
| 5114 | DwgPreparedInsertCommit &prepared) { |
| 5115 | if (!validateStagedFrame(pending.frame) || pending.entity.handle != handle || |
| 5116 | pending.entity.attlist.size() != pending.attributes.size() || |
| 5117 | !prepared.orderedAttributes.empty() || |
| 5118 | !prepared.orderedEntities.empty() || |
| 5119 | !prepared.claimedChildHandles.empty() || |
| 5120 | prepared.sequenceEnd != nullptr || prepared.claimedSeqEnd) { |
| 5121 | return false; |
| 5122 | } |
| 5123 | |
| 5124 | const std::uint32_t sequenceHandle = pending.entity.seqendH.ref; |
| 5125 | if (sequenceHandle != DRW::NoHandle) { |
| 5126 | const auto sequenceIt = m_stagedSeqEnds.find(sequenceHandle); |
| 5127 | if (sequenceIt == m_stagedSeqEnds.end() || |
| 5128 | sequenceIt->second.owner != handle || |
| 5129 | !validateStagedFrame(sequenceIt->second.frame)) { |
| 5130 | return false; |
| 5131 | } |
| 5132 | prepared.sequenceEnd = &sequenceIt->second; |
| 5133 | } |
| 5134 | |
| 5135 | try { |
| 5136 | prepared.orderedAttributes.reserve(pending.attributes.size()); |
| 5137 | prepared.orderedEntities.reserve(pending.attributes.size()); |
| 5138 | prepared.claimedChildHandles.reserve(pending.attributes.size()); |
| 5139 | } catch (...) { |
| 5140 | return false; |
| 5141 | } |
| 5142 | |
| 5143 | const auto appendAttribute = [&pending, this, |
| 5144 | &prepared](StagedAttribState &attribute) { |
| 5145 | if (attribute.entity == nullptr || |
| 5146 | !isExpectedAttribute(pending.entity, *attribute.entity, version) || |
| 5147 | !validateStagedFrame(attribute.frame)) { |
| 5148 | return false; |
| 5149 | } |
| 5150 | prepared.orderedAttributes.push_back(&attribute); |
| 5151 | prepared.orderedEntities.push_back(attribute.entity); |
| 5152 | return true; |
| 5153 | }; |
| 5154 | try { |
| 5155 | if (version < DRW::AC1018 && pending.entity.attribHandles.size() == 2u) { |
| 5156 | for (StagedAttribState &attribute : pending.attributes) { |
| 5157 | if (!appendAttribute(attribute)) |
| 5158 | return false; |
| 5159 | } |
| 5160 | } else { |
| 5161 | for (const dwgHandle &expected : pending.entity.attribHandles) { |
| 5162 | if (expected.ref == DRW::NoHandle) |
| 5163 | return false; |
| 5164 | const auto attributeIt = |
| 5165 | std::find_if(pending.attributes.begin(), pending.attributes.end(), |
| 5166 | [&expected](const StagedAttribState &attribute) { |
| 5167 | return attribute.entity != nullptr && |
| 5168 | attribute.entity->handle == expected.ref; |
| 5169 | }); |
| 5170 | if (attributeIt == pending.attributes.end() || |
| 5171 | !appendAttribute(*attributeIt)) { |
| 5172 | return false; |
| 5173 | } |
| 5174 | } |
| 5175 | } |
| 5176 | } catch (...) { |
| 5177 | return false; |
| 5178 | } |
| 5179 | |
| 5180 | const auto rollbackMarkers = [this, &prepared, sequenceHandle]() { |
| 5181 | for (const std::uint32_t childHandle : prepared.claimedChildHandles) |
| 5182 | m_consumedCompoundChildHandles.erase(childHandle); |
| 5183 | prepared.claimedChildHandles.clear(); |
| 5184 | if (prepared.claimedSeqEnd) { |
| 5185 | m_consumedSeqEndHandles.erase(sequenceHandle); |
| 5186 | prepared.claimedSeqEnd = false; |
| 5187 | } |
| 5188 | }; |
| 5189 | try { |
| 5190 | for (const StagedAttribState *attribute : prepared.orderedAttributes) { |
| 5191 | const auto inserted = |
| 5192 | m_consumedCompoundChildHandles.insert(attribute->entity->handle); |
| 5193 | if (!inserted.second) { |
| 5194 | rollbackMarkers(); |
| 5195 | return false; |
| 5196 | } |
| 5197 | prepared.claimedChildHandles.push_back(attribute->entity->handle); |
| 5198 | } |
| 5199 | if (prepared.sequenceEnd != nullptr) { |
| 5200 | const auto inserted = m_consumedSeqEndHandles.insert(sequenceHandle); |
| 5201 | if (!inserted.second) { |
| 5202 | rollbackMarkers(); |
| 5203 | return false; |
| 5204 | } |
| 5205 | prepared.claimedSeqEnd = true; |
| 5206 | } |
| 5207 | } catch (...) { |
| 5208 | rollbackMarkers(); |
| 5209 | return false; |
| 5210 | } |
| 5211 | return true; |
| 5212 | } |
| 5213 | |
| 5214 | dwgReader::DwgMappedEntityOutcome |
| 5215 | dwgReader::journalPreparedInsertCommit(std::uint32_t handle, |
| 5216 | PendingInsertState &pending, |
| 5217 | DwgPreparedInsertCommit &prepared) { |
| 5218 | if (m_activeBlockTransaction == nullptr || m_activeBlockOutput == nullptr || |
| 5219 | &m_activeBlockTransaction->output() != m_activeBlockOutput || |
| 5220 | pending.frame.lease == std::nullopt) { |
| 5221 | return DwgMappedEntityOutcome::Rejected; |
| 5222 | } |
| 5223 | |
| 5224 | DwgBlockScopeTransaction &transaction = *m_activeBlockTransaction; |
| 5225 | DwgEntityOutput &output = *m_activeBlockOutput; |
| 5226 | const std::uint32_t sequenceHandle = pending.entity.seqendH.ref; |
| 5227 | const auto releasePreparedMarkers = [this, &prepared, sequenceHandle]() { |
| 5228 | for (const std::uint32_t child : prepared.claimedChildHandles) |
| 5229 | m_consumedCompoundChildHandles.erase(child); |
| 5230 | prepared.claimedChildHandles.clear(); |
| 5231 | if (prepared.claimedSeqEnd) { |
| 5232 | m_consumedSeqEndHandles.erase(sequenceHandle); |
| 5233 | prepared.claimedSeqEnd = false; |
| 5234 | } |
| 5235 | }; |
| 5236 | std::size_t sourceCount = 0; |
| 5237 | std::uint64_t bodyByteCount = 0; |
| 5238 | const auto chargeFrame = [this, &sourceCount, |
| 5239 | &bodyByteCount](const DwgStagedFrame &frame) { |
| 5240 | if (!validateStagedFrame(frame) || !frame.lease.has_value() || |
| 5241 | sourceCount == std::numeric_limits<std::size_t>::max()) { |
| 5242 | return false; |
| 5243 | } |
| 5244 | const std::uint64_t frameBytes = |
| 5245 | frame.lease->classification.has_value() |
| 5246 | ? frame.lease->classification->bodyByteSize |
| 5247 | : frame.lease->bodyByteSize; |
| 5248 | std::uint64_t total = 0; |
| 5249 | if (!dwgSafety::add(bodyByteCount, frameBytes, total)) |
| 5250 | return false; |
| 5251 | bodyByteCount = total; |
| 5252 | ++sourceCount; |
| 5253 | return true; |
| 5254 | }; |
| 5255 | if (!chargeFrame(pending.frame)) { |
| 5256 | releasePreparedMarkers(); |
| 5257 | return DwgMappedEntityOutcome::Rejected; |
| 5258 | } |
| 5259 | for (const StagedAttribState *attribute : prepared.orderedAttributes) { |
| 5260 | if (attribute == nullptr || !chargeFrame(attribute->frame)) { |
| 5261 | releasePreparedMarkers(); |
| 5262 | return DwgMappedEntityOutcome::Rejected; |
| 5263 | } |
| 5264 | } |
| 5265 | if ((prepared.sequenceEnd != nullptr && |
| 5266 | !chargeFrame(prepared.sequenceEnd->frame)) || |
| 5267 | !transaction.reserveAdmission(sourceCount, sourceCount + 1u, |
| 5268 | bodyByteCount)) { |
| 5269 | releasePreparedMarkers(); |
| 5270 | return DwgMappedEntityOutcome::Rejected; |
| 5271 | } |
| 5272 | struct TransferredFrame { |
| 5273 | DwgStagedFrame *frame{nullptr}; |
| 5274 | DwgSourceFrameId source; |
| 5275 | }; |
| 5276 | std::vector<TransferredFrame> transferred; |
| 5277 | try { |
| 5278 | transferred.reserve(sourceCount); |
| 5279 | } catch (...) { |
| 5280 | releasePreparedMarkers(); |
| 5281 | return DwgMappedEntityOutcome::Rejected; |
| 5282 | } |
| 5283 | const std::size_t eventStart = transaction.output().size(); |
| 5284 | const auto rollback = [&]() { |
| 5285 | transaction.output().truncate(eventStart); |
| 5286 | bool restored = true; |
| 5287 | for (auto it = transferred.rbegin(); it != transferred.rend(); ++it) { |
| 5288 | DwgFrameMapLease restoredLease; |
| 5289 | if (it->frame == nullptr || |
| 5290 | !transaction.releaseLast(it->source, restoredLease)) { |
| 5291 | restored = false; |
| 5292 | continue; |
| 5293 | } |
| 5294 | it->frame->lease.emplace(std::move(restoredLease)); |
| 5295 | } |
| 5296 | releasePreparedMarkers(); |
| 5297 | return restored; |
| 5298 | }; |
| 5299 | const auto queueFrame = [this, &transaction, &output, |
| 5300 | &transferred](DwgStagedFrame &frame) { |
| 5301 | if (!validateStagedFrame(frame) || !frame.lease.has_value()) |
| 5302 | return false; |
| 5303 | const DwgSourceFrameId source = frame.lease->source; |
| 5304 | if (frame.publication.has_value()) { |
| 5305 | if (!output.appendFramePublication(*this, *frame.publication)) |
| 5306 | return false; |
| 5307 | } else { |
| 5308 | auto sourceScope = output.bindSource(source); |
| 5309 | if (!output.appendFrameCompletion()) |
| 5310 | return false; |
| 5311 | } |
| 5312 | if (!transaction.adopt(*frame.lease)) |
| 5313 | return false; |
| 5314 | transferred.push_back({&frame, source}); |
| 5315 | return true; |
| 5316 | }; |
| 5317 | |
| 5318 | DRW_Insert delivery; |
| 5319 | { |
| 5320 | try { |
| 5321 | delivery = pending.entity; |
| 5322 | delivery.attlist = prepared.orderedEntities; |
| 5323 | } catch (...) { |
| 5324 | releasePreparedMarkers(); |
| 5325 | return DwgMappedEntityOutcome::Rejected; |
| 5326 | } |
| 5327 | } |
| 5328 | bool committed = false; |
| 5329 | try { |
| 5330 | const DwgSourceFrameId parentSource = pending.frame.lease->source; |
| 5331 | { |
| 5332 | auto sourceScope = output.bindSource(parentSource); |
| 5333 | output.appendValue(delivery, &DRW_Interface::addInsert); |
| 5334 | } |
| 5335 | committed = queueFrame(pending.frame); |
| 5336 | for (StagedAttribState *attribute : prepared.orderedAttributes) |
| 5337 | committed = committed && queueFrame(attribute->frame); |
| 5338 | if (prepared.sequenceEnd != nullptr) |
| 5339 | committed = committed && queueFrame(prepared.sequenceEnd->frame); |
| 5340 | } catch (...) { |
| 5341 | committed = false; |
| 5342 | } |
| 5343 | if (!committed) { |
| 5344 | (void)rollback(); |
| 5345 | return DwgMappedEntityOutcome::Rejected; |
| 5346 | } |
| 5347 | |
| 5348 | for (const TransferredFrame &frame : transferred) { |
| 5349 | frame.frame->lease.reset(); |
| 5350 | frame.frame->publication.reset(); |
| 5351 | } |
| 5352 | if (prepared.sequenceEnd != nullptr) |
| 5353 | m_stagedSeqEnds.erase(sequenceHandle); |
| 5354 | m_pendingInsertStates.erase(handle); |
| 5355 | return DwgMappedEntityOutcome::CommittedCompound; |
| 5356 | } |
| 5357 | |
| 5358 | dwgReader::DwgMappedEntityOutcome dwgReader::deliverPreparedInsertCommit( |
| 5359 | std::uint32_t handle, PendingInsertState &pending, |
| 5360 | DwgPreparedInsertCommit &prepared, DRW_Interface &intfa) { |
| 5361 | if (m_activeBlockTransaction != nullptr || m_activeBlockOutput != nullptr) |
| 5362 | return journalPreparedInsertCommit(handle, pending, prepared); |
| 5363 | |
| 5364 | const std::uint32_t sequenceHandle = pending.entity.seqendH.ref; |
| 5365 | pending.entity.attlist.swap(prepared.orderedEntities); |
| 5366 | |
| 5367 | const auto publishFrame = [this, &intfa](DwgStagedFrame &frame) { |
| 5368 | if (!validateStagedFrame(frame)) |
| 5369 | return false; |
| 5370 | if (frame.lease->hasCoverage && |
| 5371 | (!frame.publication.has_value() || |
| 5372 | !publishDwgFramePublication(intfa, *frame.publication))) { |
| 5373 | return false; |
| 5374 | } |
| 5375 | if (!discardDetachedDwgSourceFrame(*frame.lease)) |
| 5376 | return false; |
| 5377 | frame.lease.reset(); |
| 5378 | frame.publication.reset(); |
| 5379 | return true; |
| 5380 | }; |
| 5381 | |
| 5382 | try { |
| 5383 | intfa.addInsert(pending.entity); |
| 5384 | } catch (...) { |
| 5385 | terminalizeInsertGroup(handle, DwgInsertTerminalReason::CallbackException); |
| 5386 | return DwgMappedEntityOutcome::Rejected; |
| 5387 | } |
| 5388 | if (!publishFrame(pending.frame)) { |
| 5389 | terminalizeInsertGroup(handle, DwgInsertTerminalReason::ReceiptFailure); |
| 5390 | return DwgMappedEntityOutcome::Rejected; |
| 5391 | } |
| 5392 | for (StagedAttribState *attribute : prepared.orderedAttributes) { |
| 5393 | if (!publishFrame(attribute->frame)) { |
| 5394 | terminalizeInsertGroup(handle, DwgInsertTerminalReason::ReceiptFailure); |
| 5395 | return DwgMappedEntityOutcome::Rejected; |
| 5396 | } |
| 5397 | } |
| 5398 | if (prepared.sequenceEnd != nullptr && |
| 5399 | !publishFrame(prepared.sequenceEnd->frame)) { |
| 5400 | terminalizeInsertGroup(handle, DwgInsertTerminalReason::ReceiptFailure); |
| 5401 | return DwgMappedEntityOutcome::Rejected; |
| 5402 | } |
| 5403 | |
| 5404 | if (version < DRW::AC1018 && pending.entity.haveNextLinks != 0 && |
| 5405 | sequenceHandle != std::numeric_limits<std::uint32_t>::max()) { |
| 5406 | nextEntLink = sequenceHandle + 1; |
| 5407 | } |
| 5408 | if (prepared.sequenceEnd != nullptr) |
| 5409 | m_stagedSeqEnds.erase(sequenceHandle); |
| 5410 | m_pendingInsertStates.erase(handle); |
| 5411 | return DwgMappedEntityOutcome::CommittedCompound; |
| 5412 | } |
| 5413 | |
| 5414 | dwgReader::DwgMappedEntityOutcome |
| 5415 | dwgReader::tryCommitPendingInsert(std::uint32_t handle, DRW_Interface &intfa) { |
| 5416 | const auto pendingIt = m_pendingInsertStates.find(handle); |
| 5417 | if (pendingIt == m_pendingInsertStates.end()) |
| 5418 | return DwgMappedEntityOutcome::StagedCompound; |
| 5419 | |
| 5420 | PendingInsertState &pending = pendingIt->second; |
| 5421 | if (!validateStagedFrame(pending.frame) || pending.entity.handle != handle || |
| 5422 | pending.entity.attlist.size() != pending.attributes.size()) { |
| 5423 | abandonPendingInsertState(handle); |
| 5424 | return DwgMappedEntityOutcome::Rejected; |
| 5425 | } |
| 5426 | if (!hasCompleteAttributeList(pending.entity, version)) |
| 5427 | return DwgMappedEntityOutcome::StagedCompound; |
| 5428 | |
| 5429 | const std::uint32_t sequenceHandle = pending.entity.seqendH.ref; |
| 5430 | if (sequenceHandle != DRW::NoHandle && |
| 5431 | m_stagedSeqEnds.find(sequenceHandle) == m_stagedSeqEnds.end()) { |
| 5432 | return DwgMappedEntityOutcome::StagedCompound; |
| 5433 | } |
| 5434 | |
| 5435 | DwgPreparedInsertCommit prepared; |
| 5436 | if (!prepareInsertCommit(handle, pending, prepared)) { |
| 5437 | abandonPendingInsertState(handle); |
| 5438 | return DwgMappedEntityOutcome::Rejected; |
| 5439 | } |
| 5440 | return deliverPreparedInsertCommit(handle, pending, prepared, intfa); |
| 5441 | } |
| 5442 | |
| 5443 | dwgReader::~dwgReader() { |
| 5444 | mapCleanUp(ltypemap); |
| 5445 | mapCleanUp(layermap); |
| 5446 | mapCleanUp(blockmap); |
| 5447 | mapCleanUp(stylemap); |
| 5448 | mapCleanUp(dimstylemap); |
| 5449 | mapCleanUp(vportmap); |
| 5450 | mapCleanUp(classesmap); |
| 5451 | mapCleanUp(blockRecordmap); |
| 5452 | mapCleanUp(appIdmap); |
| 5453 | mapCleanUp(viewmap); |
| 5454 | mapCleanUp(ucsmap); |
| 5455 | } |
| 5456 | |
| 5457 | bool dwgReader::dwgClassBitPosition(const dwgBuffer &buffer, |
| 5458 | std::uint64_t &position) noexcept { |
| 5459 | std::uint64_t bytePosition = 0; |
| 5460 | return buffer.isGood() && |
| 5461 | dwgSafety::multiply(buffer.getPosition(), 8, bytePosition) && |
| 5462 | dwgSafety::add(bytePosition, buffer.getBitPos(), position); |
| 5463 | } |
| 5464 | |
| 5465 | bool dwgReader::setDwgClassBitRange(DRW_DwgClassBitRange &range, |
| 5466 | std::uint64_t start, std::uint64_t end, |
| 5467 | DRW_DwgFrameOffsetSpace offsetSpace, |
| 5468 | bool sectionRelative) noexcept { |
| 5469 | if (end < start) |
| 5470 | return false; |
| 5471 | range.m_startBit = start; |
| 5472 | range.m_endBit = end; |
| 5473 | range.m_present = true; |
| 5474 | range.m_sectionRelative = sectionRelative; |
| 5475 | range.m_offsetSpace = offsetSpace; |
| 5476 | return true; |
| 5477 | } |
| 5478 | |
| 5479 | void dwgReader::beginDwgClassCoverage() noexcept { |
| 5480 | m_dwgClassCoverageReport.m_entries.clear(); |
| 5481 | m_dwgClassCoverageReport.m_status = DRW_DwgClassCoverageStatus::InProgress; |
| 5482 | m_dwgClassCoverageReport.m_complete = false; |
| 5483 | m_dwgClassNumberOrdinals.clear(); |
| 5484 | m_dwgClassCoveragePublished = false; |
| 5485 | m_dwgClassCoverageCaptureFailed = false; |
| 5486 | } |
| 5487 | |
| 5488 | DRW_DwgClassCoverageEntry |
| 5489 | dwgReader::makeDwgClassCoverageEntry(const DRW_Class &value, |
| 5490 | std::int32_t sectionDescriptorId) const { |
| 5491 | DRW_DwgClassCoverageEntry entry; |
| 5492 | entry.m_classNumber = value.classNum; |
| 5493 | entry.m_recordName = value.recName; |
| 5494 | entry.m_className = value.className; |
| 5495 | entry.m_appName = value.appName; |
| 5496 | entry.m_proxyFlag = value.proxyFlag; |
| 5497 | entry.m_wasAProxyFlag = value.wasaProxyFlag; |
| 5498 | entry.m_entityFlagRaw = value.entityFlagRaw; |
| 5499 | entry.m_entityFlag = value.entityFlag; |
| 5500 | entry.m_instanceCount = value.instanceCount; |
| 5501 | entry.m_dwgVersion = value.dwgVersion; |
| 5502 | entry.m_maintenanceVersion = value.maintenanceVersion; |
| 5503 | entry.m_unknown1 = value.unknown1; |
| 5504 | entry.m_unknown2 = value.unknown2; |
| 5505 | entry.m_sectionDescriptorId = sectionDescriptorId; |
| 5506 | return entry; |
| 5507 | } |
| 5508 | |
| 5509 | void dwgReader::recordDwgClassCoverageFailure( |
| 5510 | DRW_DwgClassCoverageEntry coverage, |
| 5511 | DRW_DwgClassCoverageReason reason) noexcept { |
| 5512 | coverage.m_streamOrdinal = m_dwgClassCoverageReport.m_entries.size(); |
| 5513 | coverage.m_state = DRW_DwgClassCoverageState::Failed; |
| 5514 | coverage.m_reason = reason; |
| 5515 | try { |
| 5516 | m_dwgClassCoverageReport.m_entries.push_back(std::move(coverage)); |
| 5517 | } catch (...) { |
| 5518 | m_dwgClassCoverageCaptureFailed = true; |
| 5519 | } |
| 5520 | } |
| 5521 | |
| 5522 | bool dwgReader::stageDwgClass(std::vector<DwgStagedClass> &stagedClasses, |
| 5523 | std::unique_ptr<DRW_Class> value, |
| 5524 | DRW_DwgClassCoverageEntry coverage) { |
| 5525 | if (value == nullptr) { |
| 5526 | m_dwgClassCoverageCaptureFailed = true; |
| 5527 | return false; |
| 5528 | } |
| 5529 | coverage.m_streamOrdinal = m_dwgClassCoverageReport.m_entries.size(); |
| 5530 | coverage.m_state = DRW_DwgClassCoverageState::Parsed; |
| 5531 | coverage.m_reason = DRW_DwgClassCoverageReason::None; |
| 5532 | try { |
| 5533 | m_dwgClassCoverageReport.m_entries.push_back(std::move(coverage)); |
| 5534 | const std::size_t coverageIndex = |
| 5535 | m_dwgClassCoverageReport.m_entries.size() - 1; |
| 5536 | try { |
| 5537 | stagedClasses.push_back(DwgStagedClass{std::move(value), coverageIndex}); |
| 5538 | } catch (...) { |
| 5539 | DRW_DwgClassCoverageEntry &entry = |
| 5540 | m_dwgClassCoverageReport.m_entries[coverageIndex]; |
| 5541 | entry.m_state = DRW_DwgClassCoverageState::Failed; |
| 5542 | entry.m_reason = DRW_DwgClassCoverageReason::Publish; |
| 5543 | m_dwgClassCoverageCaptureFailed = true; |
| 5544 | return false; |
| 5545 | } |
| 5546 | } catch (...) { |
| 5547 | m_dwgClassCoverageCaptureFailed = true; |
| 5548 | return false; |
| 5549 | } |
| 5550 | return true; |
| 5551 | } |
| 5552 | |
| 5553 | void dwgReader::finalizeDwgClassCoverage(DRW_Interface &intfa, |
| 5554 | bool classReadCompleted) { |
| 5555 | if (m_dwgClassCoverageReport.m_status == |
| 5556 | DRW_DwgClassCoverageStatus::NotAvailable || |
| 5557 | m_dwgClassCoveragePublished) { |
| 5558 | return; |
| 5559 | } |
| 5560 | |
| 5561 | m_dwgClassCoverageReport.m_complete = |
| 5562 | classReadCompleted && !m_dwgClassCoverageCaptureFailed && |
| 5563 | std::all_of(m_dwgClassCoverageReport.m_entries.cbegin(), |
| 5564 | m_dwgClassCoverageReport.m_entries.cend(), |
| 5565 | [](const DRW_DwgClassCoverageEntry &entry) { |
| 5566 | return entry.m_state == |
| 5567 | DRW_DwgClassCoverageState::Published; |
| 5568 | }); |
| 5569 | m_dwgClassCoverageReport.m_status = |
| 5570 | m_dwgClassCoverageReport.m_complete |
| 5571 | ? DRW_DwgClassCoverageStatus::FinalizedComplete |
| 5572 | : DRW_DwgClassCoverageStatus::FinalizedPartial; |
| 5573 | |
| 5574 | m_dwgClassCoveragePublished = true; |
| 5575 | try { |
| 5576 | intfa.addDwgClassCoverageReport(m_dwgClassCoverageReport); |
| 5577 | } catch (...) { |
| 5578 | m_dwgClassCoverageReport.m_complete = false; |
| 5579 | m_dwgClassCoverageReport.m_status = |
| 5580 | DRW_DwgClassCoverageStatus::FinalizedPartial; |
| 5581 | for (DRW_DwgClassCoverageEntry &entry : |
| 5582 | m_dwgClassCoverageReport.m_entries) { |
| 5583 | if (entry.m_state == DRW_DwgClassCoverageState::Published) |
| 5584 | entry.m_reason = DRW_DwgClassCoverageReason::Callback; |
| 5585 | } |
| 5586 | } |
| 5587 | } |
| 5588 | |
| 5589 | void dwgReader::finalizeDwgClassCoverageNoThrow( |
| 5590 | DRW_Interface &intfa, bool classReadCompleted) noexcept { |
| 5591 | try { |
| 5592 | finalizeDwgClassCoverage(intfa, classReadCompleted); |
| 5593 | } catch (...) { |
| 5594 | m_dwgClassCoverageReport.m_complete = false; |
| 5595 | m_dwgClassCoverageReport.m_status = |
| 5596 | DRW_DwgClassCoverageStatus::FinalizedPartial; |
| 5597 | } |
| 5598 | } |
| 5599 | |
| 5600 | bool dwgReader::publishDwgClasses(std::vector<DwgStagedClass> &stagedClasses) { |
| 5601 | const std::size_t maxCount = |
| 5602 | static_cast<std::size_t>(std::numeric_limits<int>::max()); |
| 5603 | if (classesmap.size() > maxCount || stagedClasses.size() > maxCount || |
| 5604 | stagedClasses.size() > maxCount - classesmap.size()) { |
| 5605 | m_dwgClassCoverageCaptureFailed = true; |
| 5606 | return false; |
| 5607 | } |
| 5608 | for (const auto &staged : stagedClasses) { |
| 5609 | if (staged.m_value == nullptr || |
| 5610 | staged.m_coverageIndex >= m_dwgClassCoverageReport.m_entries.size()) { |
| 5611 | m_dwgClassCoverageCaptureFailed = true; |
| 5612 | return false; |
| 5613 | } |
| 5614 | } |
| 5615 | if (!DRW::reserve(classesmap, static_cast<int>(classesmap.size() + |
| 5616 | stagedClasses.size())) || |
| 5617 | !DRW::reserve(m_dwgClassNumberOrdinals, |
| 5618 | static_cast<int>(stagedClasses.size()))) { |
| 5619 | m_dwgClassCoverageCaptureFailed = true; |
| 5620 | return false; |
| 5621 | } |
| 5622 | |
| 5623 | std::vector<std::uint32_t> published; |
| 5624 | if (!DRW::reserve(published, static_cast<int>(stagedClasses.size()))) { |
| 5625 | m_dwgClassCoverageCaptureFailed = true; |
| 5626 | return false; |
| 5627 | } |
| 5628 | const auto rollback = [&published, this] { |
| 5629 | for (const std::uint32_t classNumber : published) { |
| 5630 | classesmap.erase(classNumber); |
| 5631 | m_dwgClassNumberOrdinals.erase(classNumber); |
| 5632 | } |
| 5633 | }; |
| 5634 | |
| 5635 | try { |
| 5636 | for (auto &staged : stagedClasses) { |
| 5637 | DRW_DwgClassCoverageEntry &coverage = |
| 5638 | m_dwgClassCoverageReport.m_entries[staged.m_coverageIndex]; |
| 5639 | const auto result = |
| 5640 | classesmap.emplace(staged.m_value->classNum, staged.m_value.get()); |
| 5641 | if (!result.second) { |
| 5642 | coverage.m_state = DRW_DwgClassCoverageState::Failed; |
| 5643 | coverage.m_reason = DRW_DwgClassCoverageReason::Publish; |
| 5644 | rollback(); |
| 5645 | return false; |
| 5646 | } |
| 5647 | // `published` is pre-reserved and stores a trivially movable |
| 5648 | // value, so register the map insertion before the second map can |
| 5649 | // allocate. Any exception below can then roll this pointer back. |
| 5650 | published.push_back(staged.m_value->classNum); |
| 5651 | const auto ordinalResult = m_dwgClassNumberOrdinals.emplace( |
| 5652 | staged.m_value->classNum, coverage.m_streamOrdinal); |
| 5653 | if (!ordinalResult.second) { |
| 5654 | coverage.m_state = DRW_DwgClassCoverageState::Failed; |
| 5655 | coverage.m_reason = DRW_DwgClassCoverageReason::Publish; |
| 5656 | rollback(); |
| 5657 | return false; |
| 5658 | } |
| 5659 | } |
| 5660 | for (DwgStagedClass &staged : stagedClasses) { |
| 5661 | m_dwgClassCoverageReport.m_entries[staged.m_coverageIndex].m_state = |
| 5662 | DRW_DwgClassCoverageState::Published; |
| 5663 | staged.m_value.release(); |
| 5664 | } |
| 5665 | } catch (...) { |
| 5666 | rollback(); |
| 5667 | m_dwgClassCoverageCaptureFailed = true; |
| 5668 | return false; |
| 5669 | } |
| 5670 | return true; |
| 5671 | } |
| 5672 | |
| 5673 | void dwgReader::parseAttribs(DRW_Entity *e) { |
| 5674 | if (nullptr == e) { |
| 5675 | return; |
| 5676 | } |
| 5677 | |
| 5678 | std::uint32_t ltref = e->lTypeH.ref; |
| 5679 | std::uint32_t lyref = e->layerH.ref; |
| 5680 | auto lt_it = ltypemap.find(ltref); |
| 5681 | if (lt_it != ltypemap.end()) { |
| 5682 | e->lineType = (lt_it->second)->name; |
| 5683 | } |
| 5684 | auto ly_it = layermap.find(lyref); |
| 5685 | if (ly_it != layermap.end()) { |
| 5686 | e->layer = (ly_it->second)->name; |
| 5687 | } |
| 5688 | |
| 5689 | // Drain any deferred EED handle lookups now that the symbol tables |
| 5690 | // are populated. parseDwg() pushed placeholder DRW_Variants for |
| 5691 | // APPID names (DXF 1001) and layer-table refs (DXF 1003 with the |
| 5692 | // isLayerRef flag); fill in their string content here. |
| 5693 | for (const auto &p : e->pendingAppIdResolutions) { |
| 5694 | if (p.indexInExtData >= e->extData.size()) |
| 5695 | continue; |
| 5696 | auto &v = e->extData[p.indexInExtData]; |
| 5697 | if (!v) |
| 5698 | continue; |
| 5699 | auto it = appIdmap.find(p.handleRef); |
| 5700 | if (it != appIdmap.end() && it->second != nullptr) { |
| 5701 | v->addString(1001, it->second->name); |
| 5702 | } else { |
| 5703 | char fallback[24]; |
| 5704 | std::snprintf(fallback, sizeof(fallback), "ACAD_%X", p.handleRef); |
| 5705 | v->addString(1001, std::string{fallback}); |
| 5706 | } |
| 5707 | } |
| 5708 | e->pendingAppIdResolutions.clear(); |
| 5709 | |
| 5710 | for (const auto &p : e->pendingLayerRefResolutions) { |
| 5711 | if (p.indexInExtData >= e->extData.size()) |
| 5712 | continue; |
| 5713 | auto &v = e->extData[p.indexInExtData]; |
| 5714 | if (!v) |
| 5715 | continue; |
| 5716 | const std::string name = findTableName(DRW::LAYER, p.handleRef); |
| 5717 | if (!name.empty()) |
| 5718 | v->setLayerRefName(name); |
| 5719 | } |
| 5720 | e->pendingLayerRefResolutions.clear(); |
| 5721 | } |
| 5722 | |
| 5723 | void dwgReader::parseAttribs(DRW_TableEntry *e) { |
| 5724 | if (e == nullptr) |
| 5725 | return; |
| 5726 | |
| 5727 | for (const auto &p : e->pendingAppIdResolutions) { |
| 5728 | if (p.indexInExtData >= e->extData.size() || |
| 5729 | e->extData[p.indexInExtData] == nullptr) |
| 5730 | continue; |
| 5731 | auto it = appIdmap.find(p.handleRef); |
| 5732 | char fallback[24]; |
| 5733 | std::snprintf(fallback, sizeof(fallback), "ACAD_%X", p.handleRef); |
| 5734 | e->extData[p.indexInExtData]->addString(1001, it != appIdmap.end() && |
| 5735 | it->second != nullptr |
| 5736 | ? it->second->name |
| 5737 | : std::string{fallback}); |
| 5738 | } |
| 5739 | e->pendingAppIdResolutions.clear(); |
| 5740 | |
| 5741 | for (const auto &p : e->pendingLayerRefResolutions) { |
| 5742 | if (p.indexInExtData >= e->extData.size() || |
| 5743 | e->extData[p.indexInExtData] == nullptr) |
| 5744 | continue; |
| 5745 | const std::string name = findTableName(DRW::LAYER, p.handleRef); |
| 5746 | if (!name.empty()) |
| 5747 | e->extData[p.indexInExtData]->setLayerRefName(name); |
| 5748 | } |
| 5749 | e->pendingLayerRefResolutions.clear(); |
| 5750 | } |
| 5751 | |
| 5752 | std::string dwgReader::findTableName(DRW::TTYPE table, std::int32_t handle) { |
| 5753 | std::string name; |
| 5754 | switch (table) { |
| 5755 | case DRW::STYLE: { |
| 5756 | auto st_it = stylemap.find(handle); |
| 5757 | if (st_it != stylemap.end()) |
| 5758 | name = (st_it->second)->name; |
| 5759 | break; |
| 5760 | } |
| 5761 | case DRW::DIMSTYLE: { |
| 5762 | auto ds_it = dimstylemap.find(handle); |
| 5763 | if (ds_it != dimstylemap.end()) |
| 5764 | name = (ds_it->second)->name; |
| 5765 | break; |
| 5766 | } |
| 5767 | case DRW::BLOCK_RECORD: { // use DRW_Block because name are more correct |
| 5768 | // auto bk_it = blockmap.find(handle); |
| 5769 | // if (bk_it != blockmap.end()) |
| 5770 | auto bk_it = blockRecordmap.find(handle); |
| 5771 | if (bk_it != blockRecordmap.end()) |
| 5772 | name = (bk_it->second)->name; |
| 5773 | break; |
| 5774 | } |
| 5775 | /* case DRW::VPORT:{ |
| 5776 | auto vp_it = vportmap.find(handle); |
| 5777 | if (vp_it != vportmap.end()) |
| 5778 | name = (vp_it->second)->name; |
| 5779 | break;}*/ |
| 5780 | case DRW::LAYER: { |
| 5781 | auto ly_it = layermap.find(handle); |
| 5782 | if (ly_it != layermap.end()) |
| 5783 | name = (ly_it->second)->name; |
| 5784 | break; |
| 5785 | } |
| 5786 | case DRW::LTYPE: { |
| 5787 | auto lt_it = ltypemap.find(handle); |
| 5788 | if (lt_it != ltypemap.end()) |
| 5789 | name = (lt_it->second)->name; |
| 5790 | break; |
| 5791 | } |
| 5792 | default: |
| 5793 | break; |
| 5794 | } |
| 5795 | return name; |
| 5796 | } |
| 5797 | |
| 5798 | bool dwgReader::readDwgHeader(DRW_Header &hdr, dwgBuffer *buf, |
| 5799 | dwgBuffer *hBuf) { |
| 5800 | // The R2010+ bitsize_hi gate inside parseDwg keys off the APP maintenance |
| 5801 | // version (byte 0x12), not byte 0x0B — see appMaintenanceVersion in |
| 5802 | // dwgreader.h. |
| 5803 | bool ret = hdr.parseDwg(version, buf, hBuf, appMaintenanceVersion); |
| 5804 | // RLZ: copy objectControl handles |
| 5805 | return ret; |
| 5806 | } |
| 5807 | |
| 5808 | bool dwgReader::checkSentinel(dwgBuffer *buf, enum secEnum::DWGSection sec, |
| 5809 | bool start) { |
| 5810 | if (buf == nullptr || !buf->isGood()) |
| 5811 | return false; |
| 5812 | std::uint8_t readBytes[16]; |
| 5813 | for (int i = 0; i < 16; i++) { |
| 5814 | readBytes[i] = buf->getRawChar8(); |
| 5815 | DRW_DBGH(readBytes[i])DRW_dbg::dbgH(readBytes[i]); |
| 5816 | DRW_DBG(" ")DRW_dbg::dbg(" "); |
| 5817 | } |
| 5818 | if (!buf->isGood()) |
| 5819 | return false; |
| 5820 | const std::uint8_t *expected = nullptr; |
| 5821 | switch (sec) { |
| 5822 | case secEnum::FILEHEADER: |
| 5823 | if (!start) |
| 5824 | expected = dwgSentinels::FILE_HEADER_END; |
| 5825 | break; |
| 5826 | case secEnum::HEADER: |
| 5827 | expected = start ? dwgSentinels::HEADER_BEGIN : dwgSentinels::HEADER_END; |
| 5828 | break; |
| 5829 | case secEnum::CLASSES: |
| 5830 | expected = start ? dwgSentinels::CLASSES_BEGIN : dwgSentinels::CLASSES_END; |
| 5831 | break; |
| 5832 | default: |
| 5833 | break; |
| 5834 | } |
| 5835 | if (expected != nullptr) { |
| 5836 | for (int i = 0; i < 16; i++) { |
| 5837 | if (readBytes[i] != expected[i]) { |
| 5838 | DRW_DBG("\ncheckSentinel: mismatch at byte ")DRW_dbg::dbg("\ncheckSentinel: mismatch at byte "); |
| 5839 | DRW_DBG(i)DRW_dbg::dbg(i); |
| 5840 | DRW_DBG(" got ")DRW_dbg::dbg(" got "); |
| 5841 | DRW_DBGH(readBytes[i])DRW_dbg::dbgH(readBytes[i]); |
| 5842 | DRW_DBG(" expected ")DRW_dbg::dbg(" expected "); |
| 5843 | DRW_DBGH(expected[i])DRW_dbg::dbgH(expected[i]); |
| 5844 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 5845 | return false; |
| 5846 | } |
| 5847 | } |
| 5848 | } |
| 5849 | return true; |
| 5850 | } |
| 5851 | |
| 5852 | bool dwgReader::readDwgClassesTail(dwgBuffer &buffer) { |
| 5853 | const std::uint64_t start = buffer.getPosition(); |
| 5854 | if (start > buffer.size() || buffer.size() - start < 16) |
| 5855 | return false; |
| 5856 | |
| 5857 | // Prefer the current eight-byte form, then the legacy R2007 two-byte |
| 5858 | // field, and finally the R2004 no-tail form. Probe each candidate without |
| 5859 | // poisoning the publishing cursor when a candidate is not the file's |
| 5860 | // actual trailer layout. |
| 5861 | constexpr std::array<std::size_t, 3> candidateSizes = {8, 2, 0}; |
| 5862 | for (const std::size_t tailSize : candidateSizes) { |
| 5863 | if (tailSize > buffer.size() - start || |
| 5864 | buffer.size() - start - tailSize < 16) |
| 5865 | continue; |
| 5866 | dwgBuffer probe = buffer.forkIndependent(); |
| 5867 | if (!probe.setPosition(start + tailSize)) |
| 5868 | continue; |
| 5869 | probe.setBitPos(0); |
| 5870 | if (!checkSentinel(&probe, secEnum::CLASSES, false) || !probe.isGood()) |
| 5871 | continue; |
| 5872 | if (!buffer.setPosition(start + tailSize)) |
| 5873 | return false; |
| 5874 | buffer.setBitPos(0); |
| 5875 | return true; |
| 5876 | } |
| 5877 | |
| 5878 | // Preserve the historical warn-only end-sentinel policy for a malformed |
| 5879 | // but bounded trailer. The caller will still inspect the sentinel; this |
| 5880 | // fallback only prevents a compatibility probe from turning a warning |
| 5881 | // into a hard cursor failure. |
| 5882 | if (!buffer.setPosition(start)) |
| 5883 | return false; |
| 5884 | buffer.setBitPos(0); |
| 5885 | return true; |
| 5886 | } |
| 5887 | |
| 5888 | /*********** objects map ************************/ |
| 5889 | /** Note: object map are split in sections with max size 2035? |
| 5890 | * heach section are 2 bytes size + data bytes + 2 bytes crc |
| 5891 | * size value are data bytes + 2 and to calculate crc are used |
| 5892 | * 2 bytes size + data bytes |
| 5893 | * last section are 2 bytes size + 2 bytes crc (size value always 2) |
| 5894 | **/ |
| 5895 | bool dwgReader::readDwgHandles(dwgBuffer *dbuf, std::uint64_t offset, |
| 5896 | std::uint64_t size, std::uint64_t locationLimit, |
| 5897 | DwgIntegrityAddressSpace offsetSpace, |
| 5898 | std::int32_t sectionDescriptorId) { |
| 5899 | DRW_DBG("\ndwgReader::readDwgHandles\n")DRW_dbg::dbg("\ndwgReader::readDwgHandles\n"); |
| 5900 | m_dwgSourceFrameLedger.clear(); |
| 5901 | m_dwgSourceFrameIndexes.clear(); |
| 5902 | m_dwgFramePhaseSnapshots.clear(); |
| 5903 | m_dwgFrameCoverageStatus = DRW_DwgFrameCoverageStatus::NotAvailable; |
| 5904 | m_dwgFrameCoverageIntegrityViolation = false; |
| 5905 | m_dwgFrameCoveragePublished = false; |
| 5906 | const auto recordFailure = |
| 5907 | [&](DwgIntegrityCheckKind kind, std::uint64_t location = 0, |
| 5908 | bool hasLocation = false, std::uint64_t expected = 0, |
| 5909 | std::uint64_t observed = 0, bool hasValues = false) { |
| 5910 | DwgIntegrityDiagnostic diagnostic; |
| 5911 | diagnostic.severity = DwgIntegritySeverity::Error; |
| 5912 | diagnostic.offsetSpace = offsetSpace; |
| 5913 | diagnostic.phase = DwgIntegrityPhase::ObjectMap; |
| 5914 | diagnostic.kind = kind; |
| 5915 | diagnostic.logicalSectionId = secEnum::HANDLES; |
| 5916 | diagnostic.sectionDescriptorId = sectionDescriptorId; |
| 5917 | if (hasLocation && offsetSpace != DwgIntegrityAddressSpace::None) { |
| 5918 | diagnostic.fileOffset = location; |
| 5919 | diagnostic.hasFileOffset = true; |
| 5920 | } |
| 5921 | if (hasValues) { |
| 5922 | diagnostic.expected = expected; |
| 5923 | diagnostic.observed = observed; |
| 5924 | diagnostic.hasExpected = true; |
| 5925 | diagnostic.hasObserved = true; |
| 5926 | } |
| 5927 | addIntegrityDiagnostic(std::move(diagnostic)); |
| 5928 | }; |
| 5929 | if (dbuf == nullptr || size > dwgSafety::MaxBufferSize || |
| 5930 | size > dbuf->size() || offset > dbuf->size() - size || |
| 5931 | size > std::numeric_limits<std::size_t>::max()) { |
| 5932 | recordFailure(DwgIntegrityCheckKind::ObjectMapProgress, offset, true); |
| 5933 | return false; |
| 5934 | } |
| 5935 | if (!dbuf->setPosition(offset)) { |
| 5936 | recordFailure(DwgIntegrityCheckKind::ObjectMapProgress, offset, true); |
| 5937 | return false; |
| 5938 | } |
| 5939 | |
| 5940 | std::uint64_t maxPos = offset + size; |
| 5941 | DRW_DBG("\nSection HANDLES offset= ")DRW_dbg::dbg("\nSection HANDLES offset= "); |
| 5942 | DRW_DBG(offset)DRW_dbg::dbg(offset); |
| 5943 | DRW_DBG("\nSection HANDLES size= ")DRW_dbg::dbg("\nSection HANDLES size= "); |
| 5944 | DRW_DBG(size)DRW_dbg::dbg(size); |
| 5945 | DRW_DBG("\nSection HANDLES maxPos= ")DRW_dbg::dbg("\nSection HANDLES maxPos= "); |
| 5946 | DRW_DBG(maxPos)DRW_dbg::dbg(maxPos); |
| 5947 | |
| 5948 | // Each entry is >= 2 bytes (a 1-byte-minimum modular-char handle delta + |
| 5949 | // a 1-byte-minimum modular-char location delta), so size/2 is a safe |
| 5950 | // upper bound on entry count. Avoids repeated rehashing while the loop |
| 5951 | // below fills ObjectMap -- large DWGs have hundreds of thousands of |
| 5952 | // handles. |
| 5953 | std::unordered_map<std::uint32_t, objHandle> stagedMap; |
| 5954 | std::unordered_set<std::uint32_t> stagedOffsets; |
| 5955 | std::vector<objHandle> stagedEntries; |
| 5956 | if (size / 2 > static_cast<std::uint64_t>(std::numeric_limits<int>::max()) || |
| 5957 | !DRW::reserve(stagedMap, static_cast<int>(size / 2)) || |
| 5958 | !DRW::reserve(stagedOffsets, static_cast<int>(size / 2)) || |
| 5959 | !DRW::reserve(stagedEntries, static_cast<int>(size / 2))) { |
| 5960 | recordFailure(DwgIntegrityCheckKind::ObjectMapProgress, offset, true); |
| 5961 | return false; |
| 5962 | } |
| 5963 | |
| 5964 | std::uint64_t startPos = offset; |
| 5965 | bool end = false; |
| 5966 | bool sawDataGroup = false; |
| 5967 | std::uint16_t terminatorCrc = 0; |
| 5968 | std::vector<std::uint8_t> tmpByteStr; |
| 5969 | /* According to Open Design Specification for .dwg files Version 5.4.1 |
| 5970 | * chapter 23.1 (page 251), section list is terminated by empty section |
| 5971 | * (section consisting only of the checksum). When we find, we finish |
| 5972 | * reading sections. |
| 5973 | */ |
| 5974 | while (!end) { |
| 5975 | // The group size is part of the group and is followed by a two-byte |
| 5976 | // CRC. Keep both reads inside the HANDLES section boundary; the |
| 5977 | // backing buffer can contain later DWG sections. |
| 5978 | if (startPos > maxPos || maxPos - startPos < 2 || |
| 5979 | !dbuf->setPosition(startPos)) { |
| 5980 | recordFailure(DwgIntegrityCheckKind::ObjectMapProgress, startPos, true); |
| 5981 | return false; |
| 5982 | } |
| 5983 | DRW_DBG("\nstart handles section buf->curPosition()= ")DRW_dbg::dbg("\nstart handles section buf->curPosition()= " ); |
| 5984 | DRW_DBG(dbuf->getPosition())DRW_dbg::dbg(dbuf->getPosition()); |
| 5985 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 5986 | std::uint16_t pageSize = dbuf->getBERawShort16(); |
| 5987 | DRW_DBG("object map section size= ")DRW_dbg::dbg("object map section size= "); |
| 5988 | DRW_DBG(pageSize)DRW_dbg::dbg(pageSize); |
| 5989 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 5990 | if (!dbuf->isGood() || pageSize < 2 || |
| 5991 | pageSize > dwgSafety::MaxHandleMapGroupSize || |
| 5992 | pageSize > maxPos - startPos - 2) { |
| 5993 | recordFailure(DwgIntegrityCheckKind::ObjectMapProgress, startPos, true, 2, |
| 5994 | pageSize, true); |
| 5995 | DRW_DBG("object map section size out of range\n")DRW_dbg::dbg("object map section size out of range\n"); |
| 5996 | return false; |
| 5997 | } |
| 5998 | if (!dbuf->setPosition(startPos)) { |
| 5999 | recordFailure(DwgIntegrityCheckKind::ObjectMapProgress, startPos, true); |
| 6000 | return false; |
| 6001 | } |
| 6002 | if (!DRW::resize(tmpByteStr, pageSize)) { |
| 6003 | recordFailure(DwgIntegrityCheckKind::ObjectMapProgress, startPos, true); |
| 6004 | return false; |
| 6005 | } |
| 6006 | if (!dbuf->getBytes(tmpByteStr.data(), pageSize)) { |
| 6007 | recordFailure(DwgIntegrityCheckKind::ObjectMapProgress, startPos, true); |
| 6008 | return false; |
| 6009 | } |
| 6010 | if (!dbuf->isGood()) { |
| 6011 | recordFailure(DwgIntegrityCheckKind::ObjectMapProgress, startPos, true); |
| 6012 | return false; |
| 6013 | } |
| 6014 | dwgBuffer buff(tmpByteStr.data(), pageSize, &decoder); |
| 6015 | if (pageSize != 2) { |
| 6016 | sawDataGroup = true; |
| 6017 | if (!buff.setPosition(2)) { |
| 6018 | recordFailure(DwgIntegrityCheckKind::ObjectMapProgress, startPos, true); |
| 6019 | return false; |
| 6020 | } |
| 6021 | std::uint64_t lastHandle = 0; |
| 6022 | std::int64_t lastLoc = 0; |
| 6023 | // read data |
| 6024 | while (buff.getPosition() < pageSize) { |
| 6025 | std::uint64_t prevPos = buff.getPosition(); |
| 6026 | const std::uint64_t encodedHandleDelta = buff.getUModularChar(); |
| 6027 | const std::int64_t locationDelta = buff.getModularChar(); |
| 6028 | const bool locationOverflow = |
| 6029 | (locationDelta < 0 && |
| 6030 | lastLoc < |
| 6031 | std::numeric_limits<std::int64_t>::min() - locationDelta) || |
| 6032 | (locationDelta > 0 && |
| 6033 | lastLoc > |
| 6034 | std::numeric_limits<std::int64_t>::max() - locationDelta); |
| 6035 | if (!buff.isGood() || buff.getPosition() <= prevPos || |
| 6036 | encodedHandleDelta == 0 || |
| 6037 | encodedHandleDelta > std::numeric_limits<std::uint32_t>::max() || |
| 6038 | lastHandle > std::numeric_limits<std::uint32_t>::max() - |
| 6039 | encodedHandleDelta || |
| 6040 | locationOverflow) { |
| 6041 | recordFailure(DwgIntegrityCheckKind::ObjectMapProgress, |
| 6042 | startPos + prevPos, true); |
| 6043 | return false; |
| 6044 | } |
| 6045 | const auto handleDelta = static_cast<std::uint32_t>(encodedHandleDelta); |
| 6046 | lastHandle += handleDelta; |
| 6047 | lastLoc += locationDelta; |
| 6048 | DRW_DBG("object map lastHandle= ")DRW_dbg::dbg("object map lastHandle= "); |
| 6049 | DRW_DBGH(static_cast<std::uint32_t>(lastHandle))DRW_dbg::dbgH(static_cast<std::uint32_t>(lastHandle)); |
| 6050 | DRW_DBG(" lastLoc= ")DRW_dbg::dbg(" lastLoc= "); |
| 6051 | DRW_DBG(static_cast<long long>(lastLoc))DRW_dbg::dbg(static_cast<long long>(lastLoc)); |
| 6052 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 6053 | if (!buff.isGood() || buff.getPosition() <= prevPos) { |
| 6054 | recordFailure(DwgIntegrityCheckKind::ObjectMapProgress, |
| 6055 | startPos + prevPos, true); |
| 6056 | return false; |
| 6057 | } |
| 6058 | if (lastHandle == 0 || lastLoc < 0 || |
| 6059 | lastLoc > std::numeric_limits<std::uint32_t>::max() || |
| 6060 | (locationLimit != std::numeric_limits<std::uint64_t>::max() && |
| 6061 | static_cast<std::uint64_t>(lastLoc) >= locationLimit)) { |
| 6062 | recordFailure(DwgIntegrityCheckKind::ObjectMapProgress, |
| 6063 | startPos + prevPos, true); |
| 6064 | return false; |
| 6065 | } |
| 6066 | const auto handleKey = static_cast<std::uint32_t>(lastHandle); |
| 6067 | if (ObjectMap.find(handleKey) != ObjectMap.end() || |
| 6068 | stagedMap.find(handleKey) != stagedMap.end()) { |
| 6069 | recordFailure(DwgIntegrityCheckKind::ObjectMapProgress, |
| 6070 | startPos + prevPos, true, 0, handleKey, true); |
| 6071 | DRW_DBG("duplicate object-map handle\n")DRW_dbg::dbg("duplicate object-map handle\n"); |
| 6072 | return false; |
| 6073 | } |
| 6074 | const auto objectOffset = static_cast<std::uint32_t>(lastLoc); |
| 6075 | if (!stagedOffsets.insert(objectOffset).second) { |
| 6076 | recordFailure(DwgIntegrityCheckKind::ObjectMapDuplicateOffset, |
| 6077 | startPos + prevPos, true, 0, objectOffset, true); |
| 6078 | DRW_DBG("duplicate object-map offset\n")DRW_dbg::dbg("duplicate object-map offset\n"); |
| 6079 | return false; |
| 6080 | } |
| 6081 | const objHandle entry(0, handleKey, objectOffset, |
| 6082 | static_cast<std::uint64_t>(stagedEntries.size()), |
| 6083 | frameOffsetSpace(offsetSpace)); |
| 6084 | stagedMap.emplace(handleKey, entry); |
| 6085 | stagedEntries.push_back(entry); |
| 6086 | } |
| 6087 | } else { |
| 6088 | end = true; |
| 6089 | } |
| 6090 | // verify crc |
| 6091 | std::uint16_t crcCalc = buff.crc8(0xc0c1, 0, pageSize); |
| 6092 | std::uint16_t crcRead = dbuf->getBERawShort16(); |
| 6093 | DRW_DBG("object map section crc8 read= ")DRW_dbg::dbg("object map section crc8 read= "); |
| 6094 | DRW_DBG(crcRead)DRW_dbg::dbg(crcRead); |
| 6095 | DRW_DBG("\nobject map section crc8 calculated= ")DRW_dbg::dbg("\nobject map section crc8 calculated= "); |
| 6096 | DRW_DBG(crcCalc)DRW_dbg::dbg(crcCalc); |
| 6097 | DRW_DBG("\nobject section buf->curPosition()= ")DRW_dbg::dbg("\nobject section buf->curPosition()= "); |
| 6098 | DRW_DBG(dbuf->getPosition())DRW_dbg::dbg(dbuf->getPosition()); |
| 6099 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 6100 | if (!dbuf->isGood()) { |
| 6101 | recordFailure(DwgIntegrityCheckKind::ObjectMapProgress, startPos, true); |
| 6102 | return false; |
| 6103 | } |
| 6104 | if (crcCalc != crcRead) { |
| 6105 | recordFailure(DwgIntegrityCheckKind::ObjectMapCrc, startPos, true, |
| 6106 | crcRead, crcCalc, true); |
| 6107 | return false; |
| 6108 | } |
| 6109 | if (end) |
| 6110 | terminatorCrc = crcCalc; |
| 6111 | startPos = dbuf->getPosition(); |
| 6112 | } |
| 6113 | |
| 6114 | if (!end || !sawDataGroup || stagedMap.empty() || !dbuf->isGood()) { |
| 6115 | recordFailure(DwgIntegrityCheckKind::ObjectMapProgress, startPos, true); |
| 6116 | return false; |
| 6117 | } |
| 6118 | |
| 6119 | // Some R2010 files retain a duplicate empty-page trailer after the |
| 6120 | // HANDLES terminator, inside the section's declared decompressed size. |
| 6121 | // Accept only a second terminator with the checksum calculated for 00 02; |
| 6122 | // arbitrary trailing bytes remain an error. |
| 6123 | if (maxPos - startPos == 4) { |
| 6124 | const std::array<std::uint8_t, 4> trailer = { |
| 6125 | dbuf->getRawChar8(), dbuf->getRawChar8(), dbuf->getRawChar8(), |
| 6126 | dbuf->getRawChar8()}; |
| 6127 | if (!dbuf->isGood()) { |
| 6128 | recordFailure(DwgIntegrityCheckKind::ObjectMapProgress, startPos, true); |
| 6129 | return false; |
| 6130 | } |
| 6131 | const std::uint16_t trailerCrc = |
| 6132 | static_cast<std::uint16_t>(trailer[2] << 8 | trailer[3]); |
| 6133 | if (trailer[0] == 0 && trailer[1] == 2 && trailerCrc == terminatorCrc) { |
| 6134 | startPos = maxPos; |
| 6135 | } else { |
| 6136 | recordFailure(DwgIntegrityCheckKind::ObjectMapCrc, startPos, true); |
| 6137 | return false; |
| 6138 | } |
| 6139 | } |
| 6140 | if (startPos != maxPos) { |
| 6141 | recordFailure(DwgIntegrityCheckKind::ObjectMapProgress, startPos, true, |
| 6142 | maxPos, startPos, true); |
| 6143 | return false; |
| 6144 | } |
| 6145 | const auto objectMapSize = ObjectMap.size(); |
| 6146 | const auto stagedMapSize = stagedMap.size(); |
| 6147 | const auto maxContainerSize = |
| 6148 | static_cast<std::size_t>(std::numeric_limits<int>::max()); |
| 6149 | if (objectMapSize > maxContainerSize || stagedMapSize > maxContainerSize || |
| 6150 | objectMapSize > maxContainerSize - stagedMapSize) { |
| 6151 | recordFailure(DwgIntegrityCheckKind::ObjectMapProgress, startPos, true); |
| 6152 | return false; |
| 6153 | } |
| 6154 | if (!DRW::reserve(ObjectMap, |
| 6155 | static_cast<int>(objectMapSize + stagedMapSize))) { |
| 6156 | recordFailure(DwgIntegrityCheckKind::ObjectMapProgress, startPos, true); |
| 6157 | return false; |
| 6158 | } |
| 6159 | std::unordered_map<std::uint32_t, std::size_t> stagedFrameIndexes; |
| 6160 | std::vector<DRW_DwgFrameCoverageEntry> stagedFrameLedger; |
| 6161 | if (stagedEntries.size() > maxContainerSize || |
| 6162 | !DRW::reserve(stagedFrameIndexes, |
| 6163 | static_cast<int>(stagedEntries.size())) || |
| 6164 | !DRW::reserve(stagedFrameLedger, |
| 6165 | static_cast<int>(stagedEntries.size()))) { |
| 6166 | recordFailure(DwgIntegrityCheckKind::ObjectMapProgress, startPos, true); |
| 6167 | return false; |
| 6168 | } |
| 6169 | for (const objHandle &entry : stagedEntries) { |
| 6170 | DRW_DwgFrameCoverageEntry frameEntry; |
| 6171 | frameEntry.m_handle = entry.handle; |
| 6172 | frameEntry.m_sourceOffset = entry.loc; |
| 6173 | frameEntry.m_sourceMapOrdinal = entry.sourceOrdinal; |
| 6174 | frameEntry.m_sourceOffsetSpace = entry.sourceOffsetSpace; |
| 6175 | const std::size_t index = stagedFrameLedger.size(); |
| 6176 | if (!stagedFrameIndexes.emplace(entry.handle, index).second) { |
| 6177 | recordFailure(DwgIntegrityCheckKind::ObjectMapProgress, startPos, true, 0, |
| 6178 | entry.handle, true); |
| 6179 | return false; |
| 6180 | } |
| 6181 | stagedFrameLedger.push_back(frameEntry); |
| 6182 | } |
| 6183 | for (const auto &entry : stagedMap) |
| 6184 | ObjectMap.emplace(entry.first, entry.second); |
| 6185 | m_dwgSourceFrameLedger = std::move(stagedFrameLedger); |
| 6186 | m_dwgSourceFrameIndexes = std::move(stagedFrameIndexes); |
| 6187 | m_dwgFrameCoverageStatus = DRW_DwgFrameCoverageStatus::InProgress; |
| 6188 | m_dwgFrameCoveragePublished = false; |
| 6189 | return true; |
| 6190 | } |
| 6191 | |
| 6192 | /*********** objects ************************/ |
| 6193 | /** |
| 6194 | * Reads all the object referenced in the object map section of the DWG file |
| 6195 | * (using their object file offsets) |
| 6196 | */ |
| 6197 | bool dwgReader::readDwgTables(DRW_Header &hdr, dwgBuffer *dbuf, |
| 6198 | DwgIntegrityAddressSpace offsetSpace) { |
| 6199 | DRW_DBG("\ndwgReader::readDwgTables start\n")DRW_dbg::dbg("\ndwgReader::readDwgTables start\n"); |
| 6200 | bool ret = true; |
| 6201 | bool ret2 = true; |
| 6202 | objHandle oc; |
| 6203 | std::vector<DRW_UnsupportedObject> stagedRawControls; |
| 6204 | std::vector<DRW_DwgFramePublication> stagedTableFramePublications; |
| 6205 | std::unordered_set<std::uint32_t> stagedRawControlHandles; |
| 6206 | std::unordered_set<std::uint32_t> claimedTableHandles; |
| 6207 | struct StagedTableMapEntry { |
| 6208 | DwgObjectMap::node_type node; |
| 6209 | DwgSourceFrameId source; |
| 6210 | DRW_DwgFrameDisposition disposition{DRW_DwgFrameDisposition::Pending}; |
| 6211 | DRW_DwgFrameCoverageReason reason{DRW_DwgFrameCoverageReason::None}; |
| 6212 | std::uint32_t publicationCount{0}; |
| 6213 | bool hasCoverage{false}; |
| 6214 | }; |
| 6215 | std::vector<StagedTableMapEntry> erasedObjectHandles; |
| 6216 | const bool hasFrameCoverage = |
| 6217 | m_dwgFrameCoverageStatus != DRW_DwgFrameCoverageStatus::NotAvailable; |
| 6218 | try { |
| 6219 | erasedObjectHandles.reserve(ObjectMap.size()); |
| 6220 | } catch (...) { |
| 6221 | return false; |
| 6222 | } |
| 6223 | const auto clearTableState = [&] { |
| 6224 | const auto clearMap = [](auto &table) { |
| 6225 | mapCleanUp(table); |
| 6226 | table.clear(); |
| 6227 | }; |
| 6228 | clearMap(ltypemap); |
| 6229 | clearMap(layermap); |
| 6230 | clearMap(stylemap); |
| 6231 | clearMap(dimstylemap); |
| 6232 | clearMap(vportmap); |
| 6233 | clearMap(blockRecordmap); |
| 6234 | clearMap(appIdmap); |
| 6235 | clearMap(viewmap); |
| 6236 | clearMap(ucsmap); |
| 6237 | m_layerNameOrder.clear(); |
| 6238 | m_ltypeNameOrder.clear(); |
| 6239 | m_deferredRawObjects.clear(); |
| 6240 | m_deferredTableFramePublications.clear(); |
| 6241 | }; |
| 6242 | const auto eraseObject = [&](DwgObjectMap::iterator it) { |
| 6243 | if (it == ObjectMap.end()) { |
| 6244 | ret = false; |
| 6245 | return; |
| 6246 | } |
| 6247 | |
| 6248 | DwgSourceFrameLease lease; |
| 6249 | if (!borrowDwgSourceFrame(ObjectMap, it, lease)) { |
| 6250 | ret = false; |
| 6251 | return; |
| 6252 | } |
| 6253 | // Capacity is reserved for every currently mapped handle before the |
| 6254 | // table phase begins, so stage the metadata before detaching the node. |
| 6255 | erasedObjectHandles.emplace_back(); |
| 6256 | StagedTableMapEntry &staged = erasedObjectHandles.back(); |
| 6257 | staged.source = lease.source; |
| 6258 | staged.hasCoverage = lease.hasCoverage; |
| 6259 | if (hasFrameCoverage) { |
| 6260 | const auto sourceIt = m_dwgSourceFrameIndexes.find(lease.source.handle); |
| 6261 | if (sourceIt == m_dwgSourceFrameIndexes.end() || |
| 6262 | sourceIt->second >= m_dwgSourceFrameLedger.size()) { |
| 6263 | erasedObjectHandles.pop_back(); |
| 6264 | ret = false; |
| 6265 | return; |
| 6266 | } |
| 6267 | const DRW_DwgFrameCoverageEntry &entry = |
| 6268 | m_dwgSourceFrameLedger[sourceIt->second]; |
| 6269 | staged.disposition = entry.m_disposition; |
| 6270 | staged.reason = entry.m_reason; |
| 6271 | staged.publicationCount = entry.m_publicationCount; |
| 6272 | } |
| 6273 | |
| 6274 | staged.node = ObjectMap.extract(it); |
| 6275 | |
| 6276 | if (hasFrameCoverage && |
| 6277 | !markDwgFrameOutcome(lease.source, DRW_DwgFrameDisposition::Deferred, |
| 6278 | DRW_DwgFrameCoverageReason::TableDeferred)) { |
| 6279 | ObjectMap.insert(std::move(erasedObjectHandles.back().node)); |
| 6280 | erasedObjectHandles.pop_back(); |
| 6281 | ret = false; |
| 6282 | } |
| 6283 | }; |
| 6284 | const auto restoreErasedObjects = [&] { |
| 6285 | bool restored = true; |
| 6286 | for (StagedTableMapEntry &entry : erasedObjectHandles) { |
| 6287 | const auto inserted = ObjectMap.insert(std::move(entry.node)); |
| 6288 | if (!inserted.inserted) { |
| 6289 | restored = false; |
| 6290 | continue; |
| 6291 | } |
| 6292 | if (!entry.hasCoverage) |
| 6293 | continue; |
| 6294 | const auto sourceIt = m_dwgSourceFrameIndexes.find(entry.source.handle); |
| 6295 | if (sourceIt == m_dwgSourceFrameIndexes.end() || |
| 6296 | sourceIt->second >= m_dwgSourceFrameLedger.size()) { |
| 6297 | restored = false; |
| 6298 | continue; |
| 6299 | } |
| 6300 | DRW_DwgFrameCoverageEntry &sourceEntry = |
| 6301 | m_dwgSourceFrameLedger[sourceIt->second]; |
| 6302 | const DwgSourceFrameId expected{ |
| 6303 | sourceEntry.m_handle, sourceEntry.m_sourceOffset, |
| 6304 | sourceEntry.m_sourceMapOrdinal, sourceEntry.m_sourceOffsetSpace}; |
| 6305 | if (!(entry.source == expected) || |
| 6306 | sourceEntry.m_disposition != DRW_DwgFrameDisposition::Deferred || |
| 6307 | sourceEntry.m_publicationCount != 0) { |
| 6308 | restored = false; |
| 6309 | continue; |
| 6310 | } |
| 6311 | sourceEntry.m_disposition = entry.disposition; |
| 6312 | sourceEntry.m_reason = entry.reason; |
| 6313 | sourceEntry.m_publicationCount = entry.publicationCount; |
| 6314 | } |
| 6315 | return restored; |
| 6316 | }; |
| 6317 | |
| 6318 | // A reader can be retried after a malformed table phase. Do not let either |
| 6319 | // prior table entries or handles consumed by this failed attempt escape. |
| 6320 | clearTableState(); |
| 6321 | const auto abortTablePhase = [&] { |
| 6322 | clearTableState(); |
| 6323 | (void)restoreErasedObjects(); |
| 6324 | return false; |
| 6325 | }; |
| 6326 | const auto parseControl = [&](const objHandle &object, |
| 6327 | const DwgTableDescriptor &descriptor, |
| 6328 | DRW_ObjControl &control) { |
| 6329 | DwgObjectFrame frame; |
| 6330 | if (!frame.readAt(*dbuf, version, object.loc)) { |
| 6331 | recordObjectFrameFailure(object, offsetSpace); |
| 6332 | return false; |
| 6333 | } |
| 6334 | dwgBuffer buffer(frame.body().data(), frame.body().size(), &decoder); |
| 6335 | if (buffer.getObjType(version) != descriptor.controlType || |
| 6336 | !buffer.isGood()) |
| 6337 | return false; |
| 6338 | buffer.resetPosition(); |
| 6339 | if (!control.parseDwg(version, &buffer, frame.bodyBitSize()) || |
| 6340 | !buffer.isGood() || control.handle != object.handle) |
| 6341 | return false; |
| 6342 | if (version <= DRW::AC1018) |
| 6343 | return true; |
| 6344 | |
| 6345 | // Control-object handle lists use their own null/xdictionary/child |
| 6346 | // schema; they are not the standard object owner/reactor tail. |
| 6347 | return true; |
| 6348 | }; |
| 6349 | const auto parseRawControl = [&](const objHandle &object, |
| 6350 | std::int16_t expectedType, |
| 6351 | DRW_ObjControl &control) { |
| 6352 | DwgObjectFrame frame; |
| 6353 | if (!frame.readAt(*dbuf, version, object.loc)) { |
| 6354 | recordObjectFrameFailure(object, offsetSpace); |
| 6355 | return false; |
| 6356 | } |
| 6357 | dwgBuffer buffer(frame.body().data(), frame.body().size(), &decoder); |
| 6358 | if (buffer.getObjType(version) != expectedType || !buffer.isGood()) |
| 6359 | return false; |
| 6360 | buffer.resetPosition(); |
| 6361 | if (!control.parseDwg(version, &buffer, frame.bodyBitSize()) || |
| 6362 | !buffer.isGood() || control.handle != object.handle) |
| 6363 | return false; |
| 6364 | if (version <= DRW::AC1018) |
| 6365 | return true; |
| 6366 | |
| 6367 | // Control-object handle lists use their own null/xdictionary/child |
| 6368 | // schema; they are not the standard object owner/reactor tail. |
| 6369 | return true; |
| 6370 | }; |
| 6371 | const auto claimControlHandles = [&](const DRW_ObjControl &control) { |
| 6372 | for (const std::uint32_t handle : control.handlesList) { |
| 6373 | if (ObjectMap.find(handle) == ObjectMap.end()) { |
| 6374 | DRW_DBG("WARNING: control handle not found ")DRW_dbg::dbg("WARNING: control handle not found "); |
| 6375 | DRW_DBGH(handle)DRW_dbg::dbgH(handle); |
| 6376 | DRW_DBG("\\n")DRW_dbg::dbg("\\n"); |
| 6377 | return false; |
| 6378 | } |
| 6379 | if (!claimedTableHandles.insert(handle).second) { |
| 6380 | return false; |
| 6381 | } |
| 6382 | } |
| 6383 | return true; |
| 6384 | }; |
| 6385 | const auto stageControlReceipt = [&](const objHandle &object, |
| 6386 | const DwgTableDescriptor &descriptor, |
| 6387 | const DRW_ObjControl &control) { |
| 6388 | const auto sourceIt = |
| 6389 | std::find_if(erasedObjectHandles.crbegin(), erasedObjectHandles.crend(), |
| 6390 | [&object](const auto &erased) { |
| 6391 | return erased.source.handle == object.handle; |
| 6392 | }); |
| 6393 | if (sourceIt == erasedObjectHandles.crend()) |
| 6394 | return false; |
| 6395 | |
| 6396 | try { |
| 6397 | DRW_DwgFramePublication publication; |
| 6398 | publication.m_version = version; |
| 6399 | publication.m_handle = sourceIt->source.handle; |
| 6400 | publication.m_sourceOffset = sourceIt->source.offset; |
| 6401 | publication.m_sourceMapOrdinal = sourceIt->source.ordinal; |
| 6402 | publication.m_sourceOffsetSpace = sourceIt->source.offsetSpace; |
| 6403 | publication.m_hasSourceLocation = true; |
| 6404 | publication.m_encodedType = descriptor.controlType; |
| 6405 | publication.m_resolvedType = descriptor.controlType; |
| 6406 | publication.m_isEntity = false; |
| 6407 | publication.m_recordName = descriptor.controlReceiptName; |
| 6408 | publication.setCommonLinkEvidence( |
| 6409 | DRW_DwgCommonLinkEvidence::NotApplicable); |
| 6410 | publication.m_controlHandles.assign(control.handlesList.cbegin(), |
| 6411 | control.handlesList.cend()); |
| 6412 | publication.m_carrier = DRW_DwgFramePublication::Carrier::Control; |
| 6413 | stagedTableFramePublications.push_back(std::move(publication)); |
| 6414 | } catch (...) { |
| 6415 | return false; |
| 6416 | } |
| 6417 | return true; |
| 6418 | }; |
| 6419 | const auto parseTableRecord = [&](const objHandle &object, |
| 6420 | const DwgTableDescriptor &descriptor, |
| 6421 | auto &record) { |
| 6422 | DwgObjectFrame frame; |
| 6423 | if (!frame.readAt(*dbuf, version, object.loc)) { |
| 6424 | recordObjectFrameFailure(object, offsetSpace); |
| 6425 | return false; |
| 6426 | } |
| 6427 | dwgBuffer typeBuffer(frame.body().data(), frame.body().size(), &decoder); |
| 6428 | if (typeBuffer.getObjType(version) != descriptor.recordType || |
| 6429 | !typeBuffer.isGood()) { |
| 6430 | return false; |
| 6431 | } |
| 6432 | dwgBuffer buffer(frame.body().data(), frame.body().size(), &decoder); |
| 6433 | if (!record->parseDwg(version, &buffer, frame.bodyBitSize()) || |
| 6434 | !buffer.isGood() || record->handle != object.handle) |
| 6435 | return false; |
| 6436 | if (version <= DRW::AC1018) |
| 6437 | return true; |
| 6438 | |
| 6439 | RawObjectShell links; |
| 6440 | dwgBuffer linkBuffer(frame.body().data(), frame.body().size(), &decoder); |
| 6441 | if (!links.parseDwg(version, &linkBuffer, frame.bodyBitSize()) || |
| 6442 | !linkBuffer.isGood() || !links.hasDwgCommonLinkTail() || |
| 6443 | links.handle != object.handle) |
| 6444 | return false; |
| 6445 | record->parentHandle = links.parentHandle; |
| 6446 | record->reactorHandles = links.reactorHandles; |
| 6447 | record->xDictHandle = links.xDictHandle; |
| 6448 | record->setDwgCommonObjectState(links.reactorCount(), |
| 6449 | links.extensionDictionaryFlag(), |
| 6450 | links.hasDataStorageBinaryData()); |
| 6451 | record->setDwgCommonLinkTailValidated(true); |
| 6452 | return true; |
| 6453 | }; |
| 6454 | const auto addRawControl = [&](const objHandle &object, |
| 6455 | std::int16_t objectType) { |
| 6456 | if (object.handle == DRW::NoHandle || |
| 6457 | !stagedRawControlHandles.insert(object.handle).second) |
| 6458 | return false; |
| 6459 | DwgObjectFrame frame; |
| 6460 | if (!frame.readAt(*dbuf, version, object.loc)) { |
| 6461 | recordObjectFrameFailure(object, offsetSpace); |
| 6462 | return false; |
| 6463 | } |
| 6464 | |
| 6465 | dwgBuffer typeBuffer(frame.body().data(), frame.body().size(), &decoder); |
| 6466 | if (typeBuffer.getObjType(version) != objectType || !typeBuffer.isGood()) |
| 6467 | return false; |
| 6468 | |
| 6469 | DRW_UnsupportedObject raw; |
| 6470 | raw.m_version = version; |
| 6471 | raw.m_objectType = objectType; |
| 6472 | raw.m_handle = object.handle; |
| 6473 | raw.m_bodyBitSize = frame.bodyBitSize(); |
| 6474 | raw.m_objectOffset = object.loc; |
| 6475 | raw.m_objectSize = static_cast<std::uint32_t>(frame.body().size()); |
| 6476 | raw.m_rawBytes = frame.body(); |
| 6477 | stagedRawControls.push_back(std::move(raw)); |
| 6478 | return true; |
| 6479 | }; |
| 6480 | const auto insertTableRecord = [&](auto &map, auto record, |
| 6481 | const char *recordName, |
| 6482 | std::uint32_t expectedHandle, |
| 6483 | std::int16_t recordType) { |
| 6484 | if (record == nullptr || record->handle != expectedHandle) { |
| 6485 | DRW_DBG("mismatched ")DRW_dbg::dbg("mismatched "); |
| 6486 | DRW_DBG(recordName)DRW_dbg::dbg(recordName); |
| 6487 | DRW_DBG(" handle: ")DRW_dbg::dbg(" handle: "); |
| 6488 | DRW_DBGH(record == nullptr ? 0 : record->handle)DRW_dbg::dbgH(record == nullptr ? 0 : record->handle); |
| 6489 | DRW_DBG(" expected: ")DRW_dbg::dbg(" expected: "); |
| 6490 | DRW_DBGH(expectedHandle)DRW_dbg::dbgH(expectedHandle); |
| 6491 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 6492 | ret = false; |
| 6493 | return false; |
| 6494 | } |
| 6495 | const auto sourceIt = |
| 6496 | std::find_if(erasedObjectHandles.crbegin(), erasedObjectHandles.crend(), |
| 6497 | [expectedHandle](const auto &erased) { |
| 6498 | return erased.source.handle == expectedHandle; |
| 6499 | }); |
| 6500 | if (sourceIt == erasedObjectHandles.crend()) { |
| 6501 | ret = false; |
| 6502 | return false; |
| 6503 | } |
| 6504 | const DRW_TableEntry &tableRecord = *record; |
| 6505 | DRW_DwgFramePublication publication; |
| 6506 | publication.m_version = version; |
| 6507 | publication.m_handle = sourceIt->source.handle; |
| 6508 | publication.m_sourceOffset = sourceIt->source.offset; |
| 6509 | publication.m_sourceMapOrdinal = sourceIt->source.ordinal; |
| 6510 | publication.m_sourceOffsetSpace = sourceIt->source.offsetSpace; |
| 6511 | publication.m_hasSourceLocation = true; |
| 6512 | publication.m_encodedType = recordType; |
| 6513 | publication.m_resolvedType = recordType; |
| 6514 | publication.m_isEntity = false; |
| 6515 | publication.m_recordName = recordName; |
| 6516 | publication.setCommonLinkEvidence(drwDwgCommonLinkEvidenceForLinks( |
| 6517 | tableRecord.hasDwgCommonLinkTail(), tableRecord.parentHandle, |
| 6518 | tableRecord.reactorHandles, tableRecord.reactorCount(), |
| 6519 | tableRecord.xDictHandle)); |
| 6520 | publication.m_parentHandle = tableRecord.parentHandle; |
| 6521 | publication.m_reactorHandles = tableRecord.reactorHandles; |
| 6522 | publication.m_xDictHandle = tableRecord.xDictHandle; |
| 6523 | publication.m_numReactors = tableRecord.reactorCount(); |
| 6524 | publication.m_xDictFlag = tableRecord.extensionDictionaryFlag(); |
| 6525 | publication.m_carrier = DRW_DwgFramePublication::Carrier::Typed; |
| 6526 | try { |
| 6527 | stagedTableFramePublications.push_back(std::move(publication)); |
| 6528 | } catch (...) { |
| 6529 | ret = false; |
| 6530 | return false; |
| 6531 | } |
| 6532 | const bool inserted = map.emplace(record->handle, record.get()).second; |
| 6533 | if (!inserted) { |
| 6534 | DRW_DBG("duplicate ")DRW_dbg::dbg("duplicate "); |
| 6535 | DRW_DBG(recordName)DRW_dbg::dbg(recordName); |
| 6536 | DRW_DBG(" handle: ")DRW_dbg::dbg(" handle: "); |
| 6537 | DRW_DBGH(record->handle)DRW_dbg::dbgH(record->handle); |
| 6538 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 6539 | ret = false; |
| 6540 | } else { |
| 6541 | record.release(); |
| 6542 | } |
| 6543 | return inserted; |
| 6544 | }; |
| 6545 | |
| 6546 | // parse linetypes, start with linetype Control |
| 6547 | auto mit = ObjectMap.find(hdr.linetypeCtrl); |
| 6548 | if (mit == ObjectMap.end()) { |
| 6549 | DRW_DBG("\nWARNING: LineType control not found\n")DRW_dbg::dbg("\nWARNING: LineType control not found\n"); |
| 6550 | ret = false; |
| 6551 | } else { |
| 6552 | DRW_DBG("\n**********Parsing LineType control*******\n")DRW_dbg::dbg("\n**********Parsing LineType control*******\n"); |
| 6553 | oc = mit->second; |
| 6554 | eraseObject(mit); |
| 6555 | DRW_ObjControl ltControl; |
| 6556 | ret2 = parseControl(oc, kLTypeTable, ltControl); |
| 6557 | ret2 = ret2 && claimControlHandles(ltControl); |
| 6558 | ret2 = ret2 && stageControlReceipt(oc, kLTypeTable, ltControl); |
| 6559 | if (!ret2) { |
| 6560 | ltControl.handlesList.clear(); |
| 6561 | DRW_DBG("\nWARNING: LineType control parse failed\n")DRW_dbg::dbg("\nWARNING: LineType control parse failed\n"); |
| 6562 | } |
| 6563 | if (ret) |
| 6564 | ret = ret2; |
| 6565 | for (auto it = ltControl.handlesList.begin(); |
| 6566 | it != ltControl.handlesList.end(); ++it) { |
| 6567 | mit = ObjectMap.find(*it); |
| 6568 | if (mit == ObjectMap.end()) { |
| 6569 | DRW_DBG("\nWARNING: LineType not found\n")DRW_dbg::dbg("\nWARNING: LineType not found\n"); |
| 6570 | m_ltypeNameOrder.emplace_back(); // keep proxy index alignment |
| 6571 | } else { |
| 6572 | oc = mit->second; |
| 6573 | eraseObject(mit); |
| 6574 | DRW_DBG("\nLineType Handle= ")DRW_dbg::dbg("\nLineType Handle= "); |
| 6575 | DRW_DBGH(oc.handle)DRW_dbg::dbgH(oc.handle); |
| 6576 | DRW_DBG(" loc.: ")DRW_dbg::dbg(" loc.: "); |
| 6577 | DRW_DBG(oc.loc)DRW_dbg::dbg(oc.loc); |
| 6578 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 6579 | auto lt = std::make_unique<DRW_LType>(); |
| 6580 | ret2 = parseTableRecord(oc, kLTypeTable, lt); |
| 6581 | ret = ret && ret2; |
| 6582 | if (ret2) { |
| 6583 | const std::string ltypeName = lt->name; |
| 6584 | if (insertTableRecord(ltypemap, std::move(lt), "linetype", oc.handle, |
| 6585 | kLTypeTable.recordType)) |
| 6586 | m_ltypeNameOrder.push_back(ltypeName); // proxy op18 index space |
| 6587 | else |
| 6588 | m_ltypeNameOrder.emplace_back(); |
| 6589 | } else { |
| 6590 | m_ltypeNameOrder.emplace_back(); // keep proxy index alignment |
| 6591 | DRW_DBG(DRW_dbg::dbg("\nWARNING: LineType record parseDwg failed (handle skipped)\n" ) |
| 6592 | "\nWARNING: LineType record parseDwg failed (handle skipped)\n")DRW_dbg::dbg("\nWARNING: LineType record parseDwg failed (handle skipped)\n" ); |
| 6593 | } |
| 6594 | } |
| 6595 | } |
| 6596 | } |
| 6597 | |
| 6598 | if (!ret) |
| 6599 | return abortTablePhase(); |
| 6600 | |
| 6601 | // parse layers, start with layer Control |
| 6602 | mit = ObjectMap.find(hdr.layerCtrl); |
| 6603 | if (mit == ObjectMap.end()) { |
| 6604 | DRW_DBG("\nWARNING: Layer control not found\n")DRW_dbg::dbg("\nWARNING: Layer control not found\n"); |
| 6605 | ret = false; |
| 6606 | } else { |
| 6607 | DRW_DBG("\n**********Parsing Layer control*******\n")DRW_dbg::dbg("\n**********Parsing Layer control*******\n"); |
| 6608 | oc = mit->second; |
| 6609 | eraseObject(mit); |
| 6610 | DRW_ObjControl layControl; |
| 6611 | ret2 = parseControl(oc, kLayerTable, layControl); |
| 6612 | ret2 = ret2 && claimControlHandles(layControl); |
| 6613 | ret2 = ret2 && stageControlReceipt(oc, kLayerTable, layControl); |
| 6614 | if (!ret2) { |
| 6615 | layControl.handlesList.clear(); |
| 6616 | DRW_DBG("\nWARNING: Layer control parse failed\n")DRW_dbg::dbg("\nWARNING: Layer control parse failed\n"); |
| 6617 | } |
| 6618 | if (ret) |
| 6619 | ret = ret2; |
| 6620 | for (auto it = layControl.handlesList.begin(); |
| 6621 | it != layControl.handlesList.end(); ++it) { |
| 6622 | mit = ObjectMap.find(*it); |
| 6623 | if (mit == ObjectMap.end()) { |
| 6624 | DRW_DBG("\nWARNING: Layer not found (handle skipped)\n")DRW_dbg::dbg("\nWARNING: Layer not found (handle skipped)\n"); |
| 6625 | m_layerNameOrder.emplace_back(); // keep proxy index alignment |
| 6626 | } else { |
| 6627 | oc = mit->second; |
| 6628 | eraseObject(mit); |
| 6629 | DRW_DBG("Layer Handle= ")DRW_dbg::dbg("Layer Handle= "); |
| 6630 | DRW_DBGH(oc.handle)DRW_dbg::dbgH(oc.handle); |
| 6631 | DRW_DBG(" ")DRW_dbg::dbg(" "); |
| 6632 | DRW_DBG(oc.loc)DRW_dbg::dbg(oc.loc); |
| 6633 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 6634 | auto la = std::make_unique<DRW_Layer>(); |
| 6635 | ret2 = parseTableRecord(oc, kLayerTable, la); |
| 6636 | ret = ret && ret2; |
| 6637 | if (ret2) { |
| 6638 | const std::string layerName = la->name; |
| 6639 | if (insertTableRecord(layermap, std::move(la), "layer", oc.handle, |
| 6640 | kLayerTable.recordType)) |
| 6641 | m_layerNameOrder.push_back(layerName); // proxy op16 index space |
| 6642 | else |
| 6643 | m_layerNameOrder.emplace_back(); |
| 6644 | } else { |
| 6645 | m_layerNameOrder.emplace_back(); // keep proxy index alignment |
| 6646 | DRW_DBG("\nWARNING: Layer record parseDwg failed (handle skipped)\n")DRW_dbg::dbg("\nWARNING: Layer record parseDwg failed (handle skipped)\n" ); |
| 6647 | } |
| 6648 | } |
| 6649 | } |
| 6650 | } |
| 6651 | |
| 6652 | if (!ret) |
| 6653 | return abortTablePhase(); |
| 6654 | |
| 6655 | // set linetype in layer |
| 6656 | for (auto it = layermap.begin(); it != layermap.end(); ++it) { |
| 6657 | DRW_Layer *ly = it->second; |
| 6658 | std::uint32_t ref = ly->lTypeH.ref; |
| 6659 | auto lt_it = ltypemap.find(ref); |
| 6660 | if (lt_it != ltypemap.end()) { |
| 6661 | ly->lineType = (lt_it->second)->name; |
| 6662 | } |
| 6663 | } |
| 6664 | |
| 6665 | // parse text styles, start with style Control |
| 6666 | mit = ObjectMap.find(hdr.styleCtrl); |
| 6667 | if (mit == ObjectMap.end()) { |
| 6668 | DRW_DBG("\nWARNING: Style control not found\n")DRW_dbg::dbg("\nWARNING: Style control not found\n"); |
| 6669 | ret = false; |
| 6670 | } else { |
| 6671 | DRW_DBG("\n**********Parsing Style control*******\n")DRW_dbg::dbg("\n**********Parsing Style control*******\n"); |
| 6672 | oc = mit->second; |
| 6673 | eraseObject(mit); |
| 6674 | DRW_ObjControl styControl; |
| 6675 | ret2 = parseControl(oc, kStyleTable, styControl); |
| 6676 | ret2 = ret2 && claimControlHandles(styControl); |
| 6677 | ret2 = ret2 && stageControlReceipt(oc, kStyleTable, styControl); |
| 6678 | if (!ret2) { |
| 6679 | styControl.handlesList.clear(); |
| 6680 | DRW_DBG("\nWARNING: Text Style control parse failed\n")DRW_dbg::dbg("\nWARNING: Text Style control parse failed\n"); |
| 6681 | } |
| 6682 | if (ret) |
| 6683 | ret = ret2; |
| 6684 | for (auto it = styControl.handlesList.begin(); |
| 6685 | it != styControl.handlesList.end(); ++it) { |
| 6686 | mit = ObjectMap.find(*it); |
| 6687 | if (mit == ObjectMap.end()) { |
| 6688 | DRW_DBG("\nWARNING: Style not found (handle skipped)\n")DRW_dbg::dbg("\nWARNING: Style not found (handle skipped)\n"); |
| 6689 | } else { |
| 6690 | oc = mit->second; |
| 6691 | eraseObject(mit); |
| 6692 | DRW_DBG("Style Handle= ")DRW_dbg::dbg("Style Handle= "); |
| 6693 | DRW_DBGH(oc.handle)DRW_dbg::dbgH(oc.handle); |
| 6694 | DRW_DBG(" ")DRW_dbg::dbg(" "); |
| 6695 | DRW_DBG(oc.loc)DRW_dbg::dbg(oc.loc); |
| 6696 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 6697 | auto sty = std::make_unique<DRW_Textstyle>(); |
| 6698 | ret2 = parseTableRecord(oc, kStyleTable, sty); |
| 6699 | ret = ret && ret2; |
| 6700 | if (ret2) { |
| 6701 | insertTableRecord(stylemap, std::move(sty), "text style", oc.handle, |
| 6702 | kStyleTable.recordType); |
| 6703 | } else { |
| 6704 | DRW_DBG("\nWARNING: Style record parseDwg failed (handle skipped)\n")DRW_dbg::dbg("\nWARNING: Style record parseDwg failed (handle skipped)\n" ); |
| 6705 | } |
| 6706 | } |
| 6707 | } |
| 6708 | } |
| 6709 | |
| 6710 | if (!ret) |
| 6711 | return abortTablePhase(); |
| 6712 | |
| 6713 | // parse dim styles, start with dimstyle Control |
| 6714 | mit = ObjectMap.find(hdr.dimstyleCtrl); |
| 6715 | if (mit == ObjectMap.end()) { |
| 6716 | DRW_DBG("\nWARNING: Dimension Style control not found\n")DRW_dbg::dbg("\nWARNING: Dimension Style control not found\n" ); |
| 6717 | ret = false; |
| 6718 | } else { |
| 6719 | DRW_DBG("\n**********Parsing Dimension Style control*******\n")DRW_dbg::dbg("\n**********Parsing Dimension Style control*******\n" ); |
| 6720 | oc = mit->second; |
| 6721 | eraseObject(mit); |
| 6722 | DRW_ObjControl dimstyControl; |
| 6723 | ret2 = parseControl(oc, kDimStyleTable, dimstyControl); |
| 6724 | ret2 = ret2 && claimControlHandles(dimstyControl); |
| 6725 | ret2 = ret2 && stageControlReceipt(oc, kDimStyleTable, dimstyControl); |
| 6726 | if (!ret2) { |
| 6727 | dimstyControl.handlesList.clear(); |
| 6728 | DRW_DBG("\nWARNING: Dimension Style control parse failed\n")DRW_dbg::dbg("\nWARNING: Dimension Style control parse failed\n" ); |
| 6729 | } |
| 6730 | if (ret) |
| 6731 | ret = ret2; |
| 6732 | for (auto it = dimstyControl.handlesList.begin(); |
| 6733 | it != dimstyControl.handlesList.end(); ++it) { |
| 6734 | mit = ObjectMap.find(*it); |
| 6735 | if (mit == ObjectMap.end()) { |
| 6736 | DRW_DBG("\nWARNING: Dimension Style not found (handle skipped)\n")DRW_dbg::dbg("\nWARNING: Dimension Style not found (handle skipped)\n" ); |
| 6737 | } else { |
| 6738 | oc = mit->second; |
| 6739 | eraseObject(mit); |
| 6740 | DRW_DBG("Dimstyle Handle= ")DRW_dbg::dbg("Dimstyle Handle= "); |
| 6741 | DRW_DBGH(oc.handle)DRW_dbg::dbgH(oc.handle); |
| 6742 | DRW_DBG(" ")DRW_dbg::dbg(" "); |
| 6743 | DRW_DBG(oc.loc)DRW_dbg::dbg(oc.loc); |
| 6744 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 6745 | auto sty = std::make_unique<DRW_Dimstyle>(); |
| 6746 | ret2 = parseTableRecord(oc, kDimStyleTable, sty); |
| 6747 | ret = ret && ret2; |
| 6748 | if (ret2) { |
| 6749 | insertTableRecord(dimstylemap, std::move(sty), "dimension style", |
| 6750 | oc.handle, kDimStyleTable.recordType); |
| 6751 | } else { |
| 6752 | DRW_DBG("\nWARNING: Dimension Style record parseDwg failed (handle "DRW_dbg::dbg("\nWARNING: Dimension Style record parseDwg failed (handle " "skipped)\n") |
| 6753 | "skipped)\n")DRW_dbg::dbg("\nWARNING: Dimension Style record parseDwg failed (handle " "skipped)\n"); |
| 6754 | } |
| 6755 | } |
| 6756 | } |
| 6757 | } |
| 6758 | |
| 6759 | if (!ret) |
| 6760 | return abortTablePhase(); |
| 6761 | |
| 6762 | // parse vports, start with vports Control |
| 6763 | mit = ObjectMap.find(hdr.vportCtrl); |
| 6764 | if (mit == ObjectMap.end()) { |
| 6765 | DRW_DBG("\nWARNING: vports control not found\n")DRW_dbg::dbg("\nWARNING: vports control not found\n"); |
| 6766 | ret = false; |
| 6767 | } else { |
| 6768 | DRW_DBG("\n**********Parsing vports control*******\n")DRW_dbg::dbg("\n**********Parsing vports control*******\n"); |
| 6769 | oc = mit->second; |
| 6770 | eraseObject(mit); |
| 6771 | DRW_ObjControl vportControl; |
| 6772 | ret2 = parseControl(oc, kVPortTable, vportControl); |
| 6773 | ret2 = ret2 && claimControlHandles(vportControl); |
| 6774 | ret2 = ret2 && stageControlReceipt(oc, kVPortTable, vportControl); |
| 6775 | if (!ret2) { |
| 6776 | vportControl.handlesList.clear(); |
| 6777 | DRW_DBG("\nWARNING: VPorts control parse failed\n")DRW_dbg::dbg("\nWARNING: VPorts control parse failed\n"); |
| 6778 | } |
| 6779 | if (ret) |
| 6780 | ret = ret2; |
| 6781 | for (auto it = vportControl.handlesList.begin(); |
| 6782 | it != vportControl.handlesList.end(); ++it) { |
| 6783 | mit = ObjectMap.find(*it); |
| 6784 | if (mit == ObjectMap.end()) { |
| 6785 | DRW_DBG("\nWARNING: vport not found (handle skipped)\n")DRW_dbg::dbg("\nWARNING: vport not found (handle skipped)\n"); |
| 6786 | } else { |
| 6787 | oc = mit->second; |
| 6788 | eraseObject(mit); |
| 6789 | DRW_DBG("Vport Handle= ")DRW_dbg::dbg("Vport Handle= "); |
| 6790 | DRW_DBGH(oc.handle)DRW_dbg::dbgH(oc.handle); |
| 6791 | DRW_DBG(" ")DRW_dbg::dbg(" "); |
| 6792 | DRW_DBG(oc.loc)DRW_dbg::dbg(oc.loc); |
| 6793 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 6794 | auto vp = std::make_unique<DRW_Vport>(); |
| 6795 | ret2 = parseTableRecord(oc, kVPortTable, vp); |
| 6796 | ret = ret && ret2; |
| 6797 | if (ret2) { |
| 6798 | insertTableRecord(vportmap, std::move(vp), "viewport", oc.handle, |
| 6799 | kVPortTable.recordType); |
| 6800 | } else { |
| 6801 | DRW_DBG("\nWARNING: Vport record parseDwg failed (handle skipped)\n")DRW_dbg::dbg("\nWARNING: Vport record parseDwg failed (handle skipped)\n" ); |
| 6802 | } |
| 6803 | } |
| 6804 | } |
| 6805 | } |
| 6806 | |
| 6807 | if (!ret) |
| 6808 | return abortTablePhase(); |
| 6809 | |
| 6810 | // parse Block_records , start with Block_record Control |
| 6811 | mit = ObjectMap.find(hdr.blockCtrl); |
| 6812 | if (mit == ObjectMap.end()) { |
| 6813 | DRW_DBG("\nWARNING: Block_record control not found\n")DRW_dbg::dbg("\nWARNING: Block_record control not found\n"); |
| 6814 | ret = false; |
| 6815 | } else { |
| 6816 | DRW_DBG("\n**********Parsing Block_record control*******\n")DRW_dbg::dbg("\n**********Parsing Block_record control*******\n" ); |
| 6817 | oc = mit->second; |
| 6818 | eraseObject(mit); |
| 6819 | DRW_ObjControl blockControl; |
| 6820 | ret2 = parseControl(oc, kBlockTable, blockControl); |
| 6821 | ret2 = ret2 && claimControlHandles(blockControl); |
| 6822 | ret2 = ret2 && stageControlReceipt(oc, kBlockTable, blockControl); |
| 6823 | if (!ret2) { |
| 6824 | blockControl.handlesList.clear(); |
| 6825 | DRW_DBG("\nWARNING: Block Record control parse failed\n")DRW_dbg::dbg("\nWARNING: Block Record control parse failed\n" ); |
| 6826 | } |
| 6827 | if (ret) |
| 6828 | ret = ret2; |
| 6829 | for (auto it = blockControl.handlesList.begin(); |
| 6830 | it != blockControl.handlesList.end(); ++it) { |
| 6831 | mit = ObjectMap.find(*it); |
| 6832 | if (mit == ObjectMap.end()) { |
| 6833 | DRW_DBG("\nWARNING: block record not found (handle skipped)\n")DRW_dbg::dbg("\nWARNING: block record not found (handle skipped)\n" ); |
| 6834 | } else { |
| 6835 | oc = mit->second; |
| 6836 | eraseObject(mit); |
| 6837 | DRW_DBG("block record Handle= ")DRW_dbg::dbg("block record Handle= "); |
| 6838 | DRW_DBGH(oc.handle)DRW_dbg::dbgH(oc.handle); |
| 6839 | DRW_DBG(" ")DRW_dbg::dbg(" "); |
| 6840 | DRW_DBG(oc.loc)DRW_dbg::dbg(oc.loc); |
| 6841 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 6842 | auto br = std::make_unique<DRW_Block_Record>(); |
| 6843 | ret2 = parseTableRecord(oc, kBlockTable, br); |
| 6844 | ret = ret && ret2; |
| 6845 | if (ret2) { |
| 6846 | insertTableRecord(blockRecordmap, std::move(br), "block record", |
| 6847 | oc.handle, kBlockTable.recordType); |
| 6848 | } else { |
| 6849 | DRW_DBG("\nWARNING: Block_record record parseDwg failed (handle "DRW_dbg::dbg("\nWARNING: Block_record record parseDwg failed (handle " "skipped)\n") |
| 6850 | "skipped)\n")DRW_dbg::dbg("\nWARNING: Block_record record parseDwg failed (handle " "skipped)\n"); |
| 6851 | } |
| 6852 | } |
| 6853 | } |
| 6854 | } |
| 6855 | |
| 6856 | if (!ret) |
| 6857 | return abortTablePhase(); |
| 6858 | |
| 6859 | // parse appId , start with appId Control |
| 6860 | mit = ObjectMap.find(hdr.appidCtrl); |
| 6861 | if (mit == ObjectMap.end()) { |
| 6862 | DRW_DBG("\nWARNING: AppId control not found\n")DRW_dbg::dbg("\nWARNING: AppId control not found\n"); |
| 6863 | ret = false; |
| 6864 | } else { |
| 6865 | DRW_DBG("\n**********Parsing AppId control*******\n")DRW_dbg::dbg("\n**********Parsing AppId control*******\n"); |
| 6866 | oc = mit->second; |
| 6867 | eraseObject(mit); |
| 6868 | DRW_DBG("AppId Control Obj Handle= ")DRW_dbg::dbg("AppId Control Obj Handle= "); |
| 6869 | DRW_DBGH(oc.handle)DRW_dbg::dbgH(oc.handle); |
| 6870 | DRW_DBG(" ")DRW_dbg::dbg(" "); |
| 6871 | DRW_DBG(oc.loc)DRW_dbg::dbg(oc.loc); |
| 6872 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 6873 | DRW_ObjControl appIdControl; |
| 6874 | ret2 = parseControl(oc, kAppIdTable, appIdControl); |
| 6875 | ret2 = ret2 && claimControlHandles(appIdControl); |
| 6876 | ret2 = ret2 && stageControlReceipt(oc, kAppIdTable, appIdControl); |
| 6877 | if (!ret2) { |
| 6878 | appIdControl.handlesList.clear(); |
| 6879 | DRW_DBG("\nWARNING: AppId control parse failed\n")DRW_dbg::dbg("\nWARNING: AppId control parse failed\n"); |
| 6880 | } |
| 6881 | if (ret) |
| 6882 | ret = ret2; |
| 6883 | for (auto it = appIdControl.handlesList.begin(); |
| 6884 | it != appIdControl.handlesList.end(); ++it) { |
| 6885 | mit = ObjectMap.find(*it); |
| 6886 | if (mit == ObjectMap.end()) { |
| 6887 | DRW_DBG("\nWARNING: AppId not found (handle skipped)\n")DRW_dbg::dbg("\nWARNING: AppId not found (handle skipped)\n"); |
| 6888 | } else { |
| 6889 | oc = mit->second; |
| 6890 | eraseObject(mit); |
| 6891 | DRW_DBG("AppId Handle= ")DRW_dbg::dbg("AppId Handle= "); |
| 6892 | DRW_DBGH(oc.handle)DRW_dbg::dbgH(oc.handle); |
| 6893 | DRW_DBG(" ")DRW_dbg::dbg(" "); |
| 6894 | DRW_DBG(oc.loc)DRW_dbg::dbg(oc.loc); |
| 6895 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 6896 | auto ai = std::make_unique<DRW_AppId>(); |
| 6897 | ret2 = parseTableRecord(oc, kAppIdTable, ai); |
| 6898 | ret = ret && ret2; |
| 6899 | if (ret2) { |
| 6900 | insertTableRecord(appIdmap, std::move(ai), "AppId", oc.handle, |
| 6901 | kAppIdTable.recordType); |
| 6902 | } else { |
| 6903 | DRW_DBG("\nWARNING: AppId record parseDwg failed (handle skipped)\n")DRW_dbg::dbg("\nWARNING: AppId record parseDwg failed (handle skipped)\n" ); |
| 6904 | } |
| 6905 | } |
| 6906 | } |
| 6907 | } |
| 6908 | |
| 6909 | if (!ret) |
| 6910 | return abortTablePhase(); |
| 6911 | |
| 6912 | // parse View / UCS / VPortEntHeader controls |
| 6913 | // These controls are optional when their header handle is absent, but a |
| 6914 | // present control must decode completely before its records are used. |
| 6915 | mit = ObjectMap.find(hdr.viewCtrl); |
| 6916 | if (mit == ObjectMap.end()) { |
| 6917 | DRW_DBG("\nWARNING: View control not found\n")DRW_dbg::dbg("\nWARNING: View control not found\n"); |
| 6918 | } else { |
| 6919 | DRW_DBG("\n**********Parsing View control*******\n")DRW_dbg::dbg("\n**********Parsing View control*******\n"); |
| 6920 | oc = mit->second; |
| 6921 | eraseObject(mit); |
| 6922 | DRW_DBG("View Control Obj Handle= ")DRW_dbg::dbg("View Control Obj Handle= "); |
| 6923 | DRW_DBGH(oc.handle)DRW_dbg::dbgH(oc.handle); |
| 6924 | DRW_DBG(" ")DRW_dbg::dbg(" "); |
| 6925 | DRW_DBG(oc.loc)DRW_dbg::dbg(oc.loc); |
| 6926 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 6927 | DRW_ObjControl viewControl; |
| 6928 | const bool parsed = parseControl(oc, kViewTable, viewControl); |
| 6929 | const bool claimed = parsed && claimControlHandles(viewControl); |
| 6930 | const bool staged = |
| 6931 | claimed && stageControlReceipt(oc, kViewTable, viewControl); |
| 6932 | if (!staged) { |
| 6933 | DRW_DBG("\nWARNING: View control parse failed\n")DRW_dbg::dbg("\nWARNING: View control parse failed\n"); |
| 6934 | ret = false; |
| 6935 | } else { |
| 6936 | // per-record loop — populate viewmap so libdwgr.cpp processDwg |
| 6937 | // fires intfa.addView for each named view |
| 6938 | for (auto it = viewControl.handlesList.begin(); |
| 6939 | it != viewControl.handlesList.end(); ++it) { |
| 6940 | mit = ObjectMap.find(*it); |
| 6941 | if (mit == ObjectMap.end()) { |
| 6942 | DRW_DBG("\nWARNING: View record not found (handle skipped)\n")DRW_dbg::dbg("\nWARNING: View record not found (handle skipped)\n" ); |
| 6943 | } else { |
| 6944 | oc = mit->second; |
| 6945 | eraseObject(mit); |
| 6946 | DRW_DBG("View Handle= ")DRW_dbg::dbg("View Handle= "); |
| 6947 | DRW_DBGH(oc.handle)DRW_dbg::dbgH(oc.handle); |
| 6948 | DRW_DBG(" ")DRW_dbg::dbg(" "); |
| 6949 | DRW_DBG(oc.loc)DRW_dbg::dbg(oc.loc); |
| 6950 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 6951 | auto vw = std::make_unique<DRW_View>(); |
| 6952 | if (!parseTableRecord(oc, kViewTable, vw)) { |
| 6953 | ret = false; |
| 6954 | DRW_DBG(DRW_dbg::dbg("\nWARNING: View record parseDwg failed (handle skipped)\n" ) |
| 6955 | "\nWARNING: View record parseDwg failed (handle skipped)\n")DRW_dbg::dbg("\nWARNING: View record parseDwg failed (handle skipped)\n" ); |
| 6956 | } else { |
| 6957 | insertTableRecord(viewmap, std::move(vw), "view", oc.handle, |
| 6958 | kViewTable.recordType); |
| 6959 | } |
| 6960 | } |
| 6961 | } |
| 6962 | } |
| 6963 | } |
| 6964 | |
| 6965 | if (!ret) |
| 6966 | return abortTablePhase(); |
| 6967 | |
| 6968 | mit = ObjectMap.find(hdr.ucsCtrl); |
| 6969 | if (mit == ObjectMap.end()) { |
| 6970 | DRW_DBG("\nWARNING: Ucs control not found\n")DRW_dbg::dbg("\nWARNING: Ucs control not found\n"); |
| 6971 | } else { |
| 6972 | oc = mit->second; |
| 6973 | eraseObject(mit); |
| 6974 | DRW_DBG("\n**********Parsing Ucs control*******\n")DRW_dbg::dbg("\n**********Parsing Ucs control*******\n"); |
| 6975 | DRW_DBG("Ucs Control Obj Handle= ")DRW_dbg::dbg("Ucs Control Obj Handle= "); |
| 6976 | DRW_DBGH(oc.handle)DRW_dbg::dbgH(oc.handle); |
| 6977 | DRW_DBG(" ")DRW_dbg::dbg(" "); |
| 6978 | DRW_DBG(oc.loc)DRW_dbg::dbg(oc.loc); |
| 6979 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 6980 | DRW_ObjControl ucsControl; |
| 6981 | const bool parsed = parseControl(oc, kUcsTable, ucsControl); |
| 6982 | const bool claimed = parsed && claimControlHandles(ucsControl); |
| 6983 | const bool staged = |
| 6984 | claimed && stageControlReceipt(oc, kUcsTable, ucsControl); |
| 6985 | if (!staged) { |
| 6986 | DRW_DBG("\nWARNING: Ucs control parse failed\n")DRW_dbg::dbg("\nWARNING: Ucs control parse failed\n"); |
| 6987 | ret = false; |
| 6988 | } else { |
| 6989 | // per-record loop — populate ucsmap so libdwgr.cpp processDwg |
| 6990 | // fires intfa.addUCS for each named UCS |
| 6991 | for (auto it = ucsControl.handlesList.begin(); |
| 6992 | it != ucsControl.handlesList.end(); ++it) { |
| 6993 | mit = ObjectMap.find(*it); |
| 6994 | if (mit == ObjectMap.end()) { |
| 6995 | DRW_DBG("\nWARNING: Ucs record not found (handle skipped)\n")DRW_dbg::dbg("\nWARNING: Ucs record not found (handle skipped)\n" ); |
| 6996 | } else { |
| 6997 | oc = mit->second; |
| 6998 | eraseObject(mit); |
| 6999 | DRW_DBG("Ucs Handle= ")DRW_dbg::dbg("Ucs Handle= "); |
| 7000 | DRW_DBGH(oc.handle)DRW_dbg::dbgH(oc.handle); |
| 7001 | DRW_DBG(" ")DRW_dbg::dbg(" "); |
| 7002 | DRW_DBG(oc.loc)DRW_dbg::dbg(oc.loc); |
| 7003 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 7004 | auto u = std::make_unique<DRW_UCS>(); |
| 7005 | if (!parseTableRecord(oc, kUcsTable, u)) { |
| 7006 | ret = false; |
| 7007 | DRW_DBG("\nWARNING: Ucs record parseDwg failed (handle skipped)\n")DRW_dbg::dbg("\nWARNING: Ucs record parseDwg failed (handle skipped)\n" ); |
| 7008 | } else { |
| 7009 | insertTableRecord(ucsmap, std::move(u), "UCS", oc.handle, |
| 7010 | kUcsTable.recordType); |
| 7011 | } |
| 7012 | } |
| 7013 | } |
| 7014 | } |
| 7015 | } |
| 7016 | |
| 7017 | if (!ret) |
| 7018 | return abortTablePhase(); |
| 7019 | |
| 7020 | if (version < DRW::AC1018) { // r2000- |
| 7021 | mit = ObjectMap.find(hdr.vpEntHeaderCtrl); |
| 7022 | if (mit == ObjectMap.end()) { |
| 7023 | DRW_DBG("\nWARNING: vpEntHeader control not found\n")DRW_dbg::dbg("\nWARNING: vpEntHeader control not found\n"); |
| 7024 | } else { |
| 7025 | DRW_DBG("\n**********Parsing vpEntHeader control*******\n")DRW_dbg::dbg("\n**********Parsing vpEntHeader control*******\n" ); |
| 7026 | oc = mit->second; |
| 7027 | DRW_DBG("vpEntHeader Control Obj Handle= ")DRW_dbg::dbg("vpEntHeader Control Obj Handle= "); |
| 7028 | DRW_DBGH(oc.handle)DRW_dbg::dbgH(oc.handle); |
| 7029 | DRW_DBG(" ")DRW_dbg::dbg(" "); |
| 7030 | DRW_DBG(oc.loc)DRW_dbg::dbg(oc.loc); |
| 7031 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 7032 | DRW_ObjControl vpEntHeaderCtrl; |
| 7033 | const bool parsed = parseRawControl( |
| 7034 | oc, DRW_ViewportEntityHeader::kDwgControlType, vpEntHeaderCtrl); |
| 7035 | const bool claimed = parsed && claimControlHandles(vpEntHeaderCtrl); |
| 7036 | if (!claimed) { |
| 7037 | DRW_DBG("\nWARNING: vpEntHeader control parse failed\n")DRW_dbg::dbg("\nWARNING: vpEntHeader control parse failed\n"); |
| 7038 | ret = false; |
| 7039 | } else if (!addRawControl(oc, |
| 7040 | DRW_ViewportEntityHeader::kDwgControlType)) { |
| 7041 | DRW_DBG("\nWARNING: vpEntHeader control raw preservation failed\n")DRW_dbg::dbg("\nWARNING: vpEntHeader control raw preservation failed\n" ); |
| 7042 | ret = false; |
| 7043 | } |
| 7044 | // The control is needed here to discover the type-71 records, |
| 7045 | // but it is not itself an OBJECTS record. Remove it after the |
| 7046 | // control list has been consumed so the ordinary object pass does |
| 7047 | // not misclassify the fixed control type as an unsupported object. |
| 7048 | eraseObject(ObjectMap.find(oc.handle)); |
| 7049 | } |
| 7050 | } |
| 7051 | |
| 7052 | if (!ret) |
| 7053 | return abortTablePhase(); |
| 7054 | |
| 7055 | // EED in table records is parsed before APPID is available to every |
| 7056 | // caller. Resolve its deferred APPID/layer handles once all tables have |
| 7057 | // been collected, matching the entity path above. |
| 7058 | for (auto &item : ltypemap) |
| 7059 | parseAttribs(item.second); |
| 7060 | for (auto &item : layermap) |
| 7061 | parseAttribs(item.second); |
| 7062 | for (auto &item : stylemap) |
| 7063 | parseAttribs(item.second); |
| 7064 | for (auto &item : dimstylemap) |
| 7065 | parseAttribs(item.second); |
| 7066 | for (auto &item : vportmap) |
| 7067 | parseAttribs(item.second); |
| 7068 | for (auto &item : blockRecordmap) |
| 7069 | parseAttribs(item.second); |
| 7070 | for (auto &item : appIdmap) |
| 7071 | parseAttribs(item.second); |
| 7072 | for (auto &item : viewmap) |
| 7073 | parseAttribs(item.second); |
| 7074 | for (auto &item : ucsmap) |
| 7075 | parseAttribs(item.second); |
| 7076 | |
| 7077 | if (ret) { |
| 7078 | m_deferredRawObjects.clear(); |
| 7079 | for (auto &raw : stagedRawControls) |
| 7080 | m_deferredRawObjects.push_back(std::move(raw)); |
| 7081 | std::sort(stagedTableFramePublications.begin(), |
| 7082 | stagedTableFramePublications.end(), |
| 7083 | [](const DRW_DwgFramePublication &lhs, |
| 7084 | const DRW_DwgFramePublication &rhs) { |
| 7085 | return lhs.m_sourceMapOrdinal < rhs.m_sourceMapOrdinal; |
| 7086 | }); |
| 7087 | m_deferredTableFramePublications = std::move(stagedTableFramePublications); |
| 7088 | } else { |
| 7089 | clearTableState(); |
| 7090 | ret = restoreErasedObjects() && ret; |
| 7091 | } |
| 7092 | return ret; |
| 7093 | } |
| 7094 | |
| 7095 | bool dwgReader::publishDeferredTableFramePublications(DRW_Interface &intfa) { |
| 7096 | for (const DRW_DwgFramePublication &publication : |
| 7097 | m_deferredTableFramePublications) { |
| 7098 | if (!publishDwgFramePublication(intfa, publication)) |
| 7099 | return false; |
| 7100 | } |
| 7101 | m_deferredTableFramePublications.clear(); |
| 7102 | return true; |
| 7103 | } |
| 7104 | |
| 7105 | bool dwgReader::readDwgBlocks(DRW_Interface &intfa, dwgBuffer *dbuf, |
| 7106 | DwgIntegrityAddressSpace offsetSpace) { |
| 7107 | bool ret = true; |
| 7108 | if (dbuf == nullptr) |
| 7109 | return false; |
| 7110 | DRW_DBG("\nobject map total size= ")DRW_dbg::dbg("\nobject map total size= "); |
| 7111 | DRW_DBG(ObjectMap.size())DRW_dbg::dbg(ObjectMap.size()); |
| 7112 | m_consumedCompoundChildHandles.clear(); |
| 7113 | |
| 7114 | const auto parseBlock = [this](DRW_Block &block, dwgBuffer &buffer, |
| 7115 | std::uint32_t bodyBitSize) { |
| 7116 | try { |
| 7117 | return block.parseDwg(version, &buffer, bodyBitSize) && buffer.isGood(); |
| 7118 | } catch (...) { |
| 7119 | buffer.invalidate(); |
| 7120 | return false; |
| 7121 | } |
| 7122 | }; |
| 7123 | |
| 7124 | auto quarantineOwnedEntities = [&](const DRW_Block_Record &record) { |
| 7125 | const auto quarantine = [&](std::uint32_t handle) { |
| 7126 | if (handle == DRW::NoHandle) |
| 7127 | return; |
| 7128 | const auto objectIt = ObjectMap.find(handle); |
| 7129 | const auto deferredIt = objObjectMap.find(handle); |
| 7130 | if (objectIt != ObjectMap.end() && deferredIt != objObjectMap.end()) { |
| 7131 | (void)reportDwgFrameTransitionFailure(sourceFrameId(objectIt->second), |
| 7132 | objectIt->second.loc, true); |
| 7133 | ret = false; |
| 7134 | return; |
| 7135 | } |
| 7136 | if (objectIt != ObjectMap.end()) { |
| 7137 | (void)discardDwgSourceFrame(ObjectMap, objectIt); |
| 7138 | return; |
| 7139 | } |
| 7140 | if (deferredIt != objObjectMap.end()) |
| 7141 | (void)discardDwgSourceFrame(objObjectMap, deferredIt); |
| 7142 | }; |
| 7143 | quarantine(record.block); |
| 7144 | quarantine(record.endBlock); |
| 7145 | for (const std::uint32_t handle : record.entMap) { |
| 7146 | quarantine(handle); |
| 7147 | } |
| 7148 | }; |
| 7149 | |
| 7150 | const auto recordBlockFailure = |
| 7151 | [this](const DRW_Block_Record &record, std::uint32_t handle, |
| 7152 | std::int16_t type, DwgEntityFailurePhase phase) { |
| 7153 | objHandle object; |
| 7154 | object.handle = handle; |
| 7155 | recordEntityFailure(object, type, phase, record.handle); |
| 7156 | }; |
| 7157 | |
| 7158 | // BLOCK_RECORD ownership is a global graph, not a per-record property. |
| 7159 | // Preflight all modern claims before publishing any block scope so a |
| 7160 | // duplicate handle cannot emit the first block and fail only at the |
| 7161 | // second one. Legacy files have no entMap, but their BLOCK/ENDBLK pair |
| 7162 | // still benefits from the same duplicate/missing-handle quarantine. |
| 7163 | std::unordered_map<std::uint32_t, const DRW_Block_Record *> claimedHandles; |
| 7164 | std::unordered_set<const DRW_Block_Record *> invalidOwnershipRecords; |
| 7165 | const auto claimHandle = [&](const DRW_Block_Record *record, |
| 7166 | std::uint32_t handle) { |
| 7167 | if (handle == DRW::NoHandle) { |
| 7168 | recordBlockFailure(*record, handle, -1, |
| 7169 | DwgEntityFailurePhase::BlockFinalize); |
| 7170 | invalidOwnershipRecords.insert(record); |
| 7171 | return; |
| 7172 | } |
| 7173 | const auto objectIt = ObjectMap.find(handle); |
| 7174 | const auto deferredIt = objObjectMap.find(handle); |
| 7175 | if (objectIt != ObjectMap.end() && deferredIt != objObjectMap.end()) { |
| 7176 | (void)reportDwgFrameTransitionFailure(sourceFrameId(objectIt->second), |
| 7177 | objectIt->second.loc, true); |
| 7178 | recordBlockFailure(*record, handle, -1, |
| 7179 | DwgEntityFailurePhase::BlockFinalize); |
| 7180 | invalidOwnershipRecords.insert(record); |
| 7181 | return; |
| 7182 | } |
| 7183 | if (objectIt == ObjectMap.end()) { |
| 7184 | recordBlockFailure(*record, handle, -1, |
| 7185 | DwgEntityFailurePhase::BlockFinalize); |
| 7186 | invalidOwnershipRecords.insert(record); |
| 7187 | return; |
| 7188 | } |
| 7189 | const auto [it, inserted] = claimedHandles.emplace(handle, record); |
| 7190 | if (!inserted) { |
| 7191 | recordBlockFailure(*record, handle, -1, |
| 7192 | DwgEntityFailurePhase::BlockFinalize); |
| 7193 | invalidOwnershipRecords.insert(record); |
| 7194 | invalidOwnershipRecords.insert(it->second); |
| 7195 | } |
| 7196 | }; |
| 7197 | for (const auto &item : blockRecordmap) { |
| 7198 | const DRW_Block_Record *record = item.second; |
| 7199 | claimHandle(record, record->block); |
| 7200 | claimHandle(record, record->endBlock); |
| 7201 | if (version > DRW::AC1015) { |
| 7202 | for (const std::uint32_t handle : record->entMap) |
| 7203 | claimHandle(record, handle); |
| 7204 | } |
| 7205 | } |
| 7206 | if (!invalidOwnershipRecords.empty()) { |
| 7207 | for (const DRW_Block_Record *record : invalidOwnershipRecords) { |
| 7208 | quarantineOwnedEntities(*record); |
| 7209 | } |
| 7210 | ret = false; |
| 7211 | } |
| 7212 | |
| 7213 | if (version >= DRW::AC1018) { |
| 7214 | try { |
| 7215 | std::vector<const DRW_Block_Record *> records; |
| 7216 | records.reserve(blockRecordmap.size()); |
| 7217 | for (const auto &item : blockRecordmap) |
| 7218 | records.push_back(item.second); |
| 7219 | if (!preflightMappedPolylineOwnership(records, dbuf)) |
| 7220 | return false; |
| 7221 | } catch (...) { |
| 7222 | return false; |
| 7223 | } |
| 7224 | } |
| 7225 | |
| 7226 | for (auto it = blockRecordmap.begin(); it != blockRecordmap.end(); ++it) { |
| 7227 | DRW_Block_Record *bkr = it->second; |
| 7228 | if (invalidOwnershipRecords.find(bkr) != invalidOwnershipRecords.end()) |
| 7229 | continue; |
| 7230 | DRW_DBG("\nParsing Block, record handle= ")DRW_dbg::dbg("\nParsing Block, record handle= "); |
| 7231 | DRW_DBGH(it->first)DRW_dbg::dbgH(it->first); |
| 7232 | DRW_DBG(" Name= ")DRW_dbg::dbg(" Name= "); |
| 7233 | DRW_DBG(bkr->name)DRW_dbg::dbg(bkr->name); |
| 7234 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 7235 | DRW_DBG("\nFinding Block, handle= ")DRW_dbg::dbg("\nFinding Block, handle= "); |
| 7236 | DRW_DBGH(bkr->block)DRW_dbg::dbgH(bkr->block); |
| 7237 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 7238 | auto mit = ObjectMap.find(bkr->block); |
| 7239 | if (mit == ObjectMap.end()) { |
| 7240 | DRW_DBG("\nWARNING: block entity not found\n")DRW_dbg::dbg("\nWARNING: block entity not found\n"); |
| 7241 | recordBlockFailure(*bkr, bkr->block, dwgType::BLOCK, |
| 7242 | DwgEntityFailurePhase::Frame); |
| 7243 | quarantineOwnedEntities(*bkr); |
| 7244 | ret = false; |
| 7245 | continue; |
| 7246 | } |
| 7247 | objHandle oc = mit->second; |
| 7248 | DRW_DBG("Block Handle= ")DRW_dbg::dbg("Block Handle= "); |
| 7249 | DRW_DBGH(oc.handle)DRW_dbg::dbgH(oc.handle); |
| 7250 | DRW_DBG(" Location: ")DRW_dbg::dbg(" Location: "); |
| 7251 | DRW_DBG(oc.loc)DRW_dbg::dbg(oc.loc); |
| 7252 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 7253 | DwgObjectFrame frame; |
| 7254 | if (!frame.readAt(*dbuf, version, oc.loc)) { |
| 7255 | recordObjectFrameFailure(oc, offsetSpace); |
| 7256 | DRW_DBG("Invalid block entity frame\n")DRW_dbg::dbg("Invalid block entity frame\n"); |
| 7257 | recordBlockFailure(*bkr, oc.handle, dwgType::BLOCK, |
| 7258 | DwgEntityFailurePhase::Frame); |
| 7259 | quarantineOwnedEntities(*bkr); |
| 7260 | ret = false; |
| 7261 | continue; |
| 7262 | } |
| 7263 | auto &body = frame.body(); |
| 7264 | dwgBuffer buff(body.data(), body.size(), &decoder); |
| 7265 | DRW_Block bk; |
| 7266 | dwgBuffer typeBuffer = buff.forkIndependent(); |
| 7267 | if (typeBuffer.getObjType(version) != dwgType::BLOCK || |
| 7268 | !typeBuffer.isGood()) { |
| 7269 | DRW_DBG("Invalid block entity type\n")DRW_dbg::dbg("Invalid block entity type\n"); |
| 7270 | recordBlockFailure(*bkr, oc.handle, dwgType::BLOCK, |
| 7271 | DwgEntityFailurePhase::TypedBody); |
| 7272 | quarantineOwnedEntities(*bkr); |
| 7273 | ret = false; |
| 7274 | continue; |
| 7275 | } |
| 7276 | if (!parseBlock(bk, buff, frame.bodyBitSize())) { |
| 7277 | DRW_DBG("Invalid block entity body\n")DRW_dbg::dbg("Invalid block entity body\n"); |
| 7278 | recordBlockFailure(*bkr, oc.handle, dwgType::BLOCK, |
| 7279 | DwgEntityFailurePhase::TypedBody); |
| 7280 | quarantineOwnedEntities(*bkr); |
| 7281 | ret = false; |
| 7282 | continue; |
| 7283 | } |
| 7284 | if (bk.handle != oc.handle || bk.handle != bkr->block) { |
| 7285 | DRW_DBG("BLOCK handle does not match its BLOCK_RECORD\n")DRW_dbg::dbg("BLOCK handle does not match its BLOCK_RECORD\n" ); |
| 7286 | recordBlockFailure(*bkr, oc.handle, dwgType::BLOCK, |
| 7287 | DwgEntityFailurePhase::Identity); |
| 7288 | quarantineOwnedEntities(*bkr); |
| 7289 | ret = false; |
| 7290 | continue; |
| 7291 | } |
| 7292 | const objHandle blockObject = oc; |
| 7293 | parseAttribs(&bk); |
| 7294 | // complete block entity with block record data |
| 7295 | bk.basePoint = bkr->basePoint; |
| 7296 | bk.flags = bkr->flags; |
| 7297 | bk.insUnits = bkr->insUnits; |
| 7298 | bk.xrefPath = bkr->xrefPath; |
| 7299 | |
| 7300 | // Validate the ownership graph before publishing any callbacks. The |
| 7301 | // R2004+ BLOCK_HEADER vector is authoritative; a missing, duplicate, |
| 7302 | // or special block/ENDBLK handle would otherwise leave a partial block |
| 7303 | // scope and force the later ObjectMap sweep to guess ownership. |
| 7304 | if (version > DRW::AC1015) { |
| 7305 | std::unordered_set<std::uint32_t> ownedHandles; |
| 7306 | if (bkr->entMap.size() > |
| 7307 | static_cast<std::size_t>(std::numeric_limits<int>::max()) || |
| 7308 | !DRW::reserve(ownedHandles, static_cast<int>(bkr->entMap.size()))) { |
| 7309 | quarantineOwnedEntities(*bkr); |
| 7310 | ret = false; |
| 7311 | continue; |
| 7312 | } |
| 7313 | bool validOwnership = true; |
| 7314 | std::uint32_t invalidOwnershipHandle = DRW::NoHandle; |
| 7315 | for (const std::uint32_t entityHandle : bkr->entMap) { |
| 7316 | if (entityHandle == DRW::NoHandle || entityHandle == bkr->block || |
| 7317 | entityHandle == bkr->endBlock || |
| 7318 | ObjectMap.find(entityHandle) == ObjectMap.end() || |
| 7319 | !ownedHandles.insert(entityHandle).second) { |
| 7320 | validOwnership = false; |
| 7321 | invalidOwnershipHandle = entityHandle; |
| 7322 | break; |
| 7323 | } |
| 7324 | } |
| 7325 | if (validOwnership) { |
| 7326 | // BLOCK_RECORD.entMap contains entity handles only. Fixed |
| 7327 | // OBJECTS types and custom classes with entityFlag == 0 must |
| 7328 | // not be allowed into the entity walk, where they would be |
| 7329 | // deferred and later decoded under the wrong ownership. |
| 7330 | for (const std::uint32_t entityHandle : bkr->entMap) { |
| 7331 | const auto entityIt = ObjectMap.find(entityHandle); |
| 7332 | if (entityIt == ObjectMap.end()) { |
| 7333 | validOwnership = false; |
| 7334 | break; |
| 7335 | } |
| 7336 | DwgFrameClassification classification; |
| 7337 | if (!classifyDwgSourceFrame(dbuf, entityIt->second, classification)) { |
| 7338 | recordObjectFrameFailure(entityIt->second, offsetSpace); |
| 7339 | validOwnership = false; |
| 7340 | break; |
| 7341 | } |
| 7342 | if (classification.route != DwgFrameClassification::Route::Entity) { |
| 7343 | validOwnership = false; |
| 7344 | break; |
| 7345 | } |
| 7346 | } |
| 7347 | } |
| 7348 | if (!validOwnership) { |
| 7349 | DRW_DBG("Invalid BLOCK_RECORD owned-entity handle list\n")DRW_dbg::dbg("Invalid BLOCK_RECORD owned-entity handle list\n" ); |
| 7350 | recordBlockFailure(*bkr, invalidOwnershipHandle, -1, |
| 7351 | DwgEntityFailurePhase::BlockFinalize); |
| 7352 | quarantineOwnedEntities(*bkr); |
| 7353 | ret = false; |
| 7354 | continue; |
| 7355 | } |
| 7356 | } |
| 7357 | |
| 7358 | auto endIt = ObjectMap.find(bkr->endBlock); |
| 7359 | if (endIt == ObjectMap.end()) { |
| 7360 | DRW_DBG("\nWARNING: end block entity not found\n")DRW_dbg::dbg("\nWARNING: end block entity not found\n"); |
| 7361 | recordBlockFailure(*bkr, bkr->endBlock, dwgType::ENDBLK, |
| 7362 | DwgEntityFailurePhase::Frame); |
| 7363 | quarantineOwnedEntities(*bkr); |
| 7364 | ret = false; |
| 7365 | continue; |
| 7366 | } |
| 7367 | oc = endIt->second; |
| 7368 | DRW_DBG("End block Handle= ")DRW_dbg::dbg("End block Handle= "); |
| 7369 | DRW_DBGH(oc.handle)DRW_dbg::dbgH(oc.handle); |
| 7370 | DRW_DBG(" Location: ")DRW_dbg::dbg(" Location: "); |
| 7371 | DRW_DBG(oc.loc)DRW_dbg::dbg(oc.loc); |
| 7372 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 7373 | DwgObjectFrame endFrame; |
| 7374 | if (!endFrame.readAt(*dbuf, version, oc.loc)) { |
| 7375 | recordObjectFrameFailure(oc, offsetSpace); |
| 7376 | DRW_DBG("Invalid end block entity frame\n")DRW_dbg::dbg("Invalid end block entity frame\n"); |
| 7377 | recordBlockFailure(*bkr, oc.handle, dwgType::ENDBLK, |
| 7378 | DwgEntityFailurePhase::Frame); |
| 7379 | quarantineOwnedEntities(*bkr); |
| 7380 | ret = false; |
| 7381 | continue; |
| 7382 | } |
| 7383 | auto &endBody = endFrame.body(); |
| 7384 | dwgBuffer buff1(endBody.data(), endBody.size(), &decoder); |
| 7385 | DRW_Block end; |
| 7386 | end.isEnd = true; |
| 7387 | dwgBuffer endTypeBuffer = buff1.forkIndependent(); |
| 7388 | if (endTypeBuffer.getObjType(version) != dwgType::ENDBLK || |
| 7389 | !endTypeBuffer.isGood()) { |
| 7390 | DRW_DBG("Invalid end block entity type\n")DRW_dbg::dbg("Invalid end block entity type\n"); |
| 7391 | recordBlockFailure(*bkr, oc.handle, dwgType::ENDBLK, |
| 7392 | DwgEntityFailurePhase::TypedBody); |
| 7393 | quarantineOwnedEntities(*bkr); |
| 7394 | ret = false; |
| 7395 | continue; |
| 7396 | } |
| 7397 | if (!parseBlock(end, buff1, endFrame.bodyBitSize())) { |
| 7398 | DRW_DBG("Invalid end block entity body\n")DRW_dbg::dbg("Invalid end block entity body\n"); |
| 7399 | recordBlockFailure(*bkr, oc.handle, dwgType::ENDBLK, |
| 7400 | DwgEntityFailurePhase::TypedBody); |
| 7401 | quarantineOwnedEntities(*bkr); |
| 7402 | ret = false; |
| 7403 | continue; |
| 7404 | } |
| 7405 | if (end.handle != oc.handle || end.handle != bkr->endBlock) { |
| 7406 | DRW_DBG("ENDBLK handle does not match its BLOCK_RECORD\n")DRW_dbg::dbg("ENDBLK handle does not match its BLOCK_RECORD\n" ); |
| 7407 | recordBlockFailure(*bkr, oc.handle, dwgType::ENDBLK, |
| 7408 | DwgEntityFailurePhase::Identity); |
| 7409 | quarantineOwnedEntities(*bkr); |
| 7410 | ret = false; |
| 7411 | continue; |
| 7412 | } |
| 7413 | const objHandle endBlockObject = oc; |
| 7414 | parseAttribs(&end); |
| 7415 | // BLOCK and ENDBLK delimit the same BLOCK_RECORD scope. Named blocks |
| 7416 | // carry that owner in both records; modelspace/paperspace use the |
| 7417 | // ownerless form in files handled here. Reject a mismatched pair |
| 7418 | // before publishing either scope boundary. |
| 7419 | if (bk.parentHandle != end.parentHandle) { |
| 7420 | DRW_DBG("Mismatched BLOCK/ENDBLK owner handles\n")DRW_dbg::dbg("Mismatched BLOCK/ENDBLK owner handles\n"); |
| 7421 | recordBlockFailure(*bkr, bkr->handle, -1, |
| 7422 | DwgEntityFailurePhase::Identity); |
| 7423 | quarantineOwnedEntities(*bkr); |
| 7424 | ret = false; |
| 7425 | continue; |
| 7426 | } |
| 7427 | const bool isSpaceBlockRecord = isSpaceBlockRecordName(bk.name); |
| 7428 | const bool validDelimiterOwner = isSpaceBlockRecord |
| 7429 | ? bk.parentHandle == DRW::NoHandle |
| 7430 | : bk.parentHandle == bkr->handle; |
| 7431 | if (!validDelimiterOwner) { |
| 7432 | DRW_DBG("BLOCK/ENDBLK owner does not match BLOCK_RECORD\n")DRW_dbg::dbg("BLOCK/ENDBLK owner does not match BLOCK_RECORD\n" ); |
| 7433 | recordBlockFailure(*bkr, bkr->handle, -1, |
| 7434 | DwgEntityFailurePhase::Identity); |
| 7435 | quarantineOwnedEntities(*bkr); |
| 7436 | ret = false; |
| 7437 | continue; |
| 7438 | } |
| 7439 | // Keep delimiter receipts private until the complete scope has been |
| 7440 | // accepted. addBlock() necessarily precedes the owned-entity walk, |
| 7441 | // so a later child failure cannot establish a complete BLOCK graph |
| 7442 | // record. Preserve the source parent before the model/paper-space |
| 7443 | // routing below normalizes bk.parentHandle for interface delivery. |
| 7444 | const DRW_DwgFramePublication blockPublication = |
| 7445 | makeTypedEntityFramePublication(version, blockObject, dwgType::BLOCK, |
| 7446 | bk); |
| 7447 | const DRW_DwgFramePublication endBlockPublication = |
| 7448 | makeTypedEntityFramePublication(version, endBlockObject, |
| 7449 | dwgType::ENDBLK, end); |
| 7450 | const std::size_t entityFailuresBefore = m_entityParseFailures; |
| 7451 | const std::size_t entityDiagnosticsBefore = |
| 7452 | m_entityFailureDiagnostics.size(); |
| 7453 | |
| 7454 | // A modern block made solely of independently deliverable entities |
| 7455 | // can be admitted as one callback transaction. Compound and custom |
| 7456 | // entities keep their established staged delivery path below. |
| 7457 | bool journalEligible = version >= DRW::AC1018; |
| 7458 | if (journalEligible) { |
| 7459 | for (const std::uint32_t entityHandle : bkr->entMap) { |
| 7460 | const auto entityIt = ObjectMap.find(entityHandle); |
| 7461 | DwgFrameClassification classification; |
| 7462 | if (entityIt == ObjectMap.end() || |
| 7463 | !classifyDwgSourceFrame(dbuf, entityIt->second, classification) || |
| 7464 | classification.route != DwgFrameClassification::Route::Entity) { |
| 7465 | journalEligible = false; |
| 7466 | break; |
| 7467 | } |
| 7468 | } |
| 7469 | } |
| 7470 | if (journalEligible) { |
| 7471 | DwgBlockScopeTransaction transaction(*this); |
| 7472 | const auto discardUnadopted = [this](DwgFrameMapLease &lease) { |
| 7473 | if (!lease.isDetached()) |
| 7474 | return; |
| 7475 | if (lease.hasCoverage) |
| 7476 | (void)quarantineDwgFrame(lease.source); |
| 7477 | else |
| 7478 | (void)suppressDwgFrame(lease.source, false); |
| 7479 | (void)discardDetachedDwgSourceFrame(lease); |
| 7480 | }; |
| 7481 | const auto stageDelimiter = [this, &transaction, &discardUnadopted]( |
| 7482 | std::uint32_t handle, |
| 7483 | std::uint32_t bodyByteSize) { |
| 7484 | const auto delimiterIt = ObjectMap.find(handle); |
| 7485 | if (delimiterIt == ObjectMap.end()) |
| 7486 | return false; |
| 7487 | if (!transaction.reserveAdmission(1u, 2u, bodyByteSize)) |
| 7488 | return false; |
| 7489 | DwgFrameMapLease lease; |
| 7490 | if (!detachDwgSourceFrame(ObjectMap, delimiterIt, lease)) |
| 7491 | return false; |
| 7492 | lease.bodyByteSize = bodyByteSize; |
| 7493 | if (!stageDetachedDwgSourceFrame(lease) || !transaction.adopt(lease)) { |
| 7494 | discardUnadopted(lease); |
| 7495 | return false; |
| 7496 | } |
| 7497 | return true; |
| 7498 | }; |
| 7499 | |
| 7500 | bool journalled = false; |
| 7501 | try { |
| 7502 | journalled = stageDelimiter(blockObject.handle, |
| 7503 | static_cast<std::uint32_t>(body.size())) && |
| 7504 | stageDelimiter(endBlockObject.handle, |
| 7505 | static_cast<std::uint32_t>(endBody.size())); |
| 7506 | const DwgSourceFrameId blockSource = sourceFrameId(blockObject); |
| 7507 | const DwgSourceFrameId endBlockSource = sourceFrameId(endBlockObject); |
| 7508 | DRW_Block deliveryBlock = bk; |
| 7509 | const bool deferredEntityWalk = |
| 7510 | deliveryBlock.parentHandle == DRW::NoHandle; |
| 7511 | if (deferredEntityWalk) |
| 7512 | deliveryBlock.parentHandle = bkr->handle; |
| 7513 | |
| 7514 | if (journalled) { |
| 7515 | auto blockScope = transaction.output().bindSource(blockSource); |
| 7516 | transaction.output().appendValue(deliveryBlock, |
| 7517 | &DRW_Interface::addBlock); |
| 7518 | } |
| 7519 | if (journalled && deferredEntityWalk) { |
| 7520 | auto endBlockScope = transaction.output().bindSource(endBlockSource); |
| 7521 | journalled = transaction.output().appendEndBlock(); |
| 7522 | } |
| 7523 | if (journalled) { |
| 7524 | journalled = walkJournalledBlockRecordEntities( |
| 7525 | bkr, dbuf, intfa, transaction, |
| 7526 | deferredEntityWalk ? DRW::NoHandle : bk.parentHandle, bkr->handle, |
| 7527 | offsetSpace); |
| 7528 | } |
| 7529 | if (journalled && !deferredEntityWalk) { |
| 7530 | auto endBlockScope = transaction.output().bindSource(endBlockSource); |
| 7531 | journalled = transaction.output().appendEndBlock(); |
| 7532 | } |
| 7533 | std::optional<DRW_DwgBlockReachability> reachability; |
| 7534 | if (journalled) { |
| 7535 | const auto receiptSource = [this](const DwgSourceFrameId &source) |
| 7536 | -> std::optional<DRW_DwgSourceFrame> { |
| 7537 | const auto sourceIt = m_dwgSourceFrameIndexes.find(source.handle); |
| 7538 | if (source.handle == DRW::NoHandle || |
| 7539 | sourceIt == m_dwgSourceFrameIndexes.cend() || |
| 7540 | sourceIt->second >= m_dwgSourceFrameLedger.size()) { |
| 7541 | return std::nullopt; |
| 7542 | } |
| 7543 | const DRW_DwgFrameCoverageEntry &entry = |
| 7544 | m_dwgSourceFrameLedger[sourceIt->second]; |
| 7545 | if (entry.m_handle != source.handle || |
| 7546 | entry.m_sourceOffset != source.offset || |
| 7547 | entry.m_sourceMapOrdinal != source.ordinal || |
| 7548 | entry.m_sourceOffsetSpace != source.offsetSpace) { |
| 7549 | return std::nullopt; |
| 7550 | } |
| 7551 | return DRW_DwgSourceFrame{entry.m_handle, entry.m_sourceOffset, |
| 7552 | entry.m_sourceMapOrdinal, |
| 7553 | entry.m_sourceOffsetSpace, true}; |
| 7554 | }; |
| 7555 | const auto recordSource = sourceFrameIdForHandle(bkr->handle); |
| 7556 | const auto recordReceipt = receiptSource(recordSource); |
| 7557 | // Direct reader probes can exercise a block scope without |
| 7558 | // first parsing its BLOCK_RECORD table frame. Such a |
| 7559 | // scope has no public record source to certify; genuine |
| 7560 | // DWG reads always retain that source in the ledger. |
| 7561 | if (recordReceipt.has_value()) { |
| 7562 | const auto recordIndex = m_dwgSourceFrameIndexes.find(bkr->handle); |
| 7563 | if (recordIndex == m_dwgSourceFrameIndexes.cend() || |
| 7564 | recordIndex->second >= m_dwgSourceFrameLedger.size() || |
| 7565 | m_dwgSourceFrameLedger[recordIndex->second].m_disposition != |
| 7566 | DRW_DwgFrameDisposition::Published || |
| 7567 | m_dwgSourceFrameLedger[recordIndex->second] |
| 7568 | .m_publicationCount != 1u) { |
| 7569 | journalled = false; |
| 7570 | } else { |
| 7571 | const auto blockReceipt = receiptSource(blockSource); |
| 7572 | const auto endBlockReceipt = receiptSource(endBlockSource); |
| 7573 | if (!blockReceipt.has_value() || !endBlockReceipt.has_value()) { |
| 7574 | journalled = false; |
| 7575 | } else { |
| 7576 | DRW_DwgBlockReachability receipt; |
| 7577 | receipt.m_version = version; |
| 7578 | receipt.m_complete = true; |
| 7579 | receipt.m_blockRecord = *recordReceipt; |
| 7580 | receipt.m_block = *blockReceipt; |
| 7581 | receipt.m_endBlock = *endBlockReceipt; |
| 7582 | try { |
| 7583 | receipt.m_entities.reserve(bkr->entMap.size()); |
| 7584 | for (const std::uint32_t entityHandle : bkr->entMap) { |
| 7585 | const auto entityReceipt = |
| 7586 | receiptSource(sourceFrameIdForHandle(entityHandle)); |
| 7587 | if (!entityReceipt.has_value()) { |
| 7588 | journalled = false; |
| 7589 | break; |
| 7590 | } |
| 7591 | receipt.m_entities.push_back(*entityReceipt); |
| 7592 | } |
| 7593 | } catch (...) { |
| 7594 | journalled = false; |
| 7595 | } |
| 7596 | if (journalled) |
| 7597 | reachability.emplace(std::move(receipt)); |
| 7598 | } |
| 7599 | } |
| 7600 | } |
| 7601 | } |
| 7602 | if (journalled) { |
| 7603 | journalled = |
| 7604 | transaction.output().appendFramePublication( |
| 7605 | *this, blockPublication, |
| 7606 | {nullptr, nullptr, |
| 7607 | reachability.has_value() ? &*reachability : nullptr}) && |
| 7608 | transaction.output().appendFramePublication(*this, |
| 7609 | endBlockPublication); |
| 7610 | } |
| 7611 | if (journalled) { |
| 7612 | journalled = transaction.replay(intfa); |
| 7613 | if (journalled) |
| 7614 | bkr->name = bk.name; |
| 7615 | } |
| 7616 | } catch (...) { |
| 7617 | journalled = false; |
| 7618 | } |
| 7619 | |
| 7620 | if (!journalled) { |
| 7621 | (void)transaction.abort(); |
| 7622 | if (hasPendingCompoundStateForBlock(*bkr)) |
| 7623 | (void)abandonStagedCompoundState(); |
| 7624 | if (m_entityFailureDiagnostics.size() == entityDiagnosticsBefore) { |
| 7625 | recordBlockFailure(*bkr, bkr->handle, -1, |
| 7626 | DwgEntityFailurePhase::BlockFinalize); |
| 7627 | } |
| 7628 | quarantineOwnedEntities(*bkr); |
| 7629 | ret = false; |
| 7630 | } |
| 7631 | continue; |
| 7632 | } |
| 7633 | |
| 7634 | /**read & send block entities**/ |
| 7635 | // Modelspace / paperspace block_records have no DWG-side parent |
| 7636 | // handle (the legacy "330 not set like dxf in ModelSpace & PaperSpace" |
| 7637 | // case). Their entities are still walked here, but post-endBlock so |
| 7638 | // they land in the interface's modelspace container rather than in |
| 7639 | // the just-opened addBlock scope. Walking in entMap / firstEH..lastEH |
| 7640 | // order also guarantees POLYLINE parents precede their VERTEX |
| 7641 | // children, which the staged POLYLINE chain's bounded source lookup |
| 7642 | // requires. |
| 7643 | const bool deferredEntityWalk = (bk.parentHandle == DRW::NoHandle); |
| 7644 | if (deferredEntityWalk) { |
| 7645 | bk.parentHandle = bkr->handle; |
| 7646 | } |
| 7647 | bool blockScopeOpened = false; |
| 7648 | bool blockScopeFailure = false; |
| 7649 | bool blockEntityWalkSucceeded = true; |
| 7650 | try { |
| 7651 | intfa.addBlock(bk); |
| 7652 | blockScopeOpened = true; |
| 7653 | // and update block record name |
| 7654 | bkr->name = bk.name; |
| 7655 | |
| 7656 | if (!deferredEntityWalk) { |
| 7657 | const bool walked = walkBlockRecordEntities( |
| 7658 | bkr, dbuf, intfa, bk.parentHandle, bkr->handle, offsetSpace); |
| 7659 | blockEntityWalkSucceeded = walked; |
| 7660 | ret = walked && ret; |
| 7661 | blockScopeFailure = blockScopeFailure || !walked; |
| 7662 | } |
| 7663 | } catch (...) { |
| 7664 | // Interface callbacks are outside the byte parser's control. Do |
| 7665 | // not let one callback exception escape with a half-admitted |
| 7666 | // block; the scope is closed below exactly once when opened. |
| 7667 | ret = false; |
| 7668 | blockScopeFailure = true; |
| 7669 | } |
| 7670 | |
| 7671 | if (blockScopeOpened) { |
| 7672 | try { |
| 7673 | intfa.endBlock(); |
| 7674 | } catch (...) { |
| 7675 | ret = false; |
| 7676 | blockScopeFailure = true; |
| 7677 | } |
| 7678 | } |
| 7679 | |
| 7680 | if (deferredEntityWalk && !blockScopeFailure) { |
| 7681 | // currentBlock has just been reset to the interface's modelspace |
| 7682 | // container; dispatched entities flow there. |
| 7683 | try { |
| 7684 | const bool walked = walkBlockRecordEntities( |
| 7685 | bkr, dbuf, intfa, DRW::NoHandle, bkr->handle, offsetSpace); |
| 7686 | blockEntityWalkSucceeded = walked; |
| 7687 | ret = walked && ret; |
| 7688 | blockScopeFailure = blockScopeFailure || !walked; |
| 7689 | } catch (...) { |
| 7690 | ret = false; |
| 7691 | blockScopeFailure = true; |
| 7692 | } |
| 7693 | } |
| 7694 | |
| 7695 | blockScopeFailure = blockScopeFailure || !blockEntityWalkSucceeded || |
| 7696 | m_entityParseFailures != entityFailuresBefore; |
| 7697 | |
| 7698 | if (blockScopeOpened && !blockScopeFailure && |
| 7699 | blockEntityWalkSucceeded |
| 7700 | // An unresolved INSERT delays only the delimiters of the block |
| 7701 | // that owns it; independent scopes can complete normally. |
| 7702 | && !hasPendingCompoundStateForBlock(*bkr)) { |
| 7703 | const auto commitDelimiter = [&](std::uint32_t handle, |
| 7704 | DwgSourceFrameLease &lease) { |
| 7705 | const auto delimiterIt = ObjectMap.find(handle); |
| 7706 | return delimiterIt != ObjectMap.end() && |
| 7707 | borrowDwgSourceFrame(ObjectMap, delimiterIt, lease); |
| 7708 | }; |
| 7709 | DwgSourceFrameLease blockLease; |
| 7710 | DwgSourceFrameLease endBlockLease; |
| 7711 | if (!commitDelimiter(blockObject.handle, blockLease) || |
| 7712 | !commitDelimiter(endBlockObject.handle, endBlockLease)) { |
| 7713 | ret = false; |
| 7714 | blockScopeFailure = true; |
| 7715 | } else { |
| 7716 | const auto blockIt = ObjectMap.find(blockObject.handle); |
| 7717 | const auto endIt = ObjectMap.find(endBlockObject.handle); |
| 7718 | if (blockIt == ObjectMap.end() || endIt == ObjectMap.end()) { |
| 7719 | ret = false; |
| 7720 | blockScopeFailure = true; |
| 7721 | } else { |
| 7722 | // Both entries were borrowed above. Node extraction is |
| 7723 | // allocation-free, so commit the delimiter pair without |
| 7724 | // a state where one delimiter has been removed and the |
| 7725 | // other remains in the source map. |
| 7726 | DwgObjectMap::node_type blockNode = ObjectMap.extract(blockIt); |
| 7727 | DwgObjectMap::node_type endBlockNode = ObjectMap.extract(endIt); |
| 7728 | if (blockNode.empty() || endBlockNode.empty()) { |
| 7729 | if (!blockNode.empty()) |
| 7730 | ObjectMap.insert(std::move(blockNode)); |
| 7731 | if (!endBlockNode.empty()) |
| 7732 | ObjectMap.insert(std::move(endBlockNode)); |
| 7733 | ret = false; |
| 7734 | blockScopeFailure = true; |
| 7735 | } |
| 7736 | } |
| 7737 | } |
| 7738 | } |
| 7739 | |
| 7740 | if (blockScopeOpened && !blockScopeFailure && blockEntityWalkSucceeded && |
| 7741 | !hasPendingCompoundStateForBlock(*bkr) && |
| 7742 | m_dwgFrameCoverageStatus != DRW_DwgFrameCoverageStatus::NotAvailable) { |
| 7743 | try { |
| 7744 | if (!publishDwgFramePublication(intfa, blockPublication) || |
| 7745 | !publishDwgFramePublication(intfa, endBlockPublication)) { |
| 7746 | ret = false; |
| 7747 | blockScopeFailure = true; |
| 7748 | } |
| 7749 | } catch (...) { |
| 7750 | ret = false; |
| 7751 | blockScopeFailure = true; |
| 7752 | } |
| 7753 | } |
| 7754 | |
| 7755 | if (blockScopeFailure) { |
| 7756 | if (m_entityFailureDiagnostics.size() == entityDiagnosticsBefore) { |
| 7757 | recordBlockFailure(*bkr, bkr->handle, -1, |
| 7758 | DwgEntityFailurePhase::BlockFinalize); |
| 7759 | } |
| 7760 | quarantineOwnedEntities(*bkr); |
| 7761 | } |
| 7762 | } |
| 7763 | |
| 7764 | return ret; |
| 7765 | } |
| 7766 | |
| 7767 | bool dwgReader::walkBlockRecordEntities(DRW_Block_Record *bkr, dwgBuffer *dbuf, |
| 7768 | DRW_Interface &intfa, |
| 7769 | std::uint32_t expectedOwner, |
| 7770 | std::uint32_t rawBlockOwner, |
| 7771 | DwgIntegrityAddressSpace offsetSpace) { |
| 7772 | // Per-entity parseDwg failures are warnings, not section failures — |
| 7773 | // we keep walking so a single bad entity doesn't drop the rest of |
| 7774 | // the block. Structural failures (entity-not-found in ObjectMap) |
| 7775 | // remain in `ret` so the caller knows the block walk was incomplete. |
| 7776 | bool ret = true; |
| 7777 | objHandle oc; |
| 7778 | const std::uint32_t previousOwner = expectedBlockEntityOwner; |
| 7779 | const std::uint32_t previousRawOwner = rawBlockEntityOwner; |
| 7780 | const bool previousOwnerlessSpaceWalk = ownerlessSpaceWalk; |
| 7781 | expectedBlockEntityOwner = expectedOwner; |
| 7782 | rawBlockEntityOwner = |
| 7783 | rawBlockOwner != DRW::NoHandle ? rawBlockOwner : expectedOwner; |
| 7784 | ownerlessSpaceWalk = |
| 7785 | expectedOwner == DRW::NoHandle && isSpaceBlockRecordName(bkr->name); |
| 7786 | const auto recordWalkFailure = [this, bkr](const objHandle &object, |
| 7787 | std::int16_t type, |
| 7788 | DwgEntityFailurePhase phase) { |
| 7789 | recordEntityFailure(object, type, phase, bkr->handle); |
| 7790 | }; |
| 7791 | const auto unresolvedCompoundHandle = [this, bkr]() -> std::uint32_t { |
| 7792 | const auto containsEntity = [bkr](std::uint32_t handle) { |
| 7793 | return std::find(bkr->entMap.cbegin(), bkr->entMap.cend(), handle) != |
| 7794 | bkr->entMap.cend(); |
| 7795 | }; |
| 7796 | const auto belongsToBlock = [bkr, |
| 7797 | &containsEntity](const DRW_Entity &entity) { |
| 7798 | return containsEntity(entity.handle) || |
| 7799 | entity.parentHandle == bkr->handle || |
| 7800 | (entity.parentHandle == DRW::NoHandle && |
| 7801 | isSpaceBlockRecordName(bkr->name)); |
| 7802 | }; |
| 7803 | for (const auto &item : m_pendingInsertStates) { |
| 7804 | if (belongsToBlock(item.second.entity)) |
| 7805 | return item.first; |
| 7806 | } |
| 7807 | for (const auto &item : m_pendingPolylineStates) { |
| 7808 | if (belongsToBlock(item.second.entity)) |
| 7809 | return item.first; |
| 7810 | } |
| 7811 | for (const auto &item : m_orphanAttribStates) { |
| 7812 | for (const StagedAttribState &attribute : item.second.attributes) { |
| 7813 | if (attribute.entity != nullptr && |
| 7814 | containsEntity(attribute.entity->handle)) { |
| 7815 | return attribute.entity->handle; |
| 7816 | } |
| 7817 | } |
| 7818 | } |
| 7819 | for (const auto &item : m_orphanPolylineVertexStates) { |
| 7820 | for (const StagedVertexState &vertex : item.second.vertices) { |
| 7821 | if (containsEntity(vertex.entity.handle)) |
| 7822 | return vertex.entity.handle; |
| 7823 | } |
| 7824 | } |
| 7825 | for (const auto &item : m_stagedSeqEnds) { |
| 7826 | if (containsEntity(item.first)) |
| 7827 | return item.first; |
| 7828 | } |
| 7829 | return static_cast<std::uint32_t>(DRW::NoHandle); |
| 7830 | }; |
| 7831 | |
| 7832 | const auto restoreState = [this, previousOwner, previousRawOwner, |
| 7833 | previousOwnerlessSpaceWalk]() noexcept { |
| 7834 | expectedBlockEntityOwner = previousOwner; |
| 7835 | rawBlockEntityOwner = previousRawOwner; |
| 7836 | ownerlessSpaceWalk = previousOwnerlessSpaceWalk; |
| 7837 | }; |
| 7838 | |
| 7839 | if (version >= DRW::AC1018) { |
| 7840 | bool hasDuplicateSource = false; |
| 7841 | std::uint32_t duplicateSourceHandle = DRW::NoHandle; |
| 7842 | std::unordered_set<std::uint32_t> sourceHandles; |
| 7843 | try { |
| 7844 | sourceHandles.reserve(bkr->entMap.size()); |
| 7845 | for (const std::uint32_t handle : bkr->entMap) { |
| 7846 | if (!sourceHandles.insert(handle).second) { |
| 7847 | hasDuplicateSource = true; |
| 7848 | duplicateSourceHandle = handle; |
| 7849 | } |
| 7850 | } |
| 7851 | } catch (...) { |
| 7852 | restoreState(); |
| 7853 | return false; |
| 7854 | } |
| 7855 | |
| 7856 | if (hasDuplicateSource) { |
| 7857 | // A BLOCK_RECORD names each physical source frame once. Consumed |
| 7858 | // markers suppress later walks; they never legitimize a duplicate |
| 7859 | // entry in this record after another entity has been published. |
| 7860 | for (const std::uint32_t handle : sourceHandles) { |
| 7861 | auto objectIt = ObjectMap.find(handle); |
| 7862 | if (objectIt != ObjectMap.end()) { |
| 7863 | (void)discardDwgSourceFrame(ObjectMap, objectIt); |
| 7864 | continue; |
| 7865 | } |
| 7866 | auto deferredIt = objObjectMap.find(handle); |
| 7867 | if (deferredIt != objObjectMap.end()) |
| 7868 | (void)discardDwgSourceFrame(objObjectMap, deferredIt); |
| 7869 | } |
| 7870 | objHandle duplicate; |
| 7871 | duplicate.handle = duplicateSourceHandle; |
| 7872 | recordWalkFailure(duplicate, -1, DwgEntityFailurePhase::BlockFinalize); |
| 7873 | ++m_entityParseFailures; |
| 7874 | restoreState(); |
| 7875 | return false; |
| 7876 | } |
| 7877 | |
| 7878 | bool ownershipPreflight = false; |
| 7879 | try { |
| 7880 | const std::vector<const DRW_Block_Record *> records = {bkr}; |
| 7881 | ownershipPreflight = preflightMappedPolylineOwnership(records, dbuf); |
| 7882 | } catch (...) { |
| 7883 | ownershipPreflight = false; |
| 7884 | } |
| 7885 | if (!ownershipPreflight) { |
| 7886 | restoreState(); |
| 7887 | return false; |
| 7888 | } |
| 7889 | } |
| 7890 | |
| 7891 | try { |
| 7892 | if (version < DRW::AC1018) { // pre 2004 |
| 7893 | std::uint32_t nextH = bkr->firstEH; |
| 7894 | std::unordered_set<std::uint32_t> visitedHandles; |
| 7895 | while (nextH != 0) { |
| 7896 | if (!visitedHandles.insert(nextH).second) { |
| 7897 | // R13-R2000 stores ownership as a next_entity chain. A |
| 7898 | // corrupt cycle must not hang the reader indefinitely. |
| 7899 | DRW_DBG("\nWARNING: Cyclic entity chain in block\n")DRW_dbg::dbg("\nWARNING: Cyclic entity chain in block\n"); |
| 7900 | ret = false; |
| 7901 | ++m_entityParseFailures; |
| 7902 | break; |
| 7903 | } |
| 7904 | auto mit = ObjectMap.find(nextH); |
| 7905 | if (mit == ObjectMap.end()) { |
| 7906 | // A broken/garbage nextEntLink at the chain end (common in real |
| 7907 | // R13–R2000 files) must NOT fail the BLOCKS section: the |
| 7908 | // remaining entities still sit in ObjectMap and are recovered by |
| 7909 | // the subsequent readDwgEntities sweep. Treat as a soft warning |
| 7910 | // (libreDWG parity) — stop chasing this chain but keep ret true. |
| 7911 | DRW_DBG("\nWARNING: Entity of block not found\n")DRW_dbg::dbg("\nWARNING: Entity of block not found\n"); |
| 7912 | ++m_entityParseFailures; |
| 7913 | break; |
| 7914 | } |
| 7915 | bool frameFailure = false; |
| 7916 | bool read = false; |
| 7917 | DwgFrameClassification classification; |
| 7918 | if (requiresLegacyCompoundHandling(dbuf, mit->second, |
| 7919 | &classification)) { |
| 7920 | DwgSourceFrameLease lease; |
| 7921 | if (!takeDwgSourceFrame(ObjectMap, mit, lease)) { |
| 7922 | ret = false; |
| 7923 | ++m_entityParseFailures; |
| 7924 | break; |
| 7925 | } |
| 7926 | oc = lease.object; |
| 7927 | read = readDwgEntity(dbuf, oc, intfa, &frameFailure, offsetSpace); |
| 7928 | if (!read) { |
| 7929 | (void)markDwgFrameOutcome(lease.source, |
| 7930 | DRW_DwgFrameDisposition::Failed); |
| 7931 | } |
| 7932 | } else { |
| 7933 | DwgFrameMapLease lease; |
| 7934 | if (!detachDwgSourceFrame(ObjectMap, mit, lease)) { |
| 7935 | ret = false; |
| 7936 | ++m_entityParseFailures; |
| 7937 | break; |
| 7938 | } |
| 7939 | lease.classification.emplace(std::move(classification)); |
| 7940 | oc = lease.object; |
| 7941 | read = readMappedDwgEntity(dbuf, lease, intfa, &frameFailure, |
| 7942 | offsetSpace); |
| 7943 | } |
| 7944 | if (!read) { |
| 7945 | ++m_entityParseFailures; |
| 7946 | } |
| 7947 | const bool identityFailure = |
| 7948 | parsedEntityHandleMismatch || parsedEntityOwnerMismatch; |
| 7949 | ret = !frameFailure && !identityFailure && ret; |
| 7950 | if (nextH == bkr->lastEH) |
| 7951 | nextH = 0; // redundant, but prevent read errors |
| 7952 | else if (nextEntLinkImplicit && nextEntLink > bkr->lastEH) |
| 7953 | // An inferred chain may advance beyond the declared tail |
| 7954 | // when a compound entity consumed its SEQEND. Equality is |
| 7955 | // still a valid next entity and must be visited. |
| 7956 | nextH = 0; |
| 7957 | else if (nextEntLinkImplicit && nextEntLink == bkr->lastEH && |
| 7958 | ObjectMap.find(nextEntLink) == ObjectMap.end()) |
| 7959 | // A compound entity may have consumed the declared tail |
| 7960 | // (normally its SEQEND) while advancing the inferred chain. |
| 7961 | nextH = 0; |
| 7962 | else |
| 7963 | nextH = nextEntLink; |
| 7964 | } |
| 7965 | } else { // 2004+ |
| 7966 | for (auto it = bkr->entMap.begin(); it != bkr->entMap.end(); ++it) { |
| 7967 | std::uint32_t nextH = *it; |
| 7968 | if (m_quarantinedEntityHandles.find(nextH) != |
| 7969 | m_quarantinedEntityHandles.end()) |
| 7970 | continue; |
| 7971 | auto mit = ObjectMap.find(nextH); |
| 7972 | if (mit == ObjectMap.end()) { |
| 7973 | if (m_consumedCompoundChildHandles.find(nextH) != |
| 7974 | m_consumedCompoundChildHandles.end() || |
| 7975 | m_consumedSeqEndHandles.find(nextH) != |
| 7976 | m_consumedSeqEndHandles.end()) |
| 7977 | continue; |
| 7978 | // Soft warning, not a section failure (libreDWG parity): a |
| 7979 | // missing entMap handle is recovered by the readDwgEntities |
| 7980 | // sweep. See the pre-2004 branch above for the rationale. |
| 7981 | DRW_DBG("\nWARNING: Entity of block not found\n")DRW_dbg::dbg("\nWARNING: Entity of block not found\n"); |
| 7982 | objHandle missing; |
| 7983 | missing.handle = nextH; |
| 7984 | recordWalkFailure(missing, -1, DwgEntityFailurePhase::BlockFinalize); |
| 7985 | ++m_entityParseFailures; |
| 7986 | continue; |
| 7987 | } |
| 7988 | bool frameFailure = false; |
| 7989 | bool read = false; |
| 7990 | DwgFrameClassification classification; |
| 7991 | if (requiresLegacyCompoundHandling(dbuf, mit->second, |
| 7992 | &classification)) { |
| 7993 | DwgSourceFrameLease lease; |
| 7994 | if (!takeDwgSourceFrame(ObjectMap, mit, lease)) { |
| 7995 | ret = false; |
| 7996 | recordWalkFailure(mit->second, classification.resolvedType, |
| 7997 | DwgEntityFailurePhase::Frame); |
| 7998 | ++m_entityParseFailures; |
| 7999 | continue; |
| 8000 | } |
| 8001 | oc = lease.object; |
| 8002 | read = readDwgEntity(dbuf, oc, intfa, &frameFailure, offsetSpace); |
| 8003 | if (!read) { |
| 8004 | (void)markDwgFrameOutcome(lease.source, |
| 8005 | DRW_DwgFrameDisposition::Failed); |
| 8006 | } |
| 8007 | } else { |
| 8008 | DwgFrameMapLease lease; |
| 8009 | if (!detachDwgSourceFrame(ObjectMap, mit, lease)) { |
| 8010 | ret = false; |
| 8011 | recordWalkFailure(mit->second, classification.resolvedType, |
| 8012 | DwgEntityFailurePhase::Frame); |
| 8013 | ++m_entityParseFailures; |
| 8014 | continue; |
| 8015 | } |
| 8016 | lease.classification.emplace(std::move(classification)); |
| 8017 | oc = lease.object; |
| 8018 | read = readMappedDwgEntity(dbuf, lease, intfa, &frameFailure, |
| 8019 | offsetSpace); |
| 8020 | } |
| 8021 | DRW_DBG("\nBlocks, parsing entity: ")DRW_dbg::dbg("\nBlocks, parsing entity: "); |
| 8022 | DRW_DBGH(oc.handle)DRW_dbg::dbgH(oc.handle); |
| 8023 | DRW_DBG(", pos: ")DRW_dbg::dbg(", pos: "); |
| 8024 | DRW_DBG(oc.loc)DRW_dbg::dbg(oc.loc); |
| 8025 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 8026 | if (!read) { |
| 8027 | recordWalkFailure(oc, classification.resolvedType, |
| 8028 | DwgEntityFailurePhase::TypedBody); |
| 8029 | ++m_entityParseFailures; |
| 8030 | } |
| 8031 | const bool identityFailure = |
| 8032 | parsedEntityHandleMismatch || parsedEntityOwnerMismatch; |
| 8033 | ret = !frameFailure && !identityFailure && ret; |
| 8034 | } |
| 8035 | } |
| 8036 | const bool hasUnresolvedCompound = hasPendingCompoundStateForBlock(*bkr); |
| 8037 | if (hasUnresolvedCompound) { |
| 8038 | ret = false; |
| 8039 | objHandle unresolved; |
| 8040 | unresolved.handle = unresolvedCompoundHandle(); |
| 8041 | recordWalkFailure(unresolved, -1, DwgEntityFailurePhase::Aggregate); |
| 8042 | if (!abandonStagedCompoundState()) |
| 8043 | ret = false; |
| 8044 | } |
| 8045 | restoreState(); |
| 8046 | return ret; |
| 8047 | } catch (...) { |
| 8048 | restoreState(); |
| 8049 | return false; |
| 8050 | } |
| 8051 | } |
| 8052 | |
| 8053 | bool dwgReader::walkJournalledBlockRecordEntities( |
| 8054 | DRW_Block_Record *bkr, dwgBuffer *dbuf, DRW_Interface &intfa, |
| 8055 | DwgBlockScopeTransaction &transaction, std::uint32_t expectedOwner, |
| 8056 | std::uint32_t rawBlockOwner, DwgIntegrityAddressSpace offsetSpace) { |
| 8057 | if (bkr == nullptr || dbuf == nullptr || version < DRW::AC1018) |
| 8058 | return false; |
| 8059 | |
| 8060 | const std::uint32_t previousOwner = expectedBlockEntityOwner; |
| 8061 | const std::uint32_t previousRawOwner = rawBlockEntityOwner; |
| 8062 | const bool previousOwnerlessSpaceWalk = ownerlessSpaceWalk; |
| 8063 | expectedBlockEntityOwner = expectedOwner; |
| 8064 | rawBlockEntityOwner = |
| 8065 | rawBlockOwner != DRW::NoHandle ? rawBlockOwner : expectedOwner; |
| 8066 | ownerlessSpaceWalk = |
| 8067 | expectedOwner == DRW::NoHandle && isSpaceBlockRecordName(bkr->name); |
| 8068 | const auto restoreState = [this, previousOwner, previousRawOwner, |
| 8069 | previousOwnerlessSpaceWalk]() noexcept { |
| 8070 | expectedBlockEntityOwner = previousOwner; |
| 8071 | rawBlockEntityOwner = previousRawOwner; |
| 8072 | ownerlessSpaceWalk = previousOwnerlessSpaceWalk; |
| 8073 | }; |
| 8074 | const auto discardUnadopted = [this](DwgFrameMapLease &lease) { |
| 8075 | if (!lease.isDetached()) |
| 8076 | return; |
| 8077 | if (lease.hasCoverage) |
| 8078 | (void)quarantineDwgFrame(lease.source); |
| 8079 | else |
| 8080 | (void)suppressDwgFrame(lease.source, false); |
| 8081 | (void)discardDetachedDwgSourceFrame(lease); |
| 8082 | }; |
| 8083 | |
| 8084 | try { |
| 8085 | std::unordered_set<std::uint32_t> handles; |
| 8086 | handles.reserve(bkr->entMap.size()); |
| 8087 | for (const std::uint32_t handle : bkr->entMap) { |
| 8088 | if (handle == DRW::NoHandle || !handles.insert(handle).second) { |
| 8089 | restoreState(); |
| 8090 | return false; |
| 8091 | } |
| 8092 | const auto sourceIt = ObjectMap.find(handle); |
| 8093 | if (sourceIt == ObjectMap.end()) { |
| 8094 | if (m_consumedCompoundChildHandles.find(handle) != |
| 8095 | m_consumedCompoundChildHandles.end() || |
| 8096 | m_consumedSeqEndHandles.find(handle) != |
| 8097 | m_consumedSeqEndHandles.end()) { |
| 8098 | continue; |
| 8099 | } |
| 8100 | restoreState(); |
| 8101 | return false; |
| 8102 | } |
| 8103 | DwgFrameClassification classification; |
| 8104 | if (!classifyDwgSourceFrame(dbuf, sourceIt->second, classification) || |
| 8105 | classification.route != DwgFrameClassification::Route::Entity) { |
| 8106 | restoreState(); |
| 8107 | return false; |
| 8108 | } |
| 8109 | if (!requiresAggregateDelivery(classification.resolvedType) && |
| 8110 | !transaction.reserveAdmission(1u, 3u, classification.bodyByteSize)) { |
| 8111 | restoreState(); |
| 8112 | return false; |
| 8113 | } |
| 8114 | |
| 8115 | DwgFrameMapLease lease; |
| 8116 | if (!detachDwgSourceFrame(ObjectMap, sourceIt, lease)) { |
| 8117 | restoreState(); |
| 8118 | return false; |
| 8119 | } |
| 8120 | lease.bodyByteSize = classification.bodyByteSize; |
| 8121 | lease.classification.emplace(std::move(classification)); |
| 8122 | bool frameFailure = false; |
| 8123 | const bool parsed = readMappedDwgEntity( |
| 8124 | dbuf, lease, intfa, &frameFailure, offsetSpace, &transaction.output(), |
| 8125 | DwgMappedEntityCompletion::Journal, &transaction); |
| 8126 | if (!parsed || frameFailure) { |
| 8127 | discardUnadopted(lease); |
| 8128 | if (m_entityParseFailures != std::numeric_limits<std::size_t>::max()) { |
| 8129 | ++m_entityParseFailures; |
| 8130 | } |
| 8131 | restoreState(); |
| 8132 | return false; |
| 8133 | } |
| 8134 | if (lease.isDetached() && !transaction.adopt(lease)) { |
| 8135 | discardUnadopted(lease); |
| 8136 | restoreState(); |
| 8137 | return false; |
| 8138 | } |
| 8139 | } |
| 8140 | if (hasPendingCompoundStateForBlock(*bkr)) { |
| 8141 | restoreState(); |
| 8142 | return false; |
| 8143 | } |
| 8144 | } catch (...) { |
| 8145 | restoreState(); |
| 8146 | return false; |
| 8147 | } |
| 8148 | restoreState(); |
| 8149 | return true; |
| 8150 | } |
| 8151 | |
| 8152 | void dwgReader::linkDataStorage(DRW_Entity &entity) { |
| 8153 | if (version <= DRW::AC1024 || entity.hasDsData == 0) |
| 8154 | return; |
| 8155 | |
| 8156 | // Linking is normally called once during entity traversal. Keep the |
| 8157 | // operation idempotent for callers that replay the traversal, rather than |
| 8158 | // turning a second visit into a duplicate-record claim. |
| 8159 | if (entity.hasDataStorageRecord) |
| 8160 | return; |
| 8161 | |
| 8162 | const bool hasExplicitKey = !entity.dataStorageHandleKey.empty(); |
| 8163 | const bool hasExplicitHandle = entity.dataStorageHandle != DRW::NoHandle; |
| 8164 | const bool hasExplicitLink = hasExplicitKey || hasExplicitHandle; |
Value stored to 'hasExplicitLink' during its initialization is never read | |
| 8165 | |
| 8166 | struct Candidate { |
| 8167 | std::size_t sectionIndex = 0; |
| 8168 | std::size_t recordIndex = 0; |
| 8169 | const DRW_DataStorageRecord *record = nullptr; |
| 8170 | }; |
| 8171 | std::vector<Candidate> candidates; |
| 8172 | |
| 8173 | for (std::size_t sectionIndex = 0; |
| 8174 | sectionIndex < m_dataStorageSections.size(); ++sectionIndex) { |
| 8175 | const DRW_DataStorageSection §ion = m_dataStorageSections[sectionIndex]; |
| 8176 | if (section.m_version != DRW::UNKNOWNV && section.m_version != version) |
| 8177 | continue; |
| 8178 | |
| 8179 | const DRW_DataStorageRecord *byKey = nullptr; |
| 8180 | const DRW_DataStorageRecord *byHandle = nullptr; |
| 8181 | if (hasExplicitKey) |
| 8182 | byKey = section.findRecordByHandleKey(entity.dataStorageHandleKey); |
| 8183 | if (hasExplicitHandle) |
| 8184 | byHandle = section.findRecordByHandle(entity.dataStorageHandle); |
| 8185 | |
| 8186 | // An encoded lexical key and an encoded numeric handle are two views |
| 8187 | // of one identity. They must agree; never use one to repair the |
| 8188 | // other or silently fall through to a different section. |
| 8189 | if (hasExplicitKey && hasExplicitHandle) { |
| 8190 | if (byKey == nullptr || byHandle == nullptr || byKey != byHandle) |
| 8191 | continue; |
| 8192 | candidates.push_back( |
| 8193 | {sectionIndex, |
| 8194 | static_cast<std::size_t>(byKey - section.records.data()), byKey}); |
| 8195 | continue; |
| 8196 | } |
| 8197 | |
| 8198 | const DRW_DataStorageRecord *record = nullptr; |
| 8199 | if (hasExplicitKey) |
| 8200 | record = byKey; |
| 8201 | else if (hasExplicitHandle) |
| 8202 | record = byHandle; |
| 8203 | else if (entity.handle != DRW::NoHandle) |
| 8204 | record = section.findRecordByHandle(entity.handle); |
| 8205 | if (record == nullptr) |
| 8206 | continue; |
| 8207 | |
| 8208 | candidates.push_back( |
| 8209 | {sectionIndex, |
| 8210 | static_cast<std::size_t>(record - section.records.data()), record}); |
| 8211 | } |
| 8212 | |
| 8213 | // There must be one section identity and one preferred record. This is |
| 8214 | // deliberately checked before mutating the entity or link accounting. |
| 8215 | if (candidates.size() != 1u) { |
| 8216 | ++m_dataStorageLinkFailures; |
| 8217 | return; |
| 8218 | } |
| 8219 | |
| 8220 | const Candidate &candidate = candidates.front(); |
| 8221 | const std::size_t sectionIndex = candidate.sectionIndex; |
| 8222 | const std::size_t recordIndex = candidate.recordIndex; |
| 8223 | const DRW_DataStorageRecord *record = candidate.record; |
| 8224 | const DRW_DataStorageSection §ion = m_dataStorageSections[sectionIndex]; |
| 8225 | if (recordIndex >= section.records.size()) { |
| 8226 | ++m_dataStorageLinkFailures; |
| 8227 | return; |
| 8228 | } |
| 8229 | |
| 8230 | if (m_dataStorageLinkedRecords.find({sectionIndex, recordIndex}) != |
| 8231 | m_dataStorageLinkedRecords.end()) { |
| 8232 | ++m_dataStorageLinkFailures; |
| 8233 | return; |
| 8234 | } |
| 8235 | |
| 8236 | entity.hasDataStorageRecord = true; |
| 8237 | entity.dataStorageHandle = record->handle; |
| 8238 | entity.dataStorageHandleKey = record->handleKey; |
| 8239 | entity.dataStorageData = record->payload; |
| 8240 | entity.dataStorageSegmentIndex = record->segmentIndex; |
| 8241 | entity.dataStorageSchemaIndex = record->schemaIndex; |
| 8242 | entity.hasDataStoragePayloadMarker = record->hasPayloadMarker; |
| 8243 | entity.dataStoragePayloadMarkerOffset = record->payloadMarkerOffset; |
| 8244 | entity.dataStoragePayloadMarkerLength = record->payloadMarkerLength; |
| 8245 | entity.dataStoragePayloadMarkerSection = record->payloadMarkerSection; |
| 8246 | m_dataStorageLinkedRecords.emplace(sectionIndex, recordIndex); |
| 8247 | } |
| 8248 | |
| 8249 | void dwgReader::finalizeDataStorageLinks() { |
| 8250 | m_dataStorageOrphanRecords = 0; |
| 8251 | for (std::size_t sectionIndex = 0; |
| 8252 | sectionIndex < m_dataStorageSections.size(); ++sectionIndex) { |
| 8253 | DRW_DataStorageSection §ion = m_dataStorageSections[sectionIndex]; |
| 8254 | section.orphanRecordCount = 0; |
| 8255 | section.diagnostics.erase( |
| 8256 | std::remove_if(section.diagnostics.begin(), section.diagnostics.end(), |
| 8257 | [](const DRW_DataStorageDiagnostic &diagnostic) { |
| 8258 | return diagnostic.code == "datastorage-orphan-record"; |
| 8259 | }), |
| 8260 | section.diagnostics.end()); |
| 8261 | for (std::size_t recordIndex = 0; recordIndex < section.records.size(); |
| 8262 | ++recordIndex) { |
| 8263 | const DRW_DataStorageRecord &record = section.records[recordIndex]; |
| 8264 | if (m_dataStorageLinkedRecords.find({sectionIndex, recordIndex}) != |
| 8265 | m_dataStorageLinkedRecords.end()) { |
| 8266 | continue; |
| 8267 | } |
| 8268 | |
| 8269 | ++section.orphanRecordCount; |
| 8270 | ++m_dataStorageOrphanRecords; |
| 8271 | DRW_DataStorageDiagnostic diagnostic; |
| 8272 | diagnostic.code = "datastorage-orphan-record"; |
| 8273 | diagnostic.message = |
| 8274 | "DataStorage record for handle " + record.handleKey + |
| 8275 | " was not referenced by a parsed modeler/surface entity"; |
| 8276 | diagnostic.handle = record.handle; |
| 8277 | diagnostic.hasHandle = true; |
| 8278 | diagnostic.offset = record.recordOffset; |
| 8279 | diagnostic.hasOffset = true; |
| 8280 | section.diagnostics.push_back(std::move(diagnostic)); |
| 8281 | } |
| 8282 | } |
| 8283 | } |
| 8284 | |
| 8285 | bool dwgReader::readMappedDwgEntity(dwgBuffer *dbuf, DwgFrameMapLease &lease, |
| 8286 | DRW_Interface &intfa, bool *frameFailure, |
| 8287 | DwgIntegrityAddressSpace offsetSpace, |
| 8288 | DwgEntityOutput *output, |
| 8289 | DwgMappedEntityCompletion completion, |
| 8290 | DwgBlockScopeTransaction *transaction) { |
| 8291 | if ((completion == DwgMappedEntityCompletion::Journal && |
| 8292 | (output == nullptr || transaction == nullptr || |
| 8293 | m_activeBlockTransaction != nullptr || |
| 8294 | m_activeBlockOutput != nullptr)) || |
| 8295 | (completion == DwgMappedEntityCompletion::Immediate && |
| 8296 | (output != nullptr || transaction != nullptr))) { |
| 8297 | if (frameFailure != nullptr) |
| 8298 | *frameFailure = true; |
| 8299 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 8300 | true); |
| 8301 | } |
| 8302 | if (!lease.isDetached() || |
| 8303 | lease.origin != DwgFrameMapLease::Origin::ObjectMap || |
| 8304 | lease.object.handle != lease.source.handle || |
| 8305 | m_activeEntityFrameLease != nullptr) { |
| 8306 | if (frameFailure != nullptr) |
| 8307 | *frameFailure = true; |
| 8308 | recordEntityFailure(lease.object, |
| 8309 | lease.classification.has_value() |
| 8310 | ? lease.classification->resolvedType |
| 8311 | : -1, |
| 8312 | DwgEntityFailurePhase::Frame); |
| 8313 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 8314 | true); |
| 8315 | } |
| 8316 | |
| 8317 | DwgFrameClassification observed; |
| 8318 | if (!lease.classification.has_value() || |
| 8319 | !classifyDwgSourceFrame(dbuf, lease.object, observed) || |
| 8320 | !classificationsMatch(*lease.classification, observed)) { |
| 8321 | recordObjectFrameFailure(lease.object, offsetSpace); |
| 8322 | if (lease.hasCoverage) { |
| 8323 | (void)markDwgFrameOutcome(lease.source, DRW_DwgFrameDisposition::Failed); |
| 8324 | } |
| 8325 | recordDwgFramePhaseSnapshot(lease, |
| 8326 | DwgFramePhaseSnapshot::Destination::Failed, |
| 8327 | DRW_DwgFrameDisposition::Failed); |
| 8328 | if (frameFailure != nullptr) |
| 8329 | *frameFailure = true; |
| 8330 | recordEntityFailure(lease.object, |
| 8331 | lease.classification.has_value() |
| 8332 | ? lease.classification->resolvedType |
| 8333 | : -1, |
| 8334 | DwgEntityFailurePhase::Classify); |
| 8335 | (void)discardDetachedDwgSourceFrame(lease); |
| 8336 | return false; |
| 8337 | } |
| 8338 | |
| 8339 | class ActiveLeaseScope { |
| 8340 | public: |
| 8341 | ActiveLeaseScope(DwgFrameMapLease *&slot, |
| 8342 | DwgMappedEntityOutcome *&outcomeSlot, |
| 8343 | DwgFrameMapLease &active, DwgMappedEntityOutcome &outcome) |
| 8344 | : m_slot(slot), m_outcomeSlot(outcomeSlot), m_previous(slot), |
| 8345 | m_previousOutcome(outcomeSlot) { |
| 8346 | m_slot = &active; |
| 8347 | m_outcomeSlot = &outcome; |
| 8348 | } |
| 8349 | |
| 8350 | ~ActiveLeaseScope() { |
| 8351 | m_slot = m_previous; |
| 8352 | m_outcomeSlot = m_previousOutcome; |
| 8353 | } |
| 8354 | |
| 8355 | private: |
| 8356 | DwgFrameMapLease *&m_slot; |
| 8357 | DwgMappedEntityOutcome *&m_outcomeSlot; |
| 8358 | DwgFrameMapLease *m_previous; |
| 8359 | DwgMappedEntityOutcome *m_previousOutcome; |
| 8360 | }; |
| 8361 | |
| 8362 | DwgMappedEntityOutcome outcome = DwgMappedEntityOutcome::PublishedSimple; |
| 8363 | ActiveLeaseScope activeLeaseScope( |
| 8364 | m_activeEntityFrameLease, m_activeMappedEntityOutcome, lease, outcome); |
| 8365 | class ActiveBlockJournalScope { |
| 8366 | public: |
| 8367 | ActiveBlockJournalScope(DwgBlockScopeTransaction *&transactionSlot, |
| 8368 | DwgEntityOutput *&outputSlot, |
| 8369 | DwgBlockScopeTransaction *transaction, |
| 8370 | DwgEntityOutput *output) noexcept |
| 8371 | : m_transactionSlot(transactionSlot), m_outputSlot(outputSlot), |
| 8372 | m_previousTransaction(transactionSlot), m_previousOutput(outputSlot) { |
| 8373 | if (transaction != nullptr) { |
| 8374 | m_transactionSlot = transaction; |
| 8375 | m_outputSlot = output; |
| 8376 | } |
| 8377 | } |
| 8378 | |
| 8379 | ~ActiveBlockJournalScope() { |
| 8380 | m_transactionSlot = m_previousTransaction; |
| 8381 | m_outputSlot = m_previousOutput; |
| 8382 | } |
| 8383 | |
| 8384 | private: |
| 8385 | DwgBlockScopeTransaction *&m_transactionSlot; |
| 8386 | DwgEntityOutput *&m_outputSlot; |
| 8387 | DwgBlockScopeTransaction *m_previousTransaction; |
| 8388 | DwgEntityOutput *m_previousOutput; |
| 8389 | }; |
| 8390 | ActiveBlockJournalScope activeBlockJournalScope( |
| 8391 | m_activeBlockTransaction, m_activeBlockOutput, transaction, output); |
| 8392 | DwgImmediateEntityOutput immediateOutput(*this, intfa); |
| 8393 | DwgEntityOutput &entityOutput = |
| 8394 | output != nullptr ? *output |
| 8395 | : static_cast<DwgEntityOutput &>(immediateOutput); |
| 8396 | DwgEntityOutput::SourceScope sourceScope = |
| 8397 | entityOutput.bindSource(lease.source); |
| 8398 | bool parsed = false; |
| 8399 | try { |
| 8400 | parsed = readDwgEntityWithOutput(dbuf, lease.object, intfa, entityOutput, |
| 8401 | frameFailure, offsetSpace); |
| 8402 | } catch (...) { |
| 8403 | parsed = false; |
| 8404 | if (frameFailure != nullptr) |
| 8405 | *frameFailure = true; |
| 8406 | } |
| 8407 | if (!parsed) |
| 8408 | outcome = DwgMappedEntityOutcome::Rejected; |
| 8409 | else if (completion == DwgMappedEntityCompletion::Journal && |
| 8410 | outcome == DwgMappedEntityOutcome::PublishedSimple) { |
| 8411 | if (!stageDetachedDwgSourceFrame(lease)) { |
| 8412 | if (frameFailure != nullptr) |
| 8413 | *frameFailure = true; |
| 8414 | outcome = DwgMappedEntityOutcome::Rejected; |
| 8415 | } else { |
| 8416 | outcome = DwgMappedEntityOutcome::JournalledSimple; |
| 8417 | } |
| 8418 | } |
| 8419 | |
| 8420 | const auto discardFailed = [this, &lease]() { |
| 8421 | if (lease.hasCoverage) { |
| 8422 | const auto sourceIt = m_dwgSourceFrameIndexes.find(lease.source.handle); |
| 8423 | if (sourceIt == m_dwgSourceFrameIndexes.end() || |
| 8424 | sourceIt->second >= m_dwgSourceFrameLedger.size()) { |
| 8425 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 8426 | true); |
| 8427 | } |
| 8428 | const DRW_DwgFrameDisposition disposition = |
| 8429 | m_dwgSourceFrameLedger[sourceIt->second].m_disposition; |
| 8430 | if (disposition == DRW_DwgFrameDisposition::Pending && |
| 8431 | !markDwgFrameOutcome(lease.source, DRW_DwgFrameDisposition::Failed)) { |
| 8432 | return false; |
| 8433 | } |
| 8434 | if (disposition != DRW_DwgFrameDisposition::Pending && |
| 8435 | disposition != DRW_DwgFrameDisposition::Failed && |
| 8436 | disposition != DRW_DwgFrameDisposition::Published) { |
| 8437 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 8438 | true); |
| 8439 | } |
| 8440 | } |
| 8441 | return discardDetachedDwgSourceFrame(lease); |
| 8442 | }; |
| 8443 | |
| 8444 | switch (outcome) { |
| 8445 | case DwgMappedEntityOutcome::PublishedSimple: |
| 8446 | if (completion != DwgMappedEntityCompletion::Immediate) { |
| 8447 | if (frameFailure != nullptr) |
| 8448 | *frameFailure = true; |
| 8449 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 8450 | true); |
| 8451 | } |
| 8452 | if (!lease.isDetached()) { |
| 8453 | if (frameFailure != nullptr) |
| 8454 | *frameFailure = true; |
| 8455 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 8456 | true); |
| 8457 | } |
| 8458 | if (lease.hasCoverage) { |
| 8459 | const auto sourceIt = m_dwgSourceFrameIndexes.find(lease.source.handle); |
| 8460 | if (sourceIt == m_dwgSourceFrameIndexes.end() || |
| 8461 | sourceIt->second >= m_dwgSourceFrameLedger.size() || |
| 8462 | m_dwgSourceFrameLedger[sourceIt->second].m_disposition != |
| 8463 | DRW_DwgFrameDisposition::Published) { |
| 8464 | if (frameFailure != nullptr) |
| 8465 | *frameFailure = true; |
| 8466 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 8467 | true); |
| 8468 | } |
| 8469 | } |
| 8470 | if (!discardDetachedDwgSourceFrame(lease)) { |
| 8471 | if (frameFailure != nullptr) |
| 8472 | *frameFailure = true; |
| 8473 | return false; |
| 8474 | } |
| 8475 | return true; |
| 8476 | |
| 8477 | case DwgMappedEntityOutcome::JournalledSimple: |
| 8478 | if (completion != DwgMappedEntityCompletion::Journal || |
| 8479 | !lease.isDetached()) { |
| 8480 | if (frameFailure != nullptr) |
| 8481 | *frameFailure = true; |
| 8482 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 8483 | true); |
| 8484 | } |
| 8485 | if (lease.hasCoverage) { |
| 8486 | const auto sourceIt = m_dwgSourceFrameIndexes.find(lease.source.handle); |
| 8487 | if (sourceIt == m_dwgSourceFrameIndexes.end() || |
| 8488 | sourceIt->second >= m_dwgSourceFrameLedger.size() || |
| 8489 | m_dwgSourceFrameLedger[sourceIt->second].m_disposition != |
| 8490 | DRW_DwgFrameDisposition::Staged || |
| 8491 | m_dwgSourceFrameLedger[sourceIt->second].m_publicationCount != 0) { |
| 8492 | if (frameFailure != nullptr) |
| 8493 | *frameFailure = true; |
| 8494 | return reportDwgFrameTransitionFailure(lease.source, lease.object.loc, |
| 8495 | true); |
| 8496 | } |
| 8497 | } |
| 8498 | return true; |
| 8499 | |
| 8500 | case DwgMappedEntityOutcome::StagedCompound: |
| 8501 | case DwgMappedEntityOutcome::CommittedCompound: |
| 8502 | case DwgMappedEntityOutcome::DeferredObject: |
| 8503 | if (!lease.isDetached()) |
| 8504 | return true; |
| 8505 | if (frameFailure != nullptr) |
| 8506 | *frameFailure = true; |
| 8507 | (void)discardFailed(); |
| 8508 | return false; |
| 8509 | |
| 8510 | case DwgMappedEntityOutcome::Rejected: |
| 8511 | if (!lease.isDetached()) |
| 8512 | return false; |
| 8513 | if (!discardFailed() && frameFailure != nullptr) |
| 8514 | *frameFailure = true; |
| 8515 | return false; |
| 8516 | } |
| 8517 | return false; |
| 8518 | } |
| 8519 | |
| 8520 | bool dwgReader::isMappedInsertSequenceEnd(dwgBuffer *dbuf, |
| 8521 | const objHandle &object) { |
| 8522 | if (dbuf == nullptr) |
| 8523 | return false; |
| 8524 | |
| 8525 | dwgBuffer frameProbe = dbuf->forkIndependent(); |
| 8526 | DwgObjectFrame frame; |
| 8527 | if (!frame.readAt(frameProbe, version, object.loc)) |
| 8528 | return false; |
| 8529 | |
| 8530 | std::vector<std::uint8_t> &body = frame.body(); |
| 8531 | dwgBuffer sequenceBuffer(body.data(), body.size(), &decoder); |
| 8532 | if (sequenceBuffer.getObjType(version) != dwgType::SEQEND || |
| 8533 | !sequenceBuffer.isGood()) { |
| 8534 | return false; |
| 8535 | } |
| 8536 | sequenceBuffer.resetPosition(); |
| 8537 | |
| 8538 | DRW_SeqEnd sequenceEnd; |
| 8539 | if (!sequenceEnd.parseDwg(version, &sequenceBuffer, frame.bodyBitSize()) || |
| 8540 | !sequenceBuffer.isGood() || sequenceEnd.handle != object.handle || |
| 8541 | sequenceEnd.parentHandle == DRW::NoHandle) { |
| 8542 | return false; |
| 8543 | } |
| 8544 | |
| 8545 | if (m_pendingInsertStates.find(sequenceEnd.parentHandle) != |
| 8546 | m_pendingInsertStates.end()) { |
| 8547 | return true; |
| 8548 | } |
| 8549 | |
| 8550 | const auto objectIt = ObjectMap.find(sequenceEnd.parentHandle); |
| 8551 | const auto deferredIt = objObjectMap.find(sequenceEnd.parentHandle); |
| 8552 | if ((objectIt == ObjectMap.end()) == (deferredIt == objObjectMap.end())) |
| 8553 | return false; |
| 8554 | |
| 8555 | const objHandle &parent = |
| 8556 | objectIt != ObjectMap.end() ? objectIt->second : deferredIt->second; |
| 8557 | dwgBuffer parentProbe = dbuf->forkIndependent(); |
| 8558 | DwgFrameClassification parentClassification; |
| 8559 | if (!classifyDwgSourceFrame(&parentProbe, parent, parentClassification) || |
| 8560 | parentClassification.route != DwgFrameClassification::Route::Entity) { |
| 8561 | return false; |
| 8562 | } |
| 8563 | return parentClassification.resolvedType == dwgType::INSERT || |
| 8564 | parentClassification.resolvedType == dwgType::MINSERT; |
| 8565 | } |
| 8566 | |
| 8567 | bool dwgReader::preflightMappedPolylineOwnership( |
| 8568 | const std::vector<const DRW_Block_Record *> &records, dwgBuffer *dbuf) { |
| 8569 | if (version < DRW::AC1018) |
| 8570 | return true; |
| 8571 | if (dbuf == nullptr) |
| 8572 | return false; |
| 8573 | |
| 8574 | struct PolylineClaims { |
| 8575 | const DRW_Block_Record *block{nullptr}; |
| 8576 | std::uint32_t parent{DRW::NoHandle}; |
| 8577 | std::int16_t type{-1}; |
| 8578 | std::vector<std::uint32_t> children; |
| 8579 | }; |
| 8580 | |
| 8581 | const auto isPolylineType = [](std::int16_t type) { |
| 8582 | return type == dwgType::POLYLINE_2D || type == dwgType::POLYLINE_3D || |
| 8583 | type == dwgType::POLYLINE_PFACE || type == dwgType::POLYLINE_MESH; |
| 8584 | }; |
| 8585 | const auto discardSource = [this](std::uint32_t handle) { |
| 8586 | const auto objectIt = ObjectMap.find(handle); |
| 8587 | if (objectIt != ObjectMap.end()) |
| 8588 | return discardDwgSourceFrame(ObjectMap, objectIt); |
| 8589 | const auto deferredIt = objObjectMap.find(handle); |
| 8590 | return deferredIt == objObjectMap.end() || |
| 8591 | discardDwgSourceFrame(objObjectMap, deferredIt); |
| 8592 | }; |
| 8593 | |
| 8594 | try { |
| 8595 | std::vector<PolylineClaims> claims; |
| 8596 | std::unordered_set<std::uint32_t> invalidParents; |
| 8597 | |
| 8598 | for (const DRW_Block_Record *block : records) { |
| 8599 | if (block == nullptr) |
| 8600 | continue; |
| 8601 | for (const std::uint32_t parentHandle : block->entMap) { |
| 8602 | const auto objectIt = ObjectMap.find(parentHandle); |
| 8603 | if (objectIt == ObjectMap.end()) |
| 8604 | continue; |
| 8605 | |
| 8606 | DwgFrameClassification classification; |
| 8607 | if (!classifyDwgSourceFrame(dbuf, objectIt->second, classification) || |
| 8608 | classification.route != DwgFrameClassification::Route::Entity || |
| 8609 | !isPolylineType(classification.resolvedType)) { |
| 8610 | continue; |
| 8611 | } |
| 8612 | |
| 8613 | dwgBuffer frameProbe = dbuf->forkIndependent(); |
| 8614 | DwgObjectFrame frame; |
| 8615 | if (!frame.readAt(frameProbe, version, objectIt->second.loc)) |
| 8616 | continue; |
| 8617 | std::vector<std::uint8_t> &body = frame.body(); |
| 8618 | dwgBuffer bodyBuffer(body.data(), body.size(), &decoder); |
| 8619 | DRW_Polyline polyline; |
| 8620 | if (!polyline.parseDwg(version, &bodyBuffer, frame.bodyBitSize()) || |
| 8621 | !bodyBuffer.isGood() || polyline.handle != parentHandle) { |
| 8622 | continue; |
| 8623 | } |
| 8624 | |
| 8625 | PolylineClaims parentClaims; |
| 8626 | parentClaims.block = block; |
| 8627 | parentClaims.parent = parentHandle; |
| 8628 | parentClaims.type = classification.resolvedType; |
| 8629 | parentClaims.children.reserve(polyline.hadlesList.size() + 1u); |
| 8630 | std::unordered_set<std::uint32_t> seenChildren; |
| 8631 | seenChildren.reserve(polyline.hadlesList.size() + 1u); |
| 8632 | for (const std::uint32_t child : polyline.hadlesList) { |
| 8633 | if (child == DRW::NoHandle || !seenChildren.insert(child).second) { |
| 8634 | invalidParents.insert(parentHandle); |
| 8635 | continue; |
| 8636 | } |
| 8637 | parentClaims.children.push_back(child); |
| 8638 | } |
| 8639 | const std::uint32_t sequenceEnd = polyline.seqEndH.ref; |
| 8640 | if (sequenceEnd == DRW::NoHandle || |
| 8641 | !seenChildren.insert(sequenceEnd).second) { |
| 8642 | invalidParents.insert(parentHandle); |
| 8643 | } else { |
| 8644 | parentClaims.children.push_back(sequenceEnd); |
| 8645 | } |
| 8646 | claims.push_back(std::move(parentClaims)); |
| 8647 | } |
| 8648 | } |
| 8649 | |
| 8650 | std::unordered_map<std::uint32_t, std::uint32_t> childOwners; |
| 8651 | childOwners.reserve(claims.size()); |
| 8652 | for (const PolylineClaims &parentClaims : claims) { |
| 8653 | for (const std::uint32_t child : parentClaims.children) { |
| 8654 | const auto [owner, inserted] = |
| 8655 | childOwners.emplace(child, parentClaims.parent); |
| 8656 | if (!inserted && owner->second != parentClaims.parent) { |
| 8657 | invalidParents.insert(owner->second); |
| 8658 | invalidParents.insert(parentClaims.parent); |
| 8659 | } |
| 8660 | } |
| 8661 | } |
| 8662 | |
| 8663 | if (invalidParents.empty()) |
| 8664 | return true; |
| 8665 | |
| 8666 | for (const PolylineClaims &parentClaims : claims) { |
| 8667 | if (invalidParents.find(parentClaims.parent) == invalidParents.end()) { |
| 8668 | continue; |
| 8669 | } |
| 8670 | objHandle parent; |
| 8671 | parent.handle = parentClaims.parent; |
| 8672 | recordEntityFailure(parent, parentClaims.type, |
| 8673 | DwgEntityFailurePhase::Aggregate, |
| 8674 | parentClaims.block->handle); |
| 8675 | try { |
| 8676 | m_invalidPolylineOwners.insert(parentClaims.parent); |
| 8677 | } catch (...) { |
| 8678 | // Quarantining the source frames below is sufficient to stop |
| 8679 | // a later recovery sweep from publishing this group. |
| 8680 | } |
| 8681 | (void)discardSource(parentClaims.parent); |
| 8682 | for (const std::uint32_t child : parentClaims.children) |
| 8683 | (void)discardSource(child); |
| 8684 | } |
| 8685 | if (invalidParents.size() > |
| 8686 | std::numeric_limits<std::size_t>::max() - m_entityParseFailures) { |
| 8687 | m_entityParseFailures = std::numeric_limits<std::size_t>::max(); |
| 8688 | } else { |
| 8689 | m_entityParseFailures += invalidParents.size(); |
| 8690 | } |
| 8691 | return false; |
| 8692 | } catch (...) { |
| 8693 | return false; |
| 8694 | } |
| 8695 | } |
| 8696 | |
| 8697 | bool dwgReader::requiresLegacyCompoundHandling( |
| 8698 | dwgBuffer *dbuf, const objHandle &object, |
| 8699 | DwgFrameClassification *classification) { |
| 8700 | DwgFrameClassification observed; |
| 8701 | if (!classifyDwgSourceFrame(dbuf, object, observed)) |
| 8702 | return true; |
| 8703 | if (classification != nullptr) |
| 8704 | *classification = observed; |
| 8705 | if (observed.route != DwgFrameClassification::Route::Entity) |
| 8706 | return false; |
| 8707 | switch (observed.resolvedType) { |
| 8708 | case dwgType::POLYLINE_2D: |
| 8709 | case dwgType::POLYLINE_3D: |
| 8710 | case dwgType::POLYLINE_PFACE: |
| 8711 | case dwgType::POLYLINE_MESH: |
| 8712 | return false; |
| 8713 | case dwgType::VERTEX_2D: |
| 8714 | case dwgType::VERTEX_3D: |
| 8715 | case dwgType::VERTEX_MESH: |
| 8716 | case dwgType::VERTEX_PFACE: |
| 8717 | case dwgType::VERTEX_PFACE_FACE: |
| 8718 | return version < DRW::AC1018; |
| 8719 | case dwgType::SEQEND: |
| 8720 | return version < DRW::AC1018 && !isMappedInsertSequenceEnd(dbuf, object); |
| 8721 | default: |
| 8722 | return false; |
| 8723 | } |
| 8724 | } |
| 8725 | |
| 8726 | bool dwgReader::readDwgEntities(DRW_Interface &intfa, dwgBuffer *dbuf, |
| 8727 | DwgIntegrityAddressSpace offsetSpace) { |
| 8728 | DRW_DBG("\nobject map total size= ")DRW_dbg::dbg("\nobject map total size= "); |
| 8729 | DRW_DBG(ObjectMap.size())DRW_dbg::dbg(ObjectMap.size()); |
| 8730 | // Bounded typed-body failures are warnings, not section failures. A bad |
| 8731 | // object frame is different: it is a structural desynchronization risk |
| 8732 | // and must fail the ENTITIES phase without publishing that entity. |
| 8733 | size_t failures = 0; |
| 8734 | bool structuralFailure = false; |
| 8735 | const bool previousSweepPolicy = rejectOwnedEntityInSweep; |
| 8736 | rejectOwnedEntityInSweep = version > DRW::AC1015; |
| 8737 | while (!ObjectMap.empty()) { |
| 8738 | auto itB = ObjectMap.begin(); |
| 8739 | if (m_quarantinedEntityHandles.find(itB->first) != |
| 8740 | m_quarantinedEntityHandles.end()) { |
| 8741 | if (!discardDwgSourceFrame(ObjectMap, itB)) { |
| 8742 | structuralFailure = true; |
| 8743 | break; |
| 8744 | } |
| 8745 | continue; |
| 8746 | } |
| 8747 | bool frameFailure = false; |
| 8748 | bool read = false; |
| 8749 | DwgFrameClassification classification; |
| 8750 | if (requiresLegacyCompoundHandling(dbuf, itB->second, &classification)) { |
| 8751 | DwgSourceFrameLease lease; |
| 8752 | if (!takeDwgSourceFrame(ObjectMap, itB, lease)) { |
| 8753 | structuralFailure = true; |
| 8754 | break; |
| 8755 | } |
| 8756 | read = |
| 8757 | readDwgEntity(dbuf, lease.object, intfa, &frameFailure, offsetSpace); |
| 8758 | if (!read) { |
| 8759 | (void)markDwgFrameOutcome(lease.source, |
| 8760 | DRW_DwgFrameDisposition::Failed); |
| 8761 | } |
| 8762 | } else { |
| 8763 | DwgFrameMapLease lease; |
| 8764 | if (!detachDwgSourceFrame(ObjectMap, itB, lease)) { |
| 8765 | structuralFailure = true; |
| 8766 | break; |
| 8767 | } |
| 8768 | lease.classification.emplace(std::move(classification)); |
| 8769 | read = |
| 8770 | readMappedDwgEntity(dbuf, lease, intfa, &frameFailure, offsetSpace); |
| 8771 | } |
| 8772 | if (!read) { |
| 8773 | ++failures; |
| 8774 | } |
| 8775 | structuralFailure = structuralFailure || frameFailure || |
| 8776 | parsedEntityHandleMismatch || parsedEntityOwnerMismatch; |
| 8777 | } |
| 8778 | rejectOwnedEntityInSweep = previousSweepPolicy; |
| 8779 | if (failures > 0) { |
| 8780 | DRW_DBG("readDwgEntities: ")DRW_dbg::dbg("readDwgEntities: "); |
| 8781 | DRW_DBG(failures)DRW_dbg::dbg(failures); |
| 8782 | DRW_DBG(" entities failed to parse (warnings, not section failure)\n")DRW_dbg::dbg(" entities failed to parse (warnings, not section failure)\n" ); |
| 8783 | m_entityParseFailures += failures; |
| 8784 | } |
| 8785 | |
| 8786 | if (!validateDeferredCompoundState()) |
| 8787 | structuralFailure = true; |
| 8788 | |
| 8789 | // A mapped compound can remain internally consistent while still lacking |
| 8790 | // a parent or a declared child. At the end of the entity sweep that is a |
| 8791 | // structural failure, not a recoverable deferred state. |
| 8792 | if (hasPendingCompoundState()) { |
| 8793 | structuralFailure = true; |
| 8794 | } |
| 8795 | |
| 8796 | abandonDeferredCompoundState(); |
| 8797 | |
| 8798 | return !structuralFailure; |
| 8799 | } |
| 8800 | |
| 8801 | bool dwgReader::validateDeferredCompoundState() { |
| 8802 | return validateStagedCompoundState(); |
| 8803 | } |
| 8804 | |
| 8805 | void dwgReader::abandonDeferredCompoundState() { |
| 8806 | (void)abandonStagedCompoundState(); |
| 8807 | } |
| 8808 | |
| 8809 | /** |
| 8810 | * Reads a dwg drawing entity (dwg object entity) given its offset in the file |
| 8811 | */ |
| 8812 | bool dwgReader::readDwgEntity(dwgBuffer *dbuf, objHandle &obj, |
| 8813 | DRW_Interface &intfa, bool *frameFailure, |
| 8814 | DwgIntegrityAddressSpace offsetSpace) { |
| 8815 | DwgImmediateEntityOutput output(*this, intfa); |
| 8816 | return readDwgEntityWithOutput(dbuf, obj, intfa, output, frameFailure, |
| 8817 | offsetSpace); |
| 8818 | } |
| 8819 | |
| 8820 | bool dwgReader::readDwgEntityWithOutput(dwgBuffer *dbuf, objHandle &obj, |
| 8821 | DRW_Interface &intfa, |
| 8822 | DwgEntityOutput &output, |
| 8823 | bool *frameFailure, |
| 8824 | DwgIntegrityAddressSpace offsetSpace) { |
| 8825 | bool ret = true; |
| 8826 | m_currentEntityFailurePhase = DwgEntityFailurePhase::None; |
| 8827 | expectedParsedEntityHandle = obj.handle; |
| 8828 | parsedEntityHandleMismatch = false; |
| 8829 | parsedEntityOwnerMismatch = false; |
| 8830 | if (frameFailure) |
| 8831 | *frameFailure = false; |
| 8832 | |
| 8833 | if (dbuf == nullptr) { |
| 8834 | recordObjectFrameFailure(obj, offsetSpace); |
| 8835 | recordEntityFailure(obj, -1, DwgEntityFailurePhase::Frame); |
| 8836 | if (frameFailure) |
| 8837 | *frameFailure = true; |
| 8838 | return false; |
| 8839 | } |
| 8840 | |
| 8841 | nextEntLink = prevEntLink = 0; // set to 0 to skip unimplemented entities |
| 8842 | nextEntLinkImplicit = false; |
| 8843 | try { |
| 8844 | DwgObjectFrame frame; |
| 8845 | if (!frame.readAt(*dbuf, version, obj.loc)) { |
| 8846 | recordObjectFrameFailure(obj, offsetSpace); |
| 8847 | recordEntityFailure(obj, -1, DwgEntityFailurePhase::Frame); |
| 8848 | if (frameFailure) |
| 8849 | *frameFailure = true; |
| 8850 | DRW_DBG(" Warning: readDwgEntity, invalid object frame\n")DRW_dbg::dbg(" Warning: readDwgEntity, invalid object frame\n" ); |
| 8851 | return false; |
| 8852 | } |
| 8853 | const std::uint32_t bs = frame.bodyBitSize(); |
| 8854 | auto &tmpByteStr = frame.body(); |
| 8855 | const std::size_t size = tmpByteStr.size(); |
| 8856 | dwgBuffer buff(tmpByteStr.data(), size, &decoder); |
| 8857 | std::int16_t oType = buff.getObjType(version); |
| 8858 | if (!buff.isGood()) { |
| 8859 | recordObjectFrameFailure(obj, offsetSpace); |
| 8860 | recordEntityFailure(obj, -1, DwgEntityFailurePhase::Frame); |
| 8861 | if (frameFailure) |
| 8862 | *frameFailure = true; |
| 8863 | DRW_DBG(" Warning: readDwgEntity, missing object type\n")DRW_dbg::dbg(" Warning: readDwgEntity, missing object type\n" ); |
| 8864 | return false; |
| 8865 | } |
| 8866 | const std::int16_t encodedType = oType; |
| 8867 | buff.resetPosition(); |
| 8868 | auto makeRawEntity = [&](int rawType, const DRW_Class *cls = nullptr, |
| 8869 | bool hasDataStorage = false, |
| 8870 | std::uint32_t parentHandle = DRW::NoHandle, |
| 8871 | const DRW_Entity *commonEntity = nullptr, |
| 8872 | bool typedPeer = true) { |
| 8873 | DRW_UnsupportedObject raw; |
| 8874 | raw.m_version = version; |
| 8875 | raw.m_objectType = rawType; |
| 8876 | raw.m_handle = obj.handle; |
| 8877 | // A BLOCK_RECORD walk can establish reachability without proving the |
| 8878 | // entity's common owner link. Keep those facts separate below. |
| 8879 | raw.m_parentHandle = DRW::NoHandle; |
| 8880 | if (commonEntity != nullptr) { |
| 8881 | raw.setCommonLinkEvidence(drwDwgCommonLinkEvidenceForLinks( |
| 8882 | commonEntity->hasDwgCommonLinkTail(), commonEntity->parentHandle, |
| 8883 | commonEntity->reactorHandles, commonEntity->dwgReactorCount(), |
| 8884 | commonEntity->xDictHandle)); |
| 8885 | if (raw.m_commonLinkEvidence != DRW_DwgCommonLinkEvidence::Unknown) { |
| 8886 | raw.m_parentHandle = commonEntity->parentHandle; |
| 8887 | raw.m_reactorHandles = commonEntity->reactorHandles; |
| 8888 | raw.m_xDictHandle = commonEntity->xDictHandle; |
| 8889 | raw.m_numReactors = commonEntity->dwgReactorCount(); |
| 8890 | raw.m_xDictFlag = commonEntity->dwgXDictionaryFlag(); |
| 8891 | } |
| 8892 | } |
| 8893 | raw.m_blockOwnerHandle = rawBlockEntityOwner != DRW::NoHandle |
| 8894 | ? rawBlockEntityOwner |
| 8895 | : parentHandle; |
| 8896 | raw.m_bodyBitSize = bs; |
| 8897 | raw.m_objectOffset = obj.loc; |
| 8898 | raw.m_objectSize = static_cast<std::uint32_t>(size); |
| 8899 | raw.m_isEntity = true; |
| 8900 | raw.m_isCustomClass = cls != nullptr; |
| 8901 | raw.m_hasDataStorage = hasDataStorage; |
| 8902 | if (cls != nullptr) { |
| 8903 | raw.m_hasClassDefinition = true; |
| 8904 | raw.m_classProxyFlag = static_cast<std::uint16_t>(cls->proxyFlag); |
| 8905 | raw.m_classAppName = cls->appName; |
| 8906 | raw.m_classWasProxy = cls->wasaProxyFlag != 0; |
| 8907 | raw.m_classEntityFlagRaw = cls->entityFlagRaw; |
| 8908 | raw.m_classDwgVersion = cls->dwgVersion; |
| 8909 | raw.m_classMaintenanceVersion = cls->maintenanceVersion; |
| 8910 | raw.m_classUnknown1 = cls->unknown1; |
| 8911 | raw.m_classUnknown2 = cls->unknown2; |
| 8912 | raw.m_recordName = cls->recName; |
| 8913 | raw.m_className = cls->className; |
| 8914 | } |
| 8915 | raw.m_rawBytes = tmpByteStr; |
| 8916 | if (m_activeEntityFrameCapture != nullptr && |
| 8917 | m_activeEntityFrameCapture->publication.m_handle == obj.handle) { |
| 8918 | DRW_DwgFramePublication &publication = |
| 8919 | m_activeEntityFrameCapture->publication; |
| 8920 | publication.setCommonLinkEvidence(raw.m_commonLinkEvidence); |
| 8921 | publication.m_parentHandle = raw.m_parentHandle; |
| 8922 | publication.m_reactorHandles = raw.m_reactorHandles; |
| 8923 | publication.m_xDictHandle = raw.m_xDictHandle; |
| 8924 | publication.m_numReactors = raw.m_numReactors; |
| 8925 | publication.m_xDictFlag = raw.m_xDictFlag; |
| 8926 | m_activeEntityFrameCapture->rawViewIssued = true; |
| 8927 | m_activeEntityFrameCapture->rawViewHasTypedPeer = |
| 8928 | m_activeEntityFrameCapture->rawViewHasTypedPeer || typedPeer; |
| 8929 | } |
| 8930 | return raw; |
| 8931 | }; |
| 8932 | auto deferObject = [&](const objHandle &candidate) { |
| 8933 | if (m_activeEntityFrameLease != nullptr) { |
| 8934 | DwgFrameMapLease &activeLease = *m_activeEntityFrameLease; |
| 8935 | if (!activeLease.isDetached() || |
| 8936 | activeLease.object.handle != candidate.handle || |
| 8937 | activeLease.source.handle != candidate.handle) { |
| 8938 | ret = false; |
| 8939 | return reportDwgFrameTransitionFailure(activeLease.source, |
| 8940 | activeLease.object.loc, true); |
| 8941 | } |
| 8942 | if (!deferDetachedDwgSourceFrame(activeLease, objObjectMap)) { |
| 8943 | DRW_DBG("duplicate deferred object handle: ")DRW_dbg::dbg("duplicate deferred object handle: "); |
| 8944 | DRW_DBGH(candidate.handle)DRW_dbg::dbgH(candidate.handle); |
| 8945 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 8946 | ret = false; |
| 8947 | return false; |
| 8948 | } |
| 8949 | if (m_activeMappedEntityOutcome != nullptr) { |
| 8950 | *m_activeMappedEntityOutcome = DwgMappedEntityOutcome::DeferredObject; |
| 8951 | } |
| 8952 | return true; |
| 8953 | } |
| 8954 | DwgSourceFrameLease lease; |
| 8955 | lease.object = candidate; |
| 8956 | lease.source = sourceFrameId(candidate); |
| 8957 | lease.hasCoverage = |
| 8958 | m_dwgFrameCoverageStatus != DRW_DwgFrameCoverageStatus::NotAvailable; |
| 8959 | if (!deferDwgSourceFrame(lease, objObjectMap)) { |
| 8960 | DRW_DBG("duplicate deferred object handle: ")DRW_dbg::dbg("duplicate deferred object handle: "); |
| 8961 | DRW_DBGH(candidate.handle)DRW_dbg::dbgH(candidate.handle); |
| 8962 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 8963 | ret = false; |
| 8964 | return false; |
| 8965 | } |
| 8966 | return true; |
| 8967 | }; |
| 8968 | // WIPEOUT and DBCOLOR use fixed DWG types above 499. Other values in |
| 8969 | // that range are file-local CLASSES ordinals and must be resolved before |
| 8970 | // dispatch. |
| 8971 | const DRW_Class *resolvedClass = nullptr; |
| 8972 | bool unresolvedCustomClass = false; |
| 8973 | const bool fixedEntityShell = |
| 8974 | version >= DRW::AC1021 && |
| 8975 | DRW_UnsupportedObject::isFixedEntityShellType(oType); |
| 8976 | const bool fixedObjectShell = |
| 8977 | version >= DRW::AC1021 && |
| 8978 | DRW_UnsupportedObject::isFixedObjectShellType(oType); |
| 8979 | if (oType > dwgObjType::PROXY_OBJECT && !dwgObjType::isFixedObject(oType) && |
| 8980 | !fixedEntityShell && !fixedObjectShell && oType != dwgType::WIPEOUT) { |
| 8981 | auto it = classesmap.find(oType); |
| 8982 | if (it == classesmap.end()) { // preserve unknown custom objects |
| 8983 | unresolvedCustomClass = true; |
| 8984 | if (expectedBlockEntityOwner == DRW::NoHandle && |
| 8985 | !(rejectOwnedEntityInSweep && version > DRW::AC1015)) { |
| 8986 | // Without an entity owner, preserve it as an opaque OBJECTS |
| 8987 | // record; a known block owner is handled by the raw entity |
| 8988 | // path below. |
| 8989 | DRW_DBG("Class ")DRW_dbg::dbg("Class "); |
| 8990 | DRW_DBG(oType)DRW_dbg::dbg(oType); |
| 8991 | DRW_DBG("not found, defer handle: ")DRW_dbg::dbg("not found, defer handle: "); |
| 8992 | DRW_DBG(obj.handle)DRW_dbg::dbg(obj.handle); |
| 8993 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 8994 | obj.type = oType; |
| 8995 | return deferObject(obj); |
| 8996 | } |
| 8997 | } else { |
| 8998 | DRW_Class *cl = it->second; |
| 8999 | resolvedClass = cl; |
| 9000 | if (cl->dwgType != 0) |
| 9001 | oType = cl->dwgType; |
| 9002 | } |
| 9003 | } |
| 9004 | |
| 9005 | // Fixed OBJECTS records are not entity candidates. Defer them before the |
| 9006 | // entity owner probe and before dispatch, including fixed codes above the |
| 9007 | // custom-class range such as BLOCKREPRESENTATION. |
| 9008 | if (dwgObjType::isFixedObject(oType) || fixedObjectShell) { |
| 9009 | obj.type = oType; |
| 9010 | if (!deferObject(obj)) |
| 9011 | return false; |
| 9012 | return true; |
| 9013 | } |
| 9014 | |
| 9015 | // CLASSES item_class_id 0x1F3 identifies an OBJECTS record. Defer it |
| 9016 | // before the entity owner probe: object owners commonly point to the |
| 9017 | // named-object dictionary and must not be mistaken for stray entities. |
| 9018 | if (resolvedClass != nullptr && resolvedClass->entityFlag == 0) { |
| 9019 | obj.type = oType; |
| 9020 | if (!deferObject(obj)) |
| 9021 | return false; |
| 9022 | return true; |
| 9023 | } |
| 9024 | |
| 9025 | // Typed entities validate their owner in entryParse(). Opaque/custom |
| 9026 | // entities have no typed parser, so probe the common entity header and |
| 9027 | // handle stream on an independent cursor. This keeps a valid raw body |
| 9028 | // lossless without publishing it under the wrong BLOCK_RECORD or outside |
| 9029 | // its owner block. |
| 9030 | if (expectedBlockEntityOwner != DRW::NoHandle || ownerlessSpaceWalk || |
| 9031 | (rejectOwnedEntityInSweep && version > DRW::AC1015)) { |
| 9032 | ProxyHostEntity ownerProbe; |
| 9033 | dwgBuffer ownerBuffer = buff.forkIndependent(); |
| 9034 | const bool compoundChild = |
| 9035 | oType == dwgType::ATTRIB || oType == dwgType::SEQEND || |
| 9036 | oType == dwgType::VERTEX_2D || oType == dwgType::VERTEX_3D || |
| 9037 | oType == dwgType::VERTEX_MESH || oType == dwgType::VERTEX_PFACE || |
| 9038 | oType == dwgType::VERTEX_PFACE_FACE; |
| 9039 | if (!compoundChild) { |
| 9040 | const bool parsedCommon = |
| 9041 | ownerProbe.parseDwg(version, &ownerBuffer, bs); |
| 9042 | // R2004 has no independent handle-stream offset after an opaque |
| 9043 | // entity body. Later versions expose the boundary through the |
| 9044 | // object-size/string-stream metadata and can be checked exactly. |
| 9045 | const bool parsedHandles = |
| 9046 | parsedCommon && version > DRW::AC1018 && |
| 9047 | ownerProbe.parseDwgEntHandle(version, &ownerBuffer) && |
| 9048 | ownerBuffer.isGood(); |
| 9049 | const DwgEntityFailurePhase ownerProbeFailurePhase = |
| 9050 | !parsedCommon ? DwgEntityFailurePhase::TypedBody |
| 9051 | : (version > DRW::AC1018 && !parsedHandles |
| 9052 | ? DwgEntityFailurePhase::CommonHandles |
| 9053 | : DwgEntityFailurePhase::Identity); |
| 9054 | if (ownerlessSpaceWalk) { |
| 9055 | const bool ownerMismatch = ownerProbe.hasOwnerHandle(); |
| 9056 | const bool identityMismatch = |
| 9057 | expectedParsedEntityHandle != DRW::NoHandle && |
| 9058 | ownerProbe.handle != expectedParsedEntityHandle; |
| 9059 | if (identityMismatch) |
| 9060 | parsedEntityHandleMismatch = true; |
| 9061 | if (identityMismatch || ownerMismatch) { |
| 9062 | parsedEntityOwnerMismatch = ownerMismatch; |
| 9063 | m_currentEntityFailurePhase = ownerProbeFailurePhase; |
| 9064 | recordObjectFrameFailure(obj, offsetSpace); |
| 9065 | recordEntityFailure(obj, oType, m_currentEntityFailurePhase); |
| 9066 | if (frameFailure) |
| 9067 | *frameFailure = true; |
| 9068 | return false; |
| 9069 | } |
| 9070 | } else if (expectedBlockEntityOwner != DRW::NoHandle) { |
| 9071 | const bool ownerMismatch = |
| 9072 | !ownerProbe.hasOwnerHandle() || |
| 9073 | (version > DRW::AC1018 && |
| 9074 | (!parsedHandles || |
| 9075 | ownerProbe.parentHandle != expectedBlockEntityOwner)); |
| 9076 | const bool identityMismatch = |
| 9077 | expectedParsedEntityHandle != DRW::NoHandle && |
| 9078 | ownerProbe.handle != expectedParsedEntityHandle; |
| 9079 | if (!parsedCommon || identityMismatch || ownerMismatch) { |
| 9080 | parsedEntityOwnerMismatch = true; |
| 9081 | m_currentEntityFailurePhase = ownerProbeFailurePhase; |
| 9082 | recordObjectFrameFailure(obj, offsetSpace); |
| 9083 | recordEntityFailure(obj, oType, m_currentEntityFailurePhase); |
| 9084 | if (frameFailure) |
| 9085 | *frameFailure = true; |
| 9086 | return false; |
| 9087 | } |
| 9088 | } else if (parsedCommon && ownerProbe.hasOwnerHandle()) { |
| 9089 | if (rejectOwnedEntityInSweep && ownerProbe.hasOwnerHandle()) { |
| 9090 | // A non-null owner is only safe to publish from the |
| 9091 | // corresponding BLOCK_RECORD walk. The ownership list is |
| 9092 | // authoritative for R2004+; a stale or missing block |
| 9093 | // record must not turn an owned entity into a top-level |
| 9094 | // callback during the recovery sweep. |
| 9095 | parsedEntityOwnerMismatch = true; |
| 9096 | m_currentEntityFailurePhase = DwgEntityFailurePhase::Identity; |
| 9097 | recordEntityFailure(obj, oType, m_currentEntityFailurePhase); |
| 9098 | return false; |
| 9099 | } |
| 9100 | } |
| 9101 | } |
| 9102 | } |
| 9103 | |
| 9104 | DwgEntityFramePublicationCapture framePublication; |
| 9105 | framePublication.publication.m_version = version; |
| 9106 | framePublication.publication.m_handle = obj.handle; |
| 9107 | framePublication.publication.m_sourceOffset = obj.loc; |
| 9108 | framePublication.publication.m_sourceMapOrdinal = obj.sourceOrdinal; |
| 9109 | framePublication.publication.m_sourceOffsetSpace = obj.sourceOffsetSpace; |
| 9110 | framePublication.publication.m_hasSourceLocation = true; |
| 9111 | framePublication.publication.m_encodedType = encodedType; |
| 9112 | framePublication.publication.m_resolvedType = oType; |
| 9113 | framePublication.publication.m_isEntity = true; |
| 9114 | framePublication.publication.m_isCustomClass = resolvedClass != nullptr; |
| 9115 | if (resolvedClass != nullptr) { |
| 9116 | framePublication.publication.m_recordName = resolvedClass->recName; |
| 9117 | framePublication.publication.m_className = resolvedClass->className; |
| 9118 | } |
| 9119 | m_activeEntityFrameCapture = &framePublication; |
| 9120 | bool framePublicationPublished = false; |
| 9121 | bool compoundFrameHandled = false; |
| 9122 | class ProxyOutputSink final : public DRW_ProxyGraphicSink { |
| 9123 | public: |
| 9124 | explicit ProxyOutputSink(DwgEntityOutput &output) noexcept |
| 9125 | : m_output(output) {} |
| 9126 | |
| 9127 | bool addArc(const DRW_Arc &value) override { |
| 9128 | try { |
| 9129 | m_output.appendValue(value, &DRW_Interface::addArc); |
| 9130 | return true; |
| 9131 | } catch (...) { |
| 9132 | return false; |
| 9133 | } |
| 9134 | } |
| 9135 | bool addCircle(const DRW_Circle &value) override { |
| 9136 | try { |
| 9137 | m_output.appendValue(value, &DRW_Interface::addCircle); |
| 9138 | return true; |
| 9139 | } catch (...) { |
| 9140 | return false; |
| 9141 | } |
| 9142 | } |
| 9143 | bool addEllipse(const DRW_Ellipse &value) override { |
| 9144 | try { |
| 9145 | m_output.appendValue(value, &DRW_Interface::addEllipse); |
| 9146 | return true; |
| 9147 | } catch (...) { |
| 9148 | return false; |
| 9149 | } |
| 9150 | } |
| 9151 | bool addLWPolyline(const DRW_LWPolyline &value) override { |
| 9152 | try { |
| 9153 | m_output.appendValue(value, &DRW_Interface::addLWPolyline); |
| 9154 | return true; |
| 9155 | } catch (...) { |
| 9156 | return false; |
| 9157 | } |
| 9158 | } |
| 9159 | bool addMesh(const DRW_Mesh &value) override { |
| 9160 | try { |
| 9161 | m_output.appendValue(value, &DRW_Interface::addMesh); |
| 9162 | return true; |
| 9163 | } catch (...) { |
| 9164 | return false; |
| 9165 | } |
| 9166 | } |
| 9167 | bool addPolyline(const DRW_Polyline &value) override { |
| 9168 | try { |
| 9169 | m_output.appendValue(value, &DRW_Interface::addPolyline); |
| 9170 | return true; |
| 9171 | } catch (...) { |
| 9172 | return false; |
| 9173 | } |
| 9174 | } |
| 9175 | bool addText(const DRW_Text &value) override { |
| 9176 | try { |
| 9177 | m_output.appendValue(value, &DRW_Interface::addText); |
| 9178 | return true; |
| 9179 | } catch (...) { |
| 9180 | return false; |
| 9181 | } |
| 9182 | } |
| 9183 | |
| 9184 | private: |
| 9185 | DwgEntityOutput &m_output; |
| 9186 | }; |
| 9187 | const auto decodeProxyGraphics = |
| 9188 | [this, &output](const DRW_Entity &host, |
| 9189 | std::size_t carrierAndReceiptEventCount) { |
| 9190 | if (host.proxyGraphics.empty()) |
| 9191 | return true; |
| 9192 | |
| 9193 | const DRW_ProxyGraphicDecodeResult preflight = |
| 9194 | DRW_ProxyGraphicDecoder::inspect(host.proxyGraphics); |
| 9195 | switch (preflight.stopReason) { |
| 9196 | case DRW_ProxyGraphicStopReason::Complete: |
| 9197 | case DRW_ProxyGraphicStopReason::ShortHeader: |
| 9198 | case DRW_ProxyGraphicStopReason::InvalidChunkSize: |
| 9199 | case DRW_ProxyGraphicStopReason::TruncatedChunk: |
| 9200 | break; |
| 9201 | default: |
| 9202 | return false; |
| 9203 | } |
| 9204 | |
| 9205 | if (m_activeBlockTransaction != nullptr) { |
| 9206 | const std::size_t requiredEventCount = |
| 9207 | preflight.recognizedPrimitiveChunkCount + |
| 9208 | carrierAndReceiptEventCount; |
| 9209 | // The block walker has already reserved the standard |
| 9210 | // semantic-event plus receipt budget for this source. |
| 9211 | if (requiredEventCount > 3u && |
| 9212 | !m_activeBlockTransaction->reserveAdmission( |
| 9213 | 0u, requiredEventCount - 3u, 0u)) { |
| 9214 | return false; |
| 9215 | } |
| 9216 | } |
| 9217 | |
| 9218 | ProxyOutputSink sink(output); |
| 9219 | const DRW_ProxyGraphicDecodeResult decoded = |
| 9220 | DRW_ProxyGraphicDecoder::decode(host.proxyGraphics, version, sink, |
| 9221 | host, m_layerNameOrder, |
| 9222 | m_ltypeNameOrder); |
| 9223 | switch (decoded.stopReason) { |
| 9224 | case DRW_ProxyGraphicStopReason::Complete: |
| 9225 | case DRW_ProxyGraphicStopReason::ShortHeader: |
| 9226 | case DRW_ProxyGraphicStopReason::InvalidChunkSize: |
| 9227 | case DRW_ProxyGraphicStopReason::TruncatedChunk: |
| 9228 | break; |
| 9229 | default: |
| 9230 | return false; |
| 9231 | } |
| 9232 | if (decoded.emittedPrimitiveCount > |
| 9233 | preflight.recognizedPrimitiveChunkCount) { |
| 9234 | return false; |
| 9235 | } |
| 9236 | if (decoded.emittedPrimitiveCount > |
| 9237 | std::numeric_limits<std::size_t>::max() - |
| 9238 | m_decodedProxyPrimitives) { |
| 9239 | return false; |
| 9240 | } |
| 9241 | m_decodedProxyPrimitives += decoded.emittedPrimitiveCount; |
| 9242 | return true; |
| 9243 | }; |
| 9244 | const auto materializeCurrentFramePublication = |
| 9245 | [&framePublication](DRW_DwgFramePublication &publication) { |
| 9246 | if (!framePublication.typedViewParsed && |
| 9247 | !framePublication.rawViewIssued) |
| 9248 | return false; |
| 9249 | publication = framePublication.publication; |
| 9250 | publication.m_carrier = |
| 9251 | framePublication.rawViewIssued |
| 9252 | ? (framePublication.rawViewHasTypedPeer |
| 9253 | ? DRW_DwgFramePublication::Carrier::TypedAndRaw |
| 9254 | : DRW_DwgFramePublication::Carrier::Raw) |
| 9255 | : DRW_DwgFramePublication::Carrier::Typed; |
| 9256 | return true; |
| 9257 | }; |
| 9258 | obj.type = oType; |
| 9259 | if (fixedEntityShell) { |
| 9260 | RawEntityShell shell; |
| 9261 | if (entryParse(shell, buff, bs, ret)) { |
| 9262 | DRW_UnsupportedObject raw = |
| 9263 | makeRawEntity(oType, nullptr, shell.hasDataStorageBinaryData(), |
| 9264 | shell.parentHandle, &shell, false); |
| 9265 | raw.m_recordName = DRW_UnsupportedObject::fixedEntityShellName(oType); |
| 9266 | framePublication.publication.m_recordName = raw.m_recordName; |
| 9267 | output.appendValue(raw, &DRW_Interface::addUnsupportedObject); |
| 9268 | } |
| 9269 | } |
| 9270 | if (!fixedEntityShell) |
| 9271 | switch (oType) { |
| 9272 | case dwgType::TEXT: { |
| 9273 | DRW_Text e; |
| 9274 | if (entryParse(e, buff, bs, ret)) { |
| 9275 | e.style = findTableName(DRW::STYLE, e.styleH.ref); |
| 9276 | output.appendValue(e, &DRW_Interface::addText); |
| 9277 | } |
| 9278 | break; |
| 9279 | } |
| 9280 | case dwgType::ATTRIB: { |
| 9281 | auto a = std::make_shared<DRW_Attrib>(); |
| 9282 | bool localRet = true; |
| 9283 | entryParse(*a, buff, bs, localRet); |
| 9284 | const std::uint32_t ownerH = a->parentHandle; |
| 9285 | if (m_activeEntityFrameLease != nullptr) { |
| 9286 | compoundFrameHandled = true; |
| 9287 | if (!localRet) { |
| 9288 | if (ownerH != DRW::NoHandle) { |
| 9289 | abandonPendingInsertState(ownerH); |
| 9290 | terminalizeOrphanAttribOwner(ownerH); |
| 9291 | } |
| 9292 | ret = false; |
| 9293 | break; |
| 9294 | } |
| 9295 | a->style = findTableName(DRW::STYLE, a->styleH.ref); |
| 9296 | DRW_DwgFramePublication attribPublication; |
| 9297 | if (!materializeCurrentFramePublication(attribPublication)) { |
| 9298 | ret = false; |
| 9299 | break; |
| 9300 | } |
| 9301 | const DwgMappedEntityOutcome outcome = |
| 9302 | stagePendingAttribute(std::move(a), attribPublication, intfa); |
| 9303 | ret = outcome != DwgMappedEntityOutcome::Rejected; |
| 9304 | if (!ret) |
| 9305 | parsedEntityOwnerMismatch = true; |
| 9306 | if (m_activeMappedEntityOutcome != nullptr) |
| 9307 | *m_activeMappedEntityOutcome = outcome; |
| 9308 | break; |
| 9309 | } |
| 9310 | // INSERT-owned attributes require a detached HANDLE-map frame. |
| 9311 | // Direct reader calls have no source ownership to stage, so they |
| 9312 | // cannot establish a second compound state machine. |
| 9313 | ret = false; |
| 9314 | break; |
| 9315 | } |
| 9316 | case dwgType::ATTDEF: { |
| 9317 | auto a = std::make_shared<DRW_Attdef>(); |
| 9318 | bool localRet = true; |
| 9319 | entryParse(*a, buff, bs, localRet); |
| 9320 | if (localRet) { |
| 9321 | a->style = findTableName(DRW::STYLE, a->styleH.ref); |
| 9322 | // ATTDEF belongs to its BLOCK definition, not to an INSERT's |
| 9323 | // trailing ATTRIB sequence. Routing it through the latter |
| 9324 | // turns valid block text into an orphan and drops it at EOF. |
| 9325 | output.appendValue(*a, &DRW_Interface::addAttDef); |
| 9326 | } else { |
| 9327 | DRW_DBG("[attdef parse failed, handle ")DRW_dbg::dbg("[attdef parse failed, handle "); |
| 9328 | DRW_DBG(obj.handle)DRW_dbg::dbg(obj.handle); |
| 9329 | DRW_DBG("]\n")DRW_dbg::dbg("]\n"); |
| 9330 | } |
| 9331 | ret = localRet; |
| 9332 | break; |
| 9333 | } |
| 9334 | case dwgType::SEQEND: { |
| 9335 | DRW_SeqEnd sequenceEnd; |
| 9336 | if (m_activeEntityFrameLease != nullptr) { |
| 9337 | compoundFrameHandled = true; |
| 9338 | if (m_consumedSeqEndHandles.find(obj.handle) != |
| 9339 | m_consumedSeqEndHandles.end() || |
| 9340 | !entryParse(sequenceEnd, buff, bs, ret)) { |
| 9341 | if (sequenceEnd.parentHandle != DRW::NoHandle) |
| 9342 | abandonPendingInsertState(sequenceEnd.parentHandle); |
| 9343 | ret = false; |
| 9344 | break; |
| 9345 | } |
| 9346 | DRW_DwgFramePublication sequencePublication; |
| 9347 | if (!materializeCurrentFramePublication(sequencePublication)) { |
| 9348 | ret = false; |
| 9349 | break; |
| 9350 | } |
| 9351 | const DwgMappedEntityOutcome outcome = stagePendingSeqEnd( |
| 9352 | obj.handle, sequenceEnd.parentHandle, sequencePublication, intfa); |
| 9353 | ret = outcome != DwgMappedEntityOutcome::Rejected; |
| 9354 | if (!ret) |
| 9355 | parsedEntityOwnerMismatch = true; |
| 9356 | if (!ret) { |
| 9357 | try { |
| 9358 | m_invalidSeqEndHandles.insert(obj.handle); |
| 9359 | } catch (...) { |
| 9360 | // The frame is already rejected; duplicate suppression |
| 9361 | // is best effort when tracking it runs out of memory. |
| 9362 | } |
| 9363 | } |
| 9364 | if (m_activeMappedEntityOutcome != nullptr) |
| 9365 | *m_activeMappedEntityOutcome = outcome; |
| 9366 | break; |
| 9367 | } |
| 9368 | // A SEQEND has no standalone entity meaning. The owning INSERT |
| 9369 | // or POLYLINE must stage it from a bounded source frame. |
| 9370 | ret = false; |
| 9371 | break; |
| 9372 | } |
| 9373 | case dwgType::INSERT: |
| 9374 | case dwgType::MINSERT: { |
| 9375 | DRW_Insert e; |
| 9376 | if (m_activeEntityFrameLease != nullptr) { |
| 9377 | compoundFrameHandled = true; |
| 9378 | if (!entryParse(e, buff, bs, ret)) { |
| 9379 | terminalizeOrphanAttribOwner(obj.handle); |
| 9380 | ret = false; |
| 9381 | break; |
| 9382 | } |
| 9383 | e.name = findTableName(DRW::BLOCK_RECORD, e.blockRecH.ref); |
| 9384 | DRW_DwgFramePublication insertPublication; |
| 9385 | if (!materializeCurrentFramePublication(insertPublication)) { |
| 9386 | terminalizeOrphanAttribOwner(e.handle); |
| 9387 | ret = false; |
| 9388 | break; |
| 9389 | } |
| 9390 | const DwgMappedEntityOutcome outcome = |
| 9391 | version >= DRW::AC1018 |
| 9392 | ? stageMappedInsertAggregate(std::move(e), insertPublication, |
| 9393 | dbuf, intfa, offsetSpace) |
| 9394 | : stageLegacyInsertAggregate(std::move(e), insertPublication, |
| 9395 | dbuf, intfa, offsetSpace); |
| 9396 | ret = outcome != DwgMappedEntityOutcome::Rejected; |
| 9397 | if (!ret) |
| 9398 | parsedEntityOwnerMismatch = true; |
| 9399 | if (m_activeMappedEntityOutcome != nullptr) |
| 9400 | *m_activeMappedEntityOutcome = outcome; |
| 9401 | if (!ret) |
| 9402 | terminalizeOrphanAttribOwner(obj.handle); |
| 9403 | break; |
| 9404 | } |
| 9405 | // INSERT-family frames are admitted only through the mapped |
| 9406 | // aggregate. Parse within the frame boundary, but do not publish |
| 9407 | // or retain source-less ownership state. |
| 9408 | (void)entryParse(e, buff, bs, ret); |
| 9409 | ret = false; |
| 9410 | break; |
| 9411 | } |
| 9412 | case dwgType::VERTEX_2D: |
| 9413 | case dwgType::VERTEX_3D: |
| 9414 | case dwgType::VERTEX_MESH: |
| 9415 | case dwgType::VERTEX_PFACE: |
| 9416 | case dwgType::VERTEX_PFACE_FACE: { |
| 9417 | DRW_Vertex vertex; |
| 9418 | if (m_activeEntityFrameLease == nullptr || version < DRW::AC1018) { |
| 9419 | ret = false; |
| 9420 | break; |
| 9421 | } |
| 9422 | compoundFrameHandled = true; |
| 9423 | if (!vertex.parseDwg(version, &buff, bs, 0.0) || !buff.isGood()) { |
| 9424 | ret = false; |
| 9425 | break; |
| 9426 | } |
| 9427 | if (vertex.handle != expectedParsedEntityHandle) { |
| 9428 | parsedEntityHandleMismatch = true; |
| 9429 | ret = false; |
| 9430 | break; |
| 9431 | } |
| 9432 | // VERTEX ownership is its containing POLYLINE, rather than the |
| 9433 | // surrounding BLOCK_RECORD. It therefore cannot use entryParse's |
| 9434 | // block-owner check, but still needs the same typed frame capture |
| 9435 | // before the staged aggregate may claim its source frame. |
| 9436 | parseAttribs(&vertex); |
| 9437 | framePublication.publication.setCommonLinkEvidence( |
| 9438 | drwDwgCommonLinkEvidenceForLinks( |
| 9439 | vertex.hasDwgCommonLinkTail(), vertex.parentHandle, |
| 9440 | vertex.reactorHandles, vertex.dwgReactorCount(), |
| 9441 | vertex.xDictHandle)); |
| 9442 | framePublication.publication.m_parentHandle = vertex.parentHandle; |
| 9443 | framePublication.publication.m_reactorHandles = vertex.reactorHandles; |
| 9444 | framePublication.publication.m_xDictHandle = vertex.xDictHandle; |
| 9445 | framePublication.publication.m_numReactors = vertex.dwgReactorCount(); |
| 9446 | framePublication.publication.m_xDictFlag = vertex.dwgXDictionaryFlag(); |
| 9447 | framePublication.typedViewParsed = true; |
| 9448 | DRW_DwgFramePublication vertexPublication; |
| 9449 | if (!materializeCurrentFramePublication(vertexPublication)) { |
| 9450 | ret = false; |
| 9451 | break; |
| 9452 | } |
| 9453 | const DwgMappedEntityOutcome outcome = stagePendingPolylineVertex( |
| 9454 | std::move(vertex), vertexPublication, intfa); |
| 9455 | ret = outcome != DwgMappedEntityOutcome::Rejected; |
| 9456 | if (!ret) |
| 9457 | parsedEntityOwnerMismatch = true; |
| 9458 | if (m_activeMappedEntityOutcome != nullptr) |
| 9459 | *m_activeMappedEntityOutcome = outcome; |
| 9460 | break; |
| 9461 | } |
| 9462 | case dwgType::POLYLINE_2D: |
| 9463 | case dwgType::POLYLINE_3D: |
| 9464 | case dwgType::POLYLINE_PFACE: |
| 9465 | case dwgType::POLYLINE_MESH: { |
| 9466 | DRW_Polyline e; |
| 9467 | if (m_activeEntityFrameLease == nullptr) { |
| 9468 | // A POLYLINE owns VERTEX and SEQEND source frames. Without a |
| 9469 | // detached parent lease there is no transactional authority |
| 9470 | // to claim that aggregate, so direct parser entry is refused. |
| 9471 | ret = false; |
| 9472 | break; |
| 9473 | } |
| 9474 | compoundFrameHandled = true; |
| 9475 | if (!entryParse(e, buff, bs, ret)) { |
| 9476 | ret = false; |
| 9477 | break; |
| 9478 | } |
| 9479 | DRW_DwgFramePublication polylinePublication; |
| 9480 | if (!materializeCurrentFramePublication(polylinePublication)) { |
| 9481 | ret = false; |
| 9482 | break; |
| 9483 | } |
| 9484 | const DwgMappedEntityOutcome outcome = |
| 9485 | version >= DRW::AC1018 |
| 9486 | ? stageMappedPolylineAggregate(std::move(e), |
| 9487 | polylinePublication, dbuf, intfa, |
| 9488 | offsetSpace) |
| 9489 | : stageLegacyPolylineChain(std::move(e), polylinePublication, |
| 9490 | dbuf, intfa, offsetSpace); |
| 9491 | ret = outcome != DwgMappedEntityOutcome::Rejected; |
| 9492 | if (!ret) |
| 9493 | parsedEntityOwnerMismatch = true; |
| 9494 | if (m_activeMappedEntityOutcome != nullptr) |
| 9495 | *m_activeMappedEntityOutcome = outcome; |
| 9496 | break; |
| 9497 | } |
| 9498 | case dwgType::ARC: { |
| 9499 | DRW_Arc e; |
| 9500 | if (entryParse(e, buff, bs, ret)) { |
| 9501 | emitWithExtrusion(e, output, &DRW_Interface::addArc); |
| 9502 | } |
| 9503 | break; |
| 9504 | } |
| 9505 | case dwgType::CIRCLE: { |
| 9506 | DRW_Circle e; |
| 9507 | if (entryParse(e, buff, bs, ret)) { |
| 9508 | emitWithExtrusion(e, output, &DRW_Interface::addCircle); |
| 9509 | } |
| 9510 | break; |
| 9511 | } |
| 9512 | case dwgType::LINE: { |
| 9513 | DRW_Line e; |
| 9514 | if (entryParse(e, buff, bs, ret)) { |
| 9515 | output.appendValue(e, &DRW_Interface::addLine); |
| 9516 | } |
| 9517 | break; |
| 9518 | } |
| 9519 | case dwgType::THREEDLINE: { |
| 9520 | DRW_3DLine e; |
| 9521 | if (entryParse(e, buff, bs, ret)) { |
| 9522 | output.appendValue(e, &DRW_Interface::add3DLine); |
| 9523 | } |
| 9524 | break; |
| 9525 | } |
| 9526 | case dwgType::DIM_ORDINATE: { |
| 9527 | DRW_DimOrdinate e; |
| 9528 | if (entryParse(e, buff, bs, ret)) { |
| 9529 | e.style = findTableName(DRW::DIMSTYLE, e.dimStyleH.ref); |
| 9530 | output.appendValue(e, &DRW_Interface::addDimOrdinate); |
| 9531 | } |
| 9532 | break; |
| 9533 | } |
| 9534 | case dwgType::DIM_LINEAR: { |
| 9535 | DRW_DimLinear e; |
| 9536 | if (entryParse(e, buff, bs, ret)) { |
| 9537 | e.style = findTableName(DRW::DIMSTYLE, e.dimStyleH.ref); |
| 9538 | output.appendValue(e, &DRW_Interface::addDimLinear); |
| 9539 | } |
| 9540 | break; |
| 9541 | } |
| 9542 | case dwgType::DIM_ALIGNED: { |
| 9543 | DRW_DimAligned e; |
| 9544 | if (entryParse(e, buff, bs, ret)) { |
| 9545 | e.style = findTableName(DRW::DIMSTYLE, e.dimStyleH.ref); |
| 9546 | output.appendValue(e, &DRW_Interface::addDimAlign); |
| 9547 | } |
| 9548 | break; |
| 9549 | } |
| 9550 | case dwgType::DIM_ANGULAR3P: { |
| 9551 | DRW_DimAngular3p e; |
| 9552 | if (entryParse(e, buff, bs, ret)) { |
| 9553 | e.style = findTableName(DRW::DIMSTYLE, e.dimStyleH.ref); |
| 9554 | output.appendValue(e, &DRW_Interface::addDimAngular3P); |
| 9555 | } |
| 9556 | break; |
| 9557 | } |
| 9558 | case dwgType::DIM_ANGULAR: { |
| 9559 | DRW_DimAngular e; |
| 9560 | if (entryParse(e, buff, bs, ret)) { |
| 9561 | e.style = findTableName(DRW::DIMSTYLE, e.dimStyleH.ref); |
| 9562 | output.appendValue(e, &DRW_Interface::addDimAngular); |
| 9563 | } |
| 9564 | break; |
| 9565 | } |
| 9566 | case dwgType::DIM_RADIAL: { |
| 9567 | DRW_DimRadial e; |
| 9568 | if (entryParse(e, buff, bs, ret)) { |
| 9569 | e.style = findTableName(DRW::DIMSTYLE, e.dimStyleH.ref); |
| 9570 | output.appendValue(e, &DRW_Interface::addDimRadial); |
| 9571 | } |
| 9572 | break; |
| 9573 | } |
| 9574 | case dwgType::DIM_DIAMETRIC: { |
| 9575 | DRW_DimDiametric e; |
| 9576 | if (entryParse(e, buff, bs, ret)) { |
| 9577 | e.style = findTableName(DRW::DIMSTYLE, e.dimStyleH.ref); |
| 9578 | output.appendValue(e, &DRW_Interface::addDimDiametric); |
| 9579 | } |
| 9580 | break; |
| 9581 | } |
| 9582 | case dwgType::POINT: { |
| 9583 | DRW_Point e; |
| 9584 | if (entryParse(e, buff, bs, ret)) { |
| 9585 | output.appendValue(e, &DRW_Interface::addPoint); |
| 9586 | } |
| 9587 | break; |
| 9588 | } |
| 9589 | case dwgType::FACE3D: { |
| 9590 | DRW_3Dface e; |
| 9591 | if (entryParse(e, buff, bs, ret)) { |
| 9592 | output.appendValue(e, &DRW_Interface::add3dFace); |
| 9593 | } |
| 9594 | break; |
| 9595 | } |
| 9596 | case dwgType::SOLID: { |
| 9597 | DRW_Solid e; |
| 9598 | if (entryParse(e, buff, bs, ret)) { |
| 9599 | emitWithExtrusion(e, output, &DRW_Interface::addSolid); |
| 9600 | } |
| 9601 | break; |
| 9602 | } |
| 9603 | case dwgType::TRACE: { |
| 9604 | DRW_Trace e; |
| 9605 | if (entryParse(e, buff, bs, ret)) { |
| 9606 | emitWithExtrusion(e, output, &DRW_Interface::addTrace); |
| 9607 | } |
| 9608 | break; |
| 9609 | } |
| 9610 | case dwgType::SHAPE: { |
| 9611 | DRW_Shape e; |
| 9612 | if (entryParse(e, buff, bs, ret)) { |
| 9613 | e.m_objectSize = static_cast<std::uint32_t>(size); |
| 9614 | e.m_rawBytes = tmpByteStr; |
| 9615 | e.m_styleName = findTableName(DRW::STYLE, e.m_shapeFileHandle); |
| 9616 | output.appendValue(e, &DRW_Interface::addShape); |
| 9617 | output.appendValue(makeRawEntity(oType, nullptr, |
| 9618 | e.hasDataStorageBinaryData(), |
| 9619 | DRW::NoHandle, &e), |
| 9620 | &DRW_Interface::addUnsupportedObject); |
| 9621 | } |
| 9622 | break; |
| 9623 | } |
| 9624 | case dwgType::VIEWPORT: { |
| 9625 | DRW_Viewport e; |
| 9626 | bool hasDataStorage = false; |
| 9627 | if (entryParse(e, buff, bs, ret)) { |
| 9628 | output.appendValue(e, &DRW_Interface::addViewport); |
| 9629 | hasDataStorage = e.hasDataStorageBinaryData(); |
| 9630 | // Preserve the validated frame for same-version replay. |
| 9631 | output.appendValue( |
| 9632 | makeRawEntity(oType, nullptr, hasDataStorage, DRW::NoHandle, &e), |
| 9633 | &DRW_Interface::addUnsupportedObject); |
| 9634 | } |
| 9635 | break; |
| 9636 | } |
| 9637 | case dwgType::ELLIPSE: { |
| 9638 | DRW_Ellipse e; |
| 9639 | if (entryParse(e, buff, bs, ret)) { |
| 9640 | emitWithExtrusion(e, output, &DRW_Interface::addEllipse); |
| 9641 | } |
| 9642 | break; |
| 9643 | } |
| 9644 | case dwgType::SPLINE: { |
| 9645 | DRW_Spline e; |
| 9646 | if (entryParse(e, buff, bs, ret)) { |
| 9647 | output.appendValue(e, &DRW_Interface::addSpline); |
| 9648 | } |
| 9649 | break; |
| 9650 | } |
| 9651 | case dwgType::REGION: { |
| 9652 | DRW_ModelerGeometry e(DRW::REGION); |
| 9653 | if (entryParse(e, buff, bs, ret)) { |
| 9654 | linkDataStorage(e); |
| 9655 | e.m_objectSize = static_cast<std::uint32_t>(size); |
| 9656 | e.m_rawBytes = tmpByteStr; |
| 9657 | output.appendValue(e, &DRW_Interface::addModelerGeometry); |
| 9658 | output.appendValue(makeRawEntity(oType, nullptr, |
| 9659 | e.hasDataStorageBinaryData(), |
| 9660 | DRW::NoHandle, &e), |
| 9661 | &DRW_Interface::addUnsupportedObject); |
| 9662 | } |
| 9663 | break; |
| 9664 | } |
| 9665 | case dwgType::SOLID3D: { |
| 9666 | DRW_ModelerGeometry e(DRW::E3DSOLID); |
| 9667 | if (entryParse(e, buff, bs, ret)) { |
| 9668 | linkDataStorage(e); |
| 9669 | e.m_objectSize = static_cast<std::uint32_t>(size); |
| 9670 | e.m_rawBytes = tmpByteStr; |
| 9671 | output.appendValue(e, &DRW_Interface::addModelerGeometry); |
| 9672 | output.appendValue(makeRawEntity(oType, nullptr, |
| 9673 | e.hasDataStorageBinaryData(), |
| 9674 | DRW::NoHandle, &e), |
| 9675 | &DRW_Interface::addUnsupportedObject); |
| 9676 | } |
| 9677 | break; |
| 9678 | } |
| 9679 | case dwgType::BODY: { |
| 9680 | DRW_ModelerGeometry e(DRW::BODY); |
| 9681 | if (entryParse(e, buff, bs, ret)) { |
| 9682 | linkDataStorage(e); |
| 9683 | e.m_objectSize = static_cast<std::uint32_t>(size); |
| 9684 | e.m_rawBytes = tmpByteStr; |
| 9685 | output.appendValue(e, &DRW_Interface::addModelerGeometry); |
| 9686 | output.appendValue(makeRawEntity(oType, nullptr, |
| 9687 | e.hasDataStorageBinaryData(), |
| 9688 | DRW::NoHandle, &e), |
| 9689 | &DRW_Interface::addUnsupportedObject); |
| 9690 | } |
| 9691 | break; |
| 9692 | } |
| 9693 | case dwgType::RAY: { |
| 9694 | DRW_Ray e; |
| 9695 | if (entryParse(e, buff, bs, ret)) { |
| 9696 | output.appendValue(e, &DRW_Interface::addRay); |
| 9697 | } |
| 9698 | break; |
| 9699 | } |
| 9700 | case dwgType::XLINE: { |
| 9701 | DRW_Xline e; |
| 9702 | if (entryParse(e, buff, bs, ret)) { |
| 9703 | output.appendValue(e, &DRW_Interface::addXline); |
| 9704 | } |
| 9705 | break; |
| 9706 | } |
| 9707 | case dwgType::MTEXT: { |
| 9708 | DRW_MText e; |
| 9709 | if (entryParse(e, buff, bs, ret)) { |
| 9710 | e.style = findTableName(DRW::STYLE, e.styleH.ref); |
| 9711 | output.appendValue(e, &DRW_Interface::addMText); |
| 9712 | } |
| 9713 | break; |
| 9714 | } |
| 9715 | case dwgType::LEADER: { |
| 9716 | DRW_Leader e; |
| 9717 | if (entryParse(e, buff, bs, ret)) { |
| 9718 | e.style = findTableName(DRW::DIMSTYLE, e.dimStyleH.ref); |
| 9719 | output.appendValue(e, &DRW_Interface::addLeader); |
| 9720 | } |
| 9721 | break; |
| 9722 | } |
| 9723 | case dwgType::TOLERANCE: { |
| 9724 | DRW_Tolerance e; |
| 9725 | if (entryParse(e, buff, bs, ret)) { |
| 9726 | output.appendValue(e, &DRW_Interface::addTolerance); |
| 9727 | } |
| 9728 | break; |
| 9729 | } |
| 9730 | case dwgType::MLINE: { |
| 9731 | DRW_MLine e; |
| 9732 | if (entryParse(e, buff, bs, ret)) { |
| 9733 | if (e.styleHandle != 0) { |
| 9734 | auto it = mlineStyleNameMap.find(e.styleHandle); |
| 9735 | if (it != mlineStyleNameMap.end() && e.styleName.empty()) { |
| 9736 | e.styleName = it->second; |
| 9737 | } |
| 9738 | } |
| 9739 | output.appendValue(e, &DRW_Interface::addMLine); |
| 9740 | } |
| 9741 | break; |
| 9742 | } |
| 9743 | case dwgType::OLE2FRAME: { |
| 9744 | DRW_Ole2Frame e; |
| 9745 | if (entryParse(e, buff, bs, ret)) { |
| 9746 | e.m_objectSize = static_cast<std::uint32_t>(size); |
| 9747 | e.m_rawBytes = tmpByteStr; |
| 9748 | output.appendValue(e, &DRW_Interface::addOle2Frame); |
| 9749 | output.appendValue(makeRawEntity(oType, nullptr, |
| 9750 | e.hasDataStorageBinaryData(), |
| 9751 | DRW::NoHandle, &e), |
| 9752 | &DRW_Interface::addUnsupportedObject); |
| 9753 | } |
| 9754 | break; |
| 9755 | } |
| 9756 | case dwgType::OLEFRAME: { |
| 9757 | DRW_OleFrame e; |
| 9758 | if (entryParse(e, buff, bs, ret)) { |
| 9759 | e.m_objectSize = static_cast<std::uint32_t>(size); |
| 9760 | e.m_rawBytes = tmpByteStr; |
| 9761 | output.appendValue(e, &DRW_Interface::addOleFrame); |
| 9762 | output.appendValue(makeRawEntity(oType, nullptr, |
| 9763 | e.hasDataStorageBinaryData(), |
| 9764 | DRW::NoHandle, &e), |
| 9765 | &DRW_Interface::addUnsupportedObject); |
| 9766 | } |
| 9767 | break; |
| 9768 | } |
| 9769 | case dwgType::LWPOLYLINE: { |
| 9770 | DRW_LWPolyline e; |
| 9771 | if (entryParse(e, buff, bs, ret)) { |
| 9772 | emitWithExtrusion(e, output, &DRW_Interface::addLWPolyline); |
| 9773 | } |
| 9774 | break; |
| 9775 | } |
| 9776 | case dwgType::HATCH: { |
| 9777 | DRW_Hatch e; |
| 9778 | if (entryParse(e, buff, bs, ret)) { |
| 9779 | output.appendValue(e, &DRW_Interface::addHatch); |
| 9780 | } |
| 9781 | break; |
| 9782 | } |
| 9783 | case dwgType::IMAGE: { |
| 9784 | DRW_Image e; |
| 9785 | if (entryParse(e, buff, bs, ret)) { |
| 9786 | output.appendValue(e, &DRW_Interface::addImage); |
| 9787 | } |
| 9788 | break; |
| 9789 | } |
| 9790 | case dwgType::WIPEOUT: { |
| 9791 | DRW_Wipeout e; |
| 9792 | if (entryParse(e, buff, bs, ret)) { |
| 9793 | output.appendValue(e, &DRW_Interface::addWipeout); |
| 9794 | } |
| 9795 | break; |
| 9796 | } |
| 9797 | case dwgType::NAVISWORKSMODEL: { |
| 9798 | DRW_NavisworksModel e; |
| 9799 | if (entryParse(e, buff, bs, ret)) { |
| 9800 | output.appendValue(e, &DRW_Interface::addNavisworksModel); |
| 9801 | } |
| 9802 | break; |
| 9803 | } |
| 9804 | case dwgObjType::PROXY_ENTITY: { |
| 9805 | DRW_ProxyEntity e; |
| 9806 | if (entryParse(e, buff, bs, ret)) { |
| 9807 | ret = decodeProxyGraphics(e, 3u); |
| 9808 | if (ret) { |
| 9809 | output.appendValue(e, &DRW_Interface::addProxyEntity); |
| 9810 | DRW_UnsupportedObject raw = |
| 9811 | makeRawEntity(oType, nullptr, false, DRW::NoHandle, &e); |
| 9812 | raw.m_recordName = "ACAD_PROXY_ENTITY"; |
| 9813 | raw.m_className = "AcDbProxyEntity"; |
| 9814 | raw.m_hasDataStorage = e.hasDataStorageBinaryData(); |
| 9815 | output.appendValue(raw, &DRW_Interface::addUnsupportedObject); |
| 9816 | } |
| 9817 | } |
| 9818 | break; |
| 9819 | } |
| 9820 | case dwgObjType::DBCOLOR: |
| 9821 | // Fixed OBJECT type 1004 is collected for the OBJECTS pass. It |
| 9822 | // is above the custom-class numeric range but is not an entity. |
| 9823 | if (!deferObject(obj)) |
| 9824 | ret = false; |
| 9825 | break; |
| 9826 | |
| 9827 | default: |
| 9828 | if (oType >= 500) { |
| 9829 | // Custom-class object (typically AutoCAD Mechanical AcDbAm*, |
| 9830 | // AcDbAssoc*, or vendor proxy entity). Rendering proxy |
| 9831 | // graphics requires an ODA spec §20.4.95 decoder, which is |
| 9832 | // out of scope here; emit a distinct token so diagnostic |
| 9833 | // tools can distinguish "missing dispatch case" from |
| 9834 | // "intentionally-skipped custom class". |
| 9835 | if (resolvedClass != nullptr && |
| 9836 | (resolvedClass->recName == "GEOPOSITIONMARKER" || |
| 9837 | resolvedClass->recName == "POSITIONMARKER" || |
| 9838 | resolvedClass->className == "AcDbGeoPositionMarker")) { |
| 9839 | DRW_GeoPositionMarker e; |
| 9840 | if (entryParse(e, buff, bs, ret)) { |
| 9841 | output.appendValue(e, &DRW_Interface::addGeoPositionMarker); |
| 9842 | output.appendValue(makeRawEntity(oType, resolvedClass, |
| 9843 | e.hasDataStorageBinaryData(), |
| 9844 | DRW::NoHandle, &e), |
| 9845 | &DRW_Interface::addUnsupportedObject); |
| 9846 | } |
| 9847 | break; |
| 9848 | } |
| 9849 | auto cit = classesmap.find(oType); |
| 9850 | if (cit != classesmap.end() && cit->second && |
| 9851 | (cit->second->recName == "ARC_DIMENSION" || |
| 9852 | cit->second->className == "AcDbArcDimension")) { |
| 9853 | DRW_DimArc e; |
| 9854 | if (entryParse(e, buff, bs, ret)) { |
| 9855 | e.style = findTableName(DRW::DIMSTYLE, e.dimStyleH.ref); |
| 9856 | output.appendValue(e, &DRW_Interface::addDimArc); |
| 9857 | } |
| 9858 | break; |
| 9859 | } |
| 9860 | if (cit != classesmap.end() && cit->second && |
| 9861 | (cit->second->recName == "LARGE_RADIAL_DIMENSION" || |
| 9862 | cit->second->className == "AcDbRadialDimensionLarge")) { |
| 9863 | // Jogged radius dimension (ODA §20.4.20). Delivered via the |
| 9864 | // existing addDimRadial callback (DRW_DimLargeRadial is-a |
| 9865 | // DRW_DimRadial); the jog point/angle ride along on the object. |
| 9866 | DRW_DimLargeRadial e; |
| 9867 | if (entryParse(e, buff, bs, ret)) { |
| 9868 | e.style = findTableName(DRW::DIMSTYLE, e.dimStyleH.ref); |
| 9869 | output.appendValue(e, &DRW_Interface::addDimRadial); |
| 9870 | } |
| 9871 | break; |
| 9872 | } |
| 9873 | if (cit != classesmap.end() && cit->second && |
| 9874 | cit->second->recName == "WIPEOUT") { |
| 9875 | DRW_Wipeout e; |
| 9876 | if (entryParse(e, buff, bs, ret)) { |
| 9877 | output.appendValue(e, &DRW_Interface::addWipeout); |
| 9878 | } |
| 9879 | break; |
| 9880 | } |
| 9881 | if (cit != classesmap.end() && cit->second && |
| 9882 | cit->second->recName == "POINTCLOUD") { |
| 9883 | DRW_PointCloud e; |
| 9884 | if (entryParse(e, buff, bs, ret)) { |
| 9885 | output.appendValue(e, &DRW_Interface::addPointCloud); |
| 9886 | } |
| 9887 | break; |
| 9888 | } |
| 9889 | if (cit != classesmap.end() && cit->second && |
| 9890 | cit->second->recName == "POINTCLOUDEX") { |
| 9891 | DRW_PointCloudEx e; |
| 9892 | if (entryParse(e, buff, bs, ret)) { |
| 9893 | output.appendValue(e, &DRW_Interface::addPointCloudEx); |
| 9894 | } |
| 9895 | break; |
| 9896 | } |
| 9897 | if (cit != classesmap.end() && cit->second && |
| 9898 | (cit->second->recName == "NAVISWORKSMODEL" || |
| 9899 | cit->second->className == "AcDbNavisworksModel")) { |
| 9900 | DRW_NavisworksModel e; |
| 9901 | if (entryParse(e, buff, bs, ret)) { |
| 9902 | output.appendValue(e, &DRW_Interface::addNavisworksModel); |
| 9903 | } |
| 9904 | break; |
| 9905 | } |
| 9906 | if (cit != classesmap.end() && cit->second && |
| 9907 | cit->second->recName == "PLANESURFACE") { |
| 9908 | DRW_PlaneSurface e; |
| 9909 | if (entryParse(e, buff, bs, ret)) { |
| 9910 | linkDataStorage(e); |
| 9911 | output.appendValue(e, &DRW_Interface::addSurface); |
| 9912 | output.appendValue(makeRawEntity(oType, cit->second, |
| 9913 | e.hasDataStorageBinaryData(), |
| 9914 | DRW::NoHandle, &e), |
| 9915 | &DRW_Interface::addUnsupportedObject); |
| 9916 | } |
| 9917 | break; |
| 9918 | } |
| 9919 | if (cit != classesmap.end() && cit->second && |
| 9920 | cit->second->recName == "EXTRUDEDSURFACE") { |
| 9921 | DRW_ExtrudedSurface e; |
| 9922 | if (entryParse(e, buff, bs, ret)) { |
| 9923 | linkDataStorage(e); |
| 9924 | output.appendValue(e, &DRW_Interface::addSurface); |
| 9925 | output.appendValue(makeRawEntity(oType, cit->second, |
| 9926 | e.hasDataStorageBinaryData(), |
| 9927 | DRW::NoHandle, &e), |
| 9928 | &DRW_Interface::addUnsupportedObject); |
| 9929 | } |
| 9930 | break; |
| 9931 | } |
| 9932 | if (cit != classesmap.end() && cit->second && |
| 9933 | cit->second->recName == "REVOLVEDSURFACE") { |
| 9934 | DRW_RevolvedSurface e; |
| 9935 | if (entryParse(e, buff, bs, ret)) { |
| 9936 | linkDataStorage(e); |
| 9937 | output.appendValue(e, &DRW_Interface::addSurface); |
| 9938 | output.appendValue(makeRawEntity(oType, cit->second, |
| 9939 | e.hasDataStorageBinaryData(), |
| 9940 | DRW::NoHandle, &e), |
| 9941 | &DRW_Interface::addUnsupportedObject); |
| 9942 | } |
| 9943 | break; |
| 9944 | } |
| 9945 | if (cit != classesmap.end() && cit->second && |
| 9946 | cit->second->recName == "SWEPTSURFACE") { |
| 9947 | DRW_SweptSurface e; |
| 9948 | if (entryParse(e, buff, bs, ret)) { |
| 9949 | linkDataStorage(e); |
| 9950 | output.appendValue(e, &DRW_Interface::addSurface); |
| 9951 | output.appendValue(makeRawEntity(oType, cit->second, |
| 9952 | e.hasDataStorageBinaryData(), |
| 9953 | DRW::NoHandle, &e), |
| 9954 | &DRW_Interface::addUnsupportedObject); |
| 9955 | } |
| 9956 | break; |
| 9957 | } |
| 9958 | if (cit != classesmap.end() && cit->second && |
| 9959 | cit->second->recName == "LOFTEDSURFACE") { |
| 9960 | DRW_LoftedSurface e; |
| 9961 | if (entryParse(e, buff, bs, ret)) { |
| 9962 | linkDataStorage(e); |
| 9963 | output.appendValue(e, &DRW_Interface::addSurface); |
| 9964 | output.appendValue(makeRawEntity(oType, cit->second, |
| 9965 | e.hasDataStorageBinaryData(), |
| 9966 | DRW::NoHandle, &e), |
| 9967 | &DRW_Interface::addUnsupportedObject); |
| 9968 | } |
| 9969 | break; |
| 9970 | } |
| 9971 | if (cit != classesmap.end() && cit->second && |
| 9972 | cit->second->recName == "NURBSURFACE") { |
| 9973 | DRW_NurbsSurface e; |
| 9974 | if (entryParse(e, buff, bs, ret)) { |
| 9975 | linkDataStorage(e); |
| 9976 | output.appendValue(e, &DRW_Interface::addSurface); |
| 9977 | output.appendValue(makeRawEntity(oType, cit->second, |
| 9978 | e.hasDataStorageBinaryData(), |
| 9979 | DRW::NoHandle, &e), |
| 9980 | &DRW_Interface::addUnsupportedObject); |
| 9981 | } |
| 9982 | break; |
| 9983 | } |
| 9984 | if (cit != classesmap.end() && cit->second && |
| 9985 | cit->second->recName == "MULTILEADER") { |
| 9986 | // MULTILEADER (AcDbMLeader, ODA spec §20.4.48). |
| 9987 | // DRW_MLeader::parseDwg fully decodes the entity: the |
| 9988 | // embedded MLeaderAnnotContext (roots, leader lines, text/ |
| 9989 | // block content), the entity-level fields, and the handle |
| 9990 | // stream. The DXF read path (dxfRW::processMultiLeader) |
| 9991 | // decodes the same nested CONTEXT_DATA{} block via |
| 9992 | // DRW_MLeader::parseDxfContextCode (drw_entities.cpp). |
| 9993 | DRW_MLeader e; |
| 9994 | if (entryParse(e, buff, bs, ret)) { |
| 9995 | output.appendValue(e, &DRW_Interface::addMLeader); |
| 9996 | } |
| 9997 | break; |
| 9998 | } |
| 9999 | if (cit != classesmap.end() && cit->second && |
| 10000 | (cit->second->recName == "MPOLYGON" || |
| 10001 | cit->second->className == "AcDbMPolygon")) { |
| 10002 | // AcDbMPolygon (hatch-derived filled polygon). addMPolygon |
| 10003 | // defaults to addHatch, so it renders as a filled hatch. |
| 10004 | DRW_MPolygon e; |
| 10005 | if (entryParse(e, buff, bs, ret)) { |
| 10006 | output.appendValue(e, &DRW_Interface::addMPolygon); |
| 10007 | } |
| 10008 | break; |
| 10009 | } |
| 10010 | if (cit != classesmap.end() && cit->second && |
| 10011 | (cit->second->recName == "RTEXT" || |
| 10012 | cit->second->className == "RText" || |
| 10013 | cit->second->className == "AcDbRText")) { |
| 10014 | // RTEXT (AutoCAD Express Tools reactive text, ODA type 1159). |
| 10015 | // Mapped onto DRW_Text and delivered via addText — the |
| 10016 | // literal text if present, else the raw DIESEL/xref string. |
| 10017 | DRW_RText e; |
| 10018 | if (entryParse(e, buff, bs, ret)) { |
| 10019 | e.style = findTableName(DRW::STYLE, e.styleH.ref); |
| 10020 | output.appendValue(e, &DRW_Interface::addText); |
| 10021 | } |
| 10022 | break; |
| 10023 | } |
| 10024 | if (cit != classesmap.end() && cit->second && |
| 10025 | (cit->second->recName == "ARCALIGNEDTEXT" || |
| 10026 | cit->second->recName == "ARC_ALIGNED_TEXT" || |
| 10027 | cit->second->className == "AcDbArcAlignedText")) { |
| 10028 | // ARCALIGNEDTEXT (Express Tools arc-aligned text, ODA type |
| 10029 | // 1163). Mapped onto DRW_Text as a 2D approximation placed |
| 10030 | // at the arc mid-point (see DRW_ArcAlignedText). The style |
| 10031 | // is a name string in the DWG body, so it is NOT resolved |
| 10032 | // from a handle here. |
| 10033 | DRW_ArcAlignedText e; |
| 10034 | if (entryParse(e, buff, bs, ret)) { |
| 10035 | output.appendValue(e, &DRW_Interface::addText); |
| 10036 | } |
| 10037 | break; |
| 10038 | } |
| 10039 | if (cit != classesmap.end() && cit->second && |
| 10040 | (cit->second->recName == "CAMERA" || |
| 10041 | cit->second->className == "AcDbCamera")) { |
| 10042 | DRW_Camera e; |
| 10043 | if (entryParse(e, buff, bs, ret)) { |
| 10044 | output.appendValue(e, &DRW_Interface::addCamera); |
| 10045 | output.appendValue( |
| 10046 | makeRawEntity(oType, cit->second, false, DRW::NoHandle, &e), |
| 10047 | &DRW_Interface::addUnsupportedObject); |
| 10048 | } |
| 10049 | break; |
| 10050 | } |
| 10051 | if (cit != classesmap.end() && cit->second && |
| 10052 | (cit->second->recName == "GEOPOSITIONMARKER" || |
| 10053 | cit->second->recName == "POSITIONMARKER" || |
| 10054 | cit->second->className == "AcDbGeoPositionMarker")) { |
| 10055 | DRW_GeoPositionMarker e; |
| 10056 | if (entryParse(e, buff, bs, ret)) { |
| 10057 | output.appendValue(e, &DRW_Interface::addGeoPositionMarker); |
| 10058 | output.appendValue(makeRawEntity(oType, cit->second, |
| 10059 | e.hasDataStorageBinaryData(), |
| 10060 | DRW::NoHandle, &e), |
| 10061 | &DRW_Interface::addUnsupportedObject); |
| 10062 | } |
| 10063 | break; |
| 10064 | } |
| 10065 | if (cit != classesmap.end() && cit->second && |
| 10066 | (cit->second->recName == "ACAD_TABLE" || |
| 10067 | cit->second->className == "AcDbTable")) { |
| 10068 | DRW_Table e; |
| 10069 | if (entryParse(e, buff, bs, ret)) { |
| 10070 | e.name = findTableName(DRW::BLOCK_RECORD, e.blockRecH.ref); |
| 10071 | output.appendValue(e, &DRW_Interface::addTable); |
| 10072 | } |
| 10073 | break; |
| 10074 | } |
| 10075 | if (cit != classesmap.end() && cit->second) { |
| 10076 | const std::string &rn = cit->second->recName; |
| 10077 | const std::string &cn = cit->second->className; |
| 10078 | if (rn == "HELIX" || cn == "AcDbHelix") { |
| 10079 | DRW_Helix e; |
| 10080 | if (entryParse(e, buff, bs, ret)) { |
| 10081 | output.appendValue(e, &DRW_Interface::addHelix); |
| 10082 | } |
| 10083 | break; |
| 10084 | } |
| 10085 | if (rn == "MESH" || cn == "AcDbSubDMesh") { |
| 10086 | DRW_Mesh e; |
| 10087 | if (entryParse(e, buff, bs, ret)) { |
| 10088 | output.appendValue(e, &DRW_Interface::addMesh); |
| 10089 | // LibreCAD renders MESH as a 2D fallback and therefore does |
| 10090 | // not retain the typed payload. Keep the validated source |
| 10091 | // frame as a raw peer so OBJECTS references such as |
| 10092 | // SORTENTSTABLE can still target the entity on replay. |
| 10093 | output.appendValue( |
| 10094 | makeRawEntity(oType, cit->second, false, DRW::NoHandle, &e), |
| 10095 | &DRW_Interface::addUnsupportedObject); |
| 10096 | } |
| 10097 | break; |
| 10098 | } |
| 10099 | if (rn == "LIGHT" || cn == "AcDbLight") { |
| 10100 | DRW_Light e; |
| 10101 | if (entryParse(e, buff, bs, ret)) { |
| 10102 | output.appendValue(e, &DRW_Interface::addLight); |
| 10103 | output.appendValue( |
| 10104 | makeRawEntity(oType, cit->second, false, DRW::NoHandle, &e), |
| 10105 | &DRW_Interface::addUnsupportedObject); |
| 10106 | } |
| 10107 | break; |
| 10108 | } |
| 10109 | if (rn == "SECTIONOBJECT" || cn == "AcDbSection") { |
| 10110 | // SECTIONOBJECT (AcDbSection) live-section plane — typed |
| 10111 | // decode restores the section geometry + metadata + the |
| 10112 | // section_settings reference for dwgTs parity. Keep the |
| 10113 | // raw shelf only when the frame passes entryParse. |
| 10114 | DRW_SectionObject e; |
| 10115 | if (entryParse(e, buff, bs, ret)) { |
| 10116 | output.appendValue(e, &DRW_Interface::addSectionObject); |
| 10117 | output.appendValue( |
| 10118 | makeRawEntity(oType, cit->second, false, DRW::NoHandle, &e), |
| 10119 | &DRW_Interface::addUnsupportedObject); |
| 10120 | } |
| 10121 | break; |
| 10122 | } |
| 10123 | // NOTE: the AcDbSurface family (SURFACE / EXTRUDED / REVOLVED / |
| 10124 | // LOFTED / SWEPT / PLANE / NURB) is dispatched by the typed |
| 10125 | // DRW_Surface arms above (intfa.addSurface); a bare AcDbSurface |
| 10126 | // with no concrete subtype falls through to the generic |
| 10127 | // custom-class handler (raw round-trip + proxy-graphics decode). |
| 10128 | if (rn == "PDFUNDERLAY" || rn == "DGNUNDERLAY" || |
| 10129 | rn == "DWFUNDERLAY" || cn == "AcDbPdfReference" || |
| 10130 | cn == "AcDbDgnReference" || cn == "AcDbDwfReference") { |
| 10131 | DRW_Underlay e; |
| 10132 | if (rn == "DGNUNDERLAY" || cn == "AcDbDgnReference") |
| 10133 | e.kind = DRW_Underlay::DGN; |
| 10134 | else if (rn == "DWFUNDERLAY" || cn == "AcDbDwfReference") |
| 10135 | e.kind = DRW_Underlay::DWF; |
| 10136 | // else default PDF |
| 10137 | if (entryParse(e, buff, bs, ret)) { |
| 10138 | output.appendValue(e, &DRW_Interface::addUnderlay); |
| 10139 | } |
| 10140 | break; |
| 10141 | } |
| 10142 | } |
| 10143 | if (cit != classesmap.end() && cit->second && |
| 10144 | cit->second->entityFlag == 0) { |
| 10145 | if (deferObject(obj)) { |
| 10146 | DRW_DBG("[entity-pass-defer-custom-object ")DRW_dbg::dbg("[entity-pass-defer-custom-object "); |
| 10147 | DRW_DBG(oType)DRW_dbg::dbg(oType); |
| 10148 | DRW_DBG(" ")DRW_dbg::dbg(" "); |
| 10149 | DRW_DBG(cit->second->recName.c_str())DRW_dbg::dbg(cit->second->recName.c_str()); |
| 10150 | DRW_DBG("]\n")DRW_dbg::dbg("]\n"); |
| 10151 | } |
| 10152 | break; |
| 10153 | } |
| 10154 | const char *className = (cit != classesmap.end() && cit->second) |
| 10155 | ? cit->second->recName.c_str() |
| 10156 | : "(unknown)"; |
| 10157 | const DRW_Class *customClass = |
| 10158 | (cit != classesmap.end() && cit->second) ? cit->second : nullptr; |
| 10159 | // The post-block sweep is only a recovery pass for records |
| 10160 | // that were not claimed by a BLOCK_RECORD. A known entity |
| 10161 | // class without a validated block walk is therefore |
| 10162 | // malformed, even when its common header happens to parse. |
| 10163 | // Model/paper-space walks and direct block reads have already |
| 10164 | // set the owner context and are intentionally unaffected. |
| 10165 | if (rejectOwnedEntityInSweep && version > DRW::AC1015 && |
| 10166 | customClass != nullptr && !ownerlessSpaceWalk) { |
| 10167 | parsedEntityOwnerMismatch = true; |
| 10168 | ret = false; |
| 10169 | break; |
| 10170 | } |
| 10171 | // R2007+ exposes the entity handle stream at objSize. Do not |
| 10172 | // raw-publish an opaque custom entity, or decode its proxy |
| 10173 | // graphics, until the common entity header and detached tail |
| 10174 | // both parse on an isolated cursor. R2000/R2004 opaque custom |
| 10175 | // bodies have no generic safe handle-stream boundary. |
| 10176 | if (version > DRW::AC1018 && customClass != nullptr) { |
| 10177 | RawEntityShell shell; |
| 10178 | dwgBuffer validationBuffer = buff.forkIndependent(); |
| 10179 | ret = shell.parseDwg(version, &validationBuffer, bs) && |
| 10180 | validationBuffer.isGood(); |
| 10181 | if (ret && shell.handle != obj.handle) { |
| 10182 | parsedEntityHandleMismatch = true; |
| 10183 | ret = false; |
| 10184 | } |
| 10185 | if (!ret) |
| 10186 | break; |
| 10187 | |
| 10188 | DRW_UnsupportedObject raw = makeRawEntity( |
| 10189 | oType, customClass, shell.hasDataStorageBinaryData(), |
| 10190 | shell.parentHandle, &shell, false); |
| 10191 | // Recover proxy graphics only after structural framing is |
| 10192 | // valid; a truncated tail must not produce child callbacks. |
| 10193 | ProxyHostEntity host; |
| 10194 | dwgBuffer proxyBuffer = buff.forkIndependent(); |
| 10195 | if (host.parseDwg(version, &proxyBuffer, bs) && |
| 10196 | proxyBuffer.isGood()) { |
| 10197 | host.parentHandle = shell.parentHandle; |
| 10198 | if (!decodeProxyGraphics(host, 2u)) { |
| 10199 | ret = false; |
| 10200 | break; |
| 10201 | } |
| 10202 | } |
| 10203 | output.appendValue(raw, &DRW_Interface::addUnsupportedObject); |
| 10204 | DRW_DBG("[custom-class-skipped ")DRW_dbg::dbg("[custom-class-skipped "); |
| 10205 | DRW_DBG(oType)DRW_dbg::dbg(oType); |
| 10206 | DRW_DBG(" ")DRW_dbg::dbg(" "); |
| 10207 | DRW_DBG(className)DRW_dbg::dbg(className); |
| 10208 | DRW_DBG("]\n")DRW_dbg::dbg("]\n"); |
| 10209 | ++m_skippedCustomClasses[className]; |
| 10210 | break; |
| 10211 | } |
| 10212 | DRW_UnsupportedObject raw; |
| 10213 | // The raw carrier still needs the common identity fields |
| 10214 | // checked. This is the only handle available before an |
| 10215 | // unknown AC1018 body reaches its unbounded tail. |
| 10216 | ProxyHostEntity identityHost; |
| 10217 | dwgBuffer identityBuffer = buff.forkIndependent(); |
| 10218 | if (!identityHost.parseDwg(version, &identityBuffer, bs) || |
| 10219 | !identityBuffer.isGood() || identityHost.handle != obj.handle) { |
| 10220 | parsedEntityHandleMismatch = true; |
| 10221 | recordObjectFrameFailure(obj, offsetSpace); |
| 10222 | if (frameFailure) |
| 10223 | *frameFailure = true; |
| 10224 | ret = false; |
| 10225 | break; |
| 10226 | } |
| 10227 | const std::uint32_t rawOwner = |
| 10228 | expectedBlockEntityOwner != DRW::NoHandle |
| 10229 | ? expectedBlockEntityOwner |
| 10230 | : identityHost.parentHandle; |
| 10231 | raw = makeRawEntity(oType, customClass, false, rawOwner, nullptr, |
| 10232 | false); |
| 10233 | if (cit != classesmap.end() && cit->second) { |
| 10234 | raw.m_recordName = cit->second->recName; |
| 10235 | raw.m_className = cit->second->className; |
| 10236 | } |
| 10237 | // Recover cached PROXY GRAPHICS before raw-netting: this class is |
| 10238 | // unmodelled, but it may carry a self-contained primitive stream |
| 10239 | // (STDPART2D, AEC_WALL/WINDOW/DOOR, …) that any reader can render. |
| 10240 | // makeRawEntity never parses, so proxyGraphics is empty here; run |
| 10241 | // the class-agnostic common prologue on a throwaway host purely to |
| 10242 | // lift the graphData bytes (buff is unconsumed at this fall-through |
| 10243 | // — every typed arm above breaks), then decode them into render |
| 10244 | // primitives. The raw object is STILL emitted below for lossless |
| 10245 | // round-trip; decoding only adds extra renderable geometry. |
| 10246 | { |
| 10247 | ProxyHostEntity host; |
| 10248 | dwgBuffer proxyBuffer = buff.forkIndependent(); |
| 10249 | if (host.parseDwg(version, &proxyBuffer, bs) && |
| 10250 | proxyBuffer.isGood()) { |
| 10251 | host.parentHandle = rawOwner; |
| 10252 | if (!decodeProxyGraphics(host, 2u)) { |
| 10253 | ret = false; |
| 10254 | break; |
| 10255 | } |
| 10256 | } |
| 10257 | } |
| 10258 | output.appendValue(raw, &DRW_Interface::addUnsupportedObject); |
| 10259 | DRW_DBG("[custom-class-skipped ")DRW_dbg::dbg("[custom-class-skipped "); |
| 10260 | DRW_DBG(oType)DRW_dbg::dbg(oType); |
| 10261 | DRW_DBG(" ")DRW_dbg::dbg(" "); |
| 10262 | DRW_DBG(className)DRW_dbg::dbg(className); |
| 10263 | DRW_DBG("]\n")DRW_dbg::dbg("]\n"); |
| 10264 | ++m_skippedCustomClasses[className]; |
| 10265 | } else { |
| 10266 | if (!deferObject(obj)) |
| 10267 | break; |
| 10268 | // Fixed oType (<500) not handled by the entity-pass switch |
| 10269 | // but queued in objObjectMap for the OBJECTS pass; the |
| 10270 | // OBJECTS switch dispatches case 42 DICTIONARY, 73 MLINESTYLE, |
| 10271 | // 82 LAYOUT, 102 IMAGEDEF etc. Older code logged this as |
| 10272 | // "unhandled" which was misleading. |
| 10273 | DRW_DBG("[entity-pass-defer ")DRW_dbg::dbg("[entity-pass-defer "); |
| 10274 | DRW_DBG(oType)DRW_dbg::dbg(oType); |
| 10275 | DRW_DBG("]\n")DRW_dbg::dbg("]\n"); |
| 10276 | } |
| 10277 | break; |
| 10278 | } |
| 10279 | if (!ret && !compoundFrameHandled) { |
| 10280 | // A frame only establishes byte bounds. A known typed record whose |
| 10281 | // body parser fails is malformed for that record and must not escape |
| 10282 | // through the raw callback as if it were a validated opaque shell. |
| 10283 | // Explicit fixed/custom shell routes publish raw data only after their |
| 10284 | // own complete parser succeeds. |
| 10285 | DRW_DBG("Warning: Entity type ")DRW_dbg::dbg("Warning: Entity type "); |
| 10286 | DRW_DBG(oType)DRW_dbg::dbg(oType); |
| 10287 | DRW_DBG("has failed, handle: ")DRW_dbg::dbg("has failed, handle: "); |
| 10288 | DRW_DBG(obj.handle)DRW_dbg::dbg(obj.handle); |
| 10289 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 10290 | markDwgFrameOutcome(sourceFrameId(obj), DRW_DwgFrameDisposition::Failed); |
| 10291 | } |
| 10292 | |
| 10293 | if (ret && !framePublicationPublished && !compoundFrameHandled && |
| 10294 | (framePublication.typedViewParsed || framePublication.rawViewIssued)) { |
| 10295 | framePublication.publication.m_carrier = |
| 10296 | framePublication.rawViewIssued |
| 10297 | ? (framePublication.rawViewHasTypedPeer |
| 10298 | ? DRW_DwgFramePublication::Carrier::TypedAndRaw |
| 10299 | : DRW_DwgFramePublication::Carrier::Raw) |
| 10300 | : DRW_DwgFramePublication::Carrier::Typed; |
| 10301 | if (!output.appendFramePublication(*this, framePublication.publication)) |
| 10302 | ret = false; |
| 10303 | } |
| 10304 | if (!ret) { |
| 10305 | DwgEntityFailurePhase phase = m_currentEntityFailurePhase; |
| 10306 | if (parsedEntityHandleMismatch || parsedEntityOwnerMismatch) |
| 10307 | phase = DwgEntityFailurePhase::Identity; |
| 10308 | else if (phase == DwgEntityFailurePhase::None) |
| 10309 | phase = compoundFrameHandled ? DwgEntityFailurePhase::Aggregate |
| 10310 | : DwgEntityFailurePhase::TypedBody; |
| 10311 | recordEntityFailure(obj, oType, phase); |
| 10312 | } |
| 10313 | m_activeEntityFrameCapture = nullptr; |
| 10314 | return ret; |
| 10315 | } catch (...) { |
| 10316 | m_activeEntityFrameCapture = nullptr; |
| 10317 | nextEntLink = prevEntLink = 0; |
| 10318 | nextEntLinkImplicit = false; |
| 10319 | if (m_dwgFrameCoverageStatus != DRW_DwgFrameCoverageStatus::NotAvailable) { |
| 10320 | (void)markDwgFrameOutcome(sourceFrameId(obj), |
| 10321 | DRW_DwgFrameDisposition::Failed, |
| 10322 | DRW_DwgFrameCoverageReason::CallbackException); |
| 10323 | } |
| 10324 | if (frameFailure) |
| 10325 | *frameFailure = true; |
| 10326 | recordEntityFailure(obj, |
| 10327 | obj.type > std::numeric_limits<std::int16_t>::max() |
| 10328 | ? -1 |
| 10329 | : static_cast<std::int16_t>(obj.type), |
| 10330 | m_currentEntityFailurePhase == |
| 10331 | DwgEntityFailurePhase::None |
| 10332 | ? DwgEntityFailurePhase::TypedBody |
| 10333 | : m_currentEntityFailurePhase); |
| 10334 | return false; |
| 10335 | } |
| 10336 | } |
| 10337 | |
| 10338 | bool dwgReader::readDwgObjects(DRW_Interface &intfa, dwgBuffer *dbuf, |
| 10339 | DwgIntegrityAddressSpace offsetSpace) { |
| 10340 | std::uint32_t i = 0; |
| 10341 | bool structuralFailure = false; |
| 10342 | DRW_DBG("\nentities map total size= ")DRW_dbg::dbg("\nentities map total size= "); |
| 10343 | DRW_DBG(ObjectMap.size())DRW_dbg::dbg(ObjectMap.size()); |
| 10344 | DRW_DBG("\nobjects map total size= ")DRW_dbg::dbg("\nobjects map total size= "); |
| 10345 | DRW_DBG(objObjectMap.size())DRW_dbg::dbg(objObjectMap.size()); |
| 10346 | std::vector<DRW_UnsupportedObject> deferredRawObjects; |
| 10347 | deferredRawObjects.swap(m_deferredRawObjects); |
| 10348 | // Per-object parseDwg failures are warnings, not section failures — |
| 10349 | // each object is read from its own ObjectMap location, so one bad |
| 10350 | // record cannot corrupt the next. Mirrors readDwgEntities resilience. |
| 10351 | size_t failures = 0; |
| 10352 | while (!objObjectMap.empty()) { |
| 10353 | auto itB = objObjectMap.begin(); |
| 10354 | if (m_quarantinedEntityHandles.find(itB->first) != |
| 10355 | m_quarantinedEntityHandles.end()) { |
| 10356 | if (!discardDwgSourceFrame(objObjectMap, itB)) { |
| 10357 | structuralFailure = true; |
| 10358 | break; |
| 10359 | } |
| 10360 | continue; |
| 10361 | } |
| 10362 | DwgFrameMapLease lease; |
| 10363 | if (!detachDwgSourceFrame(objObjectMap, itB, lease)) { |
| 10364 | structuralFailure = true; |
| 10365 | break; |
| 10366 | } |
| 10367 | bool frameFailure = false; |
| 10368 | bool read = true; |
| 10369 | if (lease.classification.has_value()) { |
| 10370 | DwgFrameClassification observed; |
| 10371 | read = classifyDwgSourceFrame(dbuf, lease.object, observed) && |
| 10372 | classificationsMatch(*lease.classification, observed); |
| 10373 | if (!read) { |
| 10374 | recordObjectFrameFailure(lease.object, offsetSpace); |
| 10375 | frameFailure = true; |
| 10376 | } |
| 10377 | } |
| 10378 | if (read && |
| 10379 | !readDwgObject(dbuf, lease.object, intfa, &frameFailure, offsetSpace)) { |
| 10380 | read = false; |
| 10381 | } |
| 10382 | if (!read) { |
| 10383 | ++failures; |
| 10384 | if (lease.hasCoverage && |
| 10385 | !markDwgFrameOutcome(lease.source, DRW_DwgFrameDisposition::Failed)) { |
| 10386 | structuralFailure = true; |
| 10387 | } |
| 10388 | } |
| 10389 | if (lease.hasCoverage && lease.classification.has_value()) { |
| 10390 | const auto sourceIt = m_dwgSourceFrameIndexes.find(lease.source.handle); |
| 10391 | if (sourceIt == m_dwgSourceFrameIndexes.end() || |
| 10392 | sourceIt->second >= m_dwgSourceFrameLedger.size()) { |
| 10393 | structuralFailure = true; |
| 10394 | } else { |
| 10395 | DRW_DwgFrameDisposition disposition = |
| 10396 | m_dwgSourceFrameLedger[sourceIt->second].m_disposition; |
| 10397 | DwgFramePhaseSnapshot::Destination destination; |
| 10398 | bool terminalDisposition = true; |
| 10399 | switch (disposition) { |
| 10400 | case DRW_DwgFrameDisposition::Published: |
| 10401 | destination = DwgFramePhaseSnapshot::Destination::Published; |
| 10402 | break; |
| 10403 | case DRW_DwgFrameDisposition::Failed: |
| 10404 | destination = DwgFramePhaseSnapshot::Destination::Failed; |
| 10405 | break; |
| 10406 | case DRW_DwgFrameDisposition::Quarantined: |
| 10407 | destination = DwgFramePhaseSnapshot::Destination::Quarantined; |
| 10408 | break; |
| 10409 | case DRW_DwgFrameDisposition::Unresolved: |
| 10410 | destination = DwgFramePhaseSnapshot::Destination::Unresolved; |
| 10411 | break; |
| 10412 | default: |
| 10413 | terminalDisposition = markDwgFrameOutcome( |
| 10414 | lease.source, DRW_DwgFrameDisposition::Failed); |
| 10415 | structuralFailure = structuralFailure || !terminalDisposition; |
| 10416 | if (terminalDisposition) { |
| 10417 | disposition = DRW_DwgFrameDisposition::Failed; |
| 10418 | destination = DwgFramePhaseSnapshot::Destination::Failed; |
| 10419 | } |
| 10420 | break; |
| 10421 | } |
| 10422 | if (terminalDisposition) { |
| 10423 | recordDwgFramePhaseSnapshot(lease, destination, disposition); |
| 10424 | } |
| 10425 | } |
| 10426 | } |
| 10427 | if (!discardDetachedDwgSourceFrame(lease)) { |
| 10428 | structuralFailure = true; |
| 10429 | } |
| 10430 | structuralFailure = structuralFailure || frameFailure; |
| 10431 | } |
| 10432 | if (failures > 0) { |
| 10433 | DRW_DBG("readDwgObjects: ")DRW_dbg::dbg("readDwgObjects: "); |
| 10434 | DRW_DBG(failures)DRW_dbg::dbg(failures); |
| 10435 | DRW_DBG(" objects failed to parse (warnings, not section failure)\n")DRW_dbg::dbg(" objects failed to parse (warnings, not section failure)\n" ); |
| 10436 | m_objectParseFailures += failures; |
| 10437 | } |
| 10438 | if (DRW_DBGGLDRW_dbg::getInstance()->getLevel() == DRW_dbg::Level::Debug) { |
| 10439 | for (auto it = remainingMap.begin(); it != remainingMap.end(); ++it) { |
| 10440 | DRW_DBG("\nnum.# ")DRW_dbg::dbg("\nnum.# "); |
| 10441 | DRW_DBG(i++)DRW_dbg::dbg(i++); |
| 10442 | DRW_DBG(" Remaining object Handle, loc, type= ")DRW_dbg::dbg(" Remaining object Handle, loc, type= "); |
| 10443 | DRW_DBG(it->first)DRW_dbg::dbg(it->first); |
| 10444 | DRW_DBG(" ")DRW_dbg::dbg(" "); |
| 10445 | DRW_DBG(it->second.loc)DRW_dbg::dbg(it->second.loc); |
| 10446 | DRW_DBG(" ")DRW_dbg::dbg(" "); |
| 10447 | DRW_DBG(it->second.type)DRW_dbg::dbg(it->second.type); |
| 10448 | } |
| 10449 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 10450 | } |
| 10451 | finalizeDataStorageLinks(); |
| 10452 | if (!structuralFailure) { |
| 10453 | structuralFailure = !publishDeferredRawObjects(intfa, deferredRawObjects); |
| 10454 | } |
| 10455 | // A bounded object body that a typed parser cannot decode is retained as |
| 10456 | // a warning, but a missing/truncated object frame invalidates the OBJECTS |
| 10457 | // section. The frame reader is the only authority for this distinction. |
| 10458 | return !structuralFailure; |
| 10459 | } |
| 10460 | |
| 10461 | bool dwgReader::publishDeferredRawObjects( |
| 10462 | DRW_Interface &intfa, std::vector<DRW_UnsupportedObject> &objects) { |
| 10463 | for (const DRW_UnsupportedObject &raw : objects) { |
| 10464 | const bool hasFrameCoverage = |
| 10465 | m_dwgFrameCoverageStatus != DRW_DwgFrameCoverageStatus::NotAvailable; |
| 10466 | DwgSourceFrameId source; |
| 10467 | if (hasFrameCoverage) { |
| 10468 | source = sourceFrameIdForHandle(raw.m_handle); |
| 10469 | if (m_dwgSourceFrameIndexes.find(raw.m_handle) == |
| 10470 | m_dwgSourceFrameIndexes.end()) { |
| 10471 | return reportDwgFrameTransitionFailure(source); |
| 10472 | } |
| 10473 | } |
| 10474 | try { |
| 10475 | intfa.addUnsupportedObject(raw); |
| 10476 | } catch (...) { |
| 10477 | if (hasFrameCoverage) { |
| 10478 | (void)markDwgFrameOutcome( |
| 10479 | source, DRW_DwgFrameDisposition::Failed, |
| 10480 | DRW_DwgFrameCoverageReason::CallbackException); |
| 10481 | } |
| 10482 | return false; |
| 10483 | } |
| 10484 | if (hasFrameCoverage) { |
| 10485 | DRW_DwgFramePublication publication; |
| 10486 | publication.m_version = raw.m_version; |
| 10487 | publication.m_handle = raw.m_handle; |
| 10488 | publication.m_sourceOffset = source.offset; |
| 10489 | publication.m_sourceMapOrdinal = source.ordinal; |
| 10490 | publication.m_sourceOffsetSpace = source.offsetSpace; |
| 10491 | publication.m_hasSourceLocation = true; |
| 10492 | publication.m_encodedType = raw.m_objectType; |
| 10493 | publication.m_resolvedType = raw.m_objectType; |
| 10494 | publication.m_isEntity = raw.m_isEntity; |
| 10495 | publication.m_isCustomClass = raw.m_isCustomClass; |
| 10496 | publication.m_recordName = raw.m_recordName; |
| 10497 | publication.m_className = raw.m_className; |
| 10498 | publication.setCommonLinkEvidence(raw.m_commonLinkEvidence); |
| 10499 | publication.m_parentHandle = raw.m_parentHandle; |
| 10500 | publication.m_reactorHandles = raw.m_reactorHandles; |
| 10501 | publication.m_xDictHandle = raw.m_xDictHandle; |
| 10502 | publication.m_numReactors = raw.m_numReactors; |
| 10503 | publication.m_xDictFlag = raw.m_xDictFlag; |
| 10504 | publication.m_carrier = DRW_DwgFramePublication::Carrier::Raw; |
| 10505 | if (!publishDwgFramePublication(intfa, std::move(publication))) |
| 10506 | return false; |
| 10507 | } |
| 10508 | } |
| 10509 | objects.clear(); |
| 10510 | return true; |
| 10511 | } |
| 10512 | |
| 10513 | /** |
| 10514 | * Reads a dwg drawing object (dwg object object) given its offset in the file |
| 10515 | */ |
| 10516 | bool dwgReader::readDwgObject(dwgBuffer *dbuf, objHandle &obj, |
| 10517 | DRW_Interface &intfa, bool *frameFailure, |
| 10518 | DwgIntegrityAddressSpace offsetSpace) { |
| 10519 | bool ret = true; |
| 10520 | const auto failStructural = [frameFailure]() { |
| 10521 | if (frameFailure) |
| 10522 | *frameFailure = true; |
| 10523 | return false; |
| 10524 | }; |
| 10525 | if (frameFailure) |
| 10526 | *frameFailure = false; |
| 10527 | |
| 10528 | if (dbuf == nullptr) { |
| 10529 | recordObjectFrameFailure(obj, offsetSpace); |
| 10530 | return failStructural(); |
| 10531 | } |
| 10532 | |
| 10533 | try { |
| 10534 | DwgObjectFrame frame; |
| 10535 | if (!frame.readAt(*dbuf, version, obj.loc)) { |
| 10536 | recordObjectFrameFailure(obj, offsetSpace); |
| 10537 | if (frameFailure) |
| 10538 | *frameFailure = true; |
| 10539 | DRW_DBG(" Warning: readDwgObject, invalid object frame\n")DRW_dbg::dbg(" Warning: readDwgObject, invalid object frame\n" ); |
| 10540 | return false; |
| 10541 | } |
| 10542 | DwgFrameClassification classification; |
| 10543 | if (!classifyDwgSourceFrame(dbuf, obj, classification)) { |
| 10544 | recordObjectFrameFailure(obj, offsetSpace); |
| 10545 | DRW_DBG(" Warning: readDwgObject, missing object type\n")DRW_dbg::dbg(" Warning: readDwgObject, missing object type\n" ); |
| 10546 | return failStructural(); |
| 10547 | } |
| 10548 | if (classification.route == DwgFrameClassification::Route::BlockDelimiter) { |
| 10549 | recordObjectFrameFailure(obj, offsetSpace); |
| 10550 | DRW_DBG(" Warning: readDwgObject, misplaced block delimiter\n")DRW_dbg::dbg(" Warning: readDwgObject, misplaced block delimiter\n" ); |
| 10551 | return failStructural(); |
| 10552 | } |
| 10553 | if (classification.route != DwgFrameClassification::Route::Object) { |
| 10554 | recordObjectFrameFailure(obj, offsetSpace); |
| 10555 | DRW_DBG(" Warning: readDwgObject, object frame routed as entity\n")DRW_dbg::dbg(" Warning: readDwgObject, object frame routed as entity\n" ); |
| 10556 | return failStructural(); |
| 10557 | } |
| 10558 | |
| 10559 | const std::uint32_t bs = frame.bodyBitSize(); |
| 10560 | auto &tmpByteStr = frame.body(); |
| 10561 | const std::size_t size = tmpByteStr.size(); |
| 10562 | dwgBuffer buff(tmpByteStr.data(), size, &decoder); |
| 10563 | const std::int16_t encodedType = classification.encodedType; |
| 10564 | const std::int16_t oType = classification.resolvedType; |
| 10565 | const DRW_Class *resolvedClass = classification.resolvedClass; |
| 10566 | const bool fixedObjectShell = classification.fixedObjectShell; |
| 10567 | const bool rawCustomObjectShell = |
| 10568 | isValidatedRawCustomObjectShell(resolvedClass); |
| 10569 | const bool centerLineActionBody = |
| 10570 | isCenterLineActionBodyClass(resolvedClass); |
| 10571 | std::optional<DRW_DwgDictionaryMembership> dictionaryMembership; |
| 10572 | std::optional<DRW_DwgGroupMembership> groupMembership; |
| 10573 | std::optional<DRW_DwgSortEntsMembership> sortEntsMembership; |
| 10574 | std::optional<DRW_DwgFieldListMembership> fieldListMembership; |
| 10575 | std::optional<DRW_DwgFieldPayloadReceipt> fieldPayloadReceipt; |
| 10576 | std::optional<DRW_Field> fieldOutput; |
| 10577 | std::optional<DRW_FieldList> fieldListOutput; |
| 10578 | std::optional<DRW_UnsupportedObject> fieldRawOutput; |
| 10579 | std::optional<DRW_DwgDictionaryWithDefaultMembership> |
| 10580 | dictionaryWithDefaultMembership; |
| 10581 | std::optional<DRW_DwgTypedReference> typedReference; |
| 10582 | obj.type = static_cast<std::uint32_t>(oType); |
| 10583 | // Validate the shared OBJECTS prologue before dispatch. Every typed table |
| 10584 | // object consumes this same common handle before its class-specific body; |
| 10585 | // checking it on an independent cursor prevents a map entry from |
| 10586 | // publishing a valid-looking object under a different handle. |
| 10587 | dwgBuffer commonBuffer = buff.forkIndependent(); |
| 10588 | dwgBuffer commonStringBuffer = buff.forkIndependent(); |
| 10589 | DRW_Dictionary commonObject; |
| 10590 | const bool commonParsed = commonObject.DRW_TableEntry::parseDwg( |
| 10591 | version, &commonBuffer, |
| 10592 | version > DRW::AC1018 ? &commonStringBuffer : nullptr, bs); |
| 10593 | if (!commonParsed || !commonBuffer.isGood() || |
| 10594 | (version > DRW::AC1018 && !commonStringBuffer.isGood()) || |
| 10595 | commonObject.handle != obj.handle) { |
| 10596 | recordObjectFrameFailure(obj, offsetSpace); |
| 10597 | DRW_DBG(" Warning: readDwgObject, invalid common prologue or handle\n")DRW_dbg::dbg(" Warning: readDwgObject, invalid common prologue or handle\n" ); |
| 10598 | return failStructural(); |
| 10599 | } |
| 10600 | if (version > DRW::AC1018 && |
| 10601 | !commonObject.DRW_TableEntry::parseDwgCommonHandleData(version, |
| 10602 | &commonBuffer)) { |
| 10603 | recordObjectFrameFailure(obj, offsetSpace); |
| 10604 | DRW_DBG(" Warning: readDwgObject, invalid common handle tail\n")DRW_dbg::dbg(" Warning: readDwgObject, invalid common handle tail\n" ); |
| 10605 | return failStructural(); |
| 10606 | } |
| 10607 | DRW_DwgFramePublication publication; |
| 10608 | publication.m_version = version; |
| 10609 | publication.m_handle = obj.handle; |
| 10610 | publication.m_sourceOffset = obj.loc; |
| 10611 | publication.m_sourceMapOrdinal = obj.sourceOrdinal; |
| 10612 | publication.m_sourceOffsetSpace = obj.sourceOffsetSpace; |
| 10613 | publication.m_hasSourceLocation = true; |
| 10614 | publication.m_encodedType = encodedType; |
| 10615 | publication.m_resolvedType = oType; |
| 10616 | publication.m_isCustomClass = resolvedClass != nullptr; |
| 10617 | if (resolvedClass != nullptr) { |
| 10618 | publication.m_recordName = resolvedClass->recName; |
| 10619 | publication.m_className = resolvedClass->className; |
| 10620 | } |
| 10621 | publication.setCommonLinkEvidence(drwDwgCommonLinkEvidenceForLinks( |
| 10622 | commonObject.hasDwgCommonLinkTail(), commonObject.parentHandle, |
| 10623 | commonObject.reactorHandles, commonObject.reactorCount(), |
| 10624 | commonObject.xDictHandle)); |
| 10625 | publication.m_parentHandle = commonObject.parentHandle; |
| 10626 | publication.m_reactorHandles = commonObject.reactorHandles; |
| 10627 | publication.m_xDictHandle = commonObject.xDictHandle; |
| 10628 | publication.m_numReactors = commonObject.reactorCount(); |
| 10629 | publication.m_xDictFlag = commonObject.extensionDictionaryFlag(); |
| 10630 | const auto failReceiptPreflight = [this, &obj]() { |
| 10631 | if (m_dwgFrameCoverageStatus != |
| 10632 | DRW_DwgFrameCoverageStatus::NotAvailable) { |
| 10633 | (void)markDwgFrameOutcome(sourceFrameId(obj), |
| 10634 | DRW_DwgFrameDisposition::Failed, |
| 10635 | DRW_DwgFrameCoverageReason::ReceiptFailure); |
| 10636 | } |
| 10637 | return false; |
| 10638 | }; |
| 10639 | bool rawViewIssued = false; |
| 10640 | bool rawViewHasTypedPeer = false; |
| 10641 | auto makeRawObject = [&](int rawType, const DRW_Class *cls = nullptr, |
| 10642 | bool typedPeer = true) { |
| 10643 | rawViewIssued = true; |
| 10644 | rawViewHasTypedPeer = rawViewHasTypedPeer || typedPeer; |
| 10645 | DRW_UnsupportedObject raw; |
| 10646 | raw.m_version = version; |
| 10647 | raw.m_objectType = rawType; |
| 10648 | raw.m_handle = obj.handle; |
| 10649 | raw.m_parentHandle = DRW::NoHandle; |
| 10650 | raw.setCommonLinkEvidence(drwDwgCommonLinkEvidenceForLinks( |
| 10651 | commonObject.hasDwgCommonLinkTail(), commonObject.parentHandle, |
| 10652 | commonObject.reactorHandles, commonObject.reactorCount(), |
| 10653 | commonObject.xDictHandle)); |
| 10654 | if (raw.m_commonLinkEvidence != DRW_DwgCommonLinkEvidence::Unknown) { |
| 10655 | raw.m_parentHandle = commonObject.parentHandle; |
| 10656 | raw.m_reactorHandles = commonObject.reactorHandles; |
| 10657 | raw.m_xDictHandle = commonObject.xDictHandle; |
| 10658 | raw.m_numReactors = commonObject.reactorCount(); |
| 10659 | raw.m_xDictFlag = commonObject.extensionDictionaryFlag(); |
| 10660 | } |
| 10661 | raw.m_bodyBitSize = bs; |
| 10662 | raw.m_objectOffset = obj.loc; |
| 10663 | raw.m_objectSize = static_cast<std::uint32_t>(size); |
| 10664 | raw.m_isEntity = false; |
| 10665 | raw.m_isCustomClass = cls != nullptr; |
| 10666 | if (cls != nullptr) { |
| 10667 | raw.m_hasClassDefinition = true; |
| 10668 | raw.m_classProxyFlag = static_cast<std::uint16_t>(cls->proxyFlag); |
| 10669 | raw.m_classAppName = cls->appName; |
| 10670 | raw.m_classWasProxy = cls->wasaProxyFlag != 0; |
| 10671 | raw.m_classEntityFlagRaw = cls->entityFlagRaw; |
| 10672 | raw.m_classDwgVersion = cls->dwgVersion; |
| 10673 | raw.m_classMaintenanceVersion = cls->maintenanceVersion; |
| 10674 | raw.m_classUnknown1 = cls->unknown1; |
| 10675 | raw.m_classUnknown2 = cls->unknown2; |
| 10676 | raw.m_recordName = cls->recName; |
| 10677 | raw.m_className = cls->className; |
| 10678 | } |
| 10679 | raw.m_hasDataStorage = commonObject.hasDataStorageBinaryData(); |
| 10680 | raw.m_rawBytes = tmpByteStr; |
| 10681 | return raw; |
| 10682 | }; |
| 10683 | // OBJECTS are parsed only after the APPID and layer tables are complete. |
| 10684 | // Resolve EED's deferred references before any typed callback observes the |
| 10685 | // record, matching the table-record and entity publication contracts. |
| 10686 | auto parseTableEntry = [this, &buff, bs](auto &entry) { |
| 10687 | const bool parsed = entry.parseDwg(version, &buff, bs) && buff.isGood(); |
| 10688 | if (parsed) |
| 10689 | parseAttribs(&entry); |
| 10690 | return parsed; |
| 10691 | }; |
| 10692 | |
| 10693 | if (fixedObjectShell) { |
| 10694 | RawObjectShell shell; |
| 10695 | ret = shell.parseDwg(version, &buff, bs) && buff.isGood(); |
| 10696 | if (ret) { |
| 10697 | DRW_UnsupportedObject raw = makeRawObject(oType, nullptr, false); |
| 10698 | raw.m_recordName = DRW_UnsupportedObject::fixedObjectShellName(oType); |
| 10699 | intfa.addUnsupportedObject(raw); |
| 10700 | } |
| 10701 | } else if (rawCustomObjectShell) { |
| 10702 | RawObjectShell shell; |
| 10703 | ret = shell.parseDwg(version, &buff, bs) && buff.isGood(); |
| 10704 | if (ret) |
| 10705 | intfa.addUnsupportedObject(makeRawObject(oType, resolvedClass, false)); |
| 10706 | } else |
| 10707 | switch (oType) { |
| 10708 | case dwgObjType::DICTIONARY: { |
| 10709 | DRW_Dictionary e; |
| 10710 | ret = parseTableEntry(e); |
| 10711 | if (ret) { |
| 10712 | if (e.hasCompleteDwgEntries()) { |
| 10713 | DRW_DwgDictionaryMembership receipt; |
| 10714 | receipt.m_version = version; |
| 10715 | receipt.m_dictionaryHandle = obj.handle; |
| 10716 | receipt.m_sourceOffset = obj.loc; |
| 10717 | receipt.m_sourceMapOrdinal = obj.sourceOrdinal; |
| 10718 | receipt.m_sourceOffsetSpace = obj.sourceOffsetSpace; |
| 10719 | receipt.m_hasSourceLocation = true; |
| 10720 | receipt.m_complete = true; |
| 10721 | receipt.m_entries.reserve(e.m_entries.size()); |
| 10722 | for (const DRW_Dictionary::Entry &entry : e.m_entries) { |
| 10723 | receipt.m_entries.push_back({entry.m_name, entry.m_handle}); |
| 10724 | } |
| 10725 | dictionaryMembership = std::move(receipt); |
| 10726 | } |
| 10727 | intfa.addDictionary(e); |
| 10728 | intfa.addUnsupportedObject(makeRawObject(oType)); |
| 10729 | } |
| 10730 | break; |
| 10731 | } |
| 10732 | case dwgObjType::GROUP: { |
| 10733 | DRW_Group e; |
| 10734 | ret = parseTableEntry(e); |
| 10735 | if (ret) { |
| 10736 | if (!e.hasCompleteDwgEntityHandles()) { |
| 10737 | ret = failReceiptPreflight(); |
| 10738 | break; |
| 10739 | } |
| 10740 | DRW_DwgGroupMembership receipt; |
| 10741 | receipt.m_version = version; |
| 10742 | receipt.m_groupHandle = obj.handle; |
| 10743 | receipt.m_sourceOffset = obj.loc; |
| 10744 | receipt.m_sourceMapOrdinal = obj.sourceOrdinal; |
| 10745 | receipt.m_sourceOffsetSpace = obj.sourceOffsetSpace; |
| 10746 | receipt.m_hasSourceLocation = true; |
| 10747 | receipt.m_complete = true; |
| 10748 | receipt.m_entries.reserve(e.m_entityHandles.size()); |
| 10749 | for (std::size_t index = 0; index < e.m_entityHandles.size(); |
| 10750 | ++index) { |
| 10751 | receipt.m_entries.push_back( |
| 10752 | {e.m_entityHandles[index], static_cast<std::uint32_t>(index)}); |
| 10753 | } |
| 10754 | groupMembership = std::move(receipt); |
| 10755 | normalizeDwgFramePublication(publication); |
| 10756 | if (m_dwgFrameCoverageStatus != |
| 10757 | DRW_DwgFrameCoverageStatus::NotAvailable && |
| 10758 | !validateDwgFramePublicationStaticArtifacts( |
| 10759 | publication, |
| 10760 | {nullptr, nullptr, nullptr, &*groupMembership})) { |
| 10761 | ret = failReceiptPreflight(); |
| 10762 | break; |
| 10763 | } |
| 10764 | intfa.addGroup(e); |
| 10765 | intfa.addUnsupportedObject(makeRawObject(oType)); |
| 10766 | } |
| 10767 | break; |
| 10768 | } |
| 10769 | case dwgObjType::MLINESTYLE: { |
| 10770 | DRW_MLineStyle e; |
| 10771 | ret = parseTableEntry(e); |
| 10772 | if (ret) { |
| 10773 | mlineStyleNameMap[obj.handle] = e.name; |
| 10774 | intfa.addMLineStyle(e); |
| 10775 | intfa.addUnsupportedObject(makeRawObject(oType)); |
| 10776 | } |
| 10777 | break; |
| 10778 | } |
| 10779 | case dwgObjType::XRECORD: { |
| 10780 | DRW_XRecord e; |
| 10781 | ret = parseTableEntry(e); |
| 10782 | if (ret) { |
| 10783 | intfa.addXRecord(e); |
| 10784 | intfa.addUnsupportedObject(makeRawObject(oType)); |
| 10785 | } |
| 10786 | break; |
| 10787 | } |
| 10788 | case dwgObjType::ACDBPLACEHOLDER: { |
| 10789 | DRW_AcDbPlaceholder e; |
| 10790 | ret = parseTableEntry(e); |
| 10791 | if (ret) { |
| 10792 | intfa.addAcDbPlaceholder(e); |
| 10793 | intfa.addUnsupportedObject(makeRawObject(oType)); |
| 10794 | } |
| 10795 | break; |
| 10796 | } |
| 10797 | case dwgObjType::VBA_PROJECT: { |
| 10798 | DRW_VbaProject e; |
| 10799 | ret = parseTableEntry(e); |
| 10800 | if (ret) { |
| 10801 | DRW_UnsupportedObject raw = makeRawObject(oType); |
| 10802 | raw.m_recordName = "VBA_PROJECT"; |
| 10803 | raw.m_className = "AcDbVbaProject"; |
| 10804 | intfa.addVbaProject(e); |
| 10805 | intfa.addUnsupportedObject(raw); |
| 10806 | } |
| 10807 | break; |
| 10808 | } |
| 10809 | case dwgObjType::PROXY_OBJECT: { |
| 10810 | DRW_ProxyObject e; |
| 10811 | // A failed proxy-object body is not a lossless raw carrier: its |
| 10812 | // metadata parser has already established that the declared |
| 10813 | // object layout is truncated or invalid. |
| 10814 | ret = parseTableEntry(e); |
| 10815 | if (ret) { |
| 10816 | DRW_UnsupportedObject raw = makeRawObject(oType); |
| 10817 | raw.m_recordName = "ACAD_PROXY_OBJECT"; |
| 10818 | raw.m_className = "AcDbProxyObject"; |
| 10819 | intfa.addProxyObject(e); |
| 10820 | intfa.addUnsupportedObject(raw); |
| 10821 | } |
| 10822 | break; |
| 10823 | } |
| 10824 | case dwgObjType::LAYOUT: { |
| 10825 | DRW_Layout e; |
| 10826 | ret = parseTableEntry(e); |
| 10827 | if (ret) { |
| 10828 | intfa.addLayout(e); |
| 10829 | intfa.addUnsupportedObject(makeRawObject(oType)); |
| 10830 | } |
| 10831 | break; |
| 10832 | } |
| 10833 | case dwgObjType::IMAGEDEF: { |
| 10834 | DRW_ImageDef e; |
| 10835 | ret = parseTableEntry(e); |
| 10836 | if (ret) { |
| 10837 | intfa.linkImage(&e); |
| 10838 | intfa.addUnsupportedObject(makeRawObject(oType)); |
| 10839 | } |
| 10840 | break; |
| 10841 | } |
| 10842 | case dwgObjType::DBCOLOR: { |
| 10843 | DRW_DbColor e; |
| 10844 | ret = parseTableEntry(e); |
| 10845 | if (ret) { |
| 10846 | std::string formatted = |
| 10847 | e.bookName.empty() ? e.name : (e.bookName + "$" + e.name); |
| 10848 | dbColorMap[obj.handle] = {e.rgb, formatted}; |
| 10849 | intfa.addDbColor(e); |
| 10850 | intfa.addUnsupportedObject(makeRawObject(oType)); |
| 10851 | } |
| 10852 | break; |
| 10853 | } |
| 10854 | case dwgObjType::VPORT_ENTITY_HEADER: { |
| 10855 | DRW_ViewportEntityHeader e; |
| 10856 | ret = parseTableEntry(e); |
| 10857 | if (ret) { |
| 10858 | intfa.addViewportEntityHeader(e); |
| 10859 | intfa.addUnsupportedObject(makeRawObject(oType)); |
| 10860 | } |
| 10861 | break; |
| 10862 | } |
| 10863 | case dwgObjType::BLOCKREPRESENTATION: { |
| 10864 | DRW_BlockRepresentationData e; |
| 10865 | ret = parseTableEntry(e); |
| 10866 | if (ret) { |
| 10867 | intfa.addBlockRepresentationData(e); |
| 10868 | intfa.addUnsupportedObject(makeRawObject(oType)); |
| 10869 | } |
| 10870 | break; |
| 10871 | } |
| 10872 | case dwgObjType::UNKNOWN_9: |
| 10873 | case dwgObjType::UNKNOWN_36: |
| 10874 | case dwgObjType::UNKNOWN_37: |
| 10875 | case dwgObjType::UNKNOWN_3A: |
| 10876 | case dwgObjType::UNKNOWN_3B: |
| 10877 | case dwgObjType::DUMMY: |
| 10878 | case dwgObjType::LONG_TRANSACTION: { |
| 10879 | // These fixed OBJECTS types are intentionally opaque in the |
| 10880 | // cross-reader oracles. The validated frame is the contract: |
| 10881 | // retain its exact bytes without guessing a version-specific |
| 10882 | // body layout or leaving it in the deferred/skipped maps. |
| 10883 | const char *recordName = nullptr; |
| 10884 | switch (oType) { |
| 10885 | case dwgObjType::UNKNOWN_9: |
| 10886 | recordName = "UNKNOWN_9"; |
| 10887 | break; |
| 10888 | case dwgObjType::UNKNOWN_36: |
| 10889 | recordName = "UNKNOWN_36"; |
| 10890 | break; |
| 10891 | case dwgObjType::UNKNOWN_37: |
| 10892 | recordName = "UNKNOWN_37"; |
| 10893 | break; |
| 10894 | case dwgObjType::UNKNOWN_3A: |
| 10895 | recordName = "UNKNOWN_3A"; |
| 10896 | break; |
| 10897 | case dwgObjType::UNKNOWN_3B: |
| 10898 | recordName = "UNKNOWN_3B"; |
| 10899 | break; |
| 10900 | case dwgObjType::DUMMY: |
| 10901 | recordName = "DUMMY"; |
| 10902 | break; |
| 10903 | case dwgObjType::LONG_TRANSACTION: |
| 10904 | recordName = "LONG_TRANSACTION"; |
| 10905 | break; |
| 10906 | default: |
| 10907 | break; |
| 10908 | } |
| 10909 | DRW_UnsupportedObject raw = makeRawObject(oType, nullptr, false); |
| 10910 | raw.m_recordName = recordName; |
| 10911 | ret = buff.isGood(); |
| 10912 | if (ret) |
| 10913 | intfa.addUnsupportedObject(raw); |
| 10914 | break; |
| 10915 | } |
| 10916 | default: |
| 10917 | // Custom-class objects (oType >= 500) — look up by classesmap |
| 10918 | // recName. MLEADERSTYLE lives here (ODA spec §20.4.87) and |
| 10919 | // mirrors the WIPEOUT-from-entity dispatch pattern added in |
| 10920 | // commit a05908400 / 8e6730e5b. |
| 10921 | if (oType >= 500) { |
| 10922 | auto cit = classesmap.find(oType); |
| 10923 | if (cit != classesmap.end() && cit->second) { |
| 10924 | const std::string &rn = cit->second->recName; |
| 10925 | if (rn == "DBCOLOR" || rn == "ACDBCOLOR" || |
| 10926 | cit->second->className == "AcDbColor") { |
| 10927 | DRW_DbColor e; |
| 10928 | ret = parseTableEntry(e); |
| 10929 | if (ret) { |
| 10930 | std::string formatted = |
| 10931 | e.bookName.empty() ? e.name : (e.bookName + "$" + e.name); |
| 10932 | dbColorMap[obj.handle] = {e.rgb, formatted}; |
| 10933 | intfa.addDbColor(e); |
| 10934 | intfa.addUnsupportedObject( |
| 10935 | makeRawObject(oType, cit->second, false)); |
| 10936 | } |
| 10937 | break; |
| 10938 | } |
| 10939 | if (rn == "VXCONTROL" || rn == "VX_CONTROL" || |
| 10940 | cit->second->className == "AcDbVxControl") { |
| 10941 | DRW_VxControl e; |
| 10942 | ret = parseTableEntry(e); |
| 10943 | if (ret) { |
| 10944 | intfa.addVxControl(e); |
| 10945 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 10946 | } |
| 10947 | break; |
| 10948 | } |
| 10949 | if (rn == "VXTABLERECORD" || rn == "VX_TABLE_RECORD" || |
| 10950 | cit->second->className == "AcDbVxTableRecord") { |
| 10951 | DRW_VxTableRecord e; |
| 10952 | ret = parseTableEntry(e); |
| 10953 | if (ret) { |
| 10954 | intfa.addVxTableRecord(e); |
| 10955 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 10956 | } |
| 10957 | break; |
| 10958 | } |
| 10959 | if (rn == "TVDEVICEPROPERTIES" || |
| 10960 | cit->second->className == "AcDbTvDeviceProperties") { |
| 10961 | DRW_TvDeviceProperties e; |
| 10962 | ret = parseTableEntry(e); |
| 10963 | if (ret) { |
| 10964 | intfa.addTvDeviceProperties(e); |
| 10965 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 10966 | } |
| 10967 | break; |
| 10968 | } |
| 10969 | if (rn == "CSACDOCUMENTOPTIONS") { |
| 10970 | DRW_CsacDocumentOptions e; |
| 10971 | ret = parseTableEntry(e); |
| 10972 | if (ret) { |
| 10973 | intfa.addCsacDocumentOptions(e); |
| 10974 | intfa.addUnsupportedObject( |
| 10975 | makeRawObject(oType, cit->second, false)); |
| 10976 | } |
| 10977 | break; |
| 10978 | } |
| 10979 | if (rn == "CONTEXTDATAMANAGER" || |
| 10980 | cit->second->className == "AcDbContextDataManager") { |
| 10981 | DRW_ContextDataManager e; |
| 10982 | ret = parseTableEntry(e); |
| 10983 | if (ret) { |
| 10984 | intfa.addContextDataManager(e); |
| 10985 | intfa.addUnsupportedObject( |
| 10986 | makeRawObject(oType, cit->second, false)); |
| 10987 | } |
| 10988 | break; |
| 10989 | } |
| 10990 | if (rn == "SUNSTUDY" || rn == "ACDBSUNSTUDY" || |
| 10991 | cit->second->className == "AcDbSunStudy") { |
| 10992 | DRW_SunStudy e; |
| 10993 | ret = parseTableEntry(e); |
| 10994 | if (ret) { |
| 10995 | intfa.addSunStudy(e); |
| 10996 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 10997 | } |
| 10998 | break; |
| 10999 | } |
| 11000 | if (rn == "MOTIONPATH" || rn == "ACDBMOTIONPATH" || |
| 11001 | cit->second->className == "AcDbMotionPath") { |
| 11002 | DRW_MotionPath e; |
| 11003 | ret = parseTableEntry(e); |
| 11004 | if (ret) { |
| 11005 | intfa.addMotionPath(e); |
| 11006 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11007 | } |
| 11008 | break; |
| 11009 | } |
| 11010 | if (rn == "CURVEPATH" || rn == "ACDBCURVEPATH" || |
| 11011 | cit->second->className == "AcDbCurvePath") { |
| 11012 | DRW_CurvePath e; |
| 11013 | ret = parseTableEntry(e); |
| 11014 | if (ret) { |
| 11015 | intfa.addCurvePath(e); |
| 11016 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11017 | } |
| 11018 | break; |
| 11019 | } |
| 11020 | if (rn == "POINTPATH" || rn == "ACDBPOINTPATH" || |
| 11021 | cit->second->className == "AcDbPointPath") { |
| 11022 | DRW_PointPath e; |
| 11023 | ret = parseTableEntry(e); |
| 11024 | if (ret) { |
| 11025 | intfa.addPointPath(e); |
| 11026 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11027 | } |
| 11028 | break; |
| 11029 | } |
| 11030 | if (rn == "OBJECT_PTR" || rn == "OBJECTPTR" || |
| 11031 | rn == "ACDBOBJECTPTR" || |
| 11032 | cit->second->className == "AcDbObjectPtr") { |
| 11033 | DRW_ObjectPtr e; |
| 11034 | ret = parseTableEntry(e); |
| 11035 | if (ret) { |
| 11036 | intfa.addObjectPtr(e); |
| 11037 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11038 | } |
| 11039 | break; |
| 11040 | } |
| 11041 | if (rn == "PARTIAL_VIEWING_INDEX" || rn == "PARTIALVIEWINGINDEX" || |
| 11042 | rn == "ACDBPARTIALVIEWINGINDEX" || |
| 11043 | cit->second->className == "AcDbPartialViewingIndex") { |
| 11044 | DRW_PartialViewingIndex e; |
| 11045 | ret = parseTableEntry(e); |
| 11046 | if (ret) { |
| 11047 | intfa.addPartialViewingIndex(e); |
| 11048 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11049 | } |
| 11050 | break; |
| 11051 | } |
| 11052 | if (rn == "RENDERSETTINGS" || rn == "ACDBRENDERSETTINGS" || |
| 11053 | cit->second->className == "AcDbRenderSettings" || |
| 11054 | rn == "RAPIDRTRENDERSETTINGS" || |
| 11055 | rn == "ACDBRAPIDRTRENDERSETTINGS" || |
| 11056 | cit->second->className == "AcDbRapidRTRenderSettings" || |
| 11057 | rn == "MENTALRAYRENDERSETTINGS" || |
| 11058 | rn == "ACDBMENTALRAYRENDERSETTINGS" || |
| 11059 | cit->second->className == "AcDbMentalRayRenderSettings" || |
| 11060 | rn == "RENDERENTRY" || |
| 11061 | cit->second->className == "AcDbRenderEntry" || |
| 11062 | rn == "RENDERENVIRONMENT" || |
| 11063 | cit->second->className == "AcDbRenderEnvironment" || |
| 11064 | rn == "RENDERGLOBAL" || |
| 11065 | cit->second->className == "AcDbRenderGlobal") { |
| 11066 | DRW_RenderSettings settings; |
| 11067 | if (rn == "RENDERSETTINGS" || rn == "ACDBRENDERSETTINGS" || |
| 11068 | cit->second->className == "AcDbRenderSettings") |
| 11069 | settings.m_kind = DRW_RenderSettings::Settings; |
| 11070 | else if (rn == "RAPIDRTRENDERSETTINGS" || |
| 11071 | rn == "ACDBRAPIDRTRENDERSETTINGS" || |
| 11072 | cit->second->className == "AcDbRapidRTRenderSettings") |
| 11073 | settings.m_kind = DRW_RenderSettings::RapidRT; |
| 11074 | else if (rn == "MENTALRAYRENDERSETTINGS" || |
| 11075 | rn == "ACDBMENTALRAYRENDERSETTINGS" || |
| 11076 | cit->second->className == "AcDbMentalRayRenderSettings") |
| 11077 | settings.m_kind = DRW_RenderSettings::MentalRay; |
| 11078 | else if (rn == "RENDERENTRY" || |
| 11079 | cit->second->className == "AcDbRenderEntry") |
| 11080 | settings.m_kind = DRW_RenderSettings::Entry; |
| 11081 | else if (rn == "RENDERENVIRONMENT" || |
| 11082 | cit->second->className == "AcDbRenderEnvironment") |
| 11083 | settings.m_kind = DRW_RenderSettings::Environment; |
| 11084 | else |
| 11085 | settings.m_kind = DRW_RenderSettings::Global; |
| 11086 | ret = parseTableEntry(settings); |
| 11087 | if (ret) { |
| 11088 | intfa.addRenderSettings(settings); |
| 11089 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11090 | } |
| 11091 | break; |
| 11092 | } |
| 11093 | if (rn == "SOLIDBACKGROUND" || rn == "SOLID_BACKGROUND" || |
| 11094 | cit->second->className == "AcDbSolidBackground" || |
| 11095 | rn == "GRADIENTBACKGROUND" || rn == "GRADIENT_BACKGROUND" || |
| 11096 | cit->second->className == "AcDbGradientBackground" || |
| 11097 | rn == "GROUNDPLANEBACKGROUND" || |
| 11098 | rn == "GROUND_PLANE_BACKGROUND" || |
| 11099 | cit->second->className == "AcDbGroundPlaneBackground" || |
| 11100 | rn == "IMAGEBACKGROUND" || rn == "IMAGE_BACKGROUND" || |
| 11101 | cit->second->className == "AcDbImageBackground" || |
| 11102 | rn == "IBLBACKGROUND" || rn == "IBL_BACKGROUND" || |
| 11103 | rn == "RAPIDRTRENDERENVIRONMENT" || |
| 11104 | cit->second->className == "AcDbIBLBackground" || |
| 11105 | rn == "SKYLIGHTBACKGROUND" || rn == "SKYLIGHT_BACKGROUND" || |
| 11106 | cit->second->className == "AcDbSkyBackground") { |
| 11107 | DRW_Background e; |
| 11108 | if (rn == "GRADIENTBACKGROUND" || rn == "GRADIENT_BACKGROUND" || |
| 11109 | cit->second->className == "AcDbGradientBackground") |
| 11110 | e.m_kind = DRW_Background::Gradient; |
| 11111 | else if (rn == "GROUNDPLANEBACKGROUND" || |
| 11112 | rn == "GROUND_PLANE_BACKGROUND" || |
| 11113 | cit->second->className == "AcDbGroundPlaneBackground") |
| 11114 | e.m_kind = DRW_Background::GroundPlane; |
| 11115 | else if (rn == "IMAGEBACKGROUND" || rn == "IMAGE_BACKGROUND" || |
| 11116 | cit->second->className == "AcDbImageBackground") |
| 11117 | e.m_kind = DRW_Background::Image; |
| 11118 | else if (rn == "IBLBACKGROUND" || rn == "IBL_BACKGROUND" || |
| 11119 | rn == "RAPIDRTRENDERENVIRONMENT" || |
| 11120 | cit->second->className == "AcDbIBLBackground") |
| 11121 | e.m_kind = DRW_Background::Ibl; |
| 11122 | else if (rn == "SKYLIGHTBACKGROUND" || |
| 11123 | rn == "SKYLIGHT_BACKGROUND" || |
| 11124 | cit->second->className == "AcDbSkyBackground") |
| 11125 | e.m_kind = DRW_Background::Skylight; |
| 11126 | ret = parseTableEntry(e); |
| 11127 | if (ret) { |
| 11128 | intfa.addBackground(e); |
| 11129 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11130 | } |
| 11131 | break; |
| 11132 | } |
| 11133 | if (rn == "SECTION_MANAGER" || rn == "SECTIONMANAGER" || |
| 11134 | cit->second->className == "AcDbSectionManager") { |
| 11135 | DRW_Section e; |
| 11136 | e.m_kind = DRW_Section::Manager; |
| 11137 | ret = parseTableEntry(e); |
| 11138 | if (ret) { |
| 11139 | intfa.addSection(e); |
| 11140 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11141 | } |
| 11142 | break; |
| 11143 | } |
| 11144 | if (rn == "SECTION_SETTINGS" || rn == "SECTIONSETTINGS" || |
| 11145 | cit->second->className == "AcDbSectionSettings") { |
| 11146 | DRW_Section e; |
| 11147 | e.m_kind = DRW_Section::Settings; |
| 11148 | ret = parseTableEntry(e); |
| 11149 | if (ret) { |
| 11150 | intfa.addSection(e); |
| 11151 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11152 | } |
| 11153 | break; |
| 11154 | } |
| 11155 | if (rn == "DICTIONARYVAR" || |
| 11156 | cit->second->className == "AcDbDictionaryVar") { |
| 11157 | DRW_DictionaryVar e; |
| 11158 | ret = parseTableEntry(e); |
| 11159 | if (ret) { |
| 11160 | intfa.addDictionaryVar(e); |
| 11161 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11162 | } |
| 11163 | break; |
| 11164 | } |
| 11165 | if (rn == "ACDBDICTIONARYWDFLT" || rn == "DICTIONARYWDFLT" || |
| 11166 | cit->second->className == "AcDbDictionaryWithDefault") { |
| 11167 | DRW_DictionaryWithDefault e; |
| 11168 | ret = parseTableEntry(e); |
| 11169 | if (ret) { |
| 11170 | if (!e.hasCompleteDwgEntries() || |
| 11171 | !e.isDwgPayloadValid(version)) { |
| 11172 | ret = failReceiptPreflight(); |
| 11173 | break; |
| 11174 | } |
| 11175 | DRW_DwgTypedReference receipt; |
| 11176 | receipt.m_version = version; |
| 11177 | receipt.m_sourceHandle = obj.handle; |
| 11178 | receipt.m_sourceOffset = obj.loc; |
| 11179 | receipt.m_sourceMapOrdinal = obj.sourceOrdinal; |
| 11180 | receipt.m_sourceOffsetSpace = obj.sourceOffsetSpace; |
| 11181 | receipt.m_hasSourceLocation = true; |
| 11182 | receipt.m_complete = true; |
| 11183 | receipt.m_encodedType = encodedType; |
| 11184 | receipt.m_resolvedType = oType; |
| 11185 | const auto classOrdinal = m_dwgClassNumberOrdinals.find( |
| 11186 | static_cast<std::uint32_t>(encodedType)); |
| 11187 | if (classOrdinal != m_dwgClassNumberOrdinals.end()) { |
| 11188 | receipt.m_classStreamOrdinal = classOrdinal->second; |
| 11189 | } |
| 11190 | receipt.m_field = DRW_DwgTypedReferenceField::DictionaryDefault; |
| 11191 | receipt.m_referenceCode = DRW::DwgHardPointer; |
| 11192 | receipt.m_targetHandle = e.m_defaultEntryHandle; |
| 11193 | typedReference = std::move(receipt); |
| 11194 | normalizeDwgFramePublication(publication); |
| 11195 | DRW_DwgDictionaryWithDefaultMembership membership; |
| 11196 | membership.m_version = publication.m_version; |
| 11197 | membership.m_dictionaryHandle = publication.m_handle; |
| 11198 | membership.m_sourceOffset = publication.m_sourceOffset; |
| 11199 | membership.m_sourceMapOrdinal = publication.m_sourceMapOrdinal; |
| 11200 | membership.m_sourceOffsetSpace = |
| 11201 | publication.m_sourceOffsetSpace; |
| 11202 | membership.m_hasSourceLocation = |
| 11203 | publication.m_hasSourceLocation; |
| 11204 | membership.m_complete = true; |
| 11205 | membership.m_encodedType = publication.m_encodedType; |
| 11206 | membership.m_resolvedType = publication.m_resolvedType; |
| 11207 | membership.m_recordName = publication.m_recordName; |
| 11208 | membership.m_className = publication.m_className; |
| 11209 | membership.m_classStreamOrdinal = |
| 11210 | publication.m_classStreamOrdinal; |
| 11211 | membership.m_cloning = e.cloning; |
| 11212 | membership.m_hardOwner = e.hardOwner; |
| 11213 | membership.m_defaultEntryHandle = e.m_defaultEntryHandle; |
| 11214 | membership.m_entries.reserve(e.m_entries.size()); |
| 11215 | for (const DRW_Dictionary::Entry &entry : e.m_entries) { |
| 11216 | membership.m_entries.push_back( |
| 11217 | {entry.m_name, entry.m_handle}); |
| 11218 | } |
| 11219 | dictionaryWithDefaultMembership = std::move(membership); |
| 11220 | if (m_dwgFrameCoverageStatus != |
| 11221 | DRW_DwgFrameCoverageStatus::NotAvailable && |
| 11222 | !validateDwgFramePublicationStaticArtifacts( |
| 11223 | publication, |
| 11224 | {nullptr, &*typedReference, nullptr, nullptr, nullptr, |
| 11225 | nullptr, &*dictionaryWithDefaultMembership})) { |
| 11226 | ret = failReceiptPreflight(); |
| 11227 | break; |
| 11228 | } |
| 11229 | intfa.addDictionaryWithDefault(e); |
| 11230 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11231 | } |
| 11232 | break; |
| 11233 | } |
| 11234 | if (rn == "XRECORD" || cit->second->className == "AcDbXrecord") { |
| 11235 | DRW_XRecord e; |
| 11236 | ret = parseTableEntry(e); |
| 11237 | if (ret) { |
| 11238 | intfa.addXRecord(e); |
| 11239 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11240 | } |
| 11241 | break; |
| 11242 | } |
| 11243 | if (rn == "FIELD" || cit->second->className == "AcDbField") { |
| 11244 | DRW_Field e; |
| 11245 | ret = parseTableEntry(e); |
| 11246 | if (ret) { |
| 11247 | if (e.hasCompleteDwgPayload()) { |
| 11248 | normalizeDwgFramePublication(publication); |
| 11249 | DRW_DwgFieldPayloadReceipt receipt; |
| 11250 | receipt.m_version = version; |
| 11251 | receipt.m_fieldHandle = obj.handle; |
| 11252 | receipt.m_sourceOffset = obj.loc; |
| 11253 | receipt.m_sourceMapOrdinal = obj.sourceOrdinal; |
| 11254 | receipt.m_sourceOffsetSpace = obj.sourceOffsetSpace; |
| 11255 | receipt.m_hasSourceLocation = true; |
| 11256 | receipt.m_complete = true; |
| 11257 | receipt.m_encodedType = encodedType; |
| 11258 | receipt.m_resolvedType = oType; |
| 11259 | receipt.m_recordName = publication.m_recordName; |
| 11260 | receipt.m_className = publication.m_className; |
| 11261 | receipt.m_classStreamOrdinal = |
| 11262 | publication.m_classStreamOrdinal; |
| 11263 | receipt.m_field = e; |
| 11264 | fieldPayloadReceipt = std::move(receipt); |
| 11265 | if (m_dwgFrameCoverageStatus != |
| 11266 | DRW_DwgFrameCoverageStatus::NotAvailable && |
| 11267 | !validateDwgFramePublicationStaticArtifacts( |
| 11268 | publication, |
| 11269 | {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, |
| 11270 | nullptr, &*fieldPayloadReceipt})) { |
| 11271 | ret = failReceiptPreflight(); |
| 11272 | break; |
| 11273 | } |
| 11274 | fieldOutput = std::move(e); |
| 11275 | fieldRawOutput = makeRawObject(oType, cit->second); |
| 11276 | } else { |
| 11277 | fieldRawOutput = makeRawObject(oType, cit->second, false); |
| 11278 | } |
| 11279 | } |
| 11280 | break; |
| 11281 | } |
| 11282 | if (rn == "FIELDLIST" || |
| 11283 | cit->second->className == "AcDbFieldList") { |
| 11284 | DRW_FieldList e; |
| 11285 | ret = parseTableEntry(e); |
| 11286 | if (ret) { |
| 11287 | if (!e.hasCompleteDwgEntries()) { |
| 11288 | ret = failReceiptPreflight(); |
| 11289 | break; |
| 11290 | } |
| 11291 | normalizeDwgFramePublication(publication); |
| 11292 | DRW_DwgFieldListMembership receipt; |
| 11293 | receipt.m_version = version; |
| 11294 | receipt.m_listHandle = obj.handle; |
| 11295 | receipt.m_sourceOffset = obj.loc; |
| 11296 | receipt.m_sourceMapOrdinal = obj.sourceOrdinal; |
| 11297 | receipt.m_sourceOffsetSpace = obj.sourceOffsetSpace; |
| 11298 | receipt.m_hasSourceLocation = true; |
| 11299 | receipt.m_complete = true; |
| 11300 | receipt.m_encodedType = encodedType; |
| 11301 | receipt.m_resolvedType = oType; |
| 11302 | receipt.m_recordName = publication.m_recordName; |
| 11303 | receipt.m_className = publication.m_className; |
| 11304 | receipt.m_classStreamOrdinal = publication.m_classStreamOrdinal; |
| 11305 | receipt.m_entries.reserve(e.m_fieldHandles.size()); |
| 11306 | for (std::size_t index = 0; index < e.m_fieldHandles.size(); |
| 11307 | ++index) { |
| 11308 | receipt.m_entries.push_back( |
| 11309 | {e.m_fieldHandles[index], |
| 11310 | static_cast<std::uint32_t>(index)}); |
| 11311 | } |
| 11312 | fieldListMembership = std::move(receipt); |
| 11313 | if (m_dwgFrameCoverageStatus != |
| 11314 | DRW_DwgFrameCoverageStatus::NotAvailable && |
| 11315 | !validateDwgFramePublicationStaticArtifacts( |
| 11316 | publication, {nullptr, nullptr, nullptr, nullptr, |
| 11317 | nullptr, &*fieldListMembership})) { |
| 11318 | ret = failReceiptPreflight(); |
| 11319 | break; |
| 11320 | } |
| 11321 | fieldListOutput = std::move(e); |
| 11322 | fieldRawOutput = makeRawObject(oType, cit->second); |
| 11323 | } |
| 11324 | break; |
| 11325 | } |
| 11326 | if (rn == "DATATABLE" || |
| 11327 | cit->second->className == "AcDbDataTable") { |
| 11328 | DRW_DataTable e; |
| 11329 | ret = parseTableEntry(e); |
| 11330 | // DATATABLE's body is a DEBUG_CLASS ("(varies)") whose |
| 11331 | // cell walk may drift; parseDwg graceful-degrades to the |
| 11332 | // decoded prefix, and the raw shelf preserves the exact |
| 11333 | // bytes so the round-trip stays faithful regardless. |
| 11334 | if (ret) { |
| 11335 | intfa.addDataTable(e); |
| 11336 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11337 | } |
| 11338 | break; |
| 11339 | } |
| 11340 | if (rn == "RASTERVARIABLES" || |
| 11341 | cit->second->className == "AcDbRasterVariables") { |
| 11342 | DRW_RasterVariables e; |
| 11343 | ret = parseTableEntry(e); |
| 11344 | if (ret) { |
| 11345 | intfa.addRasterVariables(e); |
| 11346 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11347 | } |
| 11348 | break; |
| 11349 | } |
| 11350 | if (rn == "WIPEOUTVARIABLES" || |
| 11351 | cit->second->className == "AcDbWipeoutVariables") { |
| 11352 | DRW_WipeoutVariables e; |
| 11353 | ret = parseTableEntry(e); |
| 11354 | if (ret) { |
| 11355 | intfa.addWipeoutVariables(e); |
| 11356 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11357 | } |
| 11358 | break; |
| 11359 | } |
| 11360 | if (rn == "SORTENTSTABLE" || |
| 11361 | cit->second->className == "AcDbSortentsTable") { |
| 11362 | DRW_SortEntsTable e; |
| 11363 | ret = parseTableEntry(e); |
| 11364 | if (ret) { |
| 11365 | if (!e.hasCompleteDwgEntries()) { |
| 11366 | ret = failReceiptPreflight(); |
| 11367 | break; |
| 11368 | } |
| 11369 | normalizeDwgFramePublication(publication); |
| 11370 | DRW_DwgSortEntsMembership receipt; |
| 11371 | receipt.m_version = version; |
| 11372 | receipt.m_tableHandle = obj.handle; |
| 11373 | receipt.m_sourceOffset = obj.loc; |
| 11374 | receipt.m_sourceMapOrdinal = obj.sourceOrdinal; |
| 11375 | receipt.m_sourceOffsetSpace = obj.sourceOffsetSpace; |
| 11376 | receipt.m_hasSourceLocation = true; |
| 11377 | receipt.m_complete = true; |
| 11378 | receipt.m_encodedType = encodedType; |
| 11379 | receipt.m_resolvedType = oType; |
| 11380 | receipt.m_recordName = publication.m_recordName; |
| 11381 | receipt.m_className = publication.m_className; |
| 11382 | receipt.m_classStreamOrdinal = publication.m_classStreamOrdinal; |
| 11383 | receipt.m_blockOwnerHandle = e.m_blockOwnerHandle; |
| 11384 | receipt.m_entries.reserve(e.m_entityHandles.size()); |
| 11385 | for (std::size_t index = 0; index < e.m_entityHandles.size(); |
| 11386 | ++index) { |
| 11387 | const std::uint32_t sort = e.m_sortHandles[index]; |
| 11388 | receipt.m_entries.push_back( |
| 11389 | {e.m_entityHandles[index], sort, |
| 11390 | static_cast<std::uint32_t>(index), |
| 11391 | sort == DRW::NoHandle}); |
| 11392 | } |
| 11393 | sortEntsMembership = std::move(receipt); |
| 11394 | if (m_dwgFrameCoverageStatus != |
| 11395 | DRW_DwgFrameCoverageStatus::NotAvailable && |
| 11396 | !validateDwgFramePublicationStaticArtifacts( |
| 11397 | publication, {nullptr, nullptr, nullptr, nullptr, |
| 11398 | &*sortEntsMembership})) { |
| 11399 | ret = failReceiptPreflight(); |
| 11400 | break; |
| 11401 | } |
| 11402 | intfa.addSortEntsTable(e); |
| 11403 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11404 | } |
| 11405 | break; |
| 11406 | } |
| 11407 | if (rn == "MATERIAL" || cit->second->className == "AcDbMaterial") { |
| 11408 | DRW_Material e; |
| 11409 | ret = parseTableEntry(e); |
| 11410 | // MATERIAL's parser is truncated (name + description |
| 11411 | // only); raw replay captures the full byte image so |
| 11412 | // the round-trip stays faithful regardless. (Phase 2b.1) |
| 11413 | if (ret) { |
| 11414 | intfa.addMaterial(e); |
| 11415 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11416 | } |
| 11417 | break; |
| 11418 | } |
| 11419 | if (rn == "TABLESTYLE" || |
| 11420 | cit->second->className == "AcDbTableStyle") { |
| 11421 | DRW_TableStyle e; |
| 11422 | ret = parseTableEntry(e); |
| 11423 | // Raw replay preserves the full byte image; native |
| 11424 | // table writer (when active) claims the handle and |
| 11425 | // suppresses double-emit. (Phase 2b.2) |
| 11426 | if (ret) { |
| 11427 | intfa.addTableStyle(e); |
| 11428 | // AC1018 TABLESTYLE bodies are not fully typed by |
| 11429 | // this parser, but the validated frame remains |
| 11430 | // available through the raw representation. |
| 11431 | DRW_UnsupportedObject raw = makeRawObject(oType, cit->second); |
| 11432 | raw.m_typedPayloadValidated = true; |
| 11433 | intfa.addUnsupportedObject(raw); |
| 11434 | } |
| 11435 | break; |
| 11436 | } |
| 11437 | if (rn == "TABLECONTENT" || |
| 11438 | cit->second->className == "AcDbTableContent") { |
| 11439 | DRW_TableContentObject e; |
| 11440 | ret = parseTableEntry(e); |
| 11441 | if (ret) { |
| 11442 | intfa.addTableContent(e); |
| 11443 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11444 | } else if (version <= DRW::AC1018 && buff.isGood()) { |
| 11445 | // R2000/R2004 use a body layout that is retained |
| 11446 | // raw until a typed decoder is available. |
| 11447 | intfa.addUnsupportedObject( |
| 11448 | makeRawObject(oType, cit->second, false)); |
| 11449 | ret = true; |
| 11450 | } |
| 11451 | break; |
| 11452 | } |
| 11453 | if (rn == "CELLSTYLEMAP" || |
| 11454 | cit->second->className == "AcDbCellStyleMap") { |
| 11455 | DRW_CellStyleMap e; |
| 11456 | ret = parseTableEntry(e); |
| 11457 | if (ret) { |
| 11458 | intfa.addCellStyleMap(e); |
| 11459 | // Preserve the complete validated object frame |
| 11460 | // alongside the typed metadata. |
| 11461 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11462 | } |
| 11463 | break; |
| 11464 | } |
| 11465 | if (rn == "DIMASSOC" || cit->second->className == "AcDbDimAssoc") { |
| 11466 | DRW_DimensionAssociation e; |
| 11467 | ret = parseTableEntry(e); |
| 11468 | if (ret) { |
| 11469 | intfa.addDimensionAssociation(e); |
| 11470 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11471 | } else if (version <= DRW::AC1018 && buff.isGood()) { |
| 11472 | // DIMASSOC first appears in legacy files, while |
| 11473 | // this typed body starts at R2007. Keep the older |
| 11474 | // bounded frame raw until a legacy decoder exists. |
| 11475 | intfa.addUnsupportedObject( |
| 11476 | makeRawObject(oType, cit->second, false)); |
| 11477 | ret = true; |
| 11478 | } |
| 11479 | break; |
| 11480 | } |
| 11481 | if (rn == "ACAD_EVALUATION_GRAPH" || |
| 11482 | cit->second->className == "AcDbEvalGraph") { |
| 11483 | DRW_EvaluationGraph e; |
| 11484 | ret = parseTableEntry(e); |
| 11485 | // parseDwg now decodes the typed body at every version |
| 11486 | // (R2000/R2004 handles inline, R2007+ separate stream). |
| 11487 | // Raw replay preserves the full byte image alongside |
| 11488 | // the validated typed record. |
| 11489 | if (ret) { |
| 11490 | intfa.addEvaluationGraph(e); |
| 11491 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11492 | } |
| 11493 | break; |
| 11494 | } |
| 11495 | if (rn == "SUN" || cit->second->className == "AcDbSun") { |
| 11496 | DRW_Sun e; |
| 11497 | ret = parseTableEntry(e); |
| 11498 | if (ret) { |
| 11499 | intfa.addSun(e); |
| 11500 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11501 | } |
| 11502 | break; |
| 11503 | } |
| 11504 | if (rn.rfind("ACDBASSOC", 0) == 0 || |
| 11505 | rn == "ACDBPERSSUBENTMANAGER" || centerLineActionBody) { |
| 11506 | // Every ACDBASSOC* associativity class (plus the bare |
| 11507 | // PERSSUBENTMANAGER) routes to the shell parser: |
| 11508 | // ACTION/NETWORK/DEPENDENCY/GEOMDEPENDENCY/PERSSUBENT |
| 11509 | // and the action-param variants decode structured |
| 11510 | // fields; all other subclasses (surface/array action |
| 11511 | // bodies, generic action params, value/variable deps, |
| 11512 | // 2d-constraint groups) run the shared prefix |
| 11513 | // (suffix-inferred in parseDwg) and are preserved |
| 11514 | // byte-for-byte by the raw shelf below. |
| 11515 | DRW_AssociativeObject e(rn); |
| 11516 | ret = parseTableEntry(e); |
| 11517 | if (ret) { |
| 11518 | intfa.addAssociativeObject(e); |
| 11519 | // A validated object frame is still losslessly |
| 11520 | // readable when a class-specific suffix is outside |
| 11521 | // this typed decoder's coverage. |
| 11522 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11523 | } else if (version <= DRW::AC1018 && buff.isGood()) { |
| 11524 | // Legacy associativity subclasses may have opaque |
| 11525 | // suffixes; preserve only a structurally readable |
| 11526 | // frame, never a failed modern parse. |
| 11527 | intfa.addUnsupportedObject( |
| 11528 | makeRawObject(oType, cit->second, false)); |
| 11529 | ret = true; |
| 11530 | } |
| 11531 | break; |
| 11532 | } |
| 11533 | if (rn.rfind("ACSH_", 0) == 0) { |
| 11534 | // Every ACSH_* solid-history class routes to the shell |
| 11535 | // parser and is delivered through addAcShHistoryObject. |
| 11536 | // Structured field decode covers HISTORY, SWEEP/EXTRUSION, |
| 11537 | // BOX/WEDGE/SPHERE/CYLINDER/CONE, and (dwgTs parity) |
| 11538 | // BOOLEAN/CHAMFER/FILLET/TORUS/REVOLVE/LOFT. BREP and the |
| 11539 | // remaining classes run the shared prefix (or nothing) and |
| 11540 | // are preserved byte-for-byte by the raw shelf below. |
| 11541 | DRW_AcShHistoryObject e(rn); |
| 11542 | ret = parseTableEntry(e); |
| 11543 | if (ret) { |
| 11544 | intfa.addAcShHistoryObject(e); |
| 11545 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11546 | } |
| 11547 | break; |
| 11548 | } |
| 11549 | if (DRW_DynamicBlockObject::isDynamicBlockRecName(rn)) { |
| 11550 | // The dynamic-block object family (BLOCK*PARAMETER / |
| 11551 | // BLOCK*ACTION / BLOCK*GRIP / BLOCKGRIPLOCATIONCOMPONENT / |
| 11552 | // DYNAMICBLOCK* + singletons) — the largest custom-class |
| 11553 | // family. Every recName routes to the shell parser: the |
| 11554 | // shared AcDbEvalExpr (+ AcDbBlockElement/BlockParameter) |
| 11555 | // prefix decodes typed, BLOCKVISIBILITYPARAMETER / |
| 11556 | // the verified BLOCK*ACTION subclasses decode fully, |
| 11557 | // and the rest are preserved byte-for-byte by the raw |
| 11558 | // shelf below. parseDwg |
| 11559 | // graceful-degrades so a drift never drops the object. |
| 11560 | DRW_DynamicBlockObject e(rn); |
| 11561 | ret = parseTableEntry(e); |
| 11562 | if (ret) { |
| 11563 | intfa.addDynamicBlockObject(e); |
| 11564 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11565 | } |
| 11566 | break; |
| 11567 | } |
| 11568 | if (rn == "ACDBDETAILVIEWSTYLE" || rn == "DETAILVIEWSTYLE" || |
| 11569 | cit->second->className == "AcDbDetailViewStyle") { |
| 11570 | DRW_DetailViewStyle e; |
| 11571 | ret = parseTableEntry(e); |
| 11572 | // Raw replay preserves the full byte image (version |
| 11573 | // guard blocks cross-version replay). (Phase 2b.3) |
| 11574 | if (ret) { |
| 11575 | intfa.addDetailViewStyle(e); |
| 11576 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11577 | } |
| 11578 | break; |
| 11579 | } |
| 11580 | if (rn == "ACDBSECTIONVIEWSTYLE" || rn == "SECTIONVIEWSTYLE" || |
| 11581 | cit->second->className == "AcDbSectionViewStyle") { |
| 11582 | DRW_SectionViewStyle e; |
| 11583 | ret = parseTableEntry(e); |
| 11584 | if (ret) { |
| 11585 | intfa.addSectionViewStyle(e); |
| 11586 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11587 | } |
| 11588 | break; |
| 11589 | } |
| 11590 | if (rn == "BREAKDATA" || |
| 11591 | cit->second->className == "AcDbBreakData") { |
| 11592 | DRW_BreakData e; |
| 11593 | ret = parseTableEntry(e); |
| 11594 | if (ret) { |
| 11595 | intfa.addBreakData(e); |
| 11596 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11597 | } |
| 11598 | break; |
| 11599 | } |
| 11600 | if (rn == "BREAKPOINTREF" || |
| 11601 | cit->second->className == "AcDbBreakPointRef") { |
| 11602 | DRW_BreakPointRef e; |
| 11603 | ret = parseTableEntry(e); |
| 11604 | if (ret) { |
| 11605 | intfa.addBreakPointRef(e); |
| 11606 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11607 | } |
| 11608 | break; |
| 11609 | } |
| 11610 | if (rn == "GEODATA" || cit->second->className == "AcDbGeoData") { |
| 11611 | DRW_GeoData e; |
| 11612 | ret = parseTableEntry(e); |
| 11613 | if (ret) { |
| 11614 | intfa.addGeoData(e); |
| 11615 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11616 | } |
| 11617 | break; |
| 11618 | } |
| 11619 | if (rn == "IMAGEDEF_REACTOR" || |
| 11620 | cit->second->className == "AcDbRasterImageDefReactor") { |
| 11621 | DRW_ImageDefinitionReactor e; |
| 11622 | ret = parseTableEntry(e); |
| 11623 | // Preserving the reactor object keeps each raster |
| 11624 | // IMAGE entity's reactor handle non-dangling. (Phase 2b.4) |
| 11625 | if (ret) { |
| 11626 | intfa.addImageDefinitionReactor(e); |
| 11627 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11628 | } |
| 11629 | break; |
| 11630 | } |
| 11631 | if (rn == "SPATIAL_FILTER" || |
| 11632 | cit->second->className == "AcDbSpatialFilter") { |
| 11633 | DRW_SpatialFilter e; |
| 11634 | ret = parseTableEntry(e); |
| 11635 | if (ret) { |
| 11636 | intfa.addSpatialFilter(e); |
| 11637 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11638 | } |
| 11639 | break; |
| 11640 | } |
| 11641 | // INDEX (AcDbIndex) — ODA dwg.spec: TIMEBLL body followed |
| 11642 | // by the common object handle stream. |
| 11643 | if (rn == "INDEX" || cit->second->className == "AcDbIndex") { |
| 11644 | DRW_Index e; |
| 11645 | ret = parseTableEntry(e); |
| 11646 | if (ret) { |
| 11647 | intfa.addIndex(e); |
| 11648 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11649 | } |
| 11650 | break; |
| 11651 | } |
| 11652 | // IDBUFFER (AcDbIdBuffer) — ODA §20.4.79. List of object |
| 11653 | // handles, used by selection filters (LAYER_INDEX entries |
| 11654 | // point to one of these for the per-layer entity set). |
| 11655 | if (rn == "IDBUFFER" || cit->second->className == "AcDbIdBuffer") { |
| 11656 | DRW_IDBuffer e; |
| 11657 | ret = parseTableEntry(e); |
| 11658 | if (ret) { |
| 11659 | intfa.addIDBuffer(e); |
| 11660 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11661 | } |
| 11662 | break; |
| 11663 | } |
| 11664 | // LAYER_INDEX (AcDbLayerIndex) — ODA §20.4.83. Per-layer |
| 11665 | // entity index, used for partial-load drawings. |
| 11666 | if (rn == "LAYER_INDEX" || |
| 11667 | cit->second->className == "AcDbLayerIndex") { |
| 11668 | DRW_LayerIndex e; |
| 11669 | ret = parseTableEntry(e); |
| 11670 | if (ret) { |
| 11671 | intfa.addLayerIndex(e); |
| 11672 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11673 | } |
| 11674 | break; |
| 11675 | } |
| 11676 | // SPATIAL_INDEX (AcDbSpatialIndex) — ODA §20.4.95. |
| 11677 | // Spatial entity index; only timestamps are parsed |
| 11678 | // (body beyond is opaque per ODA spec). |
| 11679 | if (rn == "SPATIAL_INDEX" || |
| 11680 | cit->second->className == "AcDbSpatialIndex") { |
| 11681 | DRW_SpatialIndex e; |
| 11682 | ret = parseTableEntry(e); |
| 11683 | if (ret) { |
| 11684 | intfa.addSpatialIndex(e); |
| 11685 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11686 | } |
| 11687 | break; |
| 11688 | } |
| 11689 | if (rn == "TABLEGEOMETRY" || |
| 11690 | cit->second->className == "AcDbTableGeometry") { |
| 11691 | DRW_TableGeometry e; |
| 11692 | ret = parseTableEntry(e); |
| 11693 | // Raw replay preserves the full byte image. (Phase 2b.4) |
| 11694 | if (ret) { |
| 11695 | intfa.addTableGeometry(e); |
| 11696 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11697 | } |
| 11698 | break; |
| 11699 | } |
| 11700 | if (rn == "MLEADERSTYLE") { |
| 11701 | DRW_MLeaderStyle e; |
| 11702 | ret = parseTableEntry(e); |
| 11703 | if (ret) { |
| 11704 | intfa.addMLeaderStyle(&e); |
| 11705 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11706 | } |
| 11707 | break; |
| 11708 | } |
| 11709 | // recName is the DXF CLASSES section record name (code 1), |
| 11710 | // not the C++ className (code 2 = "AcDbColor"). Match the |
| 11711 | // DXF spelling "DBCOLOR" — same convention as MLEADERSTYLE |
| 11712 | // above. Populate dbColorMap so entity-side resolution in |
| 11713 | // entryParse (dwgreader.h) can patch color24 + colorName |
| 11714 | // onto entities referencing this DBCOLOR via the ENC flag |
| 11715 | // 0x40 handle. |
| 11716 | if (rn == "DBCOLOR" || cit->second->className == "AcDbColor") { |
| 11717 | DRW_DbColor e; |
| 11718 | ret = parseTableEntry(e); |
| 11719 | if (ret) { |
| 11720 | std::string formatted = |
| 11721 | e.bookName.empty() ? e.name : (e.bookName + "$" + e.name); |
| 11722 | dbColorMap[obj.handle] = {e.rgb, formatted}; |
| 11723 | intfa.addDbColor(e); |
| 11724 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11725 | } |
| 11726 | break; |
| 11727 | } |
| 11728 | // PLOTSETTINGS — plot configuration object (paper size, |
| 11729 | // margins, plotter name, etc.). DXF dispatches via |
| 11730 | // libdxfrw.cpp; the DWG path used to drop these into |
| 11731 | // remainingMap. libreDWG dwg.spec:5627 confirms |
| 11732 | // `DWG_OBJECT (PLOTSETTINGS)`; objects.in:321 marks the |
| 11733 | // dxfname as "PLOTSETTINGS". RS_FilterDXFRW already |
| 11734 | // implements addPlotSettings (margins → m_graphic). |
| 11735 | if (rn == "PLOTSETTINGS" || |
| 11736 | cit->second->className == "AcDbPlotSettings") { |
| 11737 | DRW_PlotSettings e; |
| 11738 | ret = parseTableEntry(e); |
| 11739 | if (ret) { |
| 11740 | intfa.addPlotSettings(&e); |
| 11741 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11742 | } |
| 11743 | break; |
| 11744 | } |
| 11745 | // OBJECTCONTEXTDATA (annotative per-object context) - |
| 11746 | // metadata-only shell. Text/MTEXT, dimension-family, leader, |
| 11747 | // block-reference and FCF contexts are typed for corpus |
| 11748 | // coverage, but raw DWG bytes are still emitted for lossless |
| 11749 | // replay. MLeader keeps its version-specific body raw. |
| 11750 | { |
| 11751 | DRW_ObjectContextData::Kind contextKind = |
| 11752 | DRW_ObjectContextData::Kind::Unknown; |
| 11753 | if (objectContextKindFromClassNames(rn, cit->second->className, |
| 11754 | contextKind)) { |
| 11755 | DRW_ObjectContextData e( |
| 11756 | rn.empty() ? cit->second->className : rn, contextKind); |
| 11757 | ret = parseTableEntry(e); |
| 11758 | if (ret) { |
| 11759 | intfa.addObjectContextData(e); |
| 11760 | // Context-data suffixes vary by owning entity and |
| 11761 | // DWG version. A structurally valid frame can be |
| 11762 | // preserved even when this decoder cannot type |
| 11763 | // its version-specific body. |
| 11764 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11765 | } |
| 11766 | break; |
| 11767 | } |
| 11768 | } |
| 11769 | // SCALE (AcDbScale) — annotation-scale entry, ODA §20.4.93. |
| 11770 | // Lives under ACAD_SCALELIST in the named-object dictionary. |
| 11771 | // libreDWG dwg2.spec:1195 (DWG_OBJECT (SCALE)). recName |
| 11772 | // "SCALE" or className "AcDbScale". RS_FilterDXFRW currently |
| 11773 | // discards (no annotation-scale-aware viewport) but the |
| 11774 | // parser foundation lands so future per-scale resolution |
| 11775 | // can build on a populated handle map. |
| 11776 | if (rn == "SCALE" || cit->second->className == "AcDbScale") { |
| 11777 | DRW_Scale e; |
| 11778 | ret = parseTableEntry(e); |
| 11779 | if (ret) { |
| 11780 | scaleMap[obj.handle] = e; |
| 11781 | intfa.addScale(e); |
| 11782 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11783 | } |
| 11784 | break; |
| 11785 | } |
| 11786 | // VISUALSTYLE — AcDbVisualStyle (ODA spec §20.4.95). |
| 11787 | // Typed fields are decoded for metadata consumers; the |
| 11788 | // raw shelf remains the lossless replay representation. |
| 11789 | // LibreCAD has no 3D consumer. recName "ACDB_VISUALSTYLE_CLASS" |
| 11790 | // per spec; className fallback for files using the |
| 11791 | // C++ class spelling. |
| 11792 | if (rn == "ACDB_VISUALSTYLE_CLASS" || |
| 11793 | cit->second->className == "AcDbVisualStyle") { |
| 11794 | DRW_VisualStyle e; |
| 11795 | ret = parseTableEntry(e); |
| 11796 | if (ret) { |
| 11797 | intfa.addVisualStyle(e); |
| 11798 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11799 | } |
| 11800 | break; |
| 11801 | } |
| 11802 | // UNDERLAYDEFINITION — AcDb{Pdf,Dgn,Dwf}Definition. |
| 11803 | // Three flavors share one parser; routed by recName. |
| 11804 | // Object lives in OBJECTS section AFTER entities are |
| 11805 | // parsed; LibreCAD filter caches by handle for matching. |
| 11806 | { |
| 11807 | const std::string &cn = cit->second->className; |
| 11808 | if (rn == "PDFDEFINITION" || rn == "DGNDEFINITION" || |
| 11809 | rn == "DWFDEFINITION" || cn == "AcDbPdfDefinition" || |
| 11810 | cn == "AcDbDgnDefinition" || cn == "AcDbDwfDefinition") { |
| 11811 | DRW_UnderlayDefinition e; |
| 11812 | if (rn == "DGNDEFINITION" || cn == "AcDbDgnDefinition") |
| 11813 | e.kind = DRW_UnderlayDefinition::DGN; |
| 11814 | else if (rn == "DWFDEFINITION" || cn == "AcDbDwfDefinition") |
| 11815 | e.kind = DRW_UnderlayDefinition::DWF; |
| 11816 | ret = parseTableEntry(e); |
| 11817 | if (ret) { |
| 11818 | intfa.linkUnderlay(&e); |
| 11819 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11820 | } |
| 11821 | break; |
| 11822 | } |
| 11823 | } |
| 11824 | if (rn == "POINTCLOUDDEFINITION" || |
| 11825 | rn == "POINTCLOUDDEFINITIONEX" || |
| 11826 | rn == "POINTCLOUDDEFREACTOR" || |
| 11827 | rn == "POINTCLOUDDEFREACTOREX" || |
| 11828 | cit->second->className == "AcDbPointCloudDef" || |
| 11829 | cit->second->className == "AcDbPointCloudDefEx" || |
| 11830 | cit->second->className == "AcDbPointCloudDefReactor" || |
| 11831 | cit->second->className == "AcDbPointCloudDefReactorEx") { |
| 11832 | DRW_PointCloudDef e; |
| 11833 | if (rn == "POINTCLOUDDEFINITIONEX" || |
| 11834 | cit->second->className == "AcDbPointCloudDefEx") { |
| 11835 | e.m_kind = DRW_PointCloudDef::DefinitionEx; |
| 11836 | } else if (rn == "POINTCLOUDDEFREACTOREX" || |
| 11837 | cit->second->className == |
| 11838 | "AcDbPointCloudDefReactorEx") { |
| 11839 | e.m_kind = DRW_PointCloudDef::ReactorEx; |
| 11840 | } else if (rn == "POINTCLOUDDEFREACTOR" || |
| 11841 | cit->second->className == "AcDbPointCloudDefReactor") { |
| 11842 | e.m_kind = DRW_PointCloudDef::Reactor; |
| 11843 | } |
| 11844 | ret = parseTableEntry(e); |
| 11845 | if (ret) { |
| 11846 | intfa.addPointCloudDef(e); |
| 11847 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11848 | } |
| 11849 | break; |
| 11850 | } |
| 11851 | if (rn == "NAVISWORKSMODELDEF" || |
| 11852 | cit->second->className == "AcDbNavisworksModelDef") { |
| 11853 | DRW_NavisworksModelDef e; |
| 11854 | ret = parseTableEntry(e); |
| 11855 | if (ret) { |
| 11856 | intfa.addNavisworksModelDef(e); |
| 11857 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11858 | } |
| 11859 | break; |
| 11860 | } |
| 11861 | if (rn == "POINTCLOUDCOLORMAP" || |
| 11862 | cit->second->className == "AcDbPointCloudColorMap") { |
| 11863 | DRW_PointCloudColorMap e; |
| 11864 | ret = parseTableEntry(e); |
| 11865 | if (ret) { |
| 11866 | intfa.addPointCloudColorMap(e); |
| 11867 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11868 | } |
| 11869 | break; |
| 11870 | } |
| 11871 | if (rn == "LIGHTLIST" || rn == "ACDBLIGHTLIST" || |
| 11872 | cit->second->className == "AcDbLightList") { |
| 11873 | DRW_LightList e; |
| 11874 | ret = parseTableEntry(e); |
| 11875 | if (ret) { |
| 11876 | intfa.addLightList(e); |
| 11877 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11878 | } |
| 11879 | break; |
| 11880 | } |
| 11881 | if (rn == "LAYERFILTER" || |
| 11882 | cit->second->className == "AcDbLayerFilter") { |
| 11883 | DRW_LayerFilter e; |
| 11884 | ret = parseTableEntry(e); |
| 11885 | if (ret) { |
| 11886 | intfa.addLayerFilter(e); |
| 11887 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11888 | } |
| 11889 | break; |
| 11890 | } |
| 11891 | if (rn == "DATALINK" || cit->second->className == "AcDbDataLink") { |
| 11892 | DRW_DataLink e; |
| 11893 | ret = parseTableEntry(e); |
| 11894 | if (ret) { |
| 11895 | intfa.addDataLink(e); |
| 11896 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11897 | } |
| 11898 | break; |
| 11899 | } |
| 11900 | if (rn == "GEOMAPIMAGE" || rn == "ACDBGEOMAPIMAGE" || |
| 11901 | cit->second->className == "AcDbGeomapImage") { |
| 11902 | DRW_GeoMapImage e; |
| 11903 | ret = parseTableEntry(e); |
| 11904 | if (ret) { |
| 11905 | intfa.addGeoMapImage(e); |
| 11906 | intfa.addUnsupportedObject(makeRawObject(oType, cit->second)); |
| 11907 | } |
| 11908 | break; |
| 11909 | } |
| 11910 | } |
| 11911 | } |
| 11912 | // not supported object or entity add to remaining map for debug |
| 11913 | { |
| 11914 | // R2007+ object frames expose the detached common-handle |
| 11915 | // stream. An unrecognized custom class is safe to preserve |
| 11916 | // only after that stream validates; otherwise a truncated |
| 11917 | // owner/reactor/xdictionary tail would become a public raw |
| 11918 | // callback. R2000/R2004 opaque bodies have no generic safe |
| 11919 | // boundary, so retain their existing frame-bounded fallback. |
| 11920 | if (version > DRW::AC1018 && resolvedClass != nullptr) { |
| 11921 | RawObjectShell shell; |
| 11922 | ret = shell.parseDwg(version, &buff, bs) && buff.isGood(); |
| 11923 | if (ret) { |
| 11924 | const std::string &recordName = resolvedClass->recName; |
| 11925 | const std::string &className = resolvedClass->className; |
| 11926 | const std::string &objectName = |
| 11927 | recordName.empty() ? className : recordName; |
| 11928 | ++m_skippedCustomClasses[className.empty() ? recordName.c_str() |
| 11929 | : className]; |
| 11930 | ++m_skippedUnsupportedObjects[objectName]; |
| 11931 | intfa.addUnsupportedObject( |
| 11932 | makeRawObject(oType, resolvedClass, false)); |
| 11933 | remainingMap[obj.handle] = obj; |
| 11934 | DRW_DBG("[custom-object-skipped ")DRW_dbg::dbg("[custom-object-skipped "); |
| 11935 | DRW_DBG(objectName.c_str())DRW_dbg::dbg(objectName.c_str()); |
| 11936 | DRW_DBG("]\n")DRW_dbg::dbg("]\n"); |
| 11937 | } |
| 11938 | break; |
| 11939 | } |
| 11940 | std::string objectName; |
| 11941 | std::string recordName; |
| 11942 | std::string className; |
| 11943 | if (oType >= 500) { |
| 11944 | auto cit = classesmap.find(oType); |
| 11945 | if (cit != classesmap.end() && cit->second) { |
| 11946 | recordName = cit->second->recName; |
| 11947 | className = cit->second->className; |
| 11948 | objectName = recordName.empty() ? className : recordName; |
| 11949 | const char *clsName = |
| 11950 | className.empty() ? recordName.c_str() : className.c_str(); |
| 11951 | ++m_skippedCustomClasses[clsName]; |
| 11952 | } |
| 11953 | } |
| 11954 | if (objectName.empty()) |
| 11955 | objectName = "type-" + std::to_string(oType); |
| 11956 | ++m_skippedUnsupportedObjects[objectName]; |
| 11957 | DRW_UnsupportedObject raw = makeRawObject( |
| 11958 | oType, |
| 11959 | (oType >= 500 && classesmap.find(oType) != classesmap.end()) |
| 11960 | ? classesmap.find(oType)->second |
| 11961 | : nullptr, |
| 11962 | false); |
| 11963 | raw.m_recordName = recordName; |
| 11964 | raw.m_className = className; |
| 11965 | intfa.addUnsupportedObject(raw); |
| 11966 | DRW_DBG("[unsupported-object-skipped ")DRW_dbg::dbg("[unsupported-object-skipped "); |
| 11967 | DRW_DBG(objectName.c_str())DRW_dbg::dbg(objectName.c_str()); |
| 11968 | DRW_DBG("]\n")DRW_dbg::dbg("]\n"); |
| 11969 | } |
| 11970 | remainingMap[obj.handle] = obj; |
| 11971 | break; |
| 11972 | } |
| 11973 | if (!ret) { |
| 11974 | // As with entities, a failed typed OBJECTS parser is not a raw |
| 11975 | // preservation success. Valid opaque fixed/custom shell routes |
| 11976 | // publish from their successful dispatch arms above. |
| 11977 | DRW_DBG("Warning: Object type ")DRW_dbg::dbg("Warning: Object type "); |
| 11978 | DRW_DBG(oType)DRW_dbg::dbg(oType); |
| 11979 | DRW_DBG("has failed, handle: ")DRW_dbg::dbg("has failed, handle: "); |
| 11980 | DRW_DBG(obj.handle)DRW_dbg::dbg(obj.handle); |
| 11981 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 11982 | } |
| 11983 | if (ret) { |
| 11984 | publication.m_carrier = |
| 11985 | rawViewIssued ? (rawViewHasTypedPeer |
| 11986 | ? DRW_DwgFramePublication::Carrier::TypedAndRaw |
| 11987 | : DRW_DwgFramePublication::Carrier::Raw) |
| 11988 | : DRW_DwgFramePublication::Carrier::Typed; |
| 11989 | if (!publishDwgFramePublication( |
| 11990 | intfa, publication, |
| 11991 | {dictionaryMembership ? &*dictionaryMembership : nullptr, |
| 11992 | typedReference ? &*typedReference : nullptr, nullptr, |
| 11993 | groupMembership ? &*groupMembership : nullptr, |
| 11994 | sortEntsMembership ? &*sortEntsMembership : nullptr, |
| 11995 | fieldListMembership ? &*fieldListMembership : nullptr, |
| 11996 | dictionaryWithDefaultMembership |
| 11997 | ? &*dictionaryWithDefaultMembership |
| 11998 | : nullptr, |
| 11999 | fieldPayloadReceipt ? &*fieldPayloadReceipt : nullptr}, |
| 12000 | {fieldOutput ? &*fieldOutput : nullptr, |
| 12001 | fieldListOutput ? &*fieldListOutput : nullptr, |
| 12002 | fieldRawOutput ? &*fieldRawOutput : nullptr})) |
| 12003 | ret = false; |
| 12004 | } |
| 12005 | return ret; |
| 12006 | } catch (...) { |
| 12007 | // A decoder or consumer failure must not escape the per-object |
| 12008 | // recovery loop. A HANDLE-map source can distinguish the latter |
| 12009 | // from an ordinary unsuccessful body parse in the final report. |
| 12010 | if (m_dwgFrameCoverageStatus != DRW_DwgFrameCoverageStatus::NotAvailable) { |
| 12011 | (void)markDwgFrameOutcome(sourceFrameId(obj), |
| 12012 | DRW_DwgFrameDisposition::Failed, |
| 12013 | DRW_DwgFrameCoverageReason::CallbackException); |
| 12014 | } |
| 12015 | return failStructural(); |
| 12016 | } |
| 12017 | } |
| 12018 | |
| 12019 | bool DRW_ObjControl::parseDwg(DRW::Version version, dwgBuffer *buf, |
| 12020 | std::uint32_t bs) { |
| 12021 | const auto fail = [this, buf]() { |
| 12022 | if (buf != nullptr) |
| 12023 | buf->invalidate(); |
| 12024 | reset(); |
| 12025 | return false; |
| 12026 | }; |
| 12027 | if (buf == nullptr) |
| 12028 | return fail(); |
| 12029 | reset(); |
| 12030 | |
| 12031 | int unkData = 0; |
| 12032 | bool ret = DRW_TableEntry::parseDwg(version, buf, nullptr, bs); |
| 12033 | DRW_DBG("\n***************************** parsing object control entry "DRW_dbg::dbg("\n***************************** parsing object control entry " "*********************************************\n") |
| 12034 | "*********************************************\n")DRW_dbg::dbg("\n***************************** parsing object control entry " "*********************************************\n"); |
| 12035 | if (!ret) |
| 12036 | return fail(); |
| 12037 | dwgBuffer hBuff = *buf; |
| 12038 | dwgBuffer *hBuf = buf; |
| 12039 | if (version > DRW::AC1021) { |
| 12040 | const std::uint64_t totalBits = |
| 12041 | static_cast<std::uint64_t>(buf->size()) * 8u; |
| 12042 | if (totalBits < bs || totalBits - bs > UINT32_MAX(4294967295U)) |
| 12043 | return fail(); |
| 12044 | const std::uint32_t objectBits = static_cast<std::uint32_t>(totalBits - bs); |
| 12045 | if (!hBuff.setPosition(objectBits >> 3)) |
| 12046 | return fail(); |
| 12047 | hBuff.setBitPos(static_cast<std::uint8_t>(objectBits & 7u)); |
| 12048 | hBuf = &hBuff; |
| 12049 | } |
| 12050 | const std::uint64_t totalBits = static_cast<std::uint64_t>(buf->size()) * 8u; |
| 12051 | const std::uint64_t bodyEndBit = objSize != 0 ? objSize : totalBits; |
| 12052 | if (bodyEndBit > totalBits) |
| 12053 | return fail(); |
| 12054 | // last parsed is: XDic Missing Flag 2004+ |
| 12055 | std::int32_t rawNumEntries = 0; |
| 12056 | const bool countRead = |
| 12057 | controlEntryCountUsesBitShort(oType) |
| 12058 | ? readControlBitShort(*buf, bodyEndBit, rawNumEntries) |
| 12059 | : readControlBitLong(*buf, bodyEndBit, rawNumEntries); |
| 12060 | if (!countRead || rawNumEntries < 0) |
| 12061 | return fail(); |
| 12062 | const std::uint32_t numEntries = static_cast<std::uint32_t>(rawNumEntries); |
| 12063 | const std::uint32_t phantomEntryCount = |
| 12064 | controlHasPhantomEntries(oType) ? 2U : 0U; |
| 12065 | if (numEntries > dwgSafety::MaxOwnedObjectCount || |
| 12066 | numEntries > |
| 12067 | std::numeric_limits<std::uint32_t>::max() - phantomEntryCount) |
| 12068 | return fail(); |
| 12069 | DRW_DBG(" num entries: ")DRW_dbg::dbg(" num entries: "); |
| 12070 | DRW_DBG(numEntries)DRW_dbg::dbg(numEntries); |
| 12071 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 12072 | DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); |
| 12073 | DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); |
| 12074 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 12075 | |
| 12076 | // if (oType == 68 && version== DRW::AC1015){//V2000 dimstyle seems have |
| 12077 | // one unknown byte hard handle counter?? |
| 12078 | if (oType == 68 && version > DRW::AC1014) { // dimstyle seems have one unknown |
| 12079 | // byte hard handle counter?? |
| 12080 | std::uint8_t parsedUnknown = 0; |
| 12081 | if (!readControlRawChar(*buf, bodyEndBit, parsedUnknown)) |
| 12082 | return fail(); |
| 12083 | unkData = parsedUnknown; |
| 12084 | DRW_DBG(" unknown v2000 byte: ")DRW_dbg::dbg(" unknown v2000 byte: "); |
| 12085 | DRW_DBG(unkData)DRW_dbg::dbg(unkData); |
| 12086 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 12087 | } |
| 12088 | if (version > DRW::AC1018) { // from v2007+ have a bit for strings follows |
| 12089 | // (ObjControl do not have) |
| 12090 | bool stringBit = false; |
| 12091 | if (!readControlBit(*buf, bodyEndBit, stringBit)) |
| 12092 | return fail(); |
| 12093 | DRW_DBG(" string bit for v2007+: ")DRW_dbg::dbg(" string bit for v2007+: "); |
| 12094 | DRW_DBG(stringBit)DRW_dbg::dbg(stringBit); |
| 12095 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 12096 | } |
| 12097 | if (!buf->isGood()) |
| 12098 | return fail(); |
| 12099 | |
| 12100 | dwgHandle objectH; |
| 12101 | if (!readDwgHandleChecked(*hBuf, 0, false, objectH)) |
| 12102 | return fail(); |
| 12103 | DRW_DBG(" NULL Handle: ")DRW_dbg::dbg(" NULL Handle: "); |
| 12104 | DRW_DBGHL(objectH.code, objectH.size, objectH.ref)DRW_dbg::dbgHL(objectH.code, objectH.size, objectH.ref); |
| 12105 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 12106 | DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); |
| 12107 | DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); |
| 12108 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 12109 | |
| 12110 | // if (oType == 56 && version== DRW::AC1015){//linetype in 2004 seems not |
| 12111 | // have XDicObjH or NULL handle |
| 12112 | if (xDictFlag != |
| 12113 | 1) { // linetype in 2004 seems not have XDicObjH or NULL handle |
| 12114 | dwgHandle XDicObjH; |
| 12115 | if (!readDwgHandleChecked(*hBuf, 0, false, XDicObjH)) |
| 12116 | return fail(); |
| 12117 | xDictHandle = XDicObjH.ref; |
| 12118 | DRW_DBG(" XDicObj control Handle: ")DRW_dbg::dbg(" XDicObj control Handle: "); |
| 12119 | DRW_DBGHL(XDicObjH.code, XDicObjH.size, XDicObjH.ref)DRW_dbg::dbgHL(XDicObjH.code, XDicObjH.size, XDicObjH.ref); |
| 12120 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 12121 | DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); |
| 12122 | DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); |
| 12123 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 12124 | } |
| 12125 | // add 2 for modelspace, paperspace blocks & bylayer, byblock linetypes |
| 12126 | const std::uint64_t declaredHandleCount = |
| 12127 | static_cast<std::uint64_t>(numEntries) + phantomEntryCount + |
| 12128 | static_cast<std::uint64_t>(unkData); |
| 12129 | if (declaredHandleCount > |
| 12130 | static_cast<std::uint64_t>(std::max(0, hBuf->numRemainingBytes()))) { |
| 12131 | return fail(); |
| 12132 | } |
| 12133 | |
| 12134 | const std::uint64_t childHandleCount = |
| 12135 | static_cast<std::uint64_t>(numEntries) + phantomEntryCount; |
| 12136 | std::list<std::uint32_t> parsedHandles; |
| 12137 | std::unordered_set<std::uint32_t> seenHandles; |
| 12138 | |
| 12139 | for (std::uint64_t i = 0; i < childHandleCount; i++) { |
| 12140 | if (!readDwgHandleChecked(*hBuf, handle, true, objectH)) |
| 12141 | return fail(); |
| 12142 | if (objectH.ref != 0) { // in vports R14 I found some NULL handles |
| 12143 | if (!seenHandles.insert(objectH.ref).second) |
| 12144 | return fail(); |
| 12145 | parsedHandles.push_back(objectH.ref); |
| 12146 | } |
| 12147 | DRW_DBG(" objectH Handle: ")DRW_dbg::dbg(" objectH Handle: "); |
| 12148 | DRW_DBGHL(objectH.code, objectH.size, objectH.ref)DRW_dbg::dbgHL(objectH.code, objectH.size, objectH.ref); |
| 12149 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 12150 | DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); |
| 12151 | DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); |
| 12152 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 12153 | } |
| 12154 | |
| 12155 | for (int i = 0; i < unkData; i++) { |
| 12156 | if (!readDwgHandleChecked(*hBuf, handle, true, objectH)) |
| 12157 | return fail(); |
| 12158 | DRW_DBG(" unknown Handle: ")DRW_dbg::dbg(" unknown Handle: "); |
| 12159 | DRW_DBGHL(objectH.code, objectH.size, objectH.ref)DRW_dbg::dbgHL(objectH.code, objectH.size, objectH.ref); |
| 12160 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 12161 | DRW_DBG("Remaining bytes: ")DRW_dbg::dbg("Remaining bytes: "); |
| 12162 | DRW_DBG(buf->numRemainingBytes())DRW_dbg::dbg(buf->numRemainingBytes()); |
| 12163 | DRW_DBG("\n")DRW_dbg::dbg("\n"); |
| 12164 | } |
| 12165 | handlesList = std::move(parsedHandles); |
| 12166 | return buf->isGood() && hBuf->isGood() ? true : fail(); |
| 12167 | } |