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.
Files changed (42) hide show
  1. {cridecoder-0.2.2 → cridecoder-0.2.3}/Cargo.lock +1 -1
  2. {cridecoder-0.2.2 → cridecoder-0.2.3}/Cargo.toml +1 -1
  3. {cridecoder-0.2.2 → cridecoder-0.2.3}/PKG-INFO +1 -1
  4. {cridecoder-0.2.2 → cridecoder-0.2.3}/pyproject.toml +1 -1
  5. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/acb/extractor.rs +39 -0
  6. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/acb.rs +1 -1
  7. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/lib.rs +4 -2
  8. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/usm/extractor.rs +113 -0
  9. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/usm.rs +3 -1
  10. {cridecoder-0.2.2 → cridecoder-0.2.3}/.github/copilot-instructions.md +0 -0
  11. {cridecoder-0.2.2 → cridecoder-0.2.3}/.github/dependabot.yml +0 -0
  12. {cridecoder-0.2.2 → cridecoder-0.2.3}/.github/workflows/ci.yml +0 -0
  13. {cridecoder-0.2.2 → cridecoder-0.2.3}/.github/workflows/release-crate.yml +0 -0
  14. {cridecoder-0.2.2 → cridecoder-0.2.3}/.github/workflows/release-python.yml +0 -0
  15. {cridecoder-0.2.2 → cridecoder-0.2.3}/.gitignore +0 -0
  16. {cridecoder-0.2.2 → cridecoder-0.2.3}/AGENTS.md +0 -0
  17. {cridecoder-0.2.2 → cridecoder-0.2.3}/CLAUDE.md +0 -0
  18. {cridecoder-0.2.2 → cridecoder-0.2.3}/LICENSE +0 -0
  19. {cridecoder-0.2.2 → cridecoder-0.2.3}/README.md +0 -0
  20. {cridecoder-0.2.2 → cridecoder-0.2.3}/examples/debug_acb.rs +0 -0
  21. {cridecoder-0.2.2 → cridecoder-0.2.3}/examples/test_acb.rs +0 -0
  22. {cridecoder-0.2.2 → cridecoder-0.2.3}/examples/test_hca.rs +0 -0
  23. {cridecoder-0.2.2 → cridecoder-0.2.3}/examples/test_usm.rs +0 -0
  24. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/acb/afs.rs +0 -0
  25. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/acb/builder.rs +0 -0
  26. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/acb/consts.rs +0 -0
  27. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/acb/track.rs +0 -0
  28. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/acb/utf.rs +0 -0
  29. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/hca/ath.rs +0 -0
  30. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/hca/bitreader.rs +0 -0
  31. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/hca/cipher.rs +0 -0
  32. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/hca/decoder.rs +0 -0
  33. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/hca/encoder.rs +0 -0
  34. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/hca/hca_file.rs +0 -0
  35. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/hca/imdct.rs +0 -0
  36. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/hca/tables.rs +0 -0
  37. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/hca.rs +0 -0
  38. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/python.rs +0 -0
  39. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/reader.rs +0 -0
  40. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/usm/builder.rs +0 -0
  41. {cridecoder-0.2.2 → cridecoder-0.2.3}/src/usm/metadata.rs +0 -0
  42. {cridecoder-0.2.2 → cridecoder-0.2.3}/tests/integration_tests.rs +0 -0
@@ -28,7 +28,7 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
28
28
 
29
29
  [[package]]
30
30
  name = "cridecoder"
31
- version = "0.2.2"
31
+ version = "0.2.3"
32
32
  dependencies = [
33
33
  "byteorder",
34
34
  "encoding_rs",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "cridecoder"
3
- version = "0.2.2"
3
+ version = "0.2.3"
4
4
  edition = "2021"
5
5
  description = "CRI codec library for ACB/AWB, HCA audio, and USM video extraction"
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cridecoder
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "cridecoder"
7
- version = "0.2.2"
7
+ version = "0.2.3"
8
8
  description = "CRI codec library for ACB/AWB, HCA audio, and USM video extraction"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -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::{extract_usm, extract_usm_file, Metadata, UsmError};
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::{extract_usm, extract_usm_file, UsmError};
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