1use crate::{
2 flavor::{flavor_from_tape, Ck3BinaryFlavor},
3 Ck3Error, Ck3ErrorKind, Ck3Melter, Encoding, SaveHeader,
4};
5use jomini::{
6 binary::{BinaryDeserializerBuilder, FailedResolveStrategy, TokenResolver},
7 text::ObjectReader,
8 BinaryDeserializer, BinaryTape, TextDeserializer, TextTape, Utf8Encoding,
9};
10use serde::Deserialize;
11use std::io::Cursor;
12use zip::result::ZipError;
13
14enum FileKind<'a> {
15 Text(&'a [u8]),
16 Binary(&'a [u8]),
17 Zip {
18 archive: Ck3ZipFiles<'a>,
19 metadata: &'a [u8],
20 gamestate: VerifiedIndex,
21 is_text: bool,
22 },
23}
24
25pub struct Ck3File<'a> {
29 header: SaveHeader,
30 kind: FileKind<'a>,
31}
32
33impl<'a> Ck3File<'a> {
34 pub fn from_slice(data: &[u8]) -> Result<Ck3File, Ck3Error> {
36 let header = SaveHeader::from_slice(data)?;
37 let data = &data[header.header_len()..];
38
39 let reader = Cursor::new(data);
40 match zip::ZipArchive::new(reader) {
41 Ok(mut zip) => {
42 let metadata = &data[..zip.offset() as usize];
43 let files = Ck3ZipFiles::new(&mut zip, data);
44 let gamestate_idx = files
45 .gamestate_index()
46 .ok_or(Ck3ErrorKind::ZipMissingEntry)?;
47
48 let is_text = !header.kind().is_binary();
49 Ok(Ck3File {
50 header,
51 kind: FileKind::Zip {
52 archive: files,
53 gamestate: gamestate_idx,
54 metadata,
55 is_text,
56 },
57 })
58 }
59 Err(ZipError::InvalidArchive(_)) => {
60 if header.kind().is_binary() {
61 Ok(Ck3File {
62 header,
63 kind: FileKind::Binary(data),
64 })
65 } else {
66 Ok(Ck3File {
67 header,
68 kind: FileKind::Text(data),
69 })
70 }
71 }
72 Err(e) => Err(Ck3ErrorKind::ZipArchive(e).into()),
73 }
74 }
75
76 pub fn encoding(&self) -> Encoding {
78 match &self.kind {
79 FileKind::Text(_) => Encoding::Text,
80 FileKind::Binary(_) => Encoding::Binary,
81 FileKind::Zip { is_text, .. } if *is_text => Encoding::TextZip,
82 FileKind::Zip { .. } => Encoding::BinaryZip,
83 }
84 }
85
86 pub fn size(&self) -> usize {
90 match &self.kind {
91 FileKind::Text(x) | FileKind::Binary(x) => x.len(),
92 FileKind::Zip { gamestate, .. } => gamestate.size,
93 }
94 }
95
96 pub fn parse_metadata(&self) -> Result<Ck3ParsedFile<'a>, Ck3Error> {
97 match &self.kind {
98 FileKind::Text(x) => {
99 let len = self.header.metadata_len() as usize;
105 let data = if len * 2 > x.len() { x } else { &x[..len] };
106
107 let text = Ck3Text::from_raw(data)?;
108 Ok(Ck3ParsedFile {
109 kind: Ck3ParsedFileKind::Text(text),
110 })
111 }
112 FileKind::Binary(x) => {
113 let metadata = x.get(..self.header.metadata_len() as usize).unwrap_or(x);
114 let binary = Ck3Binary::from_raw(metadata, self.header.clone())?;
115 Ok(Ck3ParsedFile {
116 kind: Ck3ParsedFileKind::Binary(binary),
117 })
118 }
119 FileKind::Zip {
120 metadata, is_text, ..
121 } if *is_text => {
122 let text = Ck3Text::from_raw(metadata)?;
123 Ok(Ck3ParsedFile {
124 kind: Ck3ParsedFileKind::Text(text),
125 })
126 }
127 FileKind::Zip { metadata, .. } => {
128 let binary = Ck3Binary::from_raw(metadata, self.header.clone())?;
129 Ok(Ck3ParsedFile {
130 kind: Ck3ParsedFileKind::Binary(binary),
131 })
132 }
133 }
134 }
135
136 pub fn parse(&self, zip_sink: &'a mut Vec<u8>) -> Result<Ck3ParsedFile<'a>, Ck3Error> {
141 match &self.kind {
142 FileKind::Text(x) => {
143 let text = Ck3Text::from_raw(x)?;
144 Ok(Ck3ParsedFile {
145 kind: Ck3ParsedFileKind::Text(text),
146 })
147 }
148 FileKind::Binary(x) => {
149 let binary = Ck3Binary::from_raw(x, self.header.clone())?;
150 Ok(Ck3ParsedFile {
151 kind: Ck3ParsedFileKind::Binary(binary),
152 })
153 }
154 FileKind::Zip {
155 archive,
156 gamestate,
157 is_text,
158 ..
159 } => {
160 let zip = archive.retrieve_file(*gamestate);
161 zip.read_to_end(zip_sink)?;
162
163 if *is_text {
164 let text = Ck3Text::from_raw(zip_sink)?;
165 Ok(Ck3ParsedFile {
166 kind: Ck3ParsedFileKind::Text(text),
167 })
168 } else {
169 let binary = Ck3Binary::from_raw(zip_sink, self.header.clone())?;
170 Ok(Ck3ParsedFile {
171 kind: Ck3ParsedFileKind::Binary(binary),
172 })
173 }
174 }
175 }
176 }
177}
178
179pub enum Ck3ParsedFileKind<'a> {
181 Text(Ck3Text<'a>),
183
184 Binary(Ck3Binary<'a>),
186}
187
188pub struct Ck3ParsedFile<'a> {
190 kind: Ck3ParsedFileKind<'a>,
191}
192
193impl<'a> Ck3ParsedFile<'a> {
194 pub fn as_text(&self) -> Option<&Ck3Text> {
196 match &self.kind {
197 Ck3ParsedFileKind::Text(x) => Some(x),
198 _ => None,
199 }
200 }
201
202 pub fn as_binary(&self) -> Option<&Ck3Binary> {
204 match &self.kind {
205 Ck3ParsedFileKind::Binary(x) => Some(x),
206 _ => None,
207 }
208 }
209
210 pub fn kind(&self) -> &Ck3ParsedFileKind {
212 &self.kind
213 }
214
215 pub fn deserializer(&self) -> Ck3Deserializer {
217 match &self.kind {
218 Ck3ParsedFileKind::Text(x) => Ck3Deserializer {
219 kind: Ck3DeserializerKind::Text(x),
220 },
221 Ck3ParsedFileKind::Binary(x) => Ck3Deserializer {
222 kind: Ck3DeserializerKind::Binary(x.deserializer()),
223 },
224 }
225 }
226}
227
228#[derive(Debug, Clone, Copy)]
229struct VerifiedIndex {
230 data_start: usize,
231 data_end: usize,
232 size: usize,
233}
234
235#[derive(Debug, Clone)]
236struct Ck3ZipFiles<'a> {
237 archive: &'a [u8],
238 gamestate_index: Option<VerifiedIndex>,
239}
240
241impl<'a> Ck3ZipFiles<'a> {
242 pub fn new(archive: &mut zip::ZipArchive<Cursor<&'a [u8]>>, data: &'a [u8]) -> Self {
243 let mut gamestate_index = None;
244
245 for index in 0..archive.len() {
246 if let Ok(file) = archive.by_index_raw(index) {
247 let size = file.size() as usize;
248 let data_start = file.data_start() as usize;
249 let data_end = data_start + file.compressed_size() as usize;
250
251 if file.name() == "gamestate" {
252 gamestate_index = Some(VerifiedIndex {
253 data_start,
254 data_end,
255 size,
256 })
257 }
258 }
259 }
260
261 Self {
262 archive: data,
263 gamestate_index,
264 }
265 }
266
267 pub fn retrieve_file(&self, index: VerifiedIndex) -> Ck3ZipFile {
268 let raw = &self.archive[index.data_start..index.data_end];
269 Ck3ZipFile {
270 raw,
271 size: index.size,
272 }
273 }
274
275 pub fn gamestate_index(&self) -> Option<VerifiedIndex> {
276 self.gamestate_index
277 }
278}
279
280struct Ck3ZipFile<'a> {
281 raw: &'a [u8],
282 size: usize,
283}
284
285impl<'a> Ck3ZipFile<'a> {
286 pub fn read_to_end(&self, buf: &mut Vec<u8>) -> Result<(), Ck3Error> {
287 let start_len = buf.len();
288 buf.resize(start_len + self.size(), 0);
289 let body = &mut buf[start_len..];
290 crate::deflate::inflate_exact(self.raw, body).map_err(Ck3ErrorKind::from)?;
291 Ok(())
292 }
293
294 pub fn size(&self) -> usize {
295 self.size
296 }
297}
298
299pub struct Ck3Text<'a> {
301 tape: TextTape<'a>,
302}
303
304impl<'a> Ck3Text<'a> {
305 pub fn from_slice(data: &'a [u8]) -> Result<Self, Ck3Error> {
306 let header = SaveHeader::from_slice(data)?;
307 Self::from_raw(&data[..header.header_len()])
308 }
309
310 pub(crate) fn from_raw(data: &'a [u8]) -> Result<Self, Ck3Error> {
311 let tape = TextTape::from_slice(data).map_err(Ck3ErrorKind::Parse)?;
312 Ok(Ck3Text { tape })
313 }
314
315 pub fn reader(&self) -> ObjectReader<Utf8Encoding> {
316 self.tape.utf8_reader()
317 }
318
319 pub fn deserialize<T>(&self) -> Result<T, Ck3Error>
320 where
321 T: Deserialize<'a>,
322 {
323 let result =
324 TextDeserializer::from_utf8_tape(&self.tape).map_err(Ck3ErrorKind::Deserialize)?;
325 Ok(result)
326 }
327}
328
329pub struct Ck3Binary<'a> {
331 tape: BinaryTape<'a>,
332 header: SaveHeader,
333}
334
335impl<'a> Ck3Binary<'a> {
336 pub fn from_slice(data: &'a [u8]) -> Result<Self, Ck3Error> {
337 let header = SaveHeader::from_slice(data)?;
338 Self::from_raw(&data[..header.header_len()], header)
339 }
340
341 pub(crate) fn from_raw(data: &'a [u8], header: SaveHeader) -> Result<Self, Ck3Error> {
342 let tape = BinaryTape::from_slice(data).map_err(Ck3ErrorKind::Parse)?;
343 Ok(Ck3Binary { tape, header })
344 }
345
346 pub fn deserializer<'b>(&'b self) -> Ck3BinaryDeserializer<'a, 'b> {
347 Ck3BinaryDeserializer {
348 builder: BinaryDeserializer::builder_flavor(flavor_from_tape(&self.tape)),
349 tape: &self.tape,
350 }
351 }
352
353 pub fn melter<'b>(&'b self) -> Ck3Melter<'a, 'b> {
354 Ck3Melter::new(&self.tape, &self.header)
355 }
356}
357
358enum Ck3DeserializerKind<'a, 'b> {
359 Text(&'b Ck3Text<'a>),
360 Binary(Ck3BinaryDeserializer<'a, 'b>),
361}
362
363pub struct Ck3Deserializer<'a, 'b> {
365 kind: Ck3DeserializerKind<'a, 'b>,
366}
367
368impl<'a, 'b> Ck3Deserializer<'a, 'b> {
369 pub fn on_failed_resolve(&mut self, strategy: FailedResolveStrategy) -> &mut Self {
370 if let Ck3DeserializerKind::Binary(x) = &mut self.kind {
371 x.on_failed_resolve(strategy);
372 }
373 self
374 }
375
376 pub fn build<T, R>(&self, resolver: &'a R) -> Result<T, Ck3Error>
377 where
378 R: TokenResolver,
379 T: Deserialize<'a>,
380 {
381 match &self.kind {
382 Ck3DeserializerKind::Text(x) => x.deserialize(),
383 Ck3DeserializerKind::Binary(x) => x.build(resolver),
384 }
385 }
386}
387
388pub struct Ck3BinaryDeserializer<'a, 'b> {
390 builder: BinaryDeserializerBuilder<Box<dyn Ck3BinaryFlavor>>,
391 tape: &'b BinaryTape<'a>,
392}
393
394impl<'a, 'b> Ck3BinaryDeserializer<'a, 'b> {
395 pub fn on_failed_resolve(&mut self, strategy: FailedResolveStrategy) -> &mut Self {
396 self.builder.on_failed_resolve(strategy);
397 self
398 }
399
400 pub fn build<T, R>(&self, resolver: &'a R) -> Result<T, Ck3Error>
401 where
402 R: TokenResolver,
403 T: Deserialize<'a>,
404 {
405 let result = self
406 .builder
407 .from_tape(self.tape, resolver)
408 .map_err(|e| match e.kind() {
409 jomini::ErrorKind::Deserialize(e2) => match e2.kind() {
410 &jomini::DeserializeErrorKind::UnknownToken { token_id } => {
411 Ck3ErrorKind::UnknownToken { token_id }
412 }
413 _ => Ck3ErrorKind::Deserialize(e),
414 },
415 _ => Ck3ErrorKind::Deserialize(e),
416 })?;
417 Ok(result)
418 }
419}