Bug Summary

File:libraries/libdxfrw/src/intern/dwgreader.cpp
Warning:line 8983, column 9
Value stored to 'unresolvedCustomClass' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name dwgreader.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/home/runner/work/LibreCAD/LibreCAD/libraries/libdxfrw -fcoverage-compilation-dir=/home/runner/work/LibreCAD/LibreCAD/libraries/libdxfrw -resource-dir /usr/lib/llvm-18/lib/clang/18 -D _REENTRANT -D MUPARSER_STATIC -D QT_NO_DEBUG -I . -I ../../../Qt/6.9.0/gcc_64/mkspecs/linux-g++ -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-18/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -std=gnu++1z -fdeprecated-macro -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcxx-exceptions -fexceptions -vectorize-loops -vectorize-slp -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/runner/work/LibreCAD/LibreCAD/out/2026-09-15-234850-5000-1 -x c++ src/intern/dwgreader.cpp
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
35namespace {
36bool isSpaceBlockRecordName(const std::string &name);
37
38bool 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
60dwgReader::DwgEntityOutput::~DwgEntityOutput() = default;
61
62dwgReader::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
68dwgReader::DwgEntityOutput::SourceScope::~SourceScope() {
69 m_output.m_activeSource = std::move(m_previous);
70}
71
72dwgReader::DwgEntityOutput::SourceScope dwgReader::DwgEntityOutput::bindSource(
73 const DwgSourceFrameId &source) noexcept {
74 return SourceScope(*this, source);
75}
76
77template <typename T, typename Callback>
78void 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
137bool 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
162bool 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
230bool 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
252class dwgReader::DwgImmediateEntityOutput final : public DwgEntityOutput {
253public:
254 DwgImmediateEntityOutput(dwgReader &reader, DRW_Interface &target)
255 : m_reader(reader), m_target(target) {}
256
257private:
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
266dwgReader::DwgBlockJournalOutput::DwgBlockJournalOutput(dwgReader &reader)
267 : m_reader(reader) {}
268
269bool 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
278bool 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
291bool 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
305void dwgReader::DwgBlockJournalOutput::truncate(
306 std::size_t eventCount) noexcept {
307 if (eventCount <= m_events.size())
308 m_events.resize(eventCount);
309}
310
311bool dwgReader::DwgBlockJournalOutput::empty() const noexcept {
312 return m_events.empty();
313}
314
315std::size_t dwgReader::DwgBlockJournalOutput::size() const noexcept {
316 return m_events.size();
317}
318
319bool 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
329dwgReader::DwgBlockScopeTransaction::DwgBlockScopeTransaction(dwgReader &reader)
330 : m_reader(reader), m_output(reader) {}
331
332dwgReader::DwgBlockScopeTransaction::~DwgBlockScopeTransaction() {
333 if (!m_finished)
334 (void)abort();
335}
336
337dwgReader::DwgBlockJournalOutput &
338dwgReader::DwgBlockScopeTransaction::output() noexcept {
339 return m_output;
340}
341
342bool 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
348bool 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
370bool 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
416bool 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
453bool 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
489std::size_t dwgReader::DwgBlockScopeTransaction::sourceCount() const noexcept {
490 return m_leases.size();
491}
492
493dwgReader::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
502bool 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
509bool 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
533bool 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
545void 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
566void 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
600void 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
609void 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
629bool 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
693bool 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
704void 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
719bool 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
732DwgSourceFrameId
733dwgReader::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
748bool 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
792bool 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
800bool 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
840bool 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
875bool 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
946bool 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
1039bool 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
1078bool 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
1203bool 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
1244bool 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
1274bool 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
1304bool 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
1364bool 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
1465bool 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
1471bool 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
1542bool 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
1560bool 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
1610bool 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
1617bool 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
1694bool dwgReader::quarantineDwgFrame(std::uint32_t handle) {
1695 if (handle == DRW::NoHandle)
1696 return false;
1697 return quarantineDwgFrame(sourceFrameIdForHandle(handle));
1698}
1699
1700bool 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
1732bool 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
1747bool 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
1759void 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
1771bool 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
1999bool 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
2228void 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
2264void 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
2277namespace {
2278
2279DRW_DwgFrameOffsetSpace
2280frameOffsetSpace(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).
2295bool 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
2308bool controlHasPhantomEntries(std::int16_t objectType) {
2309 return objectType == DRW::DwgBlockControlObjectType ||
2310 objectType == DRW::DwgLTypeControlObjectType;
2311}
2312
2313struct DwgTableDescriptor {
2314 std::int16_t controlType;
2315 std::int16_t recordType;
2316 const char *controlReceiptName;
2317};
2318
2319constexpr DwgTableDescriptor kLTypeTable{
2320 DRW::DwgLTypeControlObjectType, DRW::DwgLTypeObjectType, "LTYPE_CONTROL"};
2321constexpr DwgTableDescriptor kLayerTable{
2322 DRW::DwgLayerControlObjectType, DRW::DwgLayerObjectType, "LAYER_CONTROL"};
2323constexpr DwgTableDescriptor kStyleTable{
2324 DRW::DwgStyleControlObjectType, DRW::DwgStyleObjectType, "STYLE_CONTROL"};
2325constexpr DwgTableDescriptor kDimStyleTable{DRW::DwgDimStyleControlObjectType,
2326 DRW::DwgDimStyleObjectType,
2327 "DIMSTYLE_CONTROL"};
2328constexpr DwgTableDescriptor kVPortTable{
2329 DRW::DwgVPortControlObjectType, DRW::DwgVPortObjectType, "VPORT_CONTROL"};
2330constexpr DwgTableDescriptor kBlockTable{DRW::DwgBlockControlObjectType,
2331 DRW::DwgBlockRecordObjectType,
2332 "BLOCK_CONTROL"};
2333constexpr DwgTableDescriptor kAppIdTable{
2334 DRW::DwgAppIdControlObjectType, DRW::DwgAppIdObjectType, "APPID_CONTROL"};
2335constexpr DwgTableDescriptor kViewTable{DRW::DwgViewControlObjectType,
2336 DRW::DwgViewObjectType, "VIEW_CONTROL"};
2337constexpr DwgTableDescriptor kUcsTable{DRW::DwgUcsControlObjectType,
2338 DRW::DwgUcsObjectType, "UCS_CONTROL"};
2339
2340bool 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
2346template <typename Value, typename Reader>
2347bool 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
2365bool 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
2375bool 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
2385bool 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
2392bool 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
2398bool 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
2408template <typename T>
2409void mapCleanUp(std::unordered_map<std::uint32_t, T *> &table) {
2410 for (auto &item : table)
2411 delete item.second;
2412}
2413
2414DRW_DwgFramePublication
2415makeTypedEntityFramePublication(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
2439bool 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
2460bool 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
2466bool 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.
2520struct 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.
2532struct 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.
2543struct 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
2549protected:
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.
2558struct 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
2569bool 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
2584std::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
2601bool 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
2611bool 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
2620bool 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
2695bool 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
2745bool 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.
2824const 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
2861std::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
2899std::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
2914namespace {
2915
2916void 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
2934bool 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
2968bool 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
2978bool 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
2988std::uint64_t eedCurrentBit(const dwgBuffer &buffer) {
2989 return buffer.getPosition() * 8u + buffer.getBitPos();
2990}
2991
2992bool 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
2998bool 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
3013bool 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
3025bool 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
3036namespace {
3037
3038template <typename... Args>
3039bool 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
3055bool 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
3250bool 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
3262bool 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
3291bool 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
3333bool 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
3349bool 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
3365void 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
3437void dwgReader::abandonPendingInsertState(std::uint32_t handle) {
3438 terminalizeInsertGroup(handle, DwgInsertTerminalReason::MalformedGroup);
3439}
3440
3441void 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
3465void 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
3526void 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
3545dwgReader::DwgMappedEntityOutcome
3546dwgReader::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
3634dwgReader::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
3774dwgReader::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
4003dwgReader::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
4162dwgReader::DwgMappedEntityOutcome
4163dwgReader::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 &current) {
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 &current) {
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
4247dwgReader::DwgMappedEntityOutcome
4248dwgReader::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
4322dwgReader::DwgMappedEntityOutcome
4323dwgReader::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
4439dwgReader::DwgMappedEntityOutcome
4440dwgReader::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
4675dwgReader::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 &current) {
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 &current) {
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
4803bool 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
4889dwgReader::DwgMappedEntityOutcome
4890dwgReader::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
5032dwgReader::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
5085dwgReader::DwgMappedEntityOutcome
5086dwgReader::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
5112bool 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
5214dwgReader::DwgMappedEntityOutcome
5215dwgReader::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
5358dwgReader::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
5414dwgReader::DwgMappedEntityOutcome
5415dwgReader::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
5443dwgReader::~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
5457bool 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
5465bool 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
5479void 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
5488DRW_DwgClassCoverageEntry
5489dwgReader::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
5509void 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
5522bool 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
5553void 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
5589void 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
5600bool 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
5673void 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
5723void 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
5752std::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
5798bool 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
5808bool 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
5852bool 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 **/
5895bool 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 */
6197bool 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
7095bool 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
7105bool 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
7767bool 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
8053bool 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
8152void 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;
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 &section = 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 &section = 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
8249void dwgReader::finalizeDataStorageLinks() {
8250 m_dataStorageOrphanRecords = 0;
8251 for (std::size_t sectionIndex = 0;
8252 sectionIndex < m_dataStorageSections.size(); ++sectionIndex) {
8253 DRW_DataStorageSection &section = 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
8285bool 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
8520bool 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
8567bool 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
8697bool 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
8726bool 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
8801bool dwgReader::validateDeferredCompoundState() {
8802 return validateStagedCompoundState();
8803}
8804
8805void dwgReader::abandonDeferredCompoundState() {
8806 (void)abandonStagedCompoundState();
8807}
8808
8809/**
8810 * Reads a dwg drawing entity (dwg object entity) given its offset in the file
8811 */
8812bool 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
8820bool 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;
Value stored to 'unresolvedCustomClass' is never read
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
10338bool 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
10461bool 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 */
10516bool 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
12019bool 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}