175 auto checkedAdd = [
this](
size_t aBase,
size_t aBytes,
size_t aSourceOffset,
const wxString& aDetail )
178 throwAt( aSourceOffset, aDetail );
180 return aBase + aBytes;
183 auto requireBytes = [
this](
size_t aOffset,
const auto& aExpected,
size_t aBytes,
const wxString& aDetail )
185 for(
size_t i = 0; i < aBytes; ++i )
187 if(
m_data[aOffset + i] !=
static_cast<uint8_t
>( aExpected[i] ) )
188 throwAt( aOffset + i, aDetail );
194 if(
m_cursor.U32At( offset ) != DATABASE_CONTROLLER )
195 throwAt( offset,
"invalid first schematic controller ordinal" );
197 size_t outerStart = offset;
198 size_t outerEnd = checkedAdd( offset, 4, offset,
"controller ordinal extends past footer" );
200 for(
size_t i = 1; i <
m_pools.size(); ++i )
202 size_t source = POOL_OFFSET + i * POOL_SIZE + POOL_USED_BYTES_OFFSET;
204 checkedAdd( outerEnd,
m_pools[i].usedBytes, source,
"controller payload extends past footer" );
210 outerEnd - outerStart, 19, 0 } );
214 uint32_t sheetCount =
m_pools[3].count;
215 constexpr size_t minimumSheetBytes = SHEET_HEADER_SIZE + SHEET_POOL_COUNT * SHEET_POOL_SIZE;
216 size_t sheetCountOffset = POOL_OFFSET + 3 * POOL_SIZE + POOL_COUNT_OFFSET;
218 if( sheetCount > (
m_footerOffset - offset ) / minimumSheetBytes )
219 throwAt( sheetCountOffset,
"sheet count extent exceeds schematic payload" );
221 for( uint32_t sheet = 0; sheet < sheetCount; ++sheet )
223 size_t sheetStart = offset;
225 checkedAdd( sheetStart, SHEET_HEADER_SIZE, sheetStart,
"sheet header extends past footer" );
227 if(
m_cursor.U32At( sheetStart ) != SHEET_HEADER_SIZE ||
m_cursor.U32At( sheetStart + 4 ) != 5
228 ||
m_cursor.U32At( sheetStart + 8 ) != SHEET_HEADER_SIZE )
230 throwAt( sheetStart,
"invalid sheet database header" );
233 offset = checkedAdd( descriptors, SHEET_POOL_COUNT * SHEET_POOL_SIZE, descriptors,
234 "sheet controller directory extends past footer" );
236 for(
size_t i = 0; i < SHEET_SERIALIZED_POOL_COUNT; ++i )
238 size_t descriptor = descriptors + i * SHEET_POOL_SIZE;
239 uint32_t allocatedBytes =
m_cursor.U32At( descriptor + SHEET_POOL_ALLOCATED_BYTES_OFFSET );
240 uint32_t count =
m_cursor.U32At( descriptor + SHEET_POOL_COUNT_OFFSET );
241 uint32_t usedBytes =
m_cursor.U32At( descriptor + SHEET_POOL_USED_BYTES_OFFSET );
243 if( usedBytes > allocatedBytes )
244 throwAt( descriptor + SHEET_POOL_USED_BYTES_OFFSET,
"sheet pool serialized bytes exceed allocation" );
246 if( count == 0 && usedBytes != 0 )
247 throwAt( descriptor + SHEET_POOL_USED_BYTES_OFFSET,
"empty sheet pool has serialized bytes" );
249 offset = checkedAdd( offset, usedBytes, descriptor + SHEET_POOL_USED_BYTES_OFFSET,
250 "sheet controller payload extends past footer" );
254 SHEET_SERIALIZED_POOL_COUNT, 0 } );
259 if(
m_cursor.U32At( backPointerOffset ) != offset )
260 throwAt( backPointerOffset,
"container-item back-pointer overlaps derived schematic blocks" );
262 uint32_t containerItemCount =
m_cursor.U32At( offset );
263 size_t containerItemCountOffset = offset;
264 offset = checkedAdd( offset, 4, containerItemCountOffset,
"embedded OLE item count extends past footer" );
267 if( containerItemCount == 0 )
270 throwAt( offset,
"unowned bytes before schematic footer" );
275 constexpr size_t firstItemFrameSize = 6 + FIRST_OLE_FIXED_STATE_SIZE + 4;
276 constexpr size_t finalItemMinimum = firstItemFrameSize + FINAL_OLE_TRAILER_SIZE;
278 uint64_t minimumItemBytes = finalItemMinimum + uint64_t( containerItemCount - 1 ) * BETWEEN_OLE_TRAILER_SIZE;
281 if( minimumItemBytes > remainingItemBytes )
283 throwAt( containerItemCountOffset,
"embedded OLE item count exceeds schematic payload" );
286 size_t itemStart = offset;
287 requireBytes( itemStart, MFC_CLASS_MARKER, std::size( MFC_CLASS_MARKER ),
"invalid embedded OLE MFC class marker" );
288 uint16_t classNameLength =
m_cursor.U16At( itemStart + 4 );
290 if( classNameLength != std::size( OLE_ITEM_CLASS_NAME ) - 1 )
291 throwAt( itemStart + 4,
"invalid embedded OLE MFC class name length" );
293 size_t lengthOffset = checkedAdd( itemStart, 6, itemStart,
"OLE item class header extends past footer" );
295 checkedAdd( lengthOffset, classNameLength, itemStart + 4,
"OLE item class name extends past footer" );
296 requireBytes( itemStart + 6, OLE_ITEM_CLASS_NAME, classNameLength,
"invalid embedded OLE MFC class name" );
298 checkedAdd( lengthOffset, FIRST_OLE_FIXED_STATE_SIZE, lengthOffset,
"OLE item state extends past footer" );
299 size_t firstCfbOffset = checkedAdd( lengthOffset, 4, lengthOffset,
"OLE item length extends past footer" );
302 for( uint32_t item = 0; item < containerItemCount; ++item )
304 uint32_t cfbBytes =
m_cursor.U32At( lengthOffset );
306 if( cfbBytes < CFB_HEADER_SIZE )
307 throwAt( lengthOffset,
"embedded OLE CFB is smaller than its structural header" );
309 size_t cfbOffset = checkedAdd( lengthOffset, 4, lengthOffset,
"OLE item length extends past footer" );
310 size_t cfbEnd = checkedAdd( cfbOffset, cfbBytes, lengthOffset,
"OLE item CFB extends past footer" );
311 requireBytes( cfbOffset, CFB_MAGIC, std::size( CFB_MAGIC ),
"invalid embedded OLE CFB magic" );
312 requireBytes( cfbOffset + 8, CFB_ZERO_CLSID, std::size( CFB_ZERO_CLSID ),
313 "invalid embedded OLE CFB reserved class ID" );
314 requireBytes( cfbOffset + 28, CFB_BYTE_ORDER, std::size( CFB_BYTE_ORDER ),
315 "invalid embedded OLE CFB byte order" );
316 requireBytes( cfbOffset + 34, CFB_ZERO_PADDING, std::size( CFB_ZERO_PADDING ),
317 "invalid embedded OLE CFB header padding" );
318 size_t trailer = item + 1 == containerItemCount ? FINAL_OLE_TRAILER_SIZE : BETWEEN_OLE_TRAILER_SIZE;
319 size_t itemEnd = checkedAdd( cfbEnd, trailer, cfbEnd,
"OLE item trailer extends past footer" );
320 requireBytes( cfbEnd, FOOTER_GUID, FOOTER_GUID_SIZE,
"invalid embedded OLE trailer class ID" );
328 constexpr size_t extentOffsetInTrailer = FOOTER_GUID_SIZE;
329 constexpr size_t boxOffsetInTrailer = extentOffsetInTrailer + 16;
330 constexpr size_t sheetPlaneOffsetInTrailer = boxOffsetInTrailer + 16;
331 constexpr size_t flagsOffsetInTrailer = sheetPlaneOffsetInTrailer + 4;
332 size_t extentOffset = cfbEnd + extentOffsetInTrailer;
333 size_t boxOffset = cfbEnd + boxOffsetInTrailer;
335 oleItem.
cfb = cfbBlock;
343 oleItem.
flags =
m_cursor.U32At( cfbEnd + flagsOffsetInTrailer );
346 throwAt( cfbEnd + sheetPlaneOffsetInTrailer,
"embedded OLE item sheet index is out of range" );
353 if( item + 1 < containerItemCount )
354 lengthOffset = itemEnd - 4;
360 throwAt( offset,
"embedded OLE items do not end at schematic footer" );