1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
"""
Simple Library for reading and parsing archive files generate by the linker.
"""
import struct
from typing import List, Tuple
from dataclasses import dataclass, field
#
# Source: 'https://en.wikipedia.org/wiki/Ar_(Unix)'
#
def _utf8(s):
result = s.decode('utf-8')
if not result:
return ""
return result
@dataclass
class Header:
""" Header """
signature: int = 0
def read(self, file):
self.signature = struct.unpack(">Q", file.read(8))[0]
@dataclass
class FileBlock:
""" File Block """
name: str = None
mtime: int = 0
uid: int = 0
gid: int = 0
perms: int = 0
size: int = 0
data: bytes = None
def read(self, file):
buffer = file.read(60)
if len(buffer) != 60:
return False
name, mtime, uid, gid, perms, size, magic = (
struct.unpack('16s12s6s6s8s10s2s', buffer))
self.name = _utf8(name)
self.mtime = int(mtime, 10) if len(mtime.strip()) > 0 else 0
self.uid = int(uid, 10) if len(uid.strip()) > 0 else 0
self.gid = int(gid, 10) if len(gid.strip()) > 0 else 0
self.perms = int(perms, 8) if len(perms.strip()) > 0 else 0
self.size = int(size, 10)
self.data = file.read(self.size)
if self.size % 2 != 0:
file.read(1)
return len(self.data) == self.size
@dataclass
class AR:
""" Archive with files """
files: List[Tuple[str, bytearray]] = field(default_factory=list)
def read(path) -> AR:
"""
Read and parse archive files generate by linkers.
"""
archive = AR()
with open(path, 'rb') as file:
header = Header()
header.read(file)
if header.signature != 0x213C617263683E0A:
raise ARException(
f"invalid AR file signature: {header.signature:016X}")
while True:
block = FileBlock()
if not block.read(file):
break
if block.name.strip() == "/":
continue
if block.name.strip() == "//":
string_table = _utf8(block.data)
continue
# parse file name
ss = block.name.split("/")
name = ss[0]
if len(ss[0]) == 0:
str_index = ss[1].strip()
assert len(str_index) > 0
index = int(str_index)
name = string_table[index:].split("/")[0]
name = name.lstrip().rstrip(' ')
if name == "" or name == "/":
continue
archive.files.append((name, block.data))
return archive
|