mirror of
https://gitlab.gnome.org/jwestman/blueprint-compiler.git
synced 2025-05-04 15:59:08 -04:00
This fixes a bug where the decompiler could not recognize enums by their C identifiers because it could not correctly read attributes. Fixes #177.
322 lines
9.8 KiB
Python
322 lines
9.8 KiB
Python
# typelib.py
|
|
#
|
|
# Copyright 2022 James Westman <james@jwestman.net>
|
|
#
|
|
# This file is free software; you can redistribute it and/or modify it
|
|
# under the terms of the GNU Lesser General Public License as
|
|
# published by the Free Software Foundation; either version 3 of the
|
|
# License, or (at your option) any later version.
|
|
#
|
|
# This file is distributed in the hope that it will be useful, but
|
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
# Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Lesser General Public
|
|
# License along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
# SPDX-License-Identifier: LGPL-3.0-or-later
|
|
|
|
import math
|
|
import mmap
|
|
import os
|
|
import sys
|
|
import typing as T
|
|
from ctypes import *
|
|
|
|
from .errors import CompilerBugError
|
|
|
|
BLOB_TYPE_STRUCT = 3
|
|
BLOB_TYPE_BOXED = 4
|
|
BLOB_TYPE_ENUM = 5
|
|
BLOB_TYPE_FLAGS = 6
|
|
BLOB_TYPE_OBJECT = 7
|
|
BLOB_TYPE_INTERFACE = 8
|
|
|
|
TYPE_VOID = 0
|
|
TYPE_BOOLEAN = 1
|
|
TYPE_INT8 = 2
|
|
TYPE_UINT8 = 3
|
|
TYPE_INT16 = 4
|
|
TYPE_UINT16 = 5
|
|
TYPE_INT32 = 6
|
|
TYPE_UINT32 = 7
|
|
TYPE_INT64 = 8
|
|
TYPE_UINT64 = 9
|
|
TYPE_FLOAT = 10
|
|
TYPE_DOUBLE = 11
|
|
TYPE_GTYPE = 12
|
|
TYPE_UTF8 = 13
|
|
TYPE_FILENAME = 14
|
|
TYPE_ARRAY = 15
|
|
TYPE_INTERFACE = 16
|
|
TYPE_GLIST = 17
|
|
TYPE_GSLIST = 18
|
|
TYPE_GHASH = 19
|
|
TYPE_ERROR = 20
|
|
TYPE_UNICHAR = 21
|
|
|
|
|
|
class Field:
|
|
def __init__(self, offset: int, type: str, shift=0, mask=None):
|
|
self._offset = offset
|
|
self._type = type
|
|
if not mask or sys.byteorder == "little":
|
|
self._shift = shift
|
|
elif self._type == "u8" or self._type == "i8":
|
|
self._shift = 8 - (shift + mask)
|
|
elif self._type == "u16" or self._type == "i16":
|
|
self._shift = 16 - (shift + mask)
|
|
else:
|
|
self._shift = 32 - (shift + mask)
|
|
self._mask = (1 << mask) - 1 if mask else None
|
|
self._name = f"{offset}__{type}__{shift}__{mask}"
|
|
|
|
def __get__(self, typelib: "Typelib", _objtype=None):
|
|
if typelib is None:
|
|
return self
|
|
|
|
def shift_mask(n):
|
|
n = n >> self._shift
|
|
if self._mask:
|
|
n = n & self._mask
|
|
return n
|
|
|
|
tl = typelib[self._offset]
|
|
if self._type == "u8":
|
|
return shift_mask(tl.u8)
|
|
elif self._type == "u16":
|
|
return shift_mask(tl.u16)
|
|
elif self._type == "u32":
|
|
return shift_mask(tl.u32)
|
|
elif self._type == "i8":
|
|
return shift_mask(tl.i8)
|
|
elif self._type == "i16":
|
|
return shift_mask(tl.i16)
|
|
elif self._type == "i32":
|
|
return shift_mask(tl.i32)
|
|
elif self._type == "pointer":
|
|
return tl.header[tl.u32]
|
|
elif self._type == "offset":
|
|
return tl
|
|
elif self._type == "string":
|
|
return tl.string
|
|
elif self._type == "dir_entry":
|
|
return tl.header.dir_entry(tl.u16)
|
|
else:
|
|
raise CompilerBugError(self._type)
|
|
|
|
|
|
class Typelib:
|
|
AS_DIR_ENTRY = Field(0, "dir_entry")
|
|
|
|
HEADER_N_ENTRIES = Field(0x14, "u16")
|
|
HEADER_N_LOCAL_ENTRIES = Field(0x16, "u16")
|
|
HEADER_DIRECTORY = Field(0x18, "pointer")
|
|
HEADER_N_ATTRIBUTES = Field(0x1C, "u32")
|
|
HEADER_ATTRIBUTES = Field(0x20, "pointer")
|
|
|
|
HEADER_DEPENDENCIES = Field(0x24, "pointer")
|
|
|
|
HEADER_NAMESPACE = Field(0x2C, "string")
|
|
HEADER_NSVERSION = Field(0x30, "string")
|
|
|
|
HEADER_ENTRY_BLOB_SIZE = Field(0x3C, "u16")
|
|
HEADER_FUNCTION_BLOB_SIZE = Field(0x3E, "u16")
|
|
HEADER_CALLBACK_BLOB_SIZE = Field(0x40, "u16")
|
|
HEADER_SIGNAL_BLOB_SIZE = Field(0x42, "u16")
|
|
HEADER_ARG_BLOB_SIZE = Field(0x46, "u16")
|
|
HEADER_PROPERTY_BLOB_SIZE = Field(0x48, "u16")
|
|
HEADER_FIELD_BLOB_SIZE = Field(0x4A, "u16")
|
|
HEADER_VALUE_BLOB_SIZE = Field(0x4C, "u16")
|
|
HEADER_ATTRIBUTE_BLOB_SIZE = Field(0x4E, "u16")
|
|
HEADER_ENUM_BLOB_SIZE = Field(0x56, "u16")
|
|
HEADER_OBJECT_BLOB_SIZE = Field(0x5A, "u16")
|
|
HEADER_INTERFACE_BLOB_SIZE = Field(0x5C, "u16")
|
|
|
|
DIR_ENTRY_BLOB_TYPE = Field(0x0, "u16")
|
|
DIR_ENTRY_LOCAL = Field(0x2, "u16", 0, 1)
|
|
DIR_ENTRY_NAME = Field(0x4, "string")
|
|
DIR_ENTRY_OFFSET = Field(0x8, "pointer")
|
|
DIR_ENTRY_NAMESPACE = Field(0x8, "string")
|
|
|
|
ARG_NAME = Field(0x0, "string")
|
|
ARG_TYPE = Field(0xC, "u32")
|
|
|
|
SIGNATURE_RETURN_TYPE = Field(0x0, "u32")
|
|
SIGNATURE_N_ARGUMENTS = Field(0x6, "u16")
|
|
SIGNATURE_ARGUMENTS = Field(0x8, "offset")
|
|
|
|
ATTR_OFFSET = Field(0x0, "u32")
|
|
ATTR_NAME = Field(0x4, "string")
|
|
ATTR_VALUE = Field(0x8, "string")
|
|
|
|
TYPE_BLOB_TAG = Field(0x0, "u8", 3, 5)
|
|
TYPE_BLOB_INTERFACE = Field(0x2, "dir_entry")
|
|
TYPE_BLOB_ARRAY_INNER = Field(0x4, "u32")
|
|
|
|
BLOB_NAME = Field(0x4, "string")
|
|
|
|
STRUCT_DEPRECATED = Field(0x2, "u16", 0, 1)
|
|
|
|
ENUM_DEPRECATED = Field(0x2, "u16", 0, 1)
|
|
ENUM_GTYPE_NAME = Field(0x8, "string")
|
|
ENUM_N_VALUES = Field(0x10, "u16")
|
|
ENUM_N_METHODS = Field(0x12, "u16")
|
|
ENUM_VALUES = Field(0x18, "offset")
|
|
|
|
INTERFACE_DEPRECATED = Field(0x2, "u16", 0, 1)
|
|
INTERFACE_GTYPE_NAME = Field(0x8, "string")
|
|
INTERFACE_N_PREREQUISITES = Field(0x12, "u16")
|
|
INTERFACE_N_PROPERTIES = Field(0x14, "u16")
|
|
INTERFACE_N_METHODS = Field(0x16, "u16")
|
|
INTERFACE_N_SIGNALS = Field(0x18, "u16")
|
|
INTERFACE_N_VFUNCS = Field(0x1A, "u16")
|
|
INTERFACE_N_CONSTANTS = Field(0x1C, "u16")
|
|
INTERFACE_PREREQUISITES = Field(0x28, "offset")
|
|
|
|
OBJ_DEPRECATED = Field(0x02, "u16", 0, 1)
|
|
OBJ_ABSTRACT = Field(0x02, "u16", 1, 1)
|
|
OBJ_FUNDAMENTAL = Field(0x02, "u16", 2, 1)
|
|
OBJ_FINAL = Field(0x02, "u16", 3, 1)
|
|
OBJ_GTYPE_NAME = Field(0x08, "string")
|
|
OBJ_PARENT = Field(0x10, "dir_entry")
|
|
OBJ_GTYPE_STRUCT = Field(0x12, "string")
|
|
OBJ_N_INTERFACES = Field(0x14, "u16")
|
|
OBJ_N_FIELDS = Field(0x16, "u16")
|
|
OBJ_N_PROPERTIES = Field(0x18, "u16")
|
|
OBJ_N_METHODS = Field(0x1A, "u16")
|
|
OBJ_N_SIGNALS = Field(0x1C, "u16")
|
|
OBJ_N_VFUNCS = Field(0x1E, "u16")
|
|
OBJ_N_CONSTANTS = Field(0x20, "u16")
|
|
OBJ_N_FIELD_CALLBACKS = Field(0x22, "u16")
|
|
|
|
PROP_NAME = Field(0x0, "string")
|
|
PROP_DEPRECATED = Field(0x4, "u32", 0, 1)
|
|
PROP_READABLE = Field(0x4, "u32", 1, 1)
|
|
PROP_WRITABLE = Field(0x4, "u32", 2, 1)
|
|
PROP_CONSTRUCT = Field(0x4, "u32", 3, 1)
|
|
PROP_CONSTRUCT_ONLY = Field(0x4, "u32", 4, 1)
|
|
PROP_TYPE = Field(0xC, "u32")
|
|
|
|
SIGNAL_DEPRECATED = Field(0x0, "u16", 0, 1)
|
|
SIGNAL_DETAILED = Field(0x0, "u16", 5, 1)
|
|
SIGNAL_NAME = Field(0x4, "string")
|
|
SIGNAL_SIGNATURE = Field(0xC, "pointer")
|
|
|
|
VALUE_NAME = Field(0x4, "string")
|
|
VALUE_VALUE = Field(0x8, "i32")
|
|
|
|
def __init__(self, typelib_file, offset: int):
|
|
self._typelib_file = typelib_file
|
|
self._offset = offset
|
|
|
|
def __getitem__(self, index: int):
|
|
return Typelib(self._typelib_file, self._offset + index)
|
|
|
|
def attr(self, name):
|
|
return self.header.attr(self._offset, name)
|
|
|
|
@property
|
|
def header(self) -> "TypelibHeader":
|
|
return TypelibHeader(self._typelib_file)
|
|
|
|
@property
|
|
def u8(self) -> int:
|
|
"""Gets the 8-bit unsigned int at this location."""
|
|
return self._int(1, False)
|
|
|
|
@property
|
|
def u16(self) -> int:
|
|
"""Gets the 16-bit unsigned int at this location."""
|
|
return self._int(2, False)
|
|
|
|
@property
|
|
def u32(self) -> int:
|
|
"""Gets the 32-bit unsigned int at this location."""
|
|
return self._int(4, False)
|
|
|
|
@property
|
|
def i8(self) -> int:
|
|
"""Gets the 8-bit unsigned int at this location."""
|
|
return self._int(1, True)
|
|
|
|
@property
|
|
def i16(self) -> int:
|
|
"""Gets the 16-bit unsigned int at this location."""
|
|
return self._int(2, True)
|
|
|
|
@property
|
|
def i32(self) -> int:
|
|
"""Gets the 32-bit unsigned int at this location."""
|
|
return self._int(4, True)
|
|
|
|
@property
|
|
def string(self) -> T.Optional[str]:
|
|
"""Interprets the 32-bit unsigned int at this location as a pointer
|
|
within the typelib file, and returns the null-terminated string at that
|
|
pointer."""
|
|
|
|
loc = self.u32
|
|
if loc == 0:
|
|
return None
|
|
|
|
end = self._typelib_file.find(b"\0", loc)
|
|
return self._typelib_file[loc:end].decode("utf-8")
|
|
|
|
def _int(self, size, signed) -> int:
|
|
return int.from_bytes(
|
|
self._typelib_file[self._offset : self._offset + size],
|
|
sys.byteorder,
|
|
signed=signed,
|
|
)
|
|
|
|
|
|
class TypelibHeader(Typelib):
|
|
def __init__(self, typelib_file):
|
|
super().__init__(typelib_file, 0)
|
|
|
|
def dir_entry(self, index) -> T.Optional[Typelib]:
|
|
if index == 0:
|
|
return None
|
|
else:
|
|
return self.HEADER_DIRECTORY[(index - 1) * self.HEADER_ENTRY_BLOB_SIZE]
|
|
|
|
def attr(self, offset, name):
|
|
lower = 0
|
|
upper = self.HEADER_N_ATTRIBUTES
|
|
attr_size = self.HEADER_ATTRIBUTE_BLOB_SIZE
|
|
attrs = self.HEADER_ATTRIBUTES
|
|
mid = 0
|
|
|
|
while lower <= upper:
|
|
mid = math.floor((upper + lower) / 2)
|
|
attr = attrs[mid * attr_size]
|
|
if attr.ATTR_OFFSET < offset:
|
|
lower = mid + 1
|
|
elif attr.ATTR_OFFSET > offset:
|
|
upper = mid - 1
|
|
else:
|
|
while mid >= 0 and attrs[(mid - 1) * attr_size].ATTR_OFFSET == offset:
|
|
mid -= 1
|
|
break
|
|
if attrs[mid * attr_size].ATTR_OFFSET != offset:
|
|
# no match found
|
|
return None
|
|
while attrs[mid * attr_size].ATTR_OFFSET == offset:
|
|
if attrs[mid * attr_size].ATTR_NAME == name:
|
|
return attrs[mid * attr_size].ATTR_VALUE
|
|
mid += 1
|
|
return None
|
|
|
|
def attr_by_index(self, index):
|
|
pass
|
|
|
|
@property
|
|
def dir_entries(self):
|
|
return [self.dir_entry(i) for i in range(self[0x16].u16)]
|
|
|
|
|
|
def load_typelib(path: str) -> Typelib:
|
|
with open(path, "rb") as f:
|
|
return Typelib(f.read(), 0)
|