Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ repos:
#

- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.25
rev: '0.26'
hooks:
- id: validate-pyproject
fail_fast: true
Expand Down Expand Up @@ -89,11 +89,11 @@ repos:
- id: check-docstring-first

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.15.20
rev: v0.16.4
hooks:
- id: ruff

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v2.1.0
rev: v2.3.1
hooks:
- id: mypy
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information

project = "pyxbe"
project_copyright = f"{datetime.datetime.now().year}, Matt Borgerson"
project_copyright = f"{datetime.datetime.now(tz=datetime.UTC).year}, Matt Borgerson"
author = "Matt Borgerson"

# -- General configuration ---------------------------------------------------
Expand Down
Empty file modified setup.py
100644 → 100755
Empty file.
2 changes: 1 addition & 1 deletion tests/test_load.py
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python
import unittest
import os.path
import unittest

from xbe import Xbe

Expand Down
44 changes: 16 additions & 28 deletions xbe/__init__.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@
import logging
import struct
import time

from collections.abc import Sequence
from enum import IntFlag
from typing import (
Any,
ClassVar,
cast,
)

Expand All @@ -49,7 +49,7 @@ class XbeKernelImage:
xboxkrnl.exe model
"""

exports = {
exports: ClassVar[dict[int, str]] = {
1: "AvGetSavedDataAddress",
2: "AvSendTVEncoderOption",
3: "AvSetDisplayMode",
Expand Down Expand Up @@ -429,7 +429,7 @@ class EnumMapperMixin:
Mixin to convert integers into enumeration types.
"""

_enummap_: dict[str, type[IntFlag]] = {}
_enummap_: ClassVar[dict[str, type[IntFlag]]] = {}

def __getattribute__(self, attr):
enummap = super().__getattribute__("_enummap_")
Expand Down Expand Up @@ -461,14 +461,14 @@ def dumps(self, indent: int = 0) -> str:
for item in fields:
fname, ftype = item[0], item[1]
fval = getattr(self, fname)
s += " " * indent + ("%s: " % fname).ljust(max_name_len + 2)
s += " " * indent + (f"{fname}: ").ljust(max_name_len + 2)

uint_types = {ctypes.c_uint8, ctypes.c_uint16, ctypes.c_uint32}
if issubclass(ftype, StructurePrintMixin):
s += "\n" + fval.dumps(indent + 2)
elif ftype in uint_types:
if isinstance(fval, IntFlag):
s += f"{repr(fval)}"
s += f"{fval!r}"
else:
s += f"{fval:#x}"
elif issubclass(ftype, ctypes.Array) and ftype._type_ in uint_types:
Expand Down Expand Up @@ -1235,9 +1235,7 @@ def from_file(cls, path: str) -> "Xbe":
return cls(data)

def __repr__(self) -> str:
return "<Xbe name='{}' title_id=0x{:08x}>".format(
self.title_name, self.cert.title_id
)
return f"<Xbe name='{self.title_name}' title_id=0x{self.cert.title_id:08x}>"


class XbeSection:
Expand All @@ -1251,10 +1249,10 @@ def __init__(self, name: str, header: XbeSectionHeader, data: bytes):
self.data: bytes = data

def __repr__(self) -> str:
return "<XbeSection name='{}' vaddr=0x{:x} vsize=0x{:x}>".format(
self.name,
self.header.virtual_addr,
self.header.virtual_size,
return (
f"<XbeSection name='{self.name}'"
f" vaddr=0x{self.header.virtual_addr:x}"
f" vsize=0x{self.header.virtual_size:x}>"
)


Expand All @@ -1268,12 +1266,7 @@ def __init__(self, header: XbeLibraryVersion):
self.name: str = str(self.header.name, encoding="ascii")

def __repr__(self) -> str:
return "<XbeLibrary '%s' (%d.%d.%d)>" % (
self.name,
self.header.ver_major,
self.header.ver_minor,
self.header.ver_build,
)
return f"<XbeLibrary '{self.name}' ({self.header.ver_major}.{self.header.ver_minor}.{self.header.ver_build})>"


class XbeLibraryFeature:
Expand All @@ -1286,12 +1279,7 @@ def __init__(self, header: XbeLibraryFeatureDescriptor):
self.name: str = str(self.header.name, encoding="ascii")

def __repr__(self) -> str:
return "<XbeFeature '%s' (%d.%d.%d)>" % (
self.name,
self.header.ver_major,
self.header.ver_minor,
self.header.ver_build,
)
return f"<XbeFeature '{self.name}' ({self.header.ver_major}.{self.header.ver_minor}.{self.header.ver_build})>"


class XprImageHeader(ctypes.LittleEndianStructure, StructurePrintMixin):
Expand Down Expand Up @@ -1346,8 +1334,8 @@ def decode_bc1(w: int, h: int, data: bytes) -> list[RGBA]:
Decode a BC1 (aka DXT1) compressed image to a list of pixel real-value color
tuples

More information about BC1 can be found at: https://docs.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-block-compression#bc1
""" # noqa: E501, pylint:disable=line-too-long
More information about BC1 can be found at: https://docs.microsoft.com/en-us/windows/win32/direct3d10/d3d10-graphics-programming-guide-resources-block-compression#bc1 # pylint: disable=line-too-long
"""
assert w % 4 == 0
assert h % 4 == 0
blocks_per_row = w // 4
Expand Down Expand Up @@ -1453,7 +1441,7 @@ def decode_logo(data: bytes) -> tuple[int, int, list[RGBA]]:
# Type 1
length = get_bits(data[i], 4 - 1, 1)
val = (get_bits(data[i], 8 - 1, 4) << 4) / 255
for _ in range(0, length):
for _ in range(length):
pixels[c] = (val, val, val, 1.0)
c += 1
else:
Expand All @@ -1463,7 +1451,7 @@ def decode_logo(data: bytes) -> tuple[int, int, list[RGBA]]:
length = get_bits(d, 12 - 1, 2)
val = (get_bits(d, 16 - 1, 12) << 4) / 255
i += 1
for _ in range(0, length):
for _ in range(length):
pixels[c] = (val, val, val, 1.0)
c += 1
i += 1
Expand Down
14 changes: 5 additions & 9 deletions xbe/__main__.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import logging
import sys
import argparse
import logging
import os.path
import sys

from xbe import decode_logo, decode_xpr_image, encode_bmp, Xbe
from xbe import Xbe, decode_logo, decode_xpr_image, encode_bmp

logging.basicConfig(format="%(message)s", level=logging.DEBUG, stream=sys.stdout)

Expand All @@ -41,15 +41,11 @@ def extract_images(xbe_path: str, xbe: Xbe) -> None:
("$$XSIMAGE", "save_image"),
]:
if section_name not in xbe.sections:
print("XBE does not contain '%s' section" % section_name)
print(f"XBE does not contain '{section_name}' section")
continue

out_path = os.path.join(out_dir, xbe_name + "_" + file_name + ".bmp")
print(
"Extracting XBE image in section '{}' to '{}'".format(
section_name, out_path
)
)
print(f"Extracting XBE image in section '{section_name}' to '{out_path}'")

bmp = encode_bmp(*decode_xpr_image(xbe.sections[section_name].data))
with open(out_path, "wb") as f:
Expand Down