// signature.mbt
//
// 7z 起始头是定长 32 字节:
//
//   offset  长度  字段
//   0       6     签名 37 7A BC AF 27 1C
//   6       1     主版本号(规范要求 0)
//   7       1     次版本号
//   8       4     StartHeaderCRC(覆盖 12..32)
//   12      8     下一头偏移(相对签名头末尾;绝对位置 = 32 + offset)
//   20      8     下一头长度(可能已被编码/压缩,故是编码后的长度)
//   28      4     下一头 CRC-32
//
// 依据:py7zr《.7z format specification》"Signature Header" 一节
// (https://py7zr.readthedocs.io/en/latest/archive_format.html)。
// 实测 bsdtar/libarchive 把次版本写作 0.3、官方 7-Zip 写作 0.4,
// 因此这里只要求主版本为 0,次版本仅记录、不做限制。
//
// 本增量(F1)只做"证明头数据库真的存在且完整"这件事:定位 + 边界 + CRC。
// 头数据库的内容解析属于 F2/F3。

///|
/// 7z 文件签名(6 字节)。
pub let sevenz_signature : Bytes = b"\x37\x7A\xBC\xAF\x27\x1C"

///|
/// 起始头的定长字节数。
pub let sevenz_start_header_size : Int32 = 32

///|
/// 已校验的起始头字段。
pub struct StartHeader {
  /// 主版本号(本实现要求为 0)。
  major : Int32
  /// 次版本号(仅记录)。
  minor : Int32
  /// 下一头相对签名头末尾的偏移。
  next_header_offset : Int64
  /// 下一头长度(编码后的字节数)。
  next_header_size : Int64
  /// 下一头 CRC-32(以无符号 32 位值存放)。
  next_header_crc : Int64
}

///|
/// 下一头在归档中的绝对起点。
pub fn StartHeader::absolute_offset(self : StartHeader) -> Int64 {
  self.next_header_offset + sevenz_start_header_size.to_int64()
}

///|
/// 下一头在归档中的绝对终点(不含)。
pub fn StartHeader::absolute_end(self : StartHeader) -> Int64 {
  self.absolute_offset() + self.next_header_size
}

///|
/// 已打开的 7z 归档。F1 保证起始头与下一头区间可用;条目列表与解压在后续增量补充。
pub struct SevenZ {
  start_header : StartHeader
  data : Bytes
}

///|
/// 起始头。
pub fn SevenZ::start_header(self : SevenZ) -> StartHeader {
  self.start_header
}

///|
/// 归档总字节数。
pub fn SevenZ::size(self : SevenZ) -> Int32 {
  self.data.length()
}

///|
/// 原始归档字节。
pub fn SevenZ::bytes(self : SevenZ) -> Bytes {
  self.data
}

///|
/// 解析并校验 32 字节起始头,并证明下一头数据库完整落在文件内、CRC 正确。
///
/// 输入:完整归档字节(至少 32 字节)。
/// 输出:`SevenZ`,其起始头字段已全部校验通过。
/// 失败:截断 / 签名不符 / 主版本非 0 / 两处 CRC 不符 / 下一头区间越界。
pub fn SevenZ::open(data : Bytes) -> SevenZ raise SevenZError {
  let r = ByteReader::new(data)
  r.require(sevenz_start_header_size)
  let signature = r.read_bytes(6)
  if !signature.equal(sevenz_signature) {
    raise BadSignature(found=hex_string(signature))
  }
  let major = r.read_byte()
  let minor = r.read_byte()
  if major != 0 {
    raise UnsupportedVersion(major~, minor~)
  }
  let start_header_crc = r.read_u32()
  let next_header_offset = r.read_u64()
  let next_header_size = r.read_u64()
  let next_header_crc = r.read_u32()
  let actual_start_crc = crc32(data, 12, sevenz_start_header_size)
  if actual_start_crc != start_header_crc {
    raise BadCrc(
      what="start header",
      expected=start_header_crc,
      actual=actual_start_crc,
    )
  }
  let header_start = next_header_offset + sevenz_start_header_size.to_int64()
  let header_end = header_start + next_header_size
  if header_start < sevenz_start_header_size.to_int64() ||
    header_end > data.length().to_int64() {
    raise Malformed(
      reason="下一头区间越界(offset=" +
        next_header_offset.to_string() +
        ", size=" +
        next_header_size.to_string() +
        ", file=" +
        data.length().to_string() +
        ")",
    )
  }
  let actual_header_crc = crc32(
    data,
    header_start.to_int(),
    header_end.to_int(),
  )
  if actual_header_crc != next_header_crc {
    raise BadCrc(
      what="next header",
      expected=next_header_crc,
      actual=actual_header_crc,
    )
  }
  {
    start_header: {
      major,
      minor,
      next_header_offset,
      next_header_size,
      next_header_crc,
    },
    data,
  }
}