ANSI_FORMAT_BASE = "\x1b[{}m"
ANSI_FORMAT_RESET_CMD = "0"
ANSI_FORMAT_RESET = ANSI_FORMAT_BASE.format( ANSI_FORMAT_RESET_CMD )
ANSI_FORMAT_FOREGROUND_CMD = "38;2;{};{};{}"
ANSI_FORMAT_BACKGROUND_CMD = "48;2;{};{};{}"
ANSI_FORMAT_FOREGROUND_XTERM_CMD = "38;5;{}"
ANSI_FORMAT_BACKGROUND_XTERM_CMD = "48;5;{}"
ANSI_FORMAT_BOLD_CMD = "1"
ANSI_FORMAT_FAINT_CMD = "2"
ANSI_FORMAT_ITALIC_CMD = "3"
ANSI_FORMAT_UNDERLINE_CMD = "4"
ANSI_FORMAT_BLINK_CMD = "5"
ANSI_FORMAT_INVERTED_CMD = "7"
ANSI_ERASE_BASE = "\x1b[{}J"
ANSI_ERASE_SCREEN = ANSI_ERASE_BASE.format( 2 )
ANSI_ERASE_SCROLLBACK = ANSI_ERASE_BASE.format( 3 )
ANSI_CURSOR_SET_POSITION = "\x1b[{};{}H"
ANSI_CURSOR_MOVE_UP = "\x1b[{}A"
ANSI_CURSOR_MOVE_DOWN = "\x1b[{}B"
ANSI_CURSOR_MOVE_FORWARD = "\x1b[{}C"
ANSI_CURSOR_MOVE_BACKWARD = "\x1b[{}D"
BAR_VERT = " ▁▂▃▄▅▆▇█"
BAR_HORIZ = " ▏▎▍▌▋▊▉█"
BYTE_GLYPH_MAP = """ ☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼ !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~⌂ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñÑªº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┘┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■ """
BYTE_COLOUR_MAP = (12,) + (14,) * 32 + (11,) * 94 + (14,) * 128 + (12,)
BYTE_ESCAPE_MAP = [format_escape( x ) for x in BYTE_COLOUR_MAP]
HIGHLIGHT_COLOUR = 9
def format_address( offset: int, end: int, address_base_offset: int ):
def format_escape(
    foreground: colour.ColourType = None,
    background: colour.ColourType = None,
    bold: bool = False,
    faint: bool = False,
    italic: bool = False,
    underline: bool = False,
    blink: bool = False,
    inverted: bool = False,
):
def format_string(
    string: str,
    foreground: colour.ColourType = None,
    background: colour.ColourType = None,
    reset: bool = True,
    bold: bool = False,
    faint: bool = False,
    italic: bool = False,
    underline: bool = False,
    blink: bool = False,
    inverted: bool = False,
) -> str:
def format_pixels(
    top: colour.ColourType,
    bottom: colour.ColourType,
    reset: bool = True,
    repeat: int = 1,
):
def format_bar_graph_iter(
    data: Sequence[int],
    width: int = 64,
    height: int = 12,
    y_min: Optional[int] = None,
    y_max: Optional[int] = None,
):
def format_image_iter(
    data_fetch: Callable[[int, int, int], colour.ColourType],
    x_start: int = 0,
    y_start: int = 0,
    width: int = 32,
    height: int = 32,
    frame: Union[int, Sequence[int]] = 0,
    columns: int = 1,
    downsample: int = 1,
):
def format_hexdump_line(
    source: BytesReadType,
    offset: int,
    end: Optional[int] = None,
    major_len: int = 8,
    minor_len: int = 4,
    colour: bool = True,
    prefix: str = "",
    highlight_addr: Optional[int] = None,
    highlight_map: Optional[Dict[int, colour.ColourType]] = None,
    address_base_offset: int = 0,
    show_offsets: bool = True,
    show_glyphs: bool = True,
):
    def get_colour( index: int ):
    def get_glyph():
def format_histogram_line(
    buckets: Sequence[int], palette: Optional[Sequence[colour.ColourType]] = None
) -> str:
def format_histdump_line(
    source: BytesReadType,
    offset: int,
    length: Optional[int] = None,
    end: Optional[int] = None,
    width: int = 64,
    address_base_offset: int = 0,
    palette: Optional[Sequence[colour.ColourType]] = None,
):
    def __init__(
        self,
        source: BytesReadType,
        start: Optional[int] = None,
        end: Optional[int] = None,
        length: Optional[int] = None,
        major_len: int = 8,
        minor_len: int = 4,
        use_colour: bool = True,
        address_base: Optional[int] = None,
        before: int = 2,
        after: int = 2,
        title: Optional[str] = None,
    ):
    def update( self, marker: int ):
    def add_span( self, span: Tuple[int, int] ):
    def flush( self, final: bool = False ):
class HexdumpHighlightBuffer( object ): def enable_logging( level: str = "WARNING" ) -> None:
def grep_iter(
    pattern: str,
    source: BytesReadType,
    encoding: str = "utf8",
    fixed_string: bool = False,
    hex_format: bool = False,
    ignore_case: bool = False,
) -> Iterator[Match[bytes]]:
def grep(
    pattern: str,
    source: BytesReadType,
    encoding: str = "utf8",
    fixed_string: bool = False,
    hex_format: bool = False,
    ignore_case: bool = False,
) -> List[Match[bytes]]:
def find_iter(
    substring: Union[str, BytesReadType, Sequence[BytesReadType], Sequence[str]],
    source: Union[str, BytesReadType],
    start: Optional[int] = None,
    end: Optional[int] = None,
    length: Optional[int] = None,
    overlap: bool = False,
    ignore_case: bool = False,
    encodings: Union[List[str], Literal["all"]] = ["utf_8"],
    brute: bool = False,
    char_size: int = 1,
) -> Iterator[Any]:
def find(
    substring: Union[str, BytesReadType, Sequence[str], Sequence[BytesReadType]],
    source: Union[str, BytesReadType],
    start: Optional[int] = None,
    end: Optional[int] = None,
    length: Optional[int] = None,
    overlap: bool = False,
    ignore_case: bool = False,
    encodings: Union[str, List[str]] = ["utf_8"],
    brute: bool = False,
    char_size: int = 1,
):
def grepdump_iter(
    pattern: str,
    source: BytesReadType,
    start: Optional[int] = None,
    end: Optional[int] = None,
    length: Optional[int] = None,
    encoding: str = "utf8",
    fixed_string: bool = False,
    hex_format: bool = False,
    ignore_case: bool = False,
    major_len: int = 8,
    minor_len: int = 4,
    colour: bool = True,
    address_base: Optional[int] = None,
    before: int = 2,
    after: int = 2,
    title: Optional[str] = None,
    format: Literal["hex", "text", "json"] = "hex",
) -> Iterator[str]:
def grepdump(
    pattern: str,
    source: BytesReadType,
    start: Optional[int] = None,
    end: Optional[int] = None,
    length: Optional[int] = None,
    encoding: str = "utf8",
    fixed_string: bool = False,
    hex_format: bool = False,
    ignore_case: bool = False,
    major_len: int = 8,
    minor_len: int = 4,
    colour: bool = True,
    address_base: Optional[int] = None,
    before: int = 2,
    after: int = 2,
    title: Optional[str] = None,
    format: Literal["hex", "text", "json"] = "hex",
):
def search_iter(
    pattern: str,
    source: "Block",
    prefix: str = "source",
    depth: Optional[int] = None,
    encoding: str = "utf8",
    fixed_string: bool = False,
    hex_format: bool = False,
    ignore_case: bool = False,
) -> Iterator[str]:
    def check_field(
        offset: int, size: int, data: Union[Block, Chunk], pref: str
    ) -> Iterator[str]:
def search(
    pattern: str,
    source: "Block",
    prefix: str = "source",
    depth: Optional[int] = None,
    encoding: str = "utf8",
    fixed_string: bool = False,
    hex_format: bool = False,
    ignore_case: bool = False,
) -> List[str]:
def finddump_iter(
    substring,
    source,
    start=None,
    end=None,
    length=None,
    overlap=False,
    ignore_case=False,
    encodings=["utf_8"],
    brute=False,
    char_size=1,
    major_len=8,
    minor_len=4,
    colour=True,
    address_base=None,
    before=2,
    after=2,
    title=None,
    format="hex",
):
def finddump(
    substring,
    source,
    start=None,
    end=None,
    length=None,
    overlap=False,
    ignore_case=False,
    encodings=["utf_8"],
    brute=False,
    char_size=1,
    major_len=8,
    minor_len=4,
    colour=True,
    address_base=None,
    before=2,
    after=2,
    title=None,
    format="hex",
):
def diff_iter(
    source1: BytesReadType,
    source2: BytesReadType,
    start: Optional[int] = None,
    end: Optional[int] = None,
) -> Iterator[Tuple[int, int]]:
def diff(
    source1: BytesReadType,
    source2: BytesReadType,
    start: Optional[int] = None,
    end: Optional[int] = None,
) -> List[Tuple[int, int]]:
def objdiff_iter(
    source1: Any, source2: Any, prefix: str = "source", depth: Optional[int] = None
) -> Iterator[Tuple[str, Any, Any]]:
    def abbr( src: Any ):
def objdiff(
    source1: Any, source2: Any, prefix: str = "source", depth: Optional[int] = None
) -> List[Tuple[str, Any, Any]]:
def objdiffdump_iter(
    source1: Any, source2: Any, prefix: str = "source", depth: Optional[int] = None
):
def objdiffdump(
    source1: Any, source2: Any, prefix: str = "source", depth: Optional[int] = None
):
def histdump_iter(
    source: BytesReadType,
    start: Optional[int] = None,
    end: Optional[int] = None,
    length: Optional[int] = None,
    samples: int = 0x10000,
    width: int = 64,
    address_base: Optional[int] = None,
) -> Iterator[str]:
def histdump(
    source: BytesReadType,
    start: Optional[int] = None,
    end: Optional[int] = None,
    length: Optional[int] = None,
    samples: int = 0x10000,
    width: int = 64,
    address_base: Optional[int] = None,
):
def hexdump_iter(
    source: BytesReadType,
    start: Optional[int] = None,
    end: Optional[int] = None,
    length: Optional[int] = None,
    major_len: int = 8,
    minor_len: int = 4,
    colour: bool = True,
    address_base: Optional[int] = None,
    show_offsets: bool = True,
    show_glyphs: bool = True,
):
def hexdump(
    source: BytesReadType,
    start: Optional[int] = None,
    end: Optional[int] = None,
    length: Optional[int] = None,
    major_len: int = 8,
    minor_len: int = 4,
    colour: bool = True,
    address_base: Optional[int] = None,
    show_offsets: bool = True,
    show_glyphs: bool = True,
):
def diffdump_iter(
    source1: BytesReadType,
    source2: BytesReadType,
    start: Optional[int] = None,
    end: Optional[int] = None,
    length: Optional[int] = None,
    major_len: int = 8,
    minor_len: int = 4,
    colour: bool = True,
    before: int = 2,
    after: int = 2,
    address_base: Optional[int] = None,
) -> Iterator[str]:
def diffdump(
    source1: BytesReadType,
    source2: BytesReadType,
    start: Optional[int] = None,
    end: Optional[int] = None,
    length: Optional[int] = None,
    major_len: int = 8,
    minor_len: int = 4,
    colour: bool = True,
    before: int = 2,
    after: int = 2,
    address_base: Optional[int] = None,
):
def stats(
    source: BytesReadType,
    start: Optional[int] = None,
    end: Optional[int] = None,
    length: Optional[int] = None,
    width: int = 64,
    height: int = 12,
):
def pixdump_iter(
    source: BytesReadType,
    start: Optional[int] = None,
    end: Optional[int] = None,
    length: Optional[int] = None,
    width: int = 64,
    height: Optional[int] = None,
    palette: Optional[Sequence[ColourType]] = None,
) -> Iterator[str]:
    def data_fetch( x_pos: int, y_pos: int, _: int ) -> ColourType:
def pixdump(
    source: BytesReadType,
    start: Optional[int] = None,
    end: Optional[int] = None,
    length: Optional[int] = None,
    width: int = 64,
    height: Optional[int] = None,
    palette: Optional[Sequence[ColourType]] = None,
):
def pixdump_sweep(
    source: BytesReadType,
    range: Sequence[int] = (64,),
    delay: int = 0,
    start: Optional[int] = None,
    end: Optional[int] = None,
    length: Optional[int] = None,
    height: Optional[int] = None,
    palette: Optional[Sequence[ColourType]] = None,
):
DIFF_COLOUR_MAP = (9, 10) BYTE_REVERSE = bytes.fromhex(
    "008040c020a060e0109050d030b070f0"
    "088848c828a868e8189858d838b878f8"
    "048444c424a464e4149454d434b474f4"
    "0c8c4ccc2cac6cec1c9c5cdc3cbc7cfc"
    "028242c222a262e2129252d232b272f2"
    "0a8a4aca2aaa6aea1a9a5ada3aba7afa"
    "068646c626a666e6169656d636b676f6"
    "0e8e4ece2eae6eee1e9e5ede3ebe7efe"
    "018141c121a161e1119151d131b171f1"
    "098949c929a969e9199959d939b979f9"
    "058545c525a565e5159555d535b575f5"
    "0d8d4dcd2dad6ded1d9d5ddd3dbd7dfd"
    "038343c323a363e3139353d333b373f3"
    "0b8b4bcb2bab6beb1b9b5bdb3bbb7bfb"
    "078747c727a767e7179757d737b777f7"
    "0f8f4fcf2faf6fef1f9f5fdf3fbf7fff"
)
BIT_MASK = [(1 << size) - 1 for size in range( 0, 65 )]
def reverse_bits( number: int, size: int = 8 ) -> int:
def read_bits(
    buffer: BytesReadType,
    byte_offset: int,
    bit_offset: int,
    size: int,
    bytes_reverse: bool = False,
    bit_endian: str = "big",
    io_endian: str = "big",
):
def write_bits(
    value: int,
    buffer: BytesWriteType,
    byte_offset: int,
    bit_offset: int,
    size: int,
    bytes_reverse: bool = False,
    bit_endian: str = "big",
    io_endian: str = "big",
):
def reverse_bytes( buffer: BytesReadType ) -> bytes:
def unpack_bits( byte: int ) -> int:
def pack_bits( longbits: int ) -> int:
    def __init__(
        self,
        buffer: Optional[BytesReadType] = None,
        start_offset: Optional[Union[int, Tuple[int, int]]] = None,
        bytes_reverse: bool = False,
        bit_endian: str = "big",
        io_endian: str = "big",
    ):
    def tell( self ):
    def read( self, count: int ) -> int:
    def write( self, value: int, count: int ):
    def seek( self, offset: Union[int, Tuple[int, int]], origin: str = "start" ):
    def in_bounds( self ):
    def get_buffer( self ):
class BitStream( object ): class BaseColour( object ):
    r = 0.0
    g = 0.0
    b = 0.0
    a = 1.0

    @property
    def r_8( self ) -> int:
        return round( self.r * 255 )

    @r_8.setter
    def r_8( self, value: int ) -> None:
        self.r = value / 255

    @property
    def g_8( self ) -> int:
        return round( self.g * 255 )

    @g_8.setter
    def g_8( self, value: int ) -> None:
        self.g = value / 255

    @property
    def b_8( self ) -> int:
        return round( self.b * 255 )

    @b_8.setter
    def b_8( self, value: int ) -> None:
        self.b = value / 255

    @property
    def a_8( self ) -> int:
        return round( self.a * 255 )

    @a_8.setter
    def a_8( self, value: int ) -> None:
        self.a = value / 255

    @property
    def chroma( self ) -> float:
        M = max( self.r, self.g, self.b )
        m = min( self.r, self.g, self.b )
        return M - m

    @property
    def luma( self ) -> float:
        return 0.299 * self.r + 0.587 * self.g + 0.114 * self.b

    @property
    def rgba( self ) -> Tuple[int, int, int, int]:
        return (self.r_8, self.g_8, self.b_8, self.a_8)

    def set_rgb( self, r_8: int, g_8: int, b_8: int ) -> BaseColour:
        self.r_8 = r_8
        self.g_8 = g_8
        self.b_8 = b_8
        return self

    def set_a( self, a_8: int ) -> BaseColour:
        self.a_8 = a_8
        return self

    def set_rgba( self, r_8: int, g_8: int, b_8: int, a_8: int ) -> BaseColour:
        self.r_8 = r_8
        self.g_8 = g_8
        self.b_8 = b_8
        self.a_8 = a_8
        return self

    def clone_data( self, source: BaseColour ) -> BaseColour:
        self.r_8 = source.r_8
        self.g_8 = source.g_8
        self.b_8 = source.b_8
        self.a_8 = source.a_8
        return self

    @property
    def repr( self ) -> str:
        return f"#{self.r_8:02X}{self.g_8:02X}{self.b_8:02X}{self.a_8:02X}"

    def ansi_format( self, text: Optional[str] = None ) -> str:
        from mrcrowbar.ansi import format_string

        if text is None:
            text = f" {self.repr} "
        colour = White() if self.luma < 0.5 else Black()
        return format_string( text, colour, self )

    def print( self, text: Optional[str] = None ) -> None:
        print( self.ansi_format( text ) )

    def __eq__( self, other: Any ) -> bool:
        if isinstance( other, BaseColour ):
            return (
                (self.r_8 == other.r_8)
                and (self.g_8 == other.g_8)
                and (self.b_8 == other.b_8)
                and (self.a_8 == other.a_8)
            )
        return False class Black( BaseColour ):
    r = 0.0
    g = 0.0
    b = 0.0 class White( BaseColour ):
    r = 1.0
    g = 1.0
    b = 1.0 class Transparent( BaseColour ):
    a = 0.0 def from_palette_bytes(
    palette_bytes: BytesReadType,
    stride: int = 3,
    order: Union[Tuple[int], Tuple[int, int, int], Tuple[int, int, int, int]] = (
        0,
        1,
        2,
    ),
) -> List[BaseColour]:
    assert stride >= max( order )
    assert min( order ) >= 0
    assert len( order ) in (1, 3, 4)
    result: List[BaseColour] = []
    for i in range( math.floor( len( palette_bytes ) / stride ) ):
        if len( order ) == 1:
            result.append(
                BaseColour().set_rgb(
                    palette_bytes[stride * i + order[0]],
                    palette_bytes[stride * i + order[0]],
                    palette_bytes[stride * i + order[0]],
                )
            )
        elif len( order ) == 3:
            result.append(
                BaseColour().set_rgb(
                    palette_bytes[stride * i + order[0]],
                    palette_bytes[stride * i + order[1]],
                    palette_bytes[stride * i + order[2]],
                )
            )
        elif len( order ) == 4:
            result.append(
                BaseColour().set_rgba(
                    palette_bytes[stride * i + order[0]],
                    palette_bytes[stride * i + order[1]],
                    palette_bytes[stride * i + order[2]],
                    palette_bytes[stride * i + order[3]],
                )
            )
    return result def to_palette_bytes(
    palette: Sequence[BaseColour], stride: int = 3, order: Sequence[int] = (0, 1, 2)
) -> bytes:
    assert stride >= max( order )
    assert min( order ) >= 0
    blanks = tuple( (0 for _ in range( stride - max( order ) - 1 )) )

    def channel( colour: BaseColour, order: int ) -> int:
        if order == 0:
            return colour.r_8
        elif order == 1:
            return colour.g_8
        elif order == 2:
            return colour.b_8
        elif order == 3:
            return colour.a_8
        raise ValueError( f"{order} is not a valid order" )

    return bytes(
        itertools.chain(
            *(tuple( (channel( c, o ) for o in order) ) + blanks for c in palette)
        )
    ) TEST_PALETTE = gradient_to_palette()
from mrcrowbar import models as mrc
from mrcrowbar import ansi, utils, bits
from mrcrowbar.colour import (
    BaseColour,
    Black,
    White,
    Transparent,
    from_palette_bytes,
    to_palette_bytes,
    TEST_PALETTE,
)
    from PIL import Image as PILImage
from array import array
import itertools
import collections
import math
import sys
import io
import logging

try:
except ImportError:
    PILImage = None


logger = logging.getLogger( __name__ )



class Colour( BaseColour, mrc.Block ):