egdata_manifests_parser/types/
meta.rs1use byteorder::{LittleEndian, ReadBytesExt};
2use log::debug;
3use serde::{Deserialize, Serialize};
4use std::io::{Read, Seek};
5
6use crate::error::Error;
7use crate::parser::reader::ReadExt;
8
9#[derive(Debug, Clone, Serialize, Deserialize, Default)]
10pub struct ManifestMeta {
11 pub data_size: u32,
12 pub data_version: u8,
13 pub feature_level: i32,
14 pub is_file_data: bool,
15 pub app_id: i32,
16 #[serde(serialize_with = "trim_null_chars")]
17 pub app_name: String,
18 #[serde(serialize_with = "trim_null_chars")]
19 pub build_version: String,
20 #[serde(serialize_with = "trim_null_chars")]
21 pub launch_exe: String,
22 #[serde(serialize_with = "trim_null_chars")]
23 pub launch_command: String,
24 pub prereq_ids: Vec<String>,
25 #[serde(serialize_with = "trim_null_chars")]
26 pub prereq_name: String,
27 #[serde(serialize_with = "trim_null_chars")]
28 pub prereq_path: String,
29 #[serde(serialize_with = "trim_null_chars")]
30 pub prereq_args: String,
31 pub build_id: Option<String>,
32}
33
34fn trim_null_chars<S>(value: &String, serializer: S) -> Result<S::Ok, S::Error>
35where
36 S: serde::Serializer,
37{
38 let trimmed = value.trim_end_matches('\0');
39 serializer.serialize_str(trimmed)
40}
41
42impl ManifestMeta {
43 pub fn read_meta<R: Read + Seek>(rdr: &mut R) -> Result<(Self, u64), Error> {
44 let start_pos = rdr.stream_position()?;
45
46 debug!("Reading metadata:");
47 let data_size = rdr.read_u32::<LittleEndian>()?;
48 debug!(" Data size: {} (0x{:x})", data_size, data_size);
49
50 if data_size == 0 || data_size > 1024 * 1024 * 1024 {
52 return Err(Error::Invalid(format!(
54 "Invalid data size: {} (0x{:x}). Must be between 1 and 1GB",
55 data_size, data_size
56 )));
57 }
58
59 let data_version = rdr.read_u8()?;
60 debug!(" Data version: {} (0x{:x})", data_version, data_version);
61
62 let feature_level = rdr.read_i32::<LittleEndian>()?;
63 debug!(" Feature level: {} (0x{:x})", feature_level, feature_level);
64
65 let is_file_data = rdr.read_u8()? != 0;
66 debug!(" Is file data: {}", is_file_data);
67
68 let app_id = rdr.read_i32::<LittleEndian>()?;
69 debug!(" App ID: {} (0x{:x})", app_id, app_id);
70
71 let app_name = rdr.fstring()?;
72 debug!(" App name: {}", app_name);
73
74 let build_version = rdr.fstring()?;
75 debug!(" Build version: {}", build_version);
76
77 let launch_exe = rdr.fstring()?;
78 debug!(" Launch exe: {}", launch_exe);
79
80 let launch_command = rdr.fstring()?;
81 debug!(" Launch command: {}", launch_command);
82
83 let prereq_ids = rdr.fstring_array()?;
84 debug!(" Prerequisite IDs: {:?}", prereq_ids);
85
86 let prereq_name = rdr.fstring()?;
87 debug!(" Prerequisite name: {}", prereq_name);
88
89 let prereq_path = rdr.fstring()?;
90 debug!(" Prerequisite path: {}", prereq_path);
91
92 let prereq_args = rdr.fstring()?;
93 debug!(" Prerequisite args: {}", prereq_args);
94
95 let build_id = if data_version >= 1 {
96 let build_id = rdr.fstring()?;
97 debug!(" Build ID: {}", build_id);
98 Some(build_id)
99 } else {
100 None
101 };
102
103 let end_pos = rdr.stream_position()?;
104 let bytes_read = end_pos - start_pos;
105
106 Ok((
107 Self {
108 data_size,
109 data_version,
110 feature_level,
111 is_file_data,
112 app_id,
113 app_name,
114 build_version,
115 launch_exe,
116 launch_command,
117 prereq_ids,
118 prereq_name,
119 prereq_path,
120 prereq_args,
121 build_id,
122 },
123 bytes_read,
124 ))
125 }
126}