-
Notifications
You must be signed in to change notification settings - Fork 9
/
LIFExtractor.py
145 lines (120 loc) · 5.1 KB
/
LIFExtractor.py
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
"""
LIF Extractor - LEGO Digital Designer LIF extractor.
Copyright (C) 2012 JrMasterModelBuilder
You accept full responsibility for how you use this program.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import os
import sys
def extract(path):
def uint32(offset):
if(bytesAre == "str"):
return (ord(fileData[offset]) * 16777216) + (ord(fileData[offset+1]) * 65536) + (ord(fileData[offset+2]) * 256) + ord(fileData[offset+3])
else:
return (fileData[offset] * 16777216) + (fileData[offset+1] * 65536) + (fileData[offset+2] * 256) + fileData[offset+3]
def uint16(offset):
if(bytesAre == "str"):
return (ord(fileData[offset]) * 256) + ord(fileData[offset+1])
else:
return (fileData[offset] * 256) + fileData[offset+1]
def recurse(prefix, offset):
if(prefix == ""):
offset += 36
else:
folderList.extend([prefix])
offset += 4
for i in range(uint32(offset)):
offset += 4
entryType = uint16(offset)#1 = directory, 2 = file
offset += 6
#Build the name.
entryName = os.sep
#Check both integer and byte for different versions of Python.
while(fileData[offset+1] != 0 and fileData[offset+1] != b"\x00"):
if(bytesAre == "str"):
entryName += fileData[offset+1]
else:
entryName += chr(fileData[offset+1])
offset += 2
offset += 6
if(entryType == 1):
#Recurse through the director, and update the offset.
packedFilesOffset[0] += 20
offset = recurse(prefix + entryName, offset)
elif(entryType == 2):
#File offset.
packedFilesOffset[0] += 20
fileOffset = packedFilesOffset[0]
#File size.
fileSize = uint32(offset) - 20
offset += 24
packedFilesOffset[0] += fileSize
fileList.extend([[prefix + entryName, fileOffset, fileSize]])
#Return the offset at the end of this run to update parent runnings.
return offset
print("PROCESSING: " + path)
#Open the file if valid.
try:
with open(path, "rb") as f:
fileData = f.read()
except IOError as e:
print("\tERROR: Failed to read file.")
return False
if(len(fileData) < 4 or fileData[0:4] != b"LIFF"):
print("\tERROR: Not a LIF file.")
return False
print("\tEXTRACTING: Please wait.")
#Recurse through, creating a list of all the files.
packedFilesOffset = [84]#Array for non-global function-modifiable variable.
folderList = []
fileList = []
#Check if this version of Python treats bytes as int or str
bytesAre = type(b'a'[0]).__name__
#Recuse through the archive.
recurse("", (uint32(72)+64))
#Create output path from input path.
if(path[-4:].lower() == ".lif"):
outFolder = path[:-4]
else:
outFolder = path
#Create the first non-existant folder to extract to.
if os.path.exists(outFolder):
i = 1
while(os.path.exists(outFolder + "_" + str(i))):
i += 1
outFolder = outFolder + "_" + str(i)
#Make the output folder.
os.makedirs(outFolder)
#Create all the folders we need to extract to.
for a in folderList:
if(a[0] == ""):
continue
if not os.path.exists(outFolder + a):
os.makedirs(outFolder + a)
#Loop through the list of files, saving them.
for a in fileList:
f = open((outFolder + a[0]), "wb")
f.write(fileData[a[1]:a[1]+a[2]])
f.close()
print("\tCOMPLETE: " + str(len(fileList)) + " files in " + str(len(folderList)) + " folders extracted.")
return True
#Detect if executable or not.
fileName = sys.argv[0].split(os.sep).pop()
if(fileName[-3:] == ".py" or fileName[-4:] == ".pyw"):
runCommand = "python " + fileName
else:
runCommand = fileName
if(len(sys.argv) > 1):
for i in range(1, len(sys.argv)):
extract(sys.argv[i])
else:
print("LIF Extractor 1.0\n\nThis program will extract LIF archives to an adjacent folder.\n\nCOPYRIGHT:\n\t(C) 2012 JrMasterModelBuilder\n\nLICENSE:\n\tGNU GPLv3\n\tYou accept full responsibility for how you use this program.\n\nUSEAGE:\n\t" + runCommand + " <LIST_OF_FILE_PATHS>")