PyCriCodecsEx 0.0.3__cp312-cp312-win_amd64.whl → 0.0.4__cp312-cp312-win_amd64.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.

Potentially problematic release.


This version of PyCriCodecsEx might be problematic. Click here for more details.

Binary file
PyCriCodecsEx/__init__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.0.3"
1
+ __version__ = "0.0.4"
PyCriCodecsEx/acb.py CHANGED
@@ -14,12 +14,16 @@ from copy import deepcopy
14
14
 
15
15
  class CueNameTable(UTFViewer):
16
16
  CueIndex: int
17
+ '''Index into CueTable'''
17
18
  CueName: str
19
+ '''Name of the cue'''
18
20
 
19
21
 
20
22
  class CueTable(UTFViewer):
21
23
  CueId: int
24
+ '''Corresponds to the cue index found in CueNameTable'''
22
25
  Length: int
26
+ '''Duration of the cue in milliseconds'''
23
27
  ReferenceIndex: int
24
28
  ReferenceType: int
25
29
 
@@ -52,14 +56,21 @@ class WaveformTable(UTFViewer):
52
56
 
53
57
 
54
58
  class ACBTable(UTFViewer):
59
+ '''ACB Table View'''
60
+
55
61
  AcbGuid: bytes
62
+ '''GUID of the ACB. This SHOULD be different for each ACB file.'''
56
63
  Name: str
57
- Version: int
64
+ '''Name of the ACB. This is usually the name of the sound bank.'''
65
+ Version: int
58
66
  VersionString: str
59
67
 
60
68
  AwbFile: bytes
61
69
  CueNameTable: List[CueNameTable]
70
+ '''A list of cue names with their corresponding indices into CueTable'''
62
71
  CueTable: List[CueTable]
72
+ '''A list of cues with their corresponding references'''
73
+
63
74
  SequenceTable: List[SequenceTable]
64
75
  SynthTable: List[SynthTable]
65
76
  TrackEventTable: List[TrackEventTable]
@@ -67,7 +78,7 @@ class ACBTable(UTFViewer):
67
78
  WaveformTable: List[WaveformTable]
68
79
 
69
80
  @staticmethod
70
- def decode_tlv(data : bytes):
81
+ def _decode_tlv(data : bytes):
71
82
  pos = 0
72
83
  while pos < len(data):
73
84
  tag = data[pos : pos + 2]
@@ -76,16 +87,16 @@ class ACBTable(UTFViewer):
76
87
  pos += 3 + length
77
88
  yield (tag, value)
78
89
 
79
- def waveform_of_track(self, index: int):
80
- tlv = self.decode_tlv(self.TrackEventTable[index])
90
+ def _waveform_of_track(self, index: int):
91
+ tlv = self._decode_tlv(self.TrackEventTable[index])
81
92
  def noteOn(data: bytes):
82
93
  # Handle note on event
83
94
  tlv_type, tlv_index = AcbTrackCommandNoteOnStruct.unpack(data[:AcbTrackCommandNoteOnStruct.size])
84
95
  match tlv_type:
85
96
  case 0x02: # Synth
86
- yield from self.waveform_of_synth(tlv_index)
97
+ yield from self._waveform_of_synth(tlv_index)
87
98
  case 0x03: # Sequence
88
- yield from self.waveform_of_sequence(tlv_index)
99
+ yield from self._waveform_of_sequence(tlv_index)
89
100
  # Ignore others silently
90
101
  for code, data in tlv:
91
102
  match code:
@@ -94,13 +105,13 @@ class ACBTable(UTFViewer):
94
105
  case 2003:
95
106
  yield from noteOn(data)
96
107
 
97
- def waveform_of_sequence(self, index : int):
108
+ def _waveform_of_sequence(self, index : int):
98
109
  seq = self.SequenceTable[index]
99
110
  for i in range(seq.NumTracks):
100
111
  track_index = int.from_bytes(seq.TrackIndex[i*2:i*2+2], 'big')
101
112
  yield self.WaveformTable[track_index]
102
113
 
103
- def waveform_of_synth(self, index: int):
114
+ def _waveform_of_synth(self, index: int):
104
115
  item_type, item_index = AcbSynthReferenceStruct.unpack(self.SynthTable[index].ReferenceItems)
105
116
  match item_type:
106
117
  case 0x00: # No audio
@@ -108,33 +119,42 @@ class ACBTable(UTFViewer):
108
119
  case 0x01: # Waveform
109
120
  yield self.WaveformTable[item_index]
110
121
  case 0x02: # Yet another synth...
111
- yield from self.waveform_of_synth(item_index)
122
+ yield from self._waveform_of_synth(item_index)
112
123
  case 0x03: # Sequence
113
- yield from self.waveform_of_sequence(item_index)
124
+ yield from self._waveform_of_sequence(item_index)
114
125
  case _:
115
126
  raise NotImplementedError(f"Unknown synth reference type: {item_type} at index {index}")
116
127
 
117
128
  def waveform_of(self, index : int) -> List["WaveformTable"]:
129
+ """Retrieves the waveform(s) associated with a cue.
130
+
131
+ Cues may reference multiple waveforms, which could also be reused."""
118
132
  cue = next(filter(lambda c: c.CueId == index, self.CueTable), None)
119
133
  assert cue, "cue of index %d not found" % index
120
134
  match cue.ReferenceType:
121
135
  case 0x01:
122
136
  return [self.WaveformTable[index]]
123
137
  case 0x02:
124
- return list(self.waveform_of_synth(index))
138
+ return list(self._waveform_of_synth(index))
125
139
  case 0x03:
126
- return list(self.waveform_of_sequence(index))
140
+ return list(self._waveform_of_sequence(index))
127
141
  case 0x08:
128
142
  raise NotImplementedError("BlockSequence type not implemented yet")
129
143
  case _:
130
144
  raise NotImplementedError(f"Unknown cue reference type: {cue.ReferenceType}")
131
145
 
132
146
  @dataclass(frozen=True)
133
- class CueItem:
147
+ class PackedCueItem:
148
+ '''Helper class for read-only cue information'''
149
+
134
150
  CueId: int
151
+ '''Cue ID'''
135
152
  CueName: str
153
+ '''Cue name'''
136
154
  Length: float
137
- Waveforms: list[int] # List of waveform IDs
155
+ '''Duration in seconds'''
156
+ Waveforms: list[int]
157
+ '''List of waveform IDs, corresponds to ACB.get_waveforms()'''
138
158
 
139
159
  class ACB(UTF):
140
160
  """Use this class to read, and modify ACB files in memory."""
@@ -234,7 +254,7 @@ class ACB(UTF):
234
254
  pass
235
255
 
236
256
  @property
237
- def cues(self) -> Generator[CueItem, None, None]:
257
+ def cues(self) -> Generator[PackedCueItem, None, None]:
238
258
  """Returns a generator of **read-only** Cues.
239
259
 
240
260
  Cues reference waveform bytes by their AWB IDs, which can be accessed via `waveforms`.
@@ -242,7 +262,7 @@ class ACB(UTF):
242
262
  """
243
263
  for name, cue in zip(self.view.CueNameTable, self.view.CueTable):
244
264
  waveforms = self.view.waveform_of(cue.CueId)
245
- yield CueItem(cue.CueId, name.CueName, cue.Length / 1000.0, [waveform.MemoryAwbId for waveform in waveforms])
265
+ yield PackedCueItem(cue.CueId, name.CueName, cue.Length / 1000.0, [waveform.MemoryAwbId for waveform in waveforms])
246
266
 
247
267
  class ACBBuilder:
248
268
  """Use this class to build ACB files from an existing ACB object."""
PyCriCodecsEx/adx.py CHANGED
@@ -144,12 +144,15 @@ class ADXCodec(ADX):
144
144
  def get_metadata(self):
145
145
  return None
146
146
 
147
- def get_encoded(self):
147
+ def get_encoded(self) -> bytes:
148
148
  """Gets the encoded ADX audio data."""
149
149
  return self.adx
150
150
 
151
- def save(self, filepath: str):
152
- """Saves the encoded ADX audio to filepath"""
153
- with open(filepath, "wb") as f:
154
- f.write(self.decode(self.adx))
151
+ def save(self, filepath: str | BinaryIO):
152
+ """Saves the decoded WAV audio to filepath or a writable stream"""
153
+ if type(filepath) == str:
154
+ with open(filepath, "wb") as f:
155
+ f.write(self.decode(self.adx))
156
+ else:
157
+ filepath.write(self.decode(self.adx))
155
158
 
PyCriCodecsEx/awb.py CHANGED
@@ -1,6 +1,5 @@
1
1
  from io import BytesIO, FileIO
2
- import os
3
- from typing import BinaryIO
2
+ from typing import BinaryIO, Generator
4
3
  from struct import iter_unpack, pack
5
4
  from PyCriCodecsEx.chunk import *
6
5
  from PyCriCodecsEx.hca import HCA
@@ -55,7 +54,7 @@ class AWB:
55
54
  self.headersize = self.headersize + (self.align - (self.headersize % self.align))
56
55
  self.stream.seek(self.headersize, 0)
57
56
 
58
- def get_files(self):
57
+ def get_files(self) -> Generator[bytes, None, None]:
59
58
  """Generator function to yield all data blobs from an AWB. """
60
59
  self.stream.seek(self.headersize, 0)
61
60
  for i in range(1, len(self.ofs)):
PyCriCodecsEx/cpk.py CHANGED
@@ -1,20 +1,27 @@
1
1
  import os
2
- from typing import BinaryIO
2
+ from typing import BinaryIO, Generator
3
3
  from io import BytesIO, FileIO
4
4
  from PyCriCodecsEx.chunk import *
5
5
  from PyCriCodecsEx.utf import UTF, UTFBuilder
6
6
  from dataclasses import dataclass
7
- from concurrent.futures import ProcessPoolExecutor, as_completed
7
+ from concurrent.futures import ThreadPoolExecutor, as_completed
8
8
  from tempfile import NamedTemporaryFile
9
9
  import CriCodecsEx
10
10
 
11
- def _worker_do_compression(src : str, dst: str):
11
+ def _crilayla_compress_to_file(src : str, dst: str):
12
12
  with open(src, "rb") as fsrc, open(dst, "wb") as fdst:
13
13
  data = fsrc.read()
14
- compressed = CriCodecsEx.CriLaylaCompress(data)
15
- fdst.write(compressed)
14
+ try:
15
+ compressed = CriCodecsEx.CriLaylaCompress(data)
16
+ fdst.write(compressed)
17
+ except:
18
+ # Fallback for failed compression
19
+ # Again. FIXME.
20
+ fdst.write(data)
21
+
16
22
  @dataclass
17
- class _PackFile():
23
+ class PackedFile():
24
+ """Helper class for packed files within a CPK."""
18
25
  stream: BinaryIO
19
26
  path: str
20
27
  offset: int
@@ -22,6 +29,7 @@ class _PackFile():
22
29
  compressed : bool = False
23
30
 
24
31
  def get_bytes(self) -> bytes:
32
+ """Get the raw bytes of the packed file, decompressing if necessary."""
25
33
  self.stream.seek(self.offset)
26
34
  data = self.stream.read(self.size)
27
35
  if self.compressed:
@@ -29,6 +37,7 @@ class _PackFile():
29
37
  return data
30
38
 
31
39
  def save(self, path : str):
40
+ """Save the packed file to a specified path."""
32
41
  with open(path, "wb") as f:
33
42
  f.write(self.get_bytes())
34
43
  class _TOC():
@@ -115,6 +124,9 @@ class CPK:
115
124
 
116
125
  @property
117
126
  def mode(self):
127
+ """Get the current mode of the CPK archive. [0,1,2,3]
128
+
129
+ See also CPKBuilder"""
118
130
  TOC, ITOC, GTOC = 'TOC' in self.tables, 'ITOC' in self.tables, 'GTOC' in self.tables
119
131
  if TOC and ITOC and GTOC:
120
132
  return 3
@@ -127,8 +139,8 @@ class CPK:
127
139
  raise ValueError("Unknown CPK mode.")
128
140
 
129
141
  @property
130
- def files(self):
131
- """Retrieves a list of all files in the CPK archive."""
142
+ def files(self) -> Generator[PackedFile, None, None]:
143
+ """Creates a generator for all files in the CPK archive as PackedFile."""
132
144
  if "TOC" in self.tables:
133
145
  toctable = self.tables['TOC']
134
146
  rel_off = 0x800
@@ -139,10 +151,10 @@ class CPK:
139
151
  filename = filename[:250] + "_" + str(i) # 250 because i might be 4 digits long.
140
152
  if toctable['ExtractSize'][i] > toctable['FileSize'][i]:
141
153
  self.stream.seek(rel_off+toctable["FileOffset"][i], 0)
142
- yield _PackFile(self.stream, os.path.join(dirname,filename), self.stream.tell(), toctable['FileSize'][i], compressed=True)
154
+ yield PackedFile(self.stream, os.path.join(dirname,filename), self.stream.tell(), toctable['FileSize'][i], compressed=True)
143
155
  else:
144
156
  self.stream.seek(rel_off+toctable["FileOffset"][i], 0)
145
- yield _PackFile(self.stream, os.path.join(dirname,filename), self.stream.tell(), toctable['FileSize'][i])
157
+ yield PackedFile(self.stream, os.path.join(dirname,filename), self.stream.tell(), toctable['FileSize'][i])
146
158
  elif "ITOC" in self.tables:
147
159
  toctableL = self.tables["ITOC"]['DataL'][0]
148
160
  toctableH = self.tables["ITOC"]['DataH'][0]
@@ -154,18 +166,18 @@ class CPK:
154
166
  if i in toctableH['ID']:
155
167
  idx = toctableH['ID'].index(i)
156
168
  if toctableH['ExtractSize'][idx] > toctableH['FileSize'][idx]:
157
- yield _PackFile(self.stream, str(i), self.stream.tell(), toctableH['FileSize'][idx], compressed=True)
169
+ yield PackedFile(self.stream, str(i), self.stream.tell(), toctableH['FileSize'][idx], compressed=True)
158
170
  else:
159
- yield _PackFile(self.stream, str(i), self.stream.tell(), toctableH['FileSize'][idx])
171
+ yield PackedFile(self.stream, str(i), self.stream.tell(), toctableH['FileSize'][idx])
160
172
  if toctableH['FileSize'][idx] % align != 0:
161
173
  seek_size = (align - toctableH['FileSize'][idx] % align)
162
174
  self.stream.seek(seek_size, 1)
163
175
  elif i in toctableL['ID']:
164
176
  idx = toctableL['ID'].index(i)
165
177
  if toctableL['ExtractSize'][idx] > toctableL['FileSize'][idx]:
166
- yield _PackFile(self.stream, str(i), self.stream.tell(), toctableL['FileSize'][idx], compressed=True)
178
+ yield PackedFile(self.stream, str(i), self.stream.tell(), toctableL['FileSize'][idx], compressed=True)
167
179
  else:
168
- yield _PackFile(self.stream, str(i), self.stream.tell(), toctableL['FileSize'][idx])
180
+ yield PackedFile(self.stream, str(i), self.stream.tell(), toctableL['FileSize'][idx])
169
181
  if toctableL['FileSize'][idx] % align != 0:
170
182
  seek_size = (align - toctableL['FileSize'][idx] % align)
171
183
  self.stream.seek(seek_size, 1)
@@ -247,9 +259,8 @@ class CPKBuilder:
247
259
  compress (bool, optional): Whether to compress the file. Defaults to False.
248
260
 
249
261
  NOTE:
250
- - In ITOC-related mode, the insertion order determines the final integer ID of the files.
251
- - Compression can be VERY slow with high entropy files (e.g. encoded media). Use at discretion.
252
- """
262
+ - In ITOC-related mode, the insertion order determines the final integer ID of the files.
263
+ """
253
264
  if not dst and self.mode != 0:
254
265
  raise ValueError("Destination filename must be specified in non-ITOC mode.")
255
266
 
@@ -263,7 +274,7 @@ class CPKBuilder:
263
274
  self.outfile.write(bytes(0x800 - pack_size % 0x800))
264
275
  self.progress_cb("Write %s" % os.path.basename(filename), i + 1, len(self.files))
265
276
 
266
- def _populate_files(self, parallel : bool):
277
+ def _populate_files(self, threads : int = 1):
267
278
  self.files = []
268
279
  for src, dst, compress in self.in_files:
269
280
  if compress:
@@ -271,23 +282,15 @@ class CPKBuilder:
271
282
  self.os_files.append((tmp.name, True))
272
283
  else:
273
284
  self.os_files.append((src, False))
274
- if parallel:
275
- with ProcessPoolExecutor() as exec:
276
- futures = []
277
- for (src, _, _), (dst, compress) in zip(self.in_files,self.os_files):
278
- if compress:
279
- futures.append(exec.submit(_worker_do_compression, src, dst))
280
- for i, fut in as_completed(futures):
281
- try:
282
- fut.result()
283
- except:
284
- pass
285
- self.progress_cb("Compress %s" % os.path.basename(src), i + 1, len(futures))
286
- else:
287
- for i, ((src, _, _), (dst, compress)) in enumerate(zip(self.in_files,self.os_files)):
288
- if compress:
289
- _worker_do_compression(src, dst)
290
- self.progress_cb("Compress %s" % os.path.basename(src), i + 1, len(self.in_files))
285
+ with ThreadPoolExecutor(max_workers=threads) as exec:
286
+ futures = []
287
+ for (src, _, _), (dst, compress) in zip(self.in_files,self.os_files):
288
+ if compress:
289
+ _crilayla_compress_to_file(src, dst)
290
+ # futures.append(exec.submit(_crilayla_compress_to_file, src, dst))
291
+ for i, fut in enumerate(as_completed(futures)):
292
+ fut.result()
293
+ self.progress_cb("Compress %s" % os.path.basename(src), i + 1, len(futures))
291
294
  for (src, filename, _) , (dst, _) in zip(self.in_files,self.os_files):
292
295
  file_size = os.stat(src).st_size
293
296
  pack_size = os.stat(dst).st_size
@@ -304,23 +307,22 @@ class CPKBuilder:
304
307
  pass
305
308
  self.os_files = []
306
309
 
307
- def save(self, outfile : str | BinaryIO, parallel : bool = False):
310
+ def save(self, outfile : str | BinaryIO, threads : int = 1):
308
311
  """Build and save the bundle into a file
309
312
 
310
313
 
311
314
  Args:
312
315
  outfile (str | BinaryIO): The output file path or a writable binary stream.
313
- parallel (bool, optional): Whether to use parallel processing for file compression (if at all used). Defaults to False.
316
+ threads (int, optional): The number of threads to use for file compression. Defaults to 1.
314
317
 
315
318
  NOTE:
316
319
  - Temporary files may be created during the process if compression is used.
317
- - parallel uses multiprocessing. Make sure your main function is guarded with `if __name__ == '__main__'` clause.
318
320
  """
319
321
  assert self.in_files, "cannot save empty bundle"
320
322
  self.outfile = outfile
321
323
  if type(outfile) == str:
322
324
  self.outfile = open(outfile, "wb")
323
- self._populate_files(parallel)
325
+ self._populate_files(threads)
324
326
  if self.encrypt:
325
327
  encflag = 0
326
328
  else:
PyCriCodecsEx/hca.py CHANGED
@@ -438,14 +438,17 @@ class HCACodec(HCA):
438
438
  p.strings = b"<NULL>\x00" + p.strings
439
439
  return p.bytes()
440
440
 
441
- def get_encoded(self):
441
+ def get_encoded(self) -> bytes:
442
442
  """Gets the encoded HCA audio data."""
443
443
  self.hcastream.seek(0)
444
444
  res = self.hcastream.read()
445
445
  self.hcastream.seek(0)
446
446
  return res
447
447
 
448
- def save(self, filepath: str):
449
- """Saves the decoded WAV audio to filepath"""
450
- with open(filepath, "wb") as f:
451
- f.write(self.decode())
448
+ def save(self, filepath: str | BinaryIO):
449
+ """Saves the decoded WAV audio to filepath or a writable stream"""
450
+ if type(filepath) == str:
451
+ with open(filepath, "wb") as f:
452
+ f.write(self.decode())
453
+ else:
454
+ filepath.write(self.decode())
PyCriCodecsEx/usm.py CHANGED
@@ -150,6 +150,7 @@ class USMCrypt:
150
150
  # are still unknown how to derive them, at least video wise it is possible, no idea how it's calculated audio wise nor anything else
151
151
  # seems like it could be random values and the USM would still work.
152
152
  class FFmpegCodec:
153
+ """Base codec for FFMpeg-based Video streams"""
153
154
  filename: str
154
155
  filesize: int
155
156
 
@@ -232,7 +233,7 @@ class FFmpegCodec:
232
233
  return len(self.packets)
233
234
 
234
235
  def frames(self):
235
- """frame data, frame dict, is keyframe, duration"""
236
+ """Generator of [frame data, frame dict, is keyframe, duration]"""
236
237
  offsets = [int(packet["pos"]) for packet in self.packets] + [self.filesize]
237
238
  for i, frame in enumerate(self.packets):
238
239
  frame_size = offsets[i + 1] - offsets[i]
@@ -290,13 +291,16 @@ class FFmpegCodec:
290
291
  return SFV_list
291
292
 
292
293
  def save(self, filepath: str):
293
- '''Saves the raw, underlying video stream to a file.'''
294
+ '''Saves the underlying video stream to a file.'''
294
295
  tell = self.file.tell()
295
296
  self.file.seek(0)
296
297
  shutil.copyfileobj(self.file, open(filepath, 'wb'))
297
298
  self.file.seek(tell)
298
299
 
299
300
  class VP9Codec(FFmpegCodec):
301
+ """VP9 Video stream codec.
302
+
303
+ Only streams with `.ivf` containers are supported."""
300
304
  MPEG_CODEC = 9
301
305
  MPEG_DCPREC = 0
302
306
  VERSION = 16777984
@@ -305,6 +309,9 @@ class VP9Codec(FFmpegCodec):
305
309
  super().__init__(filename)
306
310
  assert self.format == "ivf", "must be ivf format."
307
311
  class H264Codec(FFmpegCodec):
312
+ """H264 Video stream codec.
313
+
314
+ Only streams with `.h264` containers are supported."""
308
315
  MPEG_CODEC = 5
309
316
  MPEG_DCPREC = 11
310
317
  VERSION = 0
@@ -315,6 +322,9 @@ class H264Codec(FFmpegCodec):
315
322
  self.format == "h264"
316
323
  ), "must be raw h264 data. transcode with '.h264' suffix as output"
317
324
  class MPEG1Codec(FFmpegCodec):
325
+ """MPEG1 Video stream codec.
326
+
327
+ Only streams with `.mpeg1` containers are supported."""
318
328
  MPEG_CODEC = 1
319
329
  MPEG_DCPREC = 11
320
330
  VERSION = 0
@@ -482,7 +492,7 @@ class USM(USMCrypt):
482
492
  stmid = int.to_bytes(stmid, 4, 'big', signed='False')
483
493
  yield stmid, str(filename), self.output.get(f'{stmid.decode()}_{chno}', None)
484
494
 
485
- def get_video(self):
495
+ def get_video(self) -> VP9Codec | H264Codec | MPEG1Codec:
486
496
  """Create a video codec from the available streams.
487
497
 
488
498
  NOTE: A temporary file may be created with this process to determine the stream information."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyCriCodecsEx
3
- Version: 0.0.3
3
+ Version: 0.0.4
4
4
  Summary: Criware formats library for Python
5
5
  Home-page: https://mos9527.github.io/PyCriCodecsEx/
6
6
  Classifier: Programming Language :: Python :: 3
@@ -0,0 +1,15 @@
1
+ CriCodecsEx.cp312-win_amd64.pyd,sha256=pqGVioKHyP_hufLJR6mol-osXt2GVnqMe2E2gTL3_hE,89088
2
+ PyCriCodecsEx/__init__.py,sha256=b8Tfyn46wV92NWm_uziWVxCW3zTxPV8EyFKcI9HhXcA,23
3
+ PyCriCodecsEx/acb.py,sha256=7eKR_pxn7gqhFONRUsdrIdHczxqJUnfRrVEe4vA6pFM,11549
4
+ PyCriCodecsEx/adx.py,sha256=HvYyRTjOWXnV9kY0qzxTW3YRB-4m35czxDd-RjEhG1c,6117
5
+ PyCriCodecsEx/awb.py,sha256=SCTkKvn855bpr1I8Kq24P1es89I2unJaEGgDw7hg1Do,6395
6
+ PyCriCodecsEx/chunk.py,sha256=xmc92tONmXpdbOW2g-KckTeppTXFo8Km0J95UYbbvQg,2810
7
+ PyCriCodecsEx/cpk.py,sha256=er_eBLMG8eiXUvYp0h_lDWLN8Vx8CCDJ8COE9B5Rv_A,38125
8
+ PyCriCodecsEx/hca.py,sha256=9GgAOS-mkd4pdXAF5YX0hzKt7nI7_LE5dZT5I--5Tbo,19587
9
+ PyCriCodecsEx/usm.py,sha256=MQg229op95pGw46bvdXjwDBOdwZqlEXOc2SWGggLivU,37361
10
+ PyCriCodecsEx/utf.py,sha256=0LBzpIbJiyL_4KOf_QpEJytr3PbHj8_M4Bd-Lay4wuk,28247
11
+ pycricodecsex-0.0.4.dist-info/licenses/LICENSE,sha256=B47Qr_82CmMyuL-wNFAH_P6PNJWaonv5dxo9MfgjEXw,1083
12
+ pycricodecsex-0.0.4.dist-info/METADATA,sha256=gGvEr_w5BQsqBT68VHnsnqoz2sPkCPqU1IqQy4Xk92E,1291
13
+ pycricodecsex-0.0.4.dist-info/WHEEL,sha256=8UP9x9puWI0P1V_d7K2oMTBqfeLNm21CTzZ_Ptr0NXU,101
14
+ pycricodecsex-0.0.4.dist-info/top_level.txt,sha256=mSrrEse9hT0s6nl-sWAQWAhNRuZ6jo98pbFoN3L2MXk,26
15
+ pycricodecsex-0.0.4.dist-info/RECORD,,
@@ -1,15 +0,0 @@
1
- CriCodecsEx.cp312-win_amd64.pyd,sha256=j6H6MvrIAsUnMm0-eSBtlDUJFq96OJJaRDfYk8PQjO0,89088
2
- PyCriCodecsEx/__init__.py,sha256=4dCrvStS1OgE3Vlcv-qx5-TAo377yOMEToEqCi-PwY4,23
3
- PyCriCodecsEx/acb.py,sha256=nOWggyQTPOBArzRp_7VRO9TY1diLxr2mRPKQd-sd2s0,10723
4
- PyCriCodecsEx/adx.py,sha256=s3gJBgKrwWjUahrxSJEy1GNHJrdz3iaylgr0TyTIi7M,5967
5
- PyCriCodecsEx/awb.py,sha256=t6owuEOH3Be31iBuEMwriOh0cPdVtpzsNbTPfLwg_cc,6363
6
- PyCriCodecsEx/chunk.py,sha256=xmc92tONmXpdbOW2g-KckTeppTXFo8Km0J95UYbbvQg,2810
7
- PyCriCodecsEx/cpk.py,sha256=bMJgvzPInM95R9dEdhHGW65jR9tFBKUacyOwZS2gOyA,38206
8
- PyCriCodecsEx/hca.py,sha256=BamujR3tRxMmgnPAYpUOamzOUOBs5hXWhkcBnj7Eeh4,19445
9
- PyCriCodecsEx/usm.py,sha256=LWSXXkfV1FZpymV0X6UH-lyS5gV6ecBHlLLXXXN9wMc,36972
10
- PyCriCodecsEx/utf.py,sha256=0LBzpIbJiyL_4KOf_QpEJytr3PbHj8_M4Bd-Lay4wuk,28247
11
- pycricodecsex-0.0.3.dist-info/licenses/LICENSE,sha256=B47Qr_82CmMyuL-wNFAH_P6PNJWaonv5dxo9MfgjEXw,1083
12
- pycricodecsex-0.0.3.dist-info/METADATA,sha256=buywcqSll1E63mnWNtEgwHiyAY8NbvMyP8CDLXAJLH0,1291
13
- pycricodecsex-0.0.3.dist-info/WHEEL,sha256=8UP9x9puWI0P1V_d7K2oMTBqfeLNm21CTzZ_Ptr0NXU,101
14
- pycricodecsex-0.0.3.dist-info/top_level.txt,sha256=mSrrEse9hT0s6nl-sWAQWAhNRuZ6jo98pbFoN3L2MXk,26
15
- pycricodecsex-0.0.3.dist-info/RECORD,,