Remove leading underscores where they don't make much sense

The leading underscore is meant to distinguish private (for internal
use only) APIs from public (for external use) APIs. One can argue about
where the line between public and private should be, but if something
is used from other modules (as with read_variable_length_integer) it's
not really private IMHO.

In scripts (like __main__) it also doesn't make much sense to use
leading underscores, because the entire file is never meant to be used
by external code.
This commit is contained in:
dgelessus 2019-09-30 19:01:31 +02:00
parent cb868b8005
commit 3a72bd3406
4 changed files with 56 additions and 56 deletions

View File

@ -26,12 +26,12 @@ _REZ_ATTR_NAMES = {
} }
F = typing.TypeVar("F", bound=enum.Flag) F = typing.TypeVar("F", bound=enum.Flag)
def _decompose_flags(value: F) -> typing.Sequence[F]: def decompose_flags(value: F) -> typing.Sequence[F]:
"""Decompose an enum.Flags instance into separate enum constants.""" """Decompose an enum.Flags instance into separate enum constants."""
return [bit for bit in type(value) if bit in value] return [bit for bit in type(value) if bit in value]
def _is_printable(char: str) -> bool: def is_printable(char: str) -> bool:
"""Determine whether a character is printable for our purposes. """Determine whether a character is printable for our purposes.
We mainly use Python's definition of printable (i. e. everything that Unicode does not consider a separator or "other" character). However, we also treat U+F8FF as printable, which is the private use codepoint used for the Apple logo character. We mainly use Python's definition of printable (i. e. everything that Unicode does not consider a separator or "other" character). However, we also treat U+F8FF as printable, which is the private use codepoint used for the Apple logo character.
@ -39,7 +39,7 @@ def _is_printable(char: str) -> bool:
return char.isprintable() or char == "\uf8ff" return char.isprintable() or char == "\uf8ff"
def _bytes_unescape(string: str) -> bytes: def bytes_unescape(string: str) -> bytes:
"""Convert a string containing text (in _TEXT_ENCODING) and hex escapes to a bytestring. """Convert a string containing text (in _TEXT_ENCODING) and hex escapes to a bytestring.
(We implement our own unescaping mechanism here to not depend on any of Python's string/bytes escape syntax.) (We implement our own unescaping mechanism here to not depend on any of Python's string/bytes escape syntax.)
@ -65,7 +65,7 @@ def _bytes_unescape(string: str) -> bytes:
return bytes(out) return bytes(out)
def _bytes_escape(bs: bytes, *, quote: typing.Optional[str]=None) -> str: def bytes_escape(bs: bytes, *, quote: typing.Optional[str]=None) -> str:
"""Convert a bytestring to a string (using _TEXT_ENCODING), with non-printable characters hex-escaped. """Convert a bytestring to a string (using _TEXT_ENCODING), with non-printable characters hex-escaped.
(We implement our own escaping mechanism here to not depend on Python's str or bytes repr.) (We implement our own escaping mechanism here to not depend on Python's str or bytes repr.)
@ -75,14 +75,14 @@ def _bytes_escape(bs: bytes, *, quote: typing.Optional[str]=None) -> str:
for byte, char in zip(bs, bs.decode(_TEXT_ENCODING)): for byte, char in zip(bs, bs.decode(_TEXT_ENCODING)):
if char in {quote, "\\"}: if char in {quote, "\\"}:
out.append(f"\\{char}") out.append(f"\\{char}")
elif _is_printable(char): elif is_printable(char):
out.append(char) out.append(char)
else: else:
out.append(f"\\x{byte:02x}") out.append(f"\\x{byte:02x}")
return "".join(out) return "".join(out)
def _filter_resources(rf: api.ResourceFile, filters: typing.Sequence[str]) -> typing.List[api.Resource]: def filter_resources(rf: api.ResourceFile, filters: typing.Sequence[str]) -> typing.List[api.Resource]:
matching: typing.MutableMapping[typing.Tuple[bytes, int], api.Resource] = collections.OrderedDict() matching: typing.MutableMapping[typing.Tuple[bytes, int], api.Resource] = collections.OrderedDict()
for filter in filters: for filter in filters:
@ -96,7 +96,7 @@ def _filter_resources(rf: api.ResourceFile, filters: typing.Sequence[str]) -> ty
matching[res.type, res.id] = res matching[res.type, res.id] = res
elif filter[0] == filter[-1] == "'": elif filter[0] == filter[-1] == "'":
try: try:
resources = rf[_bytes_unescape(filter[1:-1])] resources = rf[bytes_unescape(filter[1:-1])]
except KeyError: except KeyError:
continue continue
@ -114,7 +114,7 @@ def _filter_resources(rf: api.ResourceFile, filters: typing.Sequence[str]) -> ty
if not restype_str[0] == restype_str[-1] == "'": if not restype_str[0] == restype_str[-1] == "'":
raise ValueError( raise ValueError(
f"Invalid filter {filter!r}: Resource type is not a single-quoted type identifier: {restype_str!r}") f"Invalid filter {filter!r}: Resource type is not a single-quoted type identifier: {restype_str!r}")
restype = _bytes_unescape(restype_str[1:-1]) restype = bytes_unescape(restype_str[1:-1])
if len(restype) != 4: if len(restype) != 4:
raise ValueError( raise ValueError(
@ -130,7 +130,7 @@ def _filter_resources(rf: api.ResourceFile, filters: typing.Sequence[str]) -> ty
continue continue
if resid_str[0] == resid_str[-1] == '"': if resid_str[0] == resid_str[-1] == '"':
name = _bytes_unescape(resid_str[1:-1]) name = bytes_unescape(resid_str[1:-1])
for res in resources.values(): for res in resources.values():
if res.name == name: if res.name == name:
@ -155,7 +155,7 @@ def _filter_resources(rf: api.ResourceFile, filters: typing.Sequence[str]) -> ty
return list(matching.values()) return list(matching.values())
def _hexdump(data: bytes) -> None: def hexdump(data: bytes) -> None:
last_line = None last_line = None
asterisk_shown = False asterisk_shown = False
for i in range(0, len(data), 16): for i in range(0, len(data), 16):
@ -177,18 +177,18 @@ def _hexdump(data: bytes) -> None:
if data: if data:
print(f"{len(data):08x}") print(f"{len(data):08x}")
def _raw_hexdump(data: bytes) -> None: def raw_hexdump(data: bytes) -> None:
for i in range(0, len(data), 16): for i in range(0, len(data), 16):
print(" ".join(f"{byte:02x}" for byte in data[i:i + 16])) print(" ".join(f"{byte:02x}" for byte in data[i:i + 16]))
def _translate_text(data: bytes) -> str: def translate_text(data: bytes) -> str:
return data.decode(_TEXT_ENCODING).replace("\r", "\n") return data.decode(_TEXT_ENCODING).replace("\r", "\n")
def _describe_resource(res: api.Resource, *, include_type: bool, decompress: bool) -> str: def describe_resource(res: api.Resource, *, include_type: bool, decompress: bool) -> str:
id_desc_parts = [f"{res.id}"] id_desc_parts = [f"{res.id}"]
if res.name is not None: if res.name is not None:
name = _bytes_escape(res.name, quote='"') name = bytes_escape(res.name, quote='"')
id_desc_parts.append(f'"{name}"') id_desc_parts.append(f'"{name}"')
id_desc = ", ".join(id_desc_parts) id_desc = ", ".join(id_desc_parts)
@ -208,7 +208,7 @@ def _describe_resource(res: api.Resource, *, include_type: bool, decompress: boo
length_desc = f"{res.length_raw} bytes" length_desc = f"{res.length_raw} bytes"
content_desc_parts.append(length_desc) content_desc_parts.append(length_desc)
attrs = _decompose_flags(res.attributes) attrs = decompose_flags(res.attributes)
if attrs: if attrs:
content_desc_parts.append(" | ".join(attr.name for attr in attrs)) content_desc_parts.append(" | ".join(attr.name for attr in attrs))
@ -216,11 +216,11 @@ def _describe_resource(res: api.Resource, *, include_type: bool, decompress: boo
desc = f"({id_desc}): {content_desc}" desc = f"({id_desc}): {content_desc}"
if include_type: if include_type:
restype = _bytes_escape(res.type, quote="'") restype = bytes_escape(res.type, quote="'")
desc = f"'{restype}' {desc}" desc = f"'{restype}' {desc}"
return desc return desc
def _parse_args() -> argparse.Namespace: def parse_args() -> argparse.Namespace:
ap = argparse.ArgumentParser( ap = argparse.ArgumentParser(
add_help=False, add_help=False,
fromfile_prefix_chars="@", fromfile_prefix_chars="@",
@ -259,13 +259,13 @@ def _parse_args() -> argparse.Namespace:
ns = ap.parse_args() ns = ap.parse_args()
return ns return ns
def _show_header_data(data: bytes, *, format: str) -> None: def show_header_data(data: bytes, *, format: str) -> None:
if format == "dump": if format == "dump":
_hexdump(data) hexdump(data)
elif format == "dump-text": elif format == "dump-text":
print(_translate_text(data)) print(translate_text(data))
elif format == "hex": elif format == "hex":
_raw_hexdump(data) raw_hexdump(data)
elif format == "raw": elif format == "raw":
sys.stdout.buffer.write(data) sys.stdout.buffer.write(data)
elif format == "derez": elif format == "derez":
@ -274,7 +274,7 @@ def _show_header_data(data: bytes, *, format: str) -> None:
else: else:
raise ValueError(f"Unhandled output format: {format}") raise ValueError(f"Unhandled output format: {format}")
def _show_filtered_resources(resources: typing.Sequence[api.Resource], format: str, decompress: bool) -> None: def show_filtered_resources(resources: typing.Sequence[api.Resource], format: str, decompress: bool) -> None:
if not resources: if not resources:
if format in ("dump", "dump-text"): if format in ("dump", "dump-text"):
print("No resources matched the filter") print("No resources matched the filter")
@ -297,19 +297,19 @@ def _show_filtered_resources(resources: typing.Sequence[api.Resource], format: s
if format in ("dump", "dump-text"): if format in ("dump", "dump-text"):
# Human-readable info and hex or text dump # Human-readable info and hex or text dump
desc = _describe_resource(res, include_type=True, decompress=decompress) desc = describe_resource(res, include_type=True, decompress=decompress)
print(f"Resource {desc}:") print(f"Resource {desc}:")
if format == "dump": if format == "dump":
_hexdump(data) hexdump(data)
elif format == "dump-text": elif format == "dump-text":
print(_translate_text(data)) print(translate_text(data))
else: else:
raise AssertionError(f"Unhandled format: {format!r}") raise AssertionError(f"Unhandled format: {format!r}")
print() print()
elif format == "hex": elif format == "hex":
# Data only as hex # Data only as hex
_raw_hexdump(data) raw_hexdump(data)
elif format == "raw": elif format == "raw":
# Data only as raw bytes # Data only as raw bytes
@ -317,7 +317,7 @@ def _show_filtered_resources(resources: typing.Sequence[api.Resource], format: s
elif format == "derez": elif format == "derez":
# Like DeRez with no resource definitions # Like DeRez with no resource definitions
attrs = list(_decompose_flags(res.attributes)) attrs = list(decompose_flags(res.attributes))
if decompress and api.ResourceAttrs.resCompressed in attrs: if decompress and api.ResourceAttrs.resCompressed in attrs:
attrs.remove(api.ResourceAttrs.resCompressed) attrs.remove(api.ResourceAttrs.resCompressed)
@ -334,12 +334,12 @@ def _show_filtered_resources(resources: typing.Sequence[api.Resource], format: s
parts = [str(res.id)] parts = [str(res.id)]
if res.name is not None: if res.name is not None:
name = _bytes_escape(res.name, quote='"') name = bytes_escape(res.name, quote='"')
parts.append(f'"{name}"') parts.append(f'"{name}"')
parts += attr_descs parts += attr_descs
restype = _bytes_escape(res.type, quote="'") restype = bytes_escape(res.type, quote="'")
print(f"data '{restype}' ({', '.join(parts)}{attrs_comment}) {{") print(f"data '{restype}' ({', '.join(parts)}{attrs_comment}) {{")
for i in range(0, len(data), 16): for i in range(0, len(data), 16):
@ -362,16 +362,16 @@ def _show_filtered_resources(resources: typing.Sequence[api.Resource], format: s
else: else:
raise ValueError(f"Unhandled output format: {format}") raise ValueError(f"Unhandled output format: {format}")
def _list_resource_file(rf: api.ResourceFile, *, sort: bool, group: str, decompress: bool) -> None: def list_resource_file(rf: api.ResourceFile, *, sort: bool, group: str, decompress: bool) -> None:
if rf.header_system_data != bytes(len(rf.header_system_data)): if rf.header_system_data != bytes(len(rf.header_system_data)):
print("Header system data:") print("Header system data:")
_hexdump(rf.header_system_data) hexdump(rf.header_system_data)
if rf.header_application_data != bytes(len(rf.header_application_data)): if rf.header_application_data != bytes(len(rf.header_application_data)):
print("Header application data:") print("Header application data:")
_hexdump(rf.header_application_data) hexdump(rf.header_application_data)
attrs = _decompose_flags(rf.file_attributes) attrs = decompose_flags(rf.file_attributes)
if attrs: if attrs:
print("File attributes: " + " | ".join(attr.name for attr in attrs)) print("File attributes: " + " | ".join(attr.name for attr in attrs))
@ -387,20 +387,20 @@ def _list_resource_file(rf: api.ResourceFile, *, sort: bool, group: str, decompr
all_resources.sort(key=lambda res: (res.type, res.id)) all_resources.sort(key=lambda res: (res.type, res.id))
print(f"{len(all_resources)} resources:") print(f"{len(all_resources)} resources:")
for res in all_resources: for res in all_resources:
print(_describe_resource(res, include_type=True, decompress=decompress)) print(describe_resource(res, include_type=True, decompress=decompress))
elif group == "type": elif group == "type":
print(f"{len(rf)} resource types:") print(f"{len(rf)} resource types:")
restype_items: typing.Collection[typing.Tuple[bytes, typing.Mapping[int, api.Resource]]] = rf.items() restype_items: typing.Collection[typing.Tuple[bytes, typing.Mapping[int, api.Resource]]] = rf.items()
if sort: if sort:
restype_items = sorted(restype_items, key=lambda item: item[0]) restype_items = sorted(restype_items, key=lambda item: item[0])
for typecode, resources_map in restype_items: for typecode, resources_map in restype_items:
restype = _bytes_escape(typecode, quote="'") restype = bytes_escape(typecode, quote="'")
print(f"'{restype}': {len(resources_map)} resources:") print(f"'{restype}': {len(resources_map)} resources:")
resources_items: typing.Collection[typing.Tuple[int, api.Resource]] = resources_map.items() resources_items: typing.Collection[typing.Tuple[int, api.Resource]] = resources_map.items()
if sort: if sort:
resources_items = sorted(resources_items, key=lambda item: item[0]) resources_items = sorted(resources_items, key=lambda item: item[0])
for resid, res in resources_items: for resid, res in resources_items:
print(_describe_resource(res, include_type=False, decompress=decompress)) print(describe_resource(res, include_type=False, decompress=decompress))
print() print()
elif group == "id": elif group == "id":
all_resources = [] all_resources = []
@ -414,13 +414,13 @@ def _list_resource_file(rf: api.ResourceFile, *, sort: bool, group: str, decompr
if sort: if sort:
resources.sort(key=lambda res: res.type) resources.sort(key=lambda res: res.type)
for res in resources: for res in resources:
print(_describe_resource(res, include_type=True, decompress=decompress)) print(describe_resource(res, include_type=True, decompress=decompress))
print() print()
else: else:
raise AssertionError(f"Unhandled group mode: {group!r}") raise AssertionError(f"Unhandled group mode: {group!r}")
def main() -> typing.NoReturn: def main() -> typing.NoReturn:
ns = _parse_args() ns = parse_args()
if ns.file == "-": if ns.file == "-":
if ns.fork is not None: if ns.fork is not None:
@ -438,10 +438,10 @@ def main() -> typing.NoReturn:
else: else:
data = rf.header_application_data data = rf.header_application_data
_show_header_data(data, format=ns.format) show_header_data(data, format=ns.format)
elif ns.filter or ns.all: elif ns.filter or ns.all:
if ns.filter: if ns.filter:
resources = _filter_resources(rf, ns.filter) resources = filter_resources(rf, ns.filter)
else: else:
resources = [] resources = []
for reses in rf.values(): for reses in rf.values():
@ -450,9 +450,9 @@ def main() -> typing.NoReturn:
if ns.sort: if ns.sort:
resources.sort(key=lambda res: (res.type, res.id)) resources.sort(key=lambda res: (res.type, res.id))
_show_filtered_resources(resources, format=ns.format, decompress=ns.decompress) show_filtered_resources(resources, format=ns.format, decompress=ns.decompress)
else: else:
_list_resource_file(rf, sort=ns.sort, group=ns.group, decompress=ns.decompress) list_resource_file(rf, sort=ns.sort, group=ns.group, decompress=ns.decompress)
sys.exit(0) sys.exit(0)

View File

@ -100,7 +100,7 @@ class CompressedSystemHeaderInfo(CompressedHeaderInfo):
return f"{type(self).__qualname__}(header_length={self.header_length}, compression_type=0x{self.compression_type:>04x}, decompressed_length={self.decompressed_length}, dcmp_id={self.dcmp_id}, parameters={self.parameters!r})" return f"{type(self).__qualname__}(header_length={self.header_length}, compression_type=0x{self.compression_type:>04x}, decompressed_length={self.decompressed_length}, dcmp_id={self.dcmp_id}, parameters={self.parameters!r})"
def _read_variable_length_integer(data: bytes, position: int) -> typing.Tuple[int, int]: def read_variable_length_integer(data: bytes, position: int) -> typing.Tuple[int, int]:
"""Read a variable-length integer starting at the given position in the data, and return the integer as well as the number of bytes consumed. """Read a variable-length integer starting at the given position in the data, and return the integer as well as the number of bytes consumed.
This variable-length integer format is used by the 0xfe codes in the compression formats used by 'dcmp' (0) and 'dcmp' (1). This variable-length integer format is used by the 0xfe codes in the compression formats used by 'dcmp' (0) and 'dcmp' (1).

View File

@ -137,7 +137,7 @@ def decompress(header_info: common.CompressedHeaderInfo, data: bytes, *, debug:
print(f"Segment loader jump table entries") print(f"Segment loader jump table entries")
# All generated jump table entries have the same segment number. # All generated jump table entries have the same segment number.
segment_number_int, length = common._read_variable_length_integer(data, i) segment_number_int, length = common.read_variable_length_integer(data, i)
i += length i += length
if debug: if debug:
print(f"\t-> segment number: {segment_number_int:#x}") print(f"\t-> segment number: {segment_number_int:#x}")
@ -149,13 +149,13 @@ def decompress(header_info: common.CompressedHeaderInfo, data: bytes, *, debug:
# The tail is output once *without* an address in front, i. e. the first entry's address must be generated manually by a previous code. # The tail is output once *without* an address in front, i. e. the first entry's address must be generated manually by a previous code.
decompressed += entry_tail decompressed += entry_tail
count, length = common._read_variable_length_integer(data, i) count, length = common.read_variable_length_integer(data, i)
i += length i += length
if count <= 0: if count <= 0:
raise common.DecompressError(f"Jump table entry count must be greater than 0, not {count}") raise common.DecompressError(f"Jump table entry count must be greater than 0, not {count}")
# The second entry's address is stored explicitly. # The second entry's address is stored explicitly.
current_int, length = common._read_variable_length_integer(data, i) current_int, length = common.read_variable_length_integer(data, i)
i += length i += length
if debug: if debug:
print(f"-> address of second entry: {current_int:#x}") print(f"-> address of second entry: {current_int:#x}")
@ -166,7 +166,7 @@ def decompress(header_info: common.CompressedHeaderInfo, data: bytes, *, debug:
for _ in range(1, count): for _ in range(1, count):
# All further entries' addresses are stored as differences relative to the previous entry's address. # All further entries' addresses are stored as differences relative to the previous entry's address.
diff, length = common._read_variable_length_integer(data, i) diff, length = common.read_variable_length_integer(data, i)
i += length i += length
# For some reason, each difference is 6 higher than it should be. # For some reason, each difference is 6 higher than it should be.
diff -= 6 diff -= 6
@ -193,14 +193,14 @@ def decompress(header_info: common.CompressedHeaderInfo, data: bytes, *, debug:
print(f"Repeat {byte_count}-byte value") print(f"Repeat {byte_count}-byte value")
# The byte(s) to repeat, stored as a variable-length integer. The value is treated as unsigned, i. e. the integer is never negative. # The byte(s) to repeat, stored as a variable-length integer. The value is treated as unsigned, i. e. the integer is never negative.
to_repeat_int, length = common._read_variable_length_integer(data, i) to_repeat_int, length = common.read_variable_length_integer(data, i)
i += length i += length
try: try:
to_repeat = to_repeat_int.to_bytes(byte_count, "big", signed=False) to_repeat = to_repeat_int.to_bytes(byte_count, "big", signed=False)
except OverflowError: except OverflowError:
raise common.DecompressError(f"Value to repeat out of range for {byte_count}-byte repeat: {to_repeat_int:#x}") raise common.DecompressError(f"Value to repeat out of range for {byte_count}-byte repeat: {to_repeat_int:#x}")
count_m1, length = common._read_variable_length_integer(data, i) count_m1, length = common.read_variable_length_integer(data, i)
i += length i += length
count = count_m1 + 1 count = count_m1 + 1
if count <= 0: if count <= 0:
@ -217,7 +217,7 @@ def decompress(header_info: common.CompressedHeaderInfo, data: bytes, *, debug:
print(f"Difference-encoded 16-bit integers") print(f"Difference-encoded 16-bit integers")
# The first integer is stored explicitly, as a signed value. # The first integer is stored explicitly, as a signed value.
initial_int, length = common._read_variable_length_integer(data, i) initial_int, length = common.read_variable_length_integer(data, i)
i += length i += length
try: try:
initial = initial_int.to_bytes(2, "big", signed=True) initial = initial_int.to_bytes(2, "big", signed=True)
@ -227,7 +227,7 @@ def decompress(header_info: common.CompressedHeaderInfo, data: bytes, *, debug:
print(f"\t-> initial: {initial}") print(f"\t-> initial: {initial}")
decompressed += initial decompressed += initial
count, length = common._read_variable_length_integer(data, i) count, length = common.read_variable_length_integer(data, i)
i += length i += length
if count < 0: if count < 0:
raise common.DecompressError(f"Count cannot be negative: {count}") raise common.DecompressError(f"Count cannot be negative: {count}")
@ -253,7 +253,7 @@ def decompress(header_info: common.CompressedHeaderInfo, data: bytes, *, debug:
print(f"Difference-encoded 16-bit integers") print(f"Difference-encoded 16-bit integers")
# The first integer is stored explicitly, as a signed value. # The first integer is stored explicitly, as a signed value.
initial_int, length = common._read_variable_length_integer(data, i) initial_int, length = common.read_variable_length_integer(data, i)
i += length i += length
try: try:
initial = initial_int.to_bytes(4, "big", signed=True) initial = initial_int.to_bytes(4, "big", signed=True)
@ -263,7 +263,7 @@ def decompress(header_info: common.CompressedHeaderInfo, data: bytes, *, debug:
print(f"\t-> initial: {initial}") print(f"\t-> initial: {initial}")
decompressed += initial decompressed += initial
count, length = common._read_variable_length_integer(data, i) count, length = common.read_variable_length_integer(data, i)
i += length i += length
assert count >= 0 assert count >= 0
@ -271,7 +271,7 @@ def decompress(header_info: common.CompressedHeaderInfo, data: bytes, *, debug:
current_int = initial_int & 0xffffffff current_int = initial_int & 0xffffffff
for _ in range(count): for _ in range(count):
# The difference to the previous integer is stored as a variable-length integer, whose value may be negative. # The difference to the previous integer is stored as a variable-length integer, whose value may be negative.
diff, length = common._read_variable_length_integer(data, i) diff, length = common.read_variable_length_integer(data, i)
i += length i += length
# Simulate 32-bit integer wraparound. # Simulate 32-bit integer wraparound.

View File

@ -124,14 +124,14 @@ def decompress(header_info: common.CompressedHeaderInfo, data: bytes, *, debug:
print(f"Repeat {byte_count}-byte value") print(f"Repeat {byte_count}-byte value")
# The byte(s) to repeat, stored as a variable-length integer. The value is treated as unsigned, i. e. the integer is never negative. # The byte(s) to repeat, stored as a variable-length integer. The value is treated as unsigned, i. e. the integer is never negative.
to_repeat_int, length = common._read_variable_length_integer(data, i) to_repeat_int, length = common.read_variable_length_integer(data, i)
i += length i += length
try: try:
to_repeat = to_repeat_int.to_bytes(byte_count, "big", signed=False) to_repeat = to_repeat_int.to_bytes(byte_count, "big", signed=False)
except OverflowError: except OverflowError:
raise common.DecompressError(f"Value to repeat out of range for {byte_count}-byte repeat: {to_repeat_int:#x}") raise common.DecompressError(f"Value to repeat out of range for {byte_count}-byte repeat: {to_repeat_int:#x}")
count_m1, length = common._read_variable_length_integer(data, i) count_m1, length = common.read_variable_length_integer(data, i)
i += length i += length
count = count_m1 + 1 count = count_m1 + 1
if count <= 0: if count <= 0: