rpkg_rs/resource/pdefs/
hm2_parser.rs1use crate::encryption::xtea::Xtea;
2use crate::misc::resource_id::ResourceID;
3use crate::resource::pdefs::{
4 PackageDefinitionError, PackageDefinitionParser, PartitionId, PartitionInfo, PartitionType,
5};
6use lazy_regex::regex;
7use std::str::FromStr;
8
9pub struct HM2Parser;
10
11impl PackageDefinitionParser for HM2Parser {
12 fn parse(data: &[u8]) -> Result<Vec<PartitionInfo>, PackageDefinitionError> {
13 let deciphered_data = match Xtea::is_encrypted_text_file(data) {
14 true => Xtea::decrypt_text_file(data)?,
15 false => match String::from_utf8(data.to_vec()) {
16 Ok(v) => v,
17 Err(e) => return Err(PackageDefinitionError::TextEncodingError(e)),
18 },
19 };
20
21 let mut partitions: Vec<PartitionInfo> = Vec::new();
22 let mut previous_lines: [&str; 2] = ["", ""];
23
24 let partition_regex = regex!(r"@([A-z]+) patchlevel=([0-9]+)");
25
26 let resource_path_regex = regex!(r"(\[[a-z]+:/.+?]).([a-z]+)");
27
28 for line in deciphered_data.lines() {
29 let trimmed_line = line.trim();
30
31 match trimmed_line {
32 _ if trimmed_line.starts_with("//") => {} line if partition_regex.is_match(trimmed_line) => {
34 if let Some(m) = partition_regex.captures_iter(line).next() {
35 let part_type = if &m[1] == "chunk" {
36 PartitionType::Standard
37 } else {
38 PartitionType::Dlc
39 };
40
41 partitions.push(PartitionInfo {
42 name: try_read_partition_name(previous_lines.to_vec()),
43 parent: partitions.iter().map(|p| p.id.clone()).next(),
44 id: PartitionId {
45 part_type: part_type.clone(),
46 index: partitions
47 .iter()
48 .filter(|&p| p.id.part_type == part_type)
49 .count(),
50 },
51 patch_level: m[2].parse().unwrap(),
52 roots: vec![],
53 });
54 }
55 }
56 line if resource_path_regex.is_match(trimmed_line) => {
57 if let Some(m) = resource_path_regex.captures_iter(line).next() {
58 if let Some(current_partition) = partitions.last_mut() {
59 if let Ok(rid) =
60 ResourceID::from_str(format!("{}.{}", &m[1], &m[2]).as_str())
61 {
62 current_partition.roots.push(rid);
63 }
64 }
65 };
66 }
67 _ => {}
68 }
69
70 previous_lines[0] = previous_lines[1];
71 previous_lines[1] = line;
72 }
73
74 Ok(partitions)
75 }
76}
77
78fn try_read_partition_name(lines: Vec<&str>) -> Option<String> {
79 let reg = regex!(r"// --- (?:DLC|Chunk) \d{2} (.*)");
80 for line in lines {
81 if reg.is_match(line) {
82 if let Some(m) = reg.captures_iter(line).next() {
83 return Some(m[1].to_string());
84 }
85 }
86 }
87 None
88}