cridecoder 0.3.4__tar.gz → 0.3.5__tar.gz
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.
- {cridecoder-0.3.4 → cridecoder-0.3.5}/Cargo.lock +1 -1
- {cridecoder-0.3.4 → cridecoder-0.3.5}/Cargo.toml +4 -4
- {cridecoder-0.3.4 → cridecoder-0.3.5}/PKG-INFO +2 -2
- {cridecoder-0.3.4 → cridecoder-0.3.5}/pyproject.toml +1 -1
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/hca/decoder.rs +6 -4
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/hca/hca_file.rs +5 -1
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/python.rs +66 -38
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/usm/extractor.rs +6 -8
- {cridecoder-0.3.4 → cridecoder-0.3.5}/.github/copilot-instructions.md +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/.github/dependabot.yml +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/.github/workflows/ci.yml +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/.github/workflows/release-crate.yml +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/.github/workflows/release-python.yml +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/.gitignore +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/AGENTS.md +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/CLAUDE.md +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/KNOWN_GAPS.md +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/LICENSE +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/README.md +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/cridecoder.pyi +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/examples/debug_acb.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/examples/hca_attrib.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/examples/hca_parallel.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/examples/profile_acb.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/examples/profile_hca.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/examples/test_acb.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/examples/test_hca.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/examples/test_usm.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/acb/afs.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/acb/builder.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/acb/consts.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/acb/decode.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/acb/extractor.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/acb/track.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/acb/utf.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/acb.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/hca/ath.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/hca/bitreader.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/hca/cipher.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/hca/encoder.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/hca/imdct.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/hca/tables.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/hca.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/lib.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/reader.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/usm/builder.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/usm/metadata.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/src/usm.rs +0 -0
- {cridecoder-0.3.4 → cridecoder-0.3.5}/tests/integration_tests.rs +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "cridecoder"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.5"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
description = "CRI codec library for ACB/AWB, HCA audio, and USM video extraction"
|
|
6
6
|
license = "MIT"
|
|
7
|
-
authors = ["
|
|
8
|
-
repository = "https://github.com/
|
|
9
|
-
homepage = "https://github.com/
|
|
7
|
+
authors = ["Seiunx Dev"]
|
|
8
|
+
repository = "https://github.com/seiunx-dev/cridecoder"
|
|
9
|
+
homepage = "https://github.com/seiunx-dev/cridecoder"
|
|
10
10
|
readme = "README.md"
|
|
11
11
|
keywords = ["cri", "hca", "acb", "usm", "audio"]
|
|
12
12
|
categories = ["multimedia::audio", "multimedia::video", "parser-implementations"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cridecoder
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.5
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -11,7 +11,7 @@ Classifier: Topic :: Multimedia :: Sound/Audio
|
|
|
11
11
|
Classifier: Topic :: Multimedia :: Video
|
|
12
12
|
License-File: LICENSE
|
|
13
13
|
Summary: CRI codec library for ACB/AWB, HCA audio, and USM video extraction
|
|
14
|
-
Home-Page: https://github.com/
|
|
14
|
+
Home-Page: https://github.com/seiunx-dev/cridecoder
|
|
15
15
|
License: MIT
|
|
16
16
|
Requires-Python: >=3.9
|
|
17
17
|
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
@@ -179,8 +179,8 @@ const fn build_crc16_slice() -> [[u16; 256]; 8] {
|
|
|
179
179
|
/// naive loop (verified in tests) but ~5x faster over a full frame.
|
|
180
180
|
pub fn crc16_checksum(data: &[u8]) -> u16 {
|
|
181
181
|
let mut sum: u16 = 0;
|
|
182
|
-
let
|
|
183
|
-
for c in
|
|
182
|
+
let (chunks, remainder) = data.as_chunks::<8>();
|
|
183
|
+
for c in chunks {
|
|
184
184
|
let i0 = ((sum >> 8) as u8 ^ c[0]) as usize;
|
|
185
185
|
let i1 = (sum as u8 ^ c[1]) as usize;
|
|
186
186
|
sum = CRC16_SLICE[7][i0]
|
|
@@ -192,7 +192,7 @@ pub fn crc16_checksum(data: &[u8]) -> u16 {
|
|
|
192
192
|
^ CRC16_SLICE[1][c[6] as usize]
|
|
193
193
|
^ CRC16_SLICE[0][c[7] as usize];
|
|
194
194
|
}
|
|
195
|
-
for &byte in
|
|
195
|
+
for &byte in remainder {
|
|
196
196
|
sum = (sum << 8) ^ CRC16_TABLE[((sum >> 8) ^ byte as u16) as usize];
|
|
197
197
|
}
|
|
198
198
|
sum
|
|
@@ -1024,7 +1024,9 @@ impl ClHca {
|
|
|
1024
1024
|
}
|
|
1025
1025
|
}
|
|
1026
1026
|
for (ch, chunk) in out
|
|
1027
|
-
.
|
|
1027
|
+
.as_chunks_mut::<HCA_SAMPLES_PER_FRAME>()
|
|
1028
|
+
.0
|
|
1029
|
+
.iter_mut()
|
|
1028
1030
|
.enumerate()
|
|
1029
1031
|
.take(channels)
|
|
1030
1032
|
{
|
|
@@ -702,7 +702,11 @@ fn decode_chunk_pcm(a: DecodeChunkArgs<'_>) -> Result<Vec<u8>, HcaError> {
|
|
|
702
702
|
imdct_overlap(dct0, &mut a.prev[0], a.wave);
|
|
703
703
|
imdct_overlap(dct1, &mut a.prev[1], &mut wave2);
|
|
704
704
|
let out = &mut a.pcm[sf * 256..(sf + 1) * 256];
|
|
705
|
-
for (pair, (&l, &r)) in out
|
|
705
|
+
for (pair, (&l, &r)) in out
|
|
706
|
+
.as_chunks_mut::<2>()
|
|
707
|
+
.0
|
|
708
|
+
.iter_mut()
|
|
709
|
+
.zip(a.wave.iter().zip(wave2.iter()))
|
|
706
710
|
{
|
|
707
711
|
pair[0] = pcm_f32_to_i16(l);
|
|
708
712
|
pair[1] = pcm_f32_to_i16(r);
|
|
@@ -27,13 +27,13 @@ use crate::usm::UsmBuilder;
|
|
|
27
27
|
/// Returns:
|
|
28
28
|
/// List of extracted file paths, or None if the file is invalid
|
|
29
29
|
#[pyfunction]
|
|
30
|
-
fn extract_acb(acb_path: &str, output_dir: &str) -> PyResult<Option<Vec<String>>> {
|
|
30
|
+
fn extract_acb(py: Python<'_>, acb_path: &str, output_dir: &str) -> PyResult<Option<Vec<String>>> {
|
|
31
31
|
let acb_path = Path::new(acb_path);
|
|
32
32
|
let output_dir = Path::new(output_dir);
|
|
33
33
|
fs::create_dir_all(output_dir)
|
|
34
34
|
.map_err(|e| PyRuntimeError::new_err(format!("Failed to create output dir: {}", e)))?;
|
|
35
35
|
|
|
36
|
-
acb::extract_acb_from_file(acb_path, output_dir)
|
|
36
|
+
py.detach(|| acb::extract_acb_from_file(acb_path, output_dir))
|
|
37
37
|
.map_err(|e| PyRuntimeError::new_err(format!("ACB extraction failed: {}", e)))
|
|
38
38
|
}
|
|
39
39
|
|
|
@@ -61,7 +61,8 @@ fn extract_acb_tracks<'py>(
|
|
|
61
61
|
fs::create_dir_all(output_dir)
|
|
62
62
|
.map_err(|e| PyRuntimeError::new_err(format!("Failed to create output dir: {}", e)))?;
|
|
63
63
|
|
|
64
|
-
let tracks =
|
|
64
|
+
let tracks = py
|
|
65
|
+
.detach(|| acb::extract_acb_tracks_from_file(acb_path, output_dir))
|
|
65
66
|
.map_err(|e| PyRuntimeError::new_err(format!("ACB extraction failed: {}", e)))?;
|
|
66
67
|
|
|
67
68
|
let tracks = match tracks {
|
|
@@ -131,7 +132,8 @@ fn extract_acb_bytes<'py>(
|
|
|
131
132
|
py: Python<'py>,
|
|
132
133
|
acb_data: &[u8],
|
|
133
134
|
) -> PyResult<Vec<Bound<'py, pyo3::types::PyDict>>> {
|
|
134
|
-
let tracks =
|
|
135
|
+
let tracks = py
|
|
136
|
+
.detach(|| acb::extract_acb_to_memory(Cursor::new(acb_data), None))
|
|
135
137
|
.map_err(|e| PyRuntimeError::new_err(format!("ACB extraction failed: {}", e)))?;
|
|
136
138
|
|
|
137
139
|
let mut out = Vec::with_capacity(tracks.len());
|
|
@@ -164,7 +166,8 @@ fn extract_acb_unique_bytes<'py>(
|
|
|
164
166
|
py: Python<'py>,
|
|
165
167
|
acb_data: &[u8],
|
|
166
168
|
) -> PyResult<Vec<Bound<'py, pyo3::types::PyDict>>> {
|
|
167
|
-
let waveforms =
|
|
169
|
+
let waveforms = py
|
|
170
|
+
.detach(|| acb::extract_acb_unique_to_memory(Cursor::new(acb_data), None))
|
|
168
171
|
.map_err(|e| PyRuntimeError::new_err(format!("ACB extraction failed: {}", e)))?;
|
|
169
172
|
|
|
170
173
|
let mut out = Vec::with_capacity(waveforms.len());
|
|
@@ -235,7 +238,11 @@ fn decode_acb_to_wav_bytes<'py>(
|
|
|
235
238
|
/// Returns:
|
|
236
239
|
/// None on success
|
|
237
240
|
#[pyfunction]
|
|
238
|
-
fn build_acb(
|
|
241
|
+
fn build_acb(
|
|
242
|
+
py: Python<'_>,
|
|
243
|
+
tracks: Vec<(String, u32, Vec<u8>)>,
|
|
244
|
+
output_path: &str,
|
|
245
|
+
) -> PyResult<()> {
|
|
239
246
|
let mut builder = AcbBuilder::new();
|
|
240
247
|
|
|
241
248
|
for (name, cue_id, data) in tracks {
|
|
@@ -246,8 +253,7 @@ fn build_acb(tracks: Vec<(String, u32, Vec<u8>)>, output_path: &str) -> PyResult
|
|
|
246
253
|
let mut output = fs::File::create(output_path)
|
|
247
254
|
.map_err(|e| PyRuntimeError::new_err(format!("Failed to create output file: {}", e)))?;
|
|
248
255
|
|
|
249
|
-
builder
|
|
250
|
-
.build(&mut output, None)
|
|
256
|
+
py.detach(|| builder.build(&mut output, None))
|
|
251
257
|
.map_err(|e| PyRuntimeError::new_err(format!("ACB build failed: {}", e)))?;
|
|
252
258
|
|
|
253
259
|
Ok(())
|
|
@@ -261,7 +267,7 @@ fn build_acb(tracks: Vec<(String, u32, Vec<u8>)>, output_path: &str) -> PyResult
|
|
|
261
267
|
/// Returns:
|
|
262
268
|
/// ACB file data as bytes
|
|
263
269
|
#[pyfunction]
|
|
264
|
-
fn build_acb_bytes(tracks: Vec<(String, u32, Vec<u8>)>) -> PyResult<Vec<u8>> {
|
|
270
|
+
fn build_acb_bytes(py: Python<'_>, tracks: Vec<(String, u32, Vec<u8>)>) -> PyResult<Vec<u8>> {
|
|
265
271
|
let mut builder = AcbBuilder::new();
|
|
266
272
|
|
|
267
273
|
for (name, cue_id, data) in tracks {
|
|
@@ -269,12 +275,13 @@ fn build_acb_bytes(tracks: Vec<(String, u32, Vec<u8>)>) -> PyResult<Vec<u8>> {
|
|
|
269
275
|
builder.add_track(track);
|
|
270
276
|
}
|
|
271
277
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
+
py.detach(|| {
|
|
279
|
+
let mut output = Cursor::new(Vec::new());
|
|
280
|
+
builder
|
|
281
|
+
.build(&mut output, None)
|
|
282
|
+
.map(|_| output.into_inner())
|
|
283
|
+
})
|
|
284
|
+
.map_err(|e| PyRuntimeError::new_err(format!("ACB build failed: {}", e)))
|
|
278
285
|
}
|
|
279
286
|
|
|
280
287
|
/// Build a single-track music ACB from one HCA track (returns bytes).
|
|
@@ -303,6 +310,7 @@ fn build_acb_bytes(tracks: Vec<(String, u32, Vec<u8>)>) -> PyResult<Vec<u8>> {
|
|
|
303
310
|
#[pyfunction]
|
|
304
311
|
#[allow(clippy::too_many_arguments)]
|
|
305
312
|
fn build_music_acb_bytes(
|
|
313
|
+
py: Python<'_>,
|
|
306
314
|
name: String,
|
|
307
315
|
hca_data: Vec<u8>,
|
|
308
316
|
cue_id: u32,
|
|
@@ -340,12 +348,13 @@ fn build_music_acb_bytes(
|
|
|
340
348
|
);
|
|
341
349
|
builder.add_track(TrackInput::new(name, cue_id, hca_data));
|
|
342
350
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
351
|
+
py.detach(|| {
|
|
352
|
+
let mut output = Cursor::new(Vec::new());
|
|
353
|
+
builder
|
|
354
|
+
.build(&mut output, None)
|
|
355
|
+
.map(|_| output.into_inner())
|
|
356
|
+
})
|
|
357
|
+
.map_err(|e| PyRuntimeError::new_err(format!("Music ACB build failed: {}", e)))
|
|
349
358
|
}
|
|
350
359
|
|
|
351
360
|
/// Resolve the optional `threads` argument shared by the HCA decode bindings:
|
|
@@ -453,6 +462,18 @@ fn decode_hca_bytes(
|
|
|
453
462
|
#[pyfunction]
|
|
454
463
|
#[pyo3(signature = (wav_data, sample_rate=None, channels=None, bitrate=256000, encryption_key=None))]
|
|
455
464
|
fn encode_hca_bytes(
|
|
465
|
+
py: Python<'_>,
|
|
466
|
+
wav_data: &[u8],
|
|
467
|
+
sample_rate: Option<u32>,
|
|
468
|
+
channels: Option<u32>,
|
|
469
|
+
bitrate: u32,
|
|
470
|
+
encryption_key: Option<u64>,
|
|
471
|
+
) -> PyResult<Vec<u8>> {
|
|
472
|
+
py.detach(|| encode_wav_to_hca(wav_data, sample_rate, channels, bitrate, encryption_key))
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/// GIL-free core of the HCA encode bindings (WAV parse + PCM convert + encode).
|
|
476
|
+
fn encode_wav_to_hca(
|
|
456
477
|
wav_data: &[u8],
|
|
457
478
|
sample_rate: Option<u32>,
|
|
458
479
|
channels: Option<u32>,
|
|
@@ -595,13 +616,16 @@ fn encode_hca<'py>(
|
|
|
595
616
|
bitrate: u32,
|
|
596
617
|
encryption_key: Option<u64>,
|
|
597
618
|
) -> PyResult<Bound<'py, pyo3::types::PyDict>> {
|
|
598
|
-
let
|
|
599
|
-
|
|
619
|
+
let hca_data = py.detach(|| {
|
|
620
|
+
let wav_data = fs::read(wav_path)
|
|
621
|
+
.map_err(|e| PyRuntimeError::new_err(format!("Failed to read WAV: {}", e)))?;
|
|
600
622
|
|
|
601
|
-
|
|
623
|
+
let hca_data = encode_wav_to_hca(&wav_data, None, None, bitrate, encryption_key)?;
|
|
602
624
|
|
|
603
|
-
|
|
604
|
-
|
|
625
|
+
fs::write(hca_path, &hca_data)
|
|
626
|
+
.map_err(|e| PyRuntimeError::new_err(format!("Failed to write HCA: {}", e)))?;
|
|
627
|
+
Ok::<_, PyErr>(hca_data)
|
|
628
|
+
})?;
|
|
605
629
|
|
|
606
630
|
let dict = pyo3::types::PyDict::new(py);
|
|
607
631
|
dict.set_item("size", hca_data.len())?;
|
|
@@ -623,6 +647,7 @@ fn encode_hca<'py>(
|
|
|
623
647
|
#[pyfunction]
|
|
624
648
|
#[pyo3(signature = (usm_path, output_dir, key=None, export_audio=false))]
|
|
625
649
|
fn extract_usm(
|
|
650
|
+
py: Python<'_>,
|
|
626
651
|
usm_path: &str,
|
|
627
652
|
output_dir: &str,
|
|
628
653
|
key: Option<u64>,
|
|
@@ -633,7 +658,8 @@ fn extract_usm(
|
|
|
633
658
|
fs::create_dir_all(output_dir)
|
|
634
659
|
.map_err(|e| PyRuntimeError::new_err(format!("Failed to create output dir: {}", e)))?;
|
|
635
660
|
|
|
636
|
-
let files =
|
|
661
|
+
let files = py
|
|
662
|
+
.detach(|| usm::extract_usm_file(usm_path, output_dir, key, export_audio))
|
|
637
663
|
.map_err(|e| PyRuntimeError::new_err(format!("USM extraction failed: {}", e)))?;
|
|
638
664
|
|
|
639
665
|
Ok(files
|
|
@@ -663,7 +689,8 @@ fn extract_usm_bytes<'py>(
|
|
|
663
689
|
key: Option<u64>,
|
|
664
690
|
export_audio: bool,
|
|
665
691
|
) -> PyResult<Vec<Bound<'py, pyo3::types::PyDict>>> {
|
|
666
|
-
let streams =
|
|
692
|
+
let streams = py
|
|
693
|
+
.detach(|| usm::extract_usm_to_memory(Cursor::new(usm_data), b"", key, export_audio))
|
|
667
694
|
.map_err(|e| PyRuntimeError::new_err(format!("USM extraction failed: {}", e)))?;
|
|
668
695
|
|
|
669
696
|
let mut out = Vec::with_capacity(streams.len());
|
|
@@ -690,6 +717,7 @@ fn extract_usm_bytes<'py>(
|
|
|
690
717
|
#[pyfunction]
|
|
691
718
|
#[pyo3(signature = (name, video_data, output_path, encryption_key=None))]
|
|
692
719
|
fn build_usm(
|
|
720
|
+
py: Python<'_>,
|
|
693
721
|
name: &str,
|
|
694
722
|
video_data: Vec<u8>,
|
|
695
723
|
output_path: &str,
|
|
@@ -704,8 +732,7 @@ fn build_usm(
|
|
|
704
732
|
let mut output = fs::File::create(output_path)
|
|
705
733
|
.map_err(|e| PyRuntimeError::new_err(format!("Failed to create output file: {}", e)))?;
|
|
706
734
|
|
|
707
|
-
builder
|
|
708
|
-
.build(&mut output)
|
|
735
|
+
py.detach(|| builder.build(&mut output))
|
|
709
736
|
.map_err(|e| PyRuntimeError::new_err(format!("USM build failed: {}", e)))?;
|
|
710
737
|
|
|
711
738
|
Ok(())
|
|
@@ -723,6 +750,7 @@ fn build_usm(
|
|
|
723
750
|
#[pyfunction]
|
|
724
751
|
#[pyo3(signature = (name, video_data, encryption_key=None))]
|
|
725
752
|
fn build_usm_bytes(
|
|
753
|
+
py: Python<'_>,
|
|
726
754
|
name: &str,
|
|
727
755
|
video_data: Vec<u8>,
|
|
728
756
|
encryption_key: Option<u64>,
|
|
@@ -733,12 +761,11 @@ fn build_usm_bytes(
|
|
|
733
761
|
builder = builder.encryption_key(key);
|
|
734
762
|
}
|
|
735
763
|
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
.build(&mut output)
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
Ok(output.into_inner())
|
|
764
|
+
py.detach(|| {
|
|
765
|
+
let mut output = Cursor::new(Vec::new());
|
|
766
|
+
builder.build(&mut output).map(|_| output.into_inner())
|
|
767
|
+
})
|
|
768
|
+
.map_err(|e| PyRuntimeError::new_err(format!("USM build failed: {}", e)))
|
|
742
769
|
}
|
|
743
770
|
|
|
744
771
|
/// Read metadata from a USM file.
|
|
@@ -749,9 +776,10 @@ fn build_usm_bytes(
|
|
|
749
776
|
/// Returns:
|
|
750
777
|
/// Metadata as a JSON string
|
|
751
778
|
#[pyfunction]
|
|
752
|
-
fn read_usm_metadata(usm_path: &str) -> PyResult<String> {
|
|
779
|
+
fn read_usm_metadata(py: Python<'_>, usm_path: &str) -> PyResult<String> {
|
|
753
780
|
let usm_path = Path::new(usm_path);
|
|
754
|
-
let metadata =
|
|
781
|
+
let metadata = py
|
|
782
|
+
.detach(|| usm::read_metadata_file(usm_path))
|
|
755
783
|
.map_err(|e| PyRuntimeError::new_err(format!("Metadata read failed: {}", e)))?;
|
|
756
784
|
|
|
757
785
|
serde_json::to_string_pretty(&metadata)
|
|
@@ -341,15 +341,14 @@ fn mask_video(buf: &mut [u8], vmask: &VideoMask) {
|
|
|
341
341
|
// Second pass: original i in 0x100..size -> buf[0x140..len]. 0x100 is
|
|
342
342
|
// 32-aligned, so row position j maps to mask lane j.
|
|
343
343
|
{
|
|
344
|
-
let
|
|
345
|
-
for row in
|
|
346
|
-
let row: &mut [u8; MASK_LEN] = row.try_into().unwrap();
|
|
344
|
+
let (chunks, remainder) = buf[0x140..len].as_chunks_mut::<MASK_LEN>();
|
|
345
|
+
for row in chunks {
|
|
347
346
|
for j in 0..MASK_LEN {
|
|
348
347
|
row[j] ^= mask[j];
|
|
349
348
|
mask[j] = row[j] ^ vm1[j];
|
|
350
349
|
}
|
|
351
350
|
}
|
|
352
|
-
for (j, b) in
|
|
351
|
+
for (j, b) in remainder.iter_mut().enumerate() {
|
|
353
352
|
*b ^= mask[j];
|
|
354
353
|
mask[j] = *b ^ vm1[j];
|
|
355
354
|
}
|
|
@@ -371,14 +370,13 @@ fn mask_audio(buf: &mut [u8], amask: &AudioMask) {
|
|
|
371
370
|
let Some(region) = buf.get_mut(0x140..) else {
|
|
372
371
|
return;
|
|
373
372
|
};
|
|
374
|
-
let
|
|
375
|
-
for row in
|
|
376
|
-
let row: &mut [u8; MASK_LEN] = row.try_into().unwrap();
|
|
373
|
+
let (chunks, remainder) = region.as_chunks_mut::<MASK_LEN>();
|
|
374
|
+
for row in chunks {
|
|
377
375
|
for j in 0..MASK_LEN {
|
|
378
376
|
row[j] ^= amask[j];
|
|
379
377
|
}
|
|
380
378
|
}
|
|
381
|
-
for (j, b) in
|
|
379
|
+
for (j, b) in remainder.iter_mut().enumerate() {
|
|
382
380
|
*b ^= amask[j];
|
|
383
381
|
}
|
|
384
382
|
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|