PyCriCodecsEx 0.0.1__cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.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.
- CriCodecsEx.cpython-310-x86_64-linux-gnu.so +0 -0
- PyCriCodecsEx/__init__.py +1 -0
- PyCriCodecsEx/acb.py +100 -0
- PyCriCodecsEx/adx.py +16 -0
- PyCriCodecsEx/awb.py +151 -0
- PyCriCodecsEx/chunk.py +75 -0
- PyCriCodecsEx/cpk.py +732 -0
- PyCriCodecsEx/hca.py +302 -0
- PyCriCodecsEx/usm.py +1266 -0
- PyCriCodecsEx/utf.py +704 -0
- pycricodecsex-0.0.1.dist-info/METADATA +81 -0
- pycricodecsex-0.0.1.dist-info/RECORD +15 -0
- pycricodecsex-0.0.1.dist-info/WHEEL +6 -0
- pycricodecsex-0.0.1.dist-info/licenses/LICENSE +21 -0
- pycricodecsex-0.0.1.dist-info/top_level.txt +2 -0
PyCriCodecsEx/hca.py
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
from io import BytesIO, FileIO
|
|
2
|
+
from struct import *
|
|
3
|
+
from typing import BinaryIO
|
|
4
|
+
from array import array
|
|
5
|
+
import CriCodecsEx
|
|
6
|
+
|
|
7
|
+
from PyCriCodecsEx.chunk import *
|
|
8
|
+
|
|
9
|
+
HcaHeaderStruct = Struct(">4sHH")
|
|
10
|
+
HcaFmtHeaderStruct = Struct(">4sIIHH")
|
|
11
|
+
HcaCompHeaderStruct = Struct(">4sHBBBBBBBBBB")
|
|
12
|
+
HcaDecHeaderStruct = Struct(">4sHBBBBBB")
|
|
13
|
+
HcaLoopHeaderStruct = Struct(">4sIIHH")
|
|
14
|
+
HcaAthHeaderStruct = Struct(">4sH")
|
|
15
|
+
HcaVbrHeaderStruct = Struct(">4sHH")
|
|
16
|
+
HcaCiphHeaderStruct = Struct(">4sH")
|
|
17
|
+
HcaRvaHeaderStruct = Struct(">4sf")
|
|
18
|
+
|
|
19
|
+
class HCA:
|
|
20
|
+
stream: BinaryIO
|
|
21
|
+
hcastream: BinaryIO
|
|
22
|
+
HcaSig: bytes
|
|
23
|
+
version: int
|
|
24
|
+
header_size: int
|
|
25
|
+
key: int
|
|
26
|
+
subkey: int
|
|
27
|
+
hca: dict
|
|
28
|
+
filetype: str
|
|
29
|
+
wavbytes: bytearray
|
|
30
|
+
hcabytes: bytearray
|
|
31
|
+
riffSignature: bytes
|
|
32
|
+
riffSize: int
|
|
33
|
+
wave: bytes
|
|
34
|
+
fmt: bytes
|
|
35
|
+
fmtSize: int
|
|
36
|
+
fmtType: int
|
|
37
|
+
fmtChannelCount: int
|
|
38
|
+
fmtSamplingRate: int
|
|
39
|
+
fmtSamplesPerSec: int
|
|
40
|
+
fmtSamplingSize: int
|
|
41
|
+
fmtBitCount: int
|
|
42
|
+
dataSig: bytes
|
|
43
|
+
dataSize: int
|
|
44
|
+
encrypted: bool
|
|
45
|
+
enc_table: array
|
|
46
|
+
table: array
|
|
47
|
+
looping: bool
|
|
48
|
+
|
|
49
|
+
def __init__(self, stream: BinaryIO, key: int = 0, subkey: int = 0) -> None:
|
|
50
|
+
if type(stream) == str:
|
|
51
|
+
self.stream = FileIO(stream)
|
|
52
|
+
self.hcastream = FileIO(stream)
|
|
53
|
+
else:
|
|
54
|
+
# copying since for encryption and decryption we use the internal buffer in C++.
|
|
55
|
+
stream = bytearray(stream).copy()
|
|
56
|
+
self.stream = BytesIO(stream)
|
|
57
|
+
self.hcastream = BytesIO(stream)
|
|
58
|
+
if type(key) == str:
|
|
59
|
+
self.key = int(key, 16)
|
|
60
|
+
else:
|
|
61
|
+
self.key = key
|
|
62
|
+
if type(subkey) == str:
|
|
63
|
+
self.subkey = int(subkey, 16)
|
|
64
|
+
else:
|
|
65
|
+
self.subkey = subkey
|
|
66
|
+
self.hcabytes: bytearray = b''
|
|
67
|
+
self.enc_table: array = b''
|
|
68
|
+
self.table: array = b''
|
|
69
|
+
self.Pyparse_header()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def Pyparse_header(self) -> None:
|
|
73
|
+
self.HcaSig, self.version, self.header_size = HcaHeaderStruct.unpack(
|
|
74
|
+
self.hcastream.read(HcaHeaderStruct.size)
|
|
75
|
+
)
|
|
76
|
+
if self.HcaSig in [HCAType.HCA.value, HCAType.EHCA.value]:
|
|
77
|
+
if not self.hcabytes:
|
|
78
|
+
self.filetype = "hca"
|
|
79
|
+
if self.HcaSig == HCAType.EHCA.value:
|
|
80
|
+
self.encrypted = True
|
|
81
|
+
else:
|
|
82
|
+
self.encrypted = False
|
|
83
|
+
if self.HcaSig != HCAType.HCA.value and self.HcaSig != HCAType.EHCA.value:
|
|
84
|
+
raise ValueError("Invalid HCA file.")
|
|
85
|
+
elif self.HcaSig == HCAType.EHCA.value and not self.key:
|
|
86
|
+
self.key = 0xCF222F1FE0748978 # Default HCA key.
|
|
87
|
+
elif self.key < 0:
|
|
88
|
+
raise ValueError("HCA key cannot be a negative.")
|
|
89
|
+
elif self.key > 0xFFFFFFFFFFFFFFFF:
|
|
90
|
+
raise OverflowError("HCA key cannot exceed the maximum size of 8 bytes.")
|
|
91
|
+
elif self.subkey < 0:
|
|
92
|
+
raise ValueError("HCA subkey cannot be a negative.")
|
|
93
|
+
elif self.subkey > 0xFFFF:
|
|
94
|
+
raise OverflowError("HCA subkey cannot exceed 65535.")
|
|
95
|
+
|
|
96
|
+
fmtsig, temp, framecount, encoder_delay, encoder_padding = HcaFmtHeaderStruct.unpack(
|
|
97
|
+
self.hcastream.read(HcaFmtHeaderStruct.size)
|
|
98
|
+
)
|
|
99
|
+
channelcount = temp >> 24
|
|
100
|
+
samplerate = temp & 0x00FFFFFF
|
|
101
|
+
|
|
102
|
+
self.hca = dict(
|
|
103
|
+
Encrypted = self.encrypted,
|
|
104
|
+
Header=self.HcaSig,
|
|
105
|
+
version=hex(self.version),
|
|
106
|
+
HeaderSize=self.header_size,
|
|
107
|
+
FmtSig = fmtsig,
|
|
108
|
+
ChannelCount = channelcount,
|
|
109
|
+
SampleRate = samplerate,
|
|
110
|
+
FrameCount = framecount,
|
|
111
|
+
EncoderDelay = encoder_delay,
|
|
112
|
+
EncoderPadding = encoder_padding,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
while True:
|
|
116
|
+
sig = unpack(">I", self.hcastream.read(4))[0]
|
|
117
|
+
self.hcastream.seek(-4, 1)
|
|
118
|
+
sig = int.to_bytes(sig & 0x7F7F7F7F, 4, "big")
|
|
119
|
+
if sig == b"comp":
|
|
120
|
+
compsig, framesize, minres, maxres, trackcount, channelconfig, totalbandcount, basebandcount, stereobandcount, bandsperhfrgroup, r1, r2 = HcaCompHeaderStruct.unpack(
|
|
121
|
+
self.hcastream.read(HcaCompHeaderStruct.size)
|
|
122
|
+
)
|
|
123
|
+
self.hca.update(
|
|
124
|
+
dict(
|
|
125
|
+
CompSig = compsig,
|
|
126
|
+
FrameSize = framesize,
|
|
127
|
+
MinResolution = minres,
|
|
128
|
+
MaxResolution = maxres,
|
|
129
|
+
TrackCount = trackcount,
|
|
130
|
+
ChannelConfig = channelconfig,
|
|
131
|
+
TotalBandCount = totalbandcount,
|
|
132
|
+
BaseBandCount = basebandcount,
|
|
133
|
+
StereoBandCount = stereobandcount,
|
|
134
|
+
BandsPerHfrGroup = bandsperhfrgroup,
|
|
135
|
+
ReservedByte1 = r1,
|
|
136
|
+
ReservedByte2 = r2
|
|
137
|
+
)
|
|
138
|
+
)
|
|
139
|
+
elif sig == b"ciph":
|
|
140
|
+
ciphsig, ciphertype = HcaCiphHeaderStruct.unpack(
|
|
141
|
+
self.hcastream.read(HcaCiphHeaderStruct.size)
|
|
142
|
+
)
|
|
143
|
+
if ciphertype == 1:
|
|
144
|
+
self.encrypted = True
|
|
145
|
+
self.hca.update(dict(CiphSig = ciphsig, CipherType = ciphertype))
|
|
146
|
+
elif sig == b"loop":
|
|
147
|
+
self.looping = True
|
|
148
|
+
loopsig, loopstart, loopend, loopstartdelay, loopendpadding = HcaLoopHeaderStruct.unpack(
|
|
149
|
+
self.hcastream.read(HcaLoopHeaderStruct.size)
|
|
150
|
+
)
|
|
151
|
+
self.hca.update(dict(LoopSig = loopsig, LoopStart = loopstart, LoopEnd = loopend, LoopStartDelay = loopstartdelay, LoopEndPadding = loopendpadding))
|
|
152
|
+
elif sig == b"dec\00":
|
|
153
|
+
decsig, framesize, maxres, minres, totalbandcount, basebandcount, temp, stereotype = HcaDecHeaderStruct.unpack(
|
|
154
|
+
self.hcastream.read(HcaDecHeaderStruct.size)
|
|
155
|
+
)
|
|
156
|
+
trackcount = temp >> 4
|
|
157
|
+
channelconfig = temp & 0xF
|
|
158
|
+
self.hca.update(
|
|
159
|
+
dict(
|
|
160
|
+
DecSig = decsig,
|
|
161
|
+
FrameSize = framesize,
|
|
162
|
+
MinResolution = minres,
|
|
163
|
+
MaxResolution = maxres,
|
|
164
|
+
TotalBandCount = totalbandcount,
|
|
165
|
+
BaseBandCoung = basebandcount,
|
|
166
|
+
TrackCount = trackcount,
|
|
167
|
+
ChannelConfig = channelconfig,
|
|
168
|
+
StereoType = stereotype
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
elif sig == b"ath\00":
|
|
172
|
+
athsig, tabletype = HcaAthHeaderStruct.unpack(
|
|
173
|
+
self.hcastream.read(HcaAthHeaderStruct.size)
|
|
174
|
+
)
|
|
175
|
+
self.hca.update(dict(AthSig = athsig, TableType = tabletype))
|
|
176
|
+
elif sig == b"vbr\00":
|
|
177
|
+
vbrsig, maxframesize, noiselevel = HcaVbrHeaderStruct.unpack(
|
|
178
|
+
self.hcastream.read(HcaVbrHeaderStruct.size)
|
|
179
|
+
)
|
|
180
|
+
self.hca.update(dict(VbrSig = vbrsig, MaxFrameSize = maxframesize, NoiseLevel = noiselevel))
|
|
181
|
+
elif sig == b"rva\00":
|
|
182
|
+
rvasig, volume = HcaRvaHeaderStruct.unpack(
|
|
183
|
+
self.hcastream.read(HcaRvaHeaderStruct.size)
|
|
184
|
+
)
|
|
185
|
+
self.hca.update(dict(RvaSig = rvasig, Volume = volume))
|
|
186
|
+
else:
|
|
187
|
+
break
|
|
188
|
+
Crc16 = self.hcastream.read(2)
|
|
189
|
+
self.hca.update(dict(Crc16 = Crc16))
|
|
190
|
+
|
|
191
|
+
elif self.HcaSig == b"RIFF":
|
|
192
|
+
self.filetype = "wav"
|
|
193
|
+
self.riffSignature, self.riffSize, self.wave, self.fmt, self.fmtSize, self.fmtType, self.fmtChannelCount, self.fmtSamplingRate, self.fmtSamplesPerSec, self.fmtSamplingSize, self.fmtBitCount = WavHeaderStruct.unpack(
|
|
194
|
+
self.stream.read(WavHeaderStruct.size)
|
|
195
|
+
)
|
|
196
|
+
if self.riffSignature == b"RIFF" and self.wave == b'WAVE' and self.fmt == b'fmt ':
|
|
197
|
+
if self.fmtBitCount != 16:
|
|
198
|
+
raise ValueError(f"WAV bitdepth of {self.fmtBitCount} is not supported, only 16 bit WAV files are supported.")
|
|
199
|
+
elif self.fmtSize != 16:
|
|
200
|
+
raise ValueError(f"WAV file has an FMT chunk of an unsupported size: {self.fmtSize}, the only supported size is 16.")
|
|
201
|
+
if self.stream.read(4) == b"smpl":
|
|
202
|
+
self.stream.seek(-4, 1)
|
|
203
|
+
self.looping = True
|
|
204
|
+
# Will just be naming the important things here.
|
|
205
|
+
smplsig, smplesize, _, _, _, _, _, _, _, self.LoopCount, _, _, _, self.LoopStartSample, self.LoopEndSample, _, _ = WavSmplHeaderStruct.unpack(
|
|
206
|
+
self.stream.read(WavSmplHeaderStruct.size)
|
|
207
|
+
)
|
|
208
|
+
if self.LoopCount != 1:
|
|
209
|
+
self.looping = False # Unsupported multiple looping points, so backtracks, and ignores looping data.
|
|
210
|
+
self.stream.seek(-WavSmplHeaderStruct.size, 1)
|
|
211
|
+
self.stream.seek(8 + smplesize, 1)
|
|
212
|
+
else:
|
|
213
|
+
self.stream.seek(-4, 1)
|
|
214
|
+
self.looping = False
|
|
215
|
+
if self.stream.read(4) == b"note": # There's no use for this on ADX.
|
|
216
|
+
len = self.stream.read(4)
|
|
217
|
+
self.stream.seek(len+4) # + 1? + padding maybe?
|
|
218
|
+
else:
|
|
219
|
+
self.stream.seek(-4, 1)
|
|
220
|
+
if self.stream.read(4) == b"data":
|
|
221
|
+
self.stream.seek(-4, 1)
|
|
222
|
+
self.dataSig, self.dataSize = WavDataHeaderStruct.unpack(
|
|
223
|
+
self.stream.read(WavDataHeaderStruct.size)
|
|
224
|
+
)
|
|
225
|
+
else:
|
|
226
|
+
raise ValueError("Invalid or an unsupported wav file.")
|
|
227
|
+
else:
|
|
228
|
+
raise ValueError("Invalid HCA or WAV file.")
|
|
229
|
+
self.stream.seek(0)
|
|
230
|
+
self.hcastream.seek(0)
|
|
231
|
+
|
|
232
|
+
def info(self) -> dict:
|
|
233
|
+
""" Returns info related to the input file. """
|
|
234
|
+
if self.filetype == "hca":
|
|
235
|
+
return self.hca
|
|
236
|
+
elif self.filetype == "wav":
|
|
237
|
+
wav = dict(RiffSignature=self.riffSignature.decode(), riffSize=self.riffSize, WaveSignature=self.wave.decode(), fmtSignature=self.fmt.decode(), fmtSize=self.fmtSize, fmtType=self.fmtType, fmtChannelCount=self.fmtChannelCount, fmtSamplingRate=self.fmtSamplingRate, fmtSamplesPerSec=self.fmtSamplesPerSec, fmtSamplingSize=self.fmtSamplingSize, fmtBitCount=self.fmtBitCount, dataSignature=self.dataSig.decode(), dataSize=self.dataSize)
|
|
238
|
+
return wav
|
|
239
|
+
|
|
240
|
+
def decode(self) -> bytes:
|
|
241
|
+
if self.filetype == "wav":
|
|
242
|
+
raise ValueError("Input type for decoding must be an HCA file.")
|
|
243
|
+
self.hcastream.seek(0)
|
|
244
|
+
self.wavbytes = CriCodecsEx.HcaDecode(self.hcastream.read(), self.header_size, self.key, self.subkey)
|
|
245
|
+
self.stream = BytesIO(self.wavbytes)
|
|
246
|
+
self.hcastream.seek(0)
|
|
247
|
+
return bytes(self.wavbytes)
|
|
248
|
+
|
|
249
|
+
def encode(self, force_not_looping: bool = False, encrypt: bool = False, keyless: bool = False, quality_level: CriHcaQuality = CriHcaQuality.High) -> bytes:
|
|
250
|
+
if self.filetype == "hca":
|
|
251
|
+
raise ValueError("Input type for encoding must be a WAV file.")
|
|
252
|
+
if force_not_looping == False:
|
|
253
|
+
force_not_looping = 0
|
|
254
|
+
elif force_not_looping == True:
|
|
255
|
+
force_not_looping = 1
|
|
256
|
+
else:
|
|
257
|
+
raise ValueError("Forcing the encoder to not loop is by either False or True.")
|
|
258
|
+
if quality_level not in list(CriHcaQuality):
|
|
259
|
+
raise ValueError("Chosen quality level is not valid or is not the appropiate enumeration value.")
|
|
260
|
+
self.stream.seek(0)
|
|
261
|
+
self.hcabytes = CriCodecsEx.HcaEncode(self.stream.read(), force_not_looping, quality_level.value)
|
|
262
|
+
self.hcastream = BytesIO(self.hcabytes)
|
|
263
|
+
self.Pyparse_header()
|
|
264
|
+
if encrypt:
|
|
265
|
+
if self.key == 0 and not keyless:
|
|
266
|
+
self.key = 0xCF222F1FE0748978 # Default key.
|
|
267
|
+
self.encrypt(self.key, keyless)
|
|
268
|
+
return self.get_hca()
|
|
269
|
+
|
|
270
|
+
def encrypt(self, keycode: int, subkey: int = 0, keyless: bool = False) -> None:
|
|
271
|
+
if(self.encrypted):
|
|
272
|
+
raise ValueError("HCA is already encrypted.")
|
|
273
|
+
self.encrypted = True
|
|
274
|
+
enc = CriCodecsEx.HcaCrypt(self.get_hca(), 1, self.header_size, (1 if keyless else 56), keycode, subkey)
|
|
275
|
+
self.hcastream = BytesIO(enc)
|
|
276
|
+
|
|
277
|
+
def decrypt(self, keycode: int, subkey: int = 0) -> None:
|
|
278
|
+
if(not self.encrypted):
|
|
279
|
+
raise ValueError("HCA is already decrypted.")
|
|
280
|
+
self.encrypted = False
|
|
281
|
+
dec = CriCodecsEx.HcaCrypt(self.get_hca(), 0, self.header_size, 0, keycode, subkey)
|
|
282
|
+
self.hcastream = BytesIO(dec)
|
|
283
|
+
|
|
284
|
+
def get_hca(self) -> bytes:
|
|
285
|
+
""" Use this function to get the HCA file bytes after encrypting or decrypting. """
|
|
286
|
+
self.hcastream.seek(0)
|
|
287
|
+
fl: bytes = self.hcastream.read()
|
|
288
|
+
self.hcastream.seek(0)
|
|
289
|
+
return fl
|
|
290
|
+
|
|
291
|
+
def get_frames(self):
|
|
292
|
+
""" Generator function to yield Frame number, and Frame data. """
|
|
293
|
+
self.hcastream.seek(self.header_size, 0)
|
|
294
|
+
for i in range(self.hca['FrameCount']):
|
|
295
|
+
yield (i, self.hcastream.read(self.hca['FrameSize']))
|
|
296
|
+
|
|
297
|
+
def get_header(self) -> bytes:
|
|
298
|
+
""" Use this function to retrieve the HCA Header. """
|
|
299
|
+
self.hcastream.seek(0)
|
|
300
|
+
header = self.hcastream.read(self.header_size)
|
|
301
|
+
self.hcastream.seek(0)
|
|
302
|
+
return header
|