/*! * Copyright (c) 2025-present, Vanilagy and contributors * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ import { FileSlice, readI64Le, readU32Le, readU8 } from '../reader'; import { OGGS } from './ogg-misc'; export const MIN_PAGE_HEADER_SIZE = 27; export const MAX_PAGE_HEADER_SIZE = 27 + 255; export const MAX_PAGE_SIZE = MAX_PAGE_HEADER_SIZE + 255 * 255; export type Page = { headerStartPos: number; totalSize: number; dataStartPos: number; dataSize: number; headerType: number; granulePosition: number; serialNumber: number; sequenceNumber: number; checksum: number; lacingValues: Uint8Array; }; export const readPageHeader = (slice: FileSlice): Page | null => { const startPos = slice.filePos; const capturePattern = readU32Le(slice); if (capturePattern !== OGGS) { return null; } slice.skip(1); // Version const headerType = readU8(slice); const granulePosition = readI64Le(slice); const serialNumber = readU32Le(slice); const sequenceNumber = readU32Le(slice); const checksum = readU32Le(slice); const numberPageSegments = readU8(slice); const lacingValues = new Uint8Array(numberPageSegments); for (let i = 0; i < numberPageSegments; i++) { lacingValues[i] = readU8(slice); } const headerSize = 27 + numberPageSegments; const dataSize = lacingValues.reduce((a, b) => a + b, 0); const totalSize = headerSize + dataSize; return { headerStartPos: startPos, totalSize, dataStartPos: startPos + headerSize, dataSize, headerType, granulePosition, serialNumber, sequenceNumber, checksum, lacingValues, }; }; export const findNextPageHeader = (slice: FileSlice, until: number) => { while (slice.filePos < until - (4 - 1)) { // Size of word minus 1 const word = readU32Le(slice); const firstByte = word & 0xff; const secondByte = (word >>> 8) & 0xff; const thirdByte = (word >>> 16) & 0xff; const fourthByte = (word >>> 24) & 0xff; const O = 0x4f; // 'O' if (firstByte !== O && secondByte !== O && thirdByte !== O && fourthByte !== O) { continue; } slice.skip(-4); if (word === OGGS) { // We have found the capture pattern return true; } slice.skip(1); } return false; };