@@ -283,10 +283,14 @@ fn decode_saved_data(streams: &[RecordStream]) -> Option<crate::model::SavedData
283283 } )
284284}
285285
286- /// Summarise embedded OLE objects: for each top-level `Embedding N` storage, hash its `\x01Ole`
287- /// stream into an [`Embed`] (Name `Ole`, byte size, Base64-MD5), in directory order. Only the
288- /// `Ole` stream is listed (not `CompObj` / `CONTENTS`).
286+ /// Summarise embedded OLE objects: for each top-level `Embedding N` storage, hash each of its
287+ /// OLE data streams into an [`Embed`] (Name, byte size, Base64-MD5), in directory order. The
288+ /// engine emits the OLE data streams — `Ole`, `OlePres000`, `Ole10Native` — but not the
289+ /// `CompObj` (OLE class descriptor) or a `CONTENTS` sub-storage, so those are skipped.
289290fn raise_embeds ( container : & Container ) -> Vec < crate :: model:: Embed > {
291+ // Streams present under an `Embedding N` storage that the oracle does NOT list. `CompObj` is
292+ // the OLE1 class-moniker blob; `CONTENTS` (when present) is a nested storage, not object data.
293+ const SKIP : [ & str ; 2 ] = [ "CompObj" , "CONTENTS" ] ;
290294 let mut out = Vec :: new ( ) ;
291295 for s in container. streams ( ) {
292296 // Path components below the root, e.g. `["Embedding 2", "\x01Ole"]`. Only top-level
@@ -300,9 +304,9 @@ fn raise_embeds(container: &Container) -> Vec<crate::model::Embed> {
300304 let [ storage, stream] = parts. as_slice ( ) else {
301305 continue ;
302306 } ;
303- // The stream name carries a `\x01` (OLE control) prefix; strip control chars for the Name.
307+ // The stream name carries a `\x01`/`\x02` (OLE control) prefix; strip control chars for the Name.
304308 let name: String = stream. chars ( ) . filter ( |c| !c. is_control ( ) ) . collect ( ) ;
305- if storage. starts_with ( "Embedding " ) && name == "Ole" {
309+ if storage. starts_with ( "Embedding " ) && ! SKIP . contains ( & name. as_str ( ) ) {
306310 out. push ( crate :: model:: Embed {
307311 name,
308312 size : s. bytes . len ( ) as u64 ,
0 commit comments