Skip to content

Commit 4127c4d

Browse files
committed
fix: emit all OLE embed streams and the second weekly group code
1 parent ae07ca5 commit 4127c4d

2 files changed

Lines changed: 15 additions & 10 deletions

File tree

crates/rpt/src/io/mod.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
289290
fn 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,

crates/rpt/src/project/raise/data_def.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -586,20 +586,21 @@ pub(super) fn raise_group(
586586
use crate::model::FieldValueType::*;
587587
// The longer date-grouping periods are selected by the byte after the report-group order marker
588588
// (`@Group #N Order`), where the trailing structure is `01 00 <code> ff ff`: 0x03 = monthly,
589-
// 0x06 = weekly. (Daily is instead flagged at `used + 4 == 0x02`; discrete date grouping leaves
590-
// both clear.) Unmapped codes are left undecoded rather than guessed.
589+
// 0x06 / 0x08 = weekly (the engine emits two week codes — e.g. week-of-year vs a week starting
590+
// on a fixed weekday — that both render as "Weekly"). Daily is 0x01, or via the older
591+
// `used + 4 == 0x02` flag (with the selector left 0); discrete date grouping leaves both clear.
592+
// Quarterly/yearly codes are not present in the corpus, so they are left undecoded rather than
593+
// guessed.
591594
let period_code = lp_scan(&bytes, Scan::Consume)
592595
.find(|(_, s, _)| s.starts_with("@Group #") && s.ends_with(" Order"))
593596
.and_then(|(i, _, consumed)| bytes.get(i + consumed + 2).copied());
594597
let date_condition = field_types
595598
.get(&field.to_lowercase())
596599
.filter(|t| matches!(t, Date | Time | DateTime | Boolean))
597600
.and_then(|_| match period_code {
598-
// The period selector after the order marker; daily also appears via the older
599-
// `used + 4 == 0x02` flag (with the selector left 0).
600601
Some(0x01) => Some("daily".to_string()),
601602
Some(0x03) => Some("monthly".to_string()),
602-
Some(0x06) => Some("weekly".to_string()),
603+
Some(0x06) | Some(0x08) => Some("weekly".to_string()),
603604
_ if bytes.get(used + 4).copied() == Some(0x02) => Some("daily".to_string()),
604605
_ => None,
605606
});

0 commit comments

Comments
 (0)