PSArc_lib/archive/
archive_file.rs1use std::io::Read;
2
3use super::{PSArchiveCompression, PSArchiveTOC, PSArchiveTableItem, PSArchiveVersion};
4use crate::{
5 prelude::ParsableContext,
6 traits::{ConvertAsBytes, Parsable},
7};
8use anyhow::anyhow;
9
10const PSARC_HEADER: &str = "PSAR";
11
12#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
14#[derive(Debug, Clone, Default, PartialEq, Eq)]
15pub struct PSArchive {
16 pub version: PSArchiveVersion,
18 pub compression: PSArchiveCompression,
20 pub table_of_contents: PSArchiveTOC,
22 pub manifest: PSArchiveTableItem,
24 pub files: Vec<String>,
25 pub contents: Vec<PSArchiveTableItem>,
26 pub file_size: usize,
27}
28
29impl PSArchive {
30 pub fn get_manifest_size_offset(&self) -> (usize, usize) {
31 return (
32 self.contents[0].file_offset as usize - self.manifest.file_offset as usize,
33 self.manifest.file_offset as usize,
34 );
35 }
36
37 pub fn get_size_offset(&self, item: usize) -> (usize, usize) {
38 if item + 1 == self.contents.len() {
39 return (
40 self.file_size - self.contents[item].file_offset as usize,
41 self.contents[item].file_offset as usize,
42 );
43 } else {
44 return (
45 self.contents[item + 1].file_offset as usize
46 - self.contents[item].file_offset as usize
47 - 1,
48 self.contents[item].file_offset as usize,
49 );
50 }
51 }
52
53 pub fn parse_manifest(&mut self, bytes: impl ConvertAsBytes) -> Vec<String> {
54 let bytes = bytes.convert_as_bytes();
55 let (size, _) = self.get_manifest_size_offset();
56 if size > 100 {
57 let mut z = flate2::read::ZlibDecoder::new(&bytes[..]);
58 let mut s = String::new();
59 z.read_to_string(&mut s).unwrap();
60 let strings = s
61 .split("\n")
62 .collect::<Vec<&str>>()
63 .iter()
64 .map(|f| f.to_string())
65 .collect::<Vec<String>>();
66 self.files = strings.clone();
67 return strings;
68 } else {
69 let s = String::from_utf8(bytes).unwrap();
70 let strings = s
71 .split("\n")
72 .collect::<Vec<&str>>()
73 .iter()
74 .map(|f| f.to_string())
75 .collect::<Vec<String>>();
76 self.files = strings.clone();
77 return strings;
78 }
79 }
80
81 pub fn parse_file(&self, item: usize, bytes: impl ConvertAsBytes) -> Vec<u8> {
82 let bytes = bytes.convert_as_bytes();
83 let item = &self.contents[item];
84 if item.uncompressed_size > 100 {
85 let mut z = flate2::read::ZlibDecoder::new(&bytes[..]);
86 let mut s = Vec::new();
87 z.read_to_end(&mut s).unwrap();
88 return s;
89 } else {
90 return bytes;
91 }
92 }
93}
94
95impl Parsable for PSArchive {
96 type Error = anyhow::Error;
97 fn parse(bytes: impl ConvertAsBytes) -> Result<Self, Self::Error> {
98 let bytes = bytes.convert_as_bytes();
99 let header = (&bytes[0..4]).convert_as_bytes();
100 if header == PSARC_HEADER.convert_as_bytes() {
101 let version = PSArchiveVersion::parse(&bytes[0x4..0x8])?;
102 let compression = PSArchiveCompression::parse(&bytes[0x8..0xC])?;
103 let table_of_contents = PSArchiveTOC::parse(&bytes[0xC..0x20])?;
104 let mut manifest = PSArchiveTableItem::new(PSArchiveCompression::ZLIB);
105 let manifest = manifest.parse(&bytes[0x20..0x3E])?;
106 let mut contents = Vec::new();
107 for i in 0..table_of_contents.entry_count as usize {
108 let item_bytes = &bytes[0x3E + i * table_of_contents.entry_size as usize
109 ..0x3E
110 + i * table_of_contents.entry_size as usize
111 + table_of_contents.entry_size as usize];
112 let mut item = PSArchiveTableItem::new(PSArchiveCompression::ZLIB);
113 let item = item.parse(item_bytes).unwrap();
114 contents.push(item);
115 }
116 return Ok(Self {
117 version,
118 compression,
119 table_of_contents,
120 manifest,
121 files: Vec::new(),
122 contents,
123 file_size: 0,
124 });
125 } else {
126 return Err(anyhow!("Invalid header for PSArc format"));
127 }
128 }
129}
130
131#[cfg(test)]
132#[doc(hidden)]
133mod test {
134 use super::{
135 super::PSArchiveFlags, PSArchiveCompression, PSArchiveTOC, PSArchiveTableItem,
136 PSArchiveVersion,
137 };
138 use crate::prelude::*;
139
140 #[test]
141 fn test_archive_parsing() {
142 let bytes = include_bytes!("../../res/test.pak").to_vec();
143 let result = PSArchive::parse(&*bytes);
144 assert_eq!(result.is_ok(), true);
145 let mut result = result.unwrap();
146 result.file_size = bytes.len();
147 let (size, offset) = result.get_manifest_size_offset();
148 result.parse_manifest(&bytes[offset..offset + size]);
149 assert_eq!(
150 result,
151 PSArchive {
152 version: PSArchiveVersion { major: 1, minor: 4 },
153 compression: PSArchiveCompression::ZLIB,
154 table_of_contents: PSArchiveTOC {
155 length: 64,
156 entry_size: 30,
157 entry_count: 1,
158 flags: PSArchiveFlags::ABSOLUTE,
159 },
160 manifest: PSArchiveTableItem {
161 compression_type: PSArchiveCompression::ZLIB,
162 block_offset: 0,
163 uncompressed_size: 17,
164 file_offset: 96,
165 },
166 files: vec!["/data/example.xml".to_string()],
167 contents: vec![PSArchiveTableItem {
168 compression_type: PSArchiveCompression::ZLIB,
169 block_offset: 1,
170 uncompressed_size: 115,
171 file_offset: 113,
172 }],
173 file_size: 196,
174 }
175 );
176 let (size, offset) = result.get_size_offset(0);
177 let contents = String::from_utf8(result.parse_file(0, &bytes[offset..offset + size]));
178 assert_eq!(contents.is_ok(), true);
179 assert_eq!(contents.unwrap(), "<TextData>\r\n <Property name=\"color\" value=\"#ff0000\" />\r\n <Property name=\"text\" value=\"Hello there!\" />\r\n</TextData>");
180 }
181
182 #[test]
183 pub fn temp() {
184 let bytes = include_bytes!("C:\\Users\\Jacob\\Downloads\\NMSARC.AnimMBIN.pak").to_vec();
185 let result = PSArchive::parse(&*bytes).unwrap();
186 }
187}