From de44de26f869a2655525dd22c8de257242064dda Mon Sep 17 00:00:00 2001 From: Dion Moult Date: Wed, 27 May 2026 14:26:20 +1000 Subject: [PATCH] wgpu backend: clearer sidecar-load diagnostics + tilde expansion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The single "(file missing, wrong magic, or schema mismatch)" message was making triage harder than necessary. loadSidecar now expands a leading ~/ (shells skip it inside double quotes, which trips up paste- from-launcher), and on failure peeks the file's header itself to report exactly which check failed: - "Sidecar not found" — file doesn't exist - "Sidecar unreadable" — exists but open failed - "Sidecar truncated" — <12 bytes - "Sidecar magic mismatch" — wrong magic, reports got vs expected - "Sidecar schema mismatch" — wrong version, reports both numbers and suggests re-baking - "Sidecar endianness mismatch" — cross-platform load attempt Co-Authored-By: Claude Opus 4.7 --- src/ifcviewer-wgpu/WgpuViewportWindow.cpp | 47 +++++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/src/ifcviewer-wgpu/WgpuViewportWindow.cpp b/src/ifcviewer-wgpu/WgpuViewportWindow.cpp index 9ef57afa45..54cbd5e378 100644 --- a/src/ifcviewer-wgpu/WgpuViewportWindow.cpp +++ b/src/ifcviewer-wgpu/WgpuViewportWindow.cpp @@ -22,7 +22,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -317,10 +319,49 @@ uint32_t WgpuViewportWindow::loadSidecar(const QString& path) { return 0; } - auto data_opt = readSidecar(path.toStdString()); + // Tilde expansion — shells handle this inside double-quoted args, but a + // literal "~/..." from a launcher / command-line wouldn't. Cheap to do + // here so the failure mode isn't "fopen returned ENOENT". + QString resolved = path; + if (resolved.startsWith("~/")) { + resolved = QDir::homePath() + resolved.mid(1); + } + + auto data_opt = readSidecar(resolved.toStdString()); if (!data_opt) { - qWarning().noquote() << "Failed to read sidecar:" << path - << "(file missing, wrong magic, or schema mismatch)"; + // Triage: distinguish missing file from magic/version mismatch by + // peeking the header ourselves, so users know which to fix. + QFile f(resolved); + if (!f.exists()) { + qWarning().noquote() << "Sidecar not found:" << resolved; + } else if (!f.open(QIODevice::ReadOnly)) { + qWarning().noquote() << "Sidecar unreadable:" << resolved + << "(" << f.errorString() << ")"; + } else { + uint32_t header[3] = { 0, 0, 0 }; + const qint64 got = f.read(reinterpret_cast(header), sizeof(header)); + if (got < qint64(sizeof(header))) { + qWarning().noquote() << "Sidecar truncated:" << resolved + << "(only" << got << "bytes — expected ≥ 12)"; + } else if (header[0] != SIDECAR_MAGIC) { + qWarning().noquote().nospace() + << "Sidecar magic mismatch: " << resolved + << " — got 0x" << QString::number(header[0], 16) + << ", expected 0x" << QString::number(SIDECAR_MAGIC, 16) + << " (\"IFVW\")"; + } else if (header[1] != SIDECAR_VERSION) { + qWarning().noquote().nospace() + << "Sidecar schema mismatch: " << resolved + << " — file is v" << header[1] + << ", this build expects v" << SIDECAR_VERSION + << ". Re-bake the .ifc with a viewer at the matching schema."; + } else if (header[2] != SIDECAR_ENDIAN) { + qWarning().noquote() << "Sidecar endianness mismatch:" << resolved + << "(cross-platform load not supported)"; + } else { + qWarning().noquote() << "Sidecar read failed past the header:" << resolved; + } + } return 0; }