chromium/tools/mac/icons/additional_tools/maketoc.py

#!/usr/bin/env python

# Copyright 2020 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# maketoc inserts a table of contents (TOC) into an icns file if one does not
# exist, or verifies it if it is present. The icns files produced by makeicns
# do not contain TOCs. This script will add them.
#
# TOCs are not required, but are produced by recent icns-writing tools like
# icnsutil.

import struct
import sys


def _interpret_header(header):
    assert len(header) == 8
    (magic, length) = struct.unpack('>4sI', header)
    assert length >= len(header)
    return (magic, length)


def _read_header(file):
    return _interpret_header(file.read(8))


def _write_header(file, magic, length):
    header = struct.pack('>4sI', magic, length)
    file.write(header)


def make_toc(path):
    file = open(path, 'r+b')
    (file_magic, file_length) = _read_header(file)
    assert file_magic == b'icns'

    read_length = file.tell()
    chunk_list = []
    chunk_dict = {}

    for chunk_header in iter(lambda: file.read(8), b''):
        (chunk_magic, chunk_length) = _interpret_header(chunk_header)

        assert chunk_magic not in chunk_dict
        chunk_list.append(chunk_magic)
        chunk_dict[chunk_magic] = chunk_length
        read_length += chunk_length
        file.seek(chunk_length - 8, 1)

    assert read_length == file_length

    if b'TOC ' in chunk_dict:
        # TOC exists, verify it.
        assert chunk_list[0] == b'TOC '

        toc_length = chunk_dict[b'TOC ']
        assert toc_length % 8 == 0

        chunk_count = (toc_length - 8) // 8
        assert chunk_count == len(chunk_list) - 1

        # The TOC just duplicates each chunk's header in sequence except for
        # its own.
        file.seek(16)
        for i in range(0, chunk_count):
            (chunk_magic, chunk_length) = _read_header(file)
            assert chunk_magic == chunk_list[i + 1]
            assert chunk_length == chunk_dict[chunk_magic]
    else:
        # TOC is not present, create it.

        # Update the file header with the new length.
        toc_length = 8 + len(chunk_list) * 8
        file_length += toc_length
        file.seek(0)
        _write_header(file, b'icns', file_length)
        after_file_header = file.tell()

        remainder = file.read()
        file.seek(after_file_header)

        # The TOC just duplicates each chunk's header in sequence except for
        # its own.
        _write_header(file, b'TOC ', toc_length)
        for chunk_magic in chunk_list:
            _write_header(file, chunk_magic, chunk_dict[chunk_magic])

        file.write(remainder)

        assert file_length == file.tell()

    file.close()


def main(args):
    for path in args:
        print(path)
        make_toc(path)
    return 0


if __name__ == '__main__':
    sys.exit(main(sys.argv[1:]))