hacktools 0.33.0__cp312-cp312-win32.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hacktools/__init__.py +1 -0
- hacktools/arch.py +137 -0
- hacktools/assets/icon.png +0 -0
- hacktools/cmp_cri.cp312-win32.pyd +0 -0
- hacktools/cmp_lzss.cp312-win32.pyd +0 -0
- hacktools/cmp_misc.cp312-win32.pyd +0 -0
- hacktools/common.py +1460 -0
- hacktools/compression.py +216 -0
- hacktools/cpk.py +643 -0
- hacktools/gb.py +92 -0
- hacktools/gui.py +304 -0
- hacktools/nasm.py +84 -0
- hacktools/nds.py +361 -0
- hacktools/nitro.py +1751 -0
- hacktools/psp.py +1234 -0
- hacktools/psx.py +295 -0
- hacktools/wii.py +285 -0
- hacktools/ws.py +564 -0
- hacktools-0.33.0.dist-info/LICENSE +21 -0
- hacktools-0.33.0.dist-info/METADATA +102 -0
- hacktools-0.33.0.dist-info/RECORD +23 -0
- hacktools-0.33.0.dist-info/WHEEL +5 -0
- hacktools-0.33.0.dist-info/top_level.txt +1 -0
hacktools/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.33.0"
|
hacktools/arch.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from hacktools import common
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ARCHArchive:
|
|
6
|
+
def __init__(self):
|
|
7
|
+
self.filenum = 0
|
|
8
|
+
self.tableoff = 0
|
|
9
|
+
self.fatoff = 0
|
|
10
|
+
self.nameindexoff = 0
|
|
11
|
+
self.dataoff = 0
|
|
12
|
+
self.files = []
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ARCHFile:
|
|
16
|
+
def __init__(self):
|
|
17
|
+
self.name = ""
|
|
18
|
+
self.length = 0
|
|
19
|
+
self.declength = 0
|
|
20
|
+
self.offset = 0
|
|
21
|
+
self.nameoffset = 0
|
|
22
|
+
self.encoded = False
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def read(f):
|
|
26
|
+
f.seek(4) # Magic: ARCH
|
|
27
|
+
archive = ARCHArchive()
|
|
28
|
+
archive.filenum = f.readUInt()
|
|
29
|
+
archive.tableoff = f.readUInt()
|
|
30
|
+
archive.fatoff = f.readUInt()
|
|
31
|
+
archive.nameindexoff = f.readUInt()
|
|
32
|
+
archive.dataoff = f.readUInt()
|
|
33
|
+
common.logDebug("Archive:", vars(archive))
|
|
34
|
+
for i in range(archive.filenum):
|
|
35
|
+
f.seek(archive.fatoff + i * 16)
|
|
36
|
+
subfile = ARCHFile()
|
|
37
|
+
subfile.length = f.readUInt()
|
|
38
|
+
subfile.declength = f.readUInt()
|
|
39
|
+
subfile.offset = f.readUInt()
|
|
40
|
+
subfile.nameoffset = f.readUShort()
|
|
41
|
+
subfile.encoded = f.readUShort() == 1
|
|
42
|
+
f.seek(archive.tableoff + subfile.nameoffset)
|
|
43
|
+
subfile.name = f.readNullString()
|
|
44
|
+
common.logDebug("File", i, vars(subfile))
|
|
45
|
+
archive.files.append(subfile)
|
|
46
|
+
return archive
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def repack(fin, f, archive, infolder):
|
|
50
|
+
# Copy everything up to dataoff
|
|
51
|
+
fin.seek(0)
|
|
52
|
+
f.seek(0)
|
|
53
|
+
f.write(fin.read(archive.dataoff))
|
|
54
|
+
# Loop the files
|
|
55
|
+
dataoff = 0
|
|
56
|
+
for i in range(archive.filenum):
|
|
57
|
+
subfile = archive.files[i]
|
|
58
|
+
filepath = infolder + subfile.name
|
|
59
|
+
if not os.path.isfile(filepath):
|
|
60
|
+
# Just update the offset and copy the file
|
|
61
|
+
f.seek(archive.fatoff + i * 16)
|
|
62
|
+
f.seek(8, 1)
|
|
63
|
+
f.writeUInt(dataoff)
|
|
64
|
+
fin.seek(archive.dataoff + subfile.offset)
|
|
65
|
+
f.seek(archive.dataoff + dataoff)
|
|
66
|
+
f.write(fin.read(subfile.length))
|
|
67
|
+
else:
|
|
68
|
+
# Set the file as not encoded and copy it
|
|
69
|
+
size = os.path.getsize(filepath)
|
|
70
|
+
f.seek(archive.fatoff + i * 16)
|
|
71
|
+
f.writeUInt(size)
|
|
72
|
+
f.writeUInt(size)
|
|
73
|
+
f.writeUInt(dataoff)
|
|
74
|
+
f.seek(2, 1)
|
|
75
|
+
f.writeUShort(0)
|
|
76
|
+
f.seek(archive.dataoff + dataoff)
|
|
77
|
+
with common.Stream(filepath, "rb") as subf:
|
|
78
|
+
f.write(subf.read())
|
|
79
|
+
# Align with 0s
|
|
80
|
+
if f.tell() % 16 > 0:
|
|
81
|
+
f.writeZero(16 - (f.tell() % 16))
|
|
82
|
+
dataoff = f.tell() - archive.dataoff
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def extract(f, archive, outfolder):
|
|
86
|
+
for subfile in archive.files:
|
|
87
|
+
with common.Stream(outfolder + subfile.name, "wb") as fout:
|
|
88
|
+
f.seek(archive.dataoff + subfile.offset)
|
|
89
|
+
if not subfile.encoded:
|
|
90
|
+
fout.write(f.read(subfile.length))
|
|
91
|
+
else:
|
|
92
|
+
# Based on Tinke's ARCH implementation
|
|
93
|
+
startpos = f.tell()
|
|
94
|
+
buffer1 = []
|
|
95
|
+
buffer2 = []
|
|
96
|
+
for i in range(0x100):
|
|
97
|
+
buffer1.append(0)
|
|
98
|
+
buffer2.append(0)
|
|
99
|
+
while f.tell() - startpos < subfile.length:
|
|
100
|
+
# InitBuffer
|
|
101
|
+
for i in range(0x100):
|
|
102
|
+
buffer2[i] = i
|
|
103
|
+
# FillBuffer
|
|
104
|
+
index = 0
|
|
105
|
+
while index != 0x100:
|
|
106
|
+
bufid = f.readByte()
|
|
107
|
+
numloops = bufid
|
|
108
|
+
if bufid > 0x7f:
|
|
109
|
+
numloops = 0
|
|
110
|
+
index += bufid - 0x7f
|
|
111
|
+
if index == 0x100:
|
|
112
|
+
break
|
|
113
|
+
if numloops < 0:
|
|
114
|
+
continue
|
|
115
|
+
for i in range(numloops + 1):
|
|
116
|
+
byte = f.readByte()
|
|
117
|
+
buffer2[index] = byte
|
|
118
|
+
if byte != index:
|
|
119
|
+
buffer1[index] = f.readByte()
|
|
120
|
+
index += 1
|
|
121
|
+
# Process
|
|
122
|
+
numloops = (f.readByte() << 8) + f.readByte()
|
|
123
|
+
nextsamples = []
|
|
124
|
+
while True:
|
|
125
|
+
if len(nextsamples) == 0:
|
|
126
|
+
if numloops == 0:
|
|
127
|
+
break
|
|
128
|
+
numloops -= 1
|
|
129
|
+
index = f.readByte()
|
|
130
|
+
else:
|
|
131
|
+
index = nextsamples.pop()
|
|
132
|
+
if buffer2[index] == index:
|
|
133
|
+
fout.writeByte(index)
|
|
134
|
+
else:
|
|
135
|
+
nextsamples.append(buffer1[index])
|
|
136
|
+
nextsamples.append(buffer2[index])
|
|
137
|
+
index = len(nextsamples)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|