diff --git a/src/TaglibSharp/Mpeg4/BoxFactory.cs b/src/TaglibSharp/Mpeg4/BoxFactory.cs index 1c952f6a6..e0b16c073 100644 --- a/src/TaglibSharp/Mpeg4/BoxFactory.cs +++ b/src/TaglibSharp/Mpeg4/BoxFactory.cs @@ -114,6 +114,8 @@ parent.Box is IsoSampleDescriptionBox && return new IsoFreeSpaceBox (header, file, handler); else if (type == BoxType.Mean || type == BoxType.Name) return new AppleAdditionalInfoBox (header, file, handler); + else if (type == BoxType.Chpl) + return new OpaqueBox (header, file, handler); // If we still don't have a tag, and we're inside an // ItemListBox, load the box as an AnnotationBox diff --git a/src/TaglibSharp/Mpeg4/BoxTypes.cs b/src/TaglibSharp/Mpeg4/BoxTypes.cs index bcc73b295..9e3ff8dea 100644 --- a/src/TaglibSharp/Mpeg4/BoxTypes.cs +++ b/src/TaglibSharp/Mpeg4/BoxTypes.cs @@ -47,6 +47,7 @@ static class BoxType public static readonly ReadOnlyByteVector Cmt = AppleTag.FixId ("cmt"); public static readonly ReadOnlyByteVector Cond = "cond"; public static readonly ReadOnlyByteVector Covr = "covr"; + public static readonly ReadOnlyByteVector Chpl = "chpl"; public static readonly ReadOnlyByteVector Co64 = "co64"; public static readonly ReadOnlyByteVector Cpil = "cpil"; public static readonly ReadOnlyByteVector Cprt = "cprt"; diff --git a/src/TaglibSharp/Mpeg4/Boxes/OpaqueBox.cs b/src/TaglibSharp/Mpeg4/Boxes/OpaqueBox.cs new file mode 100644 index 000000000..52d7439d2 --- /dev/null +++ b/src/TaglibSharp/Mpeg4/Boxes/OpaqueBox.cs @@ -0,0 +1,105 @@ +// +// OpaqueBox.cs: Provides a box implementation that preserves its +// on-disk bytes exactly, with no re-parsing or re-rendering. +// +// This is used for atoms like the Nero chapter box (chpl) whose +// internal format has multiple versions. Re-rendering through the +// normal Box pipeline can silently corrupt the data (e.g. version +// and flags bytes). By capturing the complete atom bytes on read +// and returning them verbatim on render, we guarantee a lossless +// round-trip. +// +// Copyright (C) 2026 +// +// This library is free software; you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License version +// 2.1 as published by the Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA +// + +using System; + +namespace TagLib.Mpeg4 +{ + /// + /// This class extends to provide a box that + /// stores and renders its original on-disk bytes verbatim. + /// No header or data re-serialization is performed. + /// + public class OpaqueBox : Box + { + #region Private Fields + + /// + /// The complete on-disk atom bytes (header + data). + /// + readonly ByteVector raw_bytes; + + #endregion + + + + #region Constructors + + /// + /// Constructs and initializes a new instance of by reading the complete atom from + /// the file at the position described by the header. + /// + /// + /// A object containing the header + /// to use for the new instance. + /// + /// + /// A object to read the contents + /// of the box from. + /// + /// + /// A object containing the + /// handler that applies to the new instance. + /// + /// + /// is . + /// + public OpaqueBox (BoxHeader header, TagLib.File file, IsoHandlerBox handler) : base (header, handler) + { + if (file == null) + throw new ArgumentNullException (nameof (file)); + + file.Seek (header.Position); + raw_bytes = file.ReadBlock ((int)header.TotalBoxSize); + } + + #endregion + + + + #region Protected Methods + + /// + /// Renders the box by returning the original on-disk bytes. + /// + /// + /// Ignored. The original bytes are returned as-is. + /// + /// + /// A containing the exact bytes + /// that were on disk when the box was read. + /// + protected override ByteVector Render (ByteVector topData) + { + return raw_bytes; + } + + #endregion + } +}