cridecoder 0.2.2__tar.gz → 0.2.3__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.2.2 → cridecoder-0.2.3}/Cargo.lock +1 -1
- {cridecoder-0.2.2 → cridecoder-0.2.3}/Cargo.toml +1 -1
- {cridecoder-0.2.2 → cridecoder-0.2.3}/PKG-INFO +1 -1
- {cridecoder-0.2.2 → cridecoder-0.2.3}/pyproject.toml +1 -1
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/acb/extractor.rs +39 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/acb.rs +1 -1
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/lib.rs +4 -2
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/usm/extractor.rs +113 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/usm.rs +3 -1
- {cridecoder-0.2.2 → cridecoder-0.2.3}/.github/copilot-instructions.md +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/.github/dependabot.yml +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/.github/workflows/ci.yml +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/.github/workflows/release-crate.yml +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/.github/workflows/release-python.yml +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/.gitignore +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/AGENTS.md +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/CLAUDE.md +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/LICENSE +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/README.md +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/examples/debug_acb.rs +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/examples/test_acb.rs +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/examples/test_hca.rs +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/examples/test_usm.rs +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/acb/afs.rs +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/acb/builder.rs +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/acb/consts.rs +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/acb/track.rs +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/acb/utf.rs +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/hca/ath.rs +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/hca/bitreader.rs +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/hca/cipher.rs +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/hca/decoder.rs +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/hca/encoder.rs +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/hca/hca_file.rs +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/hca/imdct.rs +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/hca/tables.rs +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/hca.rs +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/python.rs +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/reader.rs +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/usm/builder.rs +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/src/usm/metadata.rs +0 -0
- {cridecoder-0.2.2 → cridecoder-0.2.3}/tests/integration_tests.rs +0 -0
|
@@ -9,6 +9,13 @@ use std::io::{Cursor, Read, Seek};
|
|
|
9
9
|
use std::path::Path;
|
|
10
10
|
use thiserror::Error;
|
|
11
11
|
|
|
12
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
13
|
+
pub struct ExtractedAcbTrack {
|
|
14
|
+
pub name: String,
|
|
15
|
+
pub extension: String,
|
|
16
|
+
pub data: Vec<u8>,
|
|
17
|
+
}
|
|
18
|
+
|
|
12
19
|
#[derive(Error, Debug)]
|
|
13
20
|
pub enum ExtractError {
|
|
14
21
|
#[error("IO error: {0}")]
|
|
@@ -44,6 +51,38 @@ pub fn extract_acb<R: Read + Seek>(
|
|
|
44
51
|
)
|
|
45
52
|
}
|
|
46
53
|
|
|
54
|
+
/// Extract all audio tracks from an ACB reader into memory.
|
|
55
|
+
pub fn extract_acb_to_memory<R: Read + Seek>(
|
|
56
|
+
acb_file: R,
|
|
57
|
+
acb_file_path: Option<&Path>,
|
|
58
|
+
) -> Result<Vec<ExtractedAcbTrack>, ExtractError> {
|
|
59
|
+
let utf = UtfTable::new(acb_file)?;
|
|
60
|
+
let track_list = TrackList::new(&utf)?;
|
|
61
|
+
let mut embedded_awb = load_embedded_awb(&utf.rows[0]);
|
|
62
|
+
let mut external_awbs = load_external_awbs(&utf.rows[0], acb_file_path);
|
|
63
|
+
|
|
64
|
+
let mut outputs = Vec::new();
|
|
65
|
+
for track in &track_list.tracks {
|
|
66
|
+
let data = match get_track_data(track, &mut embedded_awb, &mut external_awbs)? {
|
|
67
|
+
Some(data) => data,
|
|
68
|
+
None => continue,
|
|
69
|
+
};
|
|
70
|
+
let extension = wave_type_extension(track.enc_type);
|
|
71
|
+
let extension = if extension.is_empty() {
|
|
72
|
+
track.enc_type.to_string()
|
|
73
|
+
} else {
|
|
74
|
+
extension.trim_start_matches('.').to_string()
|
|
75
|
+
};
|
|
76
|
+
outputs.push(ExtractedAcbTrack {
|
|
77
|
+
name: track.name.clone(),
|
|
78
|
+
extension,
|
|
79
|
+
data,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
Ok(outputs)
|
|
84
|
+
}
|
|
85
|
+
|
|
47
86
|
fn load_embedded_awb(
|
|
48
87
|
row: &std::collections::HashMap<String, crate::acb::utf::Value>,
|
|
49
88
|
) -> Option<AfsArchive<Cursor<Vec<u8>>>> {
|
|
@@ -12,6 +12,6 @@ pub use builder::{
|
|
|
12
12
|
AcbBuilder, AfsArchiveBuilder, BuilderError, ColumnDef, TrackInput, UtfTableBuilder,
|
|
13
13
|
};
|
|
14
14
|
pub use consts::*;
|
|
15
|
-
pub use extractor::{extract_acb, extract_acb_from_file};
|
|
15
|
+
pub use extractor::{extract_acb, extract_acb_from_file, extract_acb_to_memory, ExtractedAcbTrack};
|
|
16
16
|
pub use track::{Track, TrackList};
|
|
17
17
|
pub use utf::{UtfHeader, UtfTable, Value};
|
|
@@ -14,7 +14,7 @@ pub mod usm;
|
|
|
14
14
|
mod python;
|
|
15
15
|
|
|
16
16
|
// ACB/AWB exports
|
|
17
|
-
pub use acb::{extract_acb, extract_acb_from_file};
|
|
17
|
+
pub use acb::{extract_acb, extract_acb_from_file, extract_acb_to_memory, ExtractedAcbTrack};
|
|
18
18
|
pub use acb::{AcbBuilder, AfsArchiveBuilder, BuilderError, TrackInput, UtfTableBuilder};
|
|
19
19
|
|
|
20
20
|
// HCA exports
|
|
@@ -22,7 +22,9 @@ pub use hca::{encode_wav_to_hca, HcaEncoder, HcaEncoderConfig, HcaEncoderError};
|
|
|
22
22
|
pub use hca::{HcaDecoder, HcaDecoderError, HcaInfo};
|
|
23
23
|
|
|
24
24
|
// USM exports
|
|
25
|
-
pub use usm::{
|
|
25
|
+
pub use usm::{
|
|
26
|
+
extract_usm, extract_usm_file, extract_usm_to_memory, ExtractedUsmStream, Metadata, UsmError,
|
|
27
|
+
};
|
|
26
28
|
pub use usm::{UsmBuilder, UsmBuilderError};
|
|
27
29
|
|
|
28
30
|
#[cfg(feature = "python")]
|
|
@@ -70,6 +70,13 @@ pub type UtfRow = std::collections::HashMap<String, UtfValue>;
|
|
|
70
70
|
/// A UTF table
|
|
71
71
|
pub type UtfTable = Vec<UtfRow>;
|
|
72
72
|
|
|
73
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
74
|
+
pub struct ExtractedUsmStream {
|
|
75
|
+
pub name: String,
|
|
76
|
+
pub extension: String,
|
|
77
|
+
pub data: Vec<u8>,
|
|
78
|
+
}
|
|
79
|
+
|
|
73
80
|
/// Read column data from a UTF table
|
|
74
81
|
fn read_column_data<R: Read + Seek>(
|
|
75
82
|
reader: &mut Reader<R>,
|
|
@@ -356,6 +363,32 @@ pub fn extract_usm<R: Read + Seek>(
|
|
|
356
363
|
Ok(output_files)
|
|
357
364
|
}
|
|
358
365
|
|
|
366
|
+
/// Extract USM video/audio streams from a reader into memory.
|
|
367
|
+
pub fn extract_usm_to_memory<R: Read + Seek>(
|
|
368
|
+
usm: R,
|
|
369
|
+
fallback_name: &[u8],
|
|
370
|
+
key: Option<u64>,
|
|
371
|
+
export_audio: bool,
|
|
372
|
+
) -> Result<Vec<ExtractedUsmStream>, UsmError> {
|
|
373
|
+
let mut reader = Reader::new(usm);
|
|
374
|
+
let (vmask, amask) = key.map(get_mask).unzip();
|
|
375
|
+
let (filename, has_audio) = parse_usm_header(&mut reader, fallback_name)?;
|
|
376
|
+
let decoded_filename = decode_shift_jis(&filename);
|
|
377
|
+
let base_name = Path::new(&decoded_filename)
|
|
378
|
+
.file_stem()
|
|
379
|
+
.and_then(|s| s.to_str())
|
|
380
|
+
.unwrap_or(&decoded_filename)
|
|
381
|
+
.to_string();
|
|
382
|
+
|
|
383
|
+
extract_usm_chunks_to_memory(
|
|
384
|
+
&mut reader,
|
|
385
|
+
base_name,
|
|
386
|
+
has_audio && export_audio,
|
|
387
|
+
vmask.as_ref(),
|
|
388
|
+
amask.as_ref(),
|
|
389
|
+
)
|
|
390
|
+
}
|
|
391
|
+
|
|
359
392
|
/// Parse USM header
|
|
360
393
|
fn parse_usm_header<R: Read + Seek>(
|
|
361
394
|
reader: &mut Reader<R>,
|
|
@@ -560,6 +593,86 @@ fn extract_usm_chunks<R: Read + Seek>(
|
|
|
560
593
|
Ok(())
|
|
561
594
|
}
|
|
562
595
|
|
|
596
|
+
fn extract_usm_chunks_to_memory<R: Read + Seek>(
|
|
597
|
+
reader: &mut Reader<R>,
|
|
598
|
+
base_name: String,
|
|
599
|
+
export_audio: bool,
|
|
600
|
+
vmask: Option<&VideoMask>,
|
|
601
|
+
amask: Option<&AudioMask>,
|
|
602
|
+
) -> Result<Vec<ExtractedUsmStream>, UsmError> {
|
|
603
|
+
let mut video = Vec::new();
|
|
604
|
+
let mut audio = if export_audio { Some(Vec::new()) } else { None };
|
|
605
|
+
|
|
606
|
+
while let Ok(next_sig) = reader.read_bytes(4) {
|
|
607
|
+
let block_size = reader.read_u32()?;
|
|
608
|
+
let current_pos = reader.stream_position()?;
|
|
609
|
+
let next_offset = current_pos + block_size as u64;
|
|
610
|
+
|
|
611
|
+
let chunk_header_size = reader.read_u16()?;
|
|
612
|
+
let chunk_footer_size = reader.read_u16()?;
|
|
613
|
+
let _ = reader.read_bytes(3)?;
|
|
614
|
+
let data_type_byte = reader.read_i8()?;
|
|
615
|
+
let data_type = (data_type_byte & 0b11) as u8;
|
|
616
|
+
reader.seek(SeekFrom::Current(16))?;
|
|
617
|
+
|
|
618
|
+
let contents_end = reader.read_bytes(13)?;
|
|
619
|
+
if &contents_end == b"#CONTENTS END" {
|
|
620
|
+
break;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
reader.seek(SeekFrom::Current(-13))?;
|
|
624
|
+
let read_data_len =
|
|
625
|
+
block_size as usize - chunk_header_size as usize - chunk_footer_size as usize;
|
|
626
|
+
|
|
627
|
+
if next_sig == b"@SFV" {
|
|
628
|
+
let content = read_usm_chunk_to_vec(reader, read_data_len, data_type, vmask, None)?;
|
|
629
|
+
video.extend_from_slice(&content);
|
|
630
|
+
} else if next_sig == b"@SFA" {
|
|
631
|
+
if let Some(audio) = audio.as_mut() {
|
|
632
|
+
let content = read_usm_chunk_to_vec(reader, read_data_len, data_type, None, amask)?;
|
|
633
|
+
audio.extend_from_slice(&content);
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
reader.seek(SeekFrom::Start(next_offset))?;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
let mut streams = vec![ExtractedUsmStream {
|
|
641
|
+
name: base_name.clone(),
|
|
642
|
+
extension: "m2v".to_string(),
|
|
643
|
+
data: video,
|
|
644
|
+
}];
|
|
645
|
+
if let Some(audio) = audio {
|
|
646
|
+
streams.push(ExtractedUsmStream {
|
|
647
|
+
name: base_name,
|
|
648
|
+
extension: "adx".to_string(),
|
|
649
|
+
data: audio,
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
Ok(streams)
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
fn read_usm_chunk_to_vec<R: Read + Seek>(
|
|
657
|
+
reader: &mut Reader<R>,
|
|
658
|
+
read_data_len: usize,
|
|
659
|
+
data_type: u8,
|
|
660
|
+
vmask: Option<&VideoMask>,
|
|
661
|
+
amask: Option<&AudioMask>,
|
|
662
|
+
) -> Result<Vec<u8>, UsmError> {
|
|
663
|
+
let content = reader.read_bytes(read_data_len)?;
|
|
664
|
+
if data_type != 0 {
|
|
665
|
+
return Ok(content);
|
|
666
|
+
}
|
|
667
|
+
if let Some(vmask) = vmask {
|
|
668
|
+
return Ok(mask_video(&content, vmask));
|
|
669
|
+
}
|
|
670
|
+
if let Some(amask) = amask {
|
|
671
|
+
return Ok(mask_audio(&content, amask));
|
|
672
|
+
}
|
|
673
|
+
Ok(content)
|
|
674
|
+
}
|
|
675
|
+
|
|
563
676
|
/// Process a chunk
|
|
564
677
|
#[allow(clippy::too_many_arguments)]
|
|
565
678
|
fn process_chunk<R: Read + Seek>(
|
|
@@ -8,7 +8,9 @@ mod extractor;
|
|
|
8
8
|
mod metadata;
|
|
9
9
|
|
|
10
10
|
pub use builder::{StreamInput, StreamType, UsmBuilder, UsmBuilderError};
|
|
11
|
-
pub use extractor::{
|
|
11
|
+
pub use extractor::{
|
|
12
|
+
extract_usm, extract_usm_file, extract_usm_to_memory, ExtractedUsmStream, UsmError,
|
|
13
|
+
};
|
|
12
14
|
pub use metadata::{
|
|
13
15
|
export_metadata_file, read_metadata, read_metadata_file, Metadata, MetadataSection,
|
|
14
16
|
};
|
|
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
|