xp3\index/
file.rs

1/*
2 * Created on Tue Dec 15 2020
3 *
4 * Copyright (c) storycraft. Licensed under the Apache Licence 2.0.
5 */
6
7use std::{convert::TryFrom, io::{Cursor, Read, Write}};
8
9use byteorder::LittleEndian;
10use encoding::{DecoderTrap, EncoderTrap, Encoding, all::UTF_16LE};
11
12use crate::{XP3Error, XP3ErrorKind, XP3_INDEX_ADLR_IDENTIFIER, XP3_INDEX_INFO_IDENTIFIER, XP3_INDEX_SEGM_IDENTIFIER, XP3_INDEX_TIME_IDENTIFIER};
13use byteorder::{ReadBytesExt, WriteBytesExt};
14
15use super::XP3Index;
16
17/// FileIndex for xp3 archive.
18/// Contains information about file and data offsets.
19#[derive(Debug, Clone)]
20pub struct XP3FileIndex {
21
22    info: XP3FileIndexInfo,
23    segments: Vec<XP3FileIndexSegment>,
24    adler: XP3FileIndexAdler,
25    time: Option<XP3FileIndexTime>
26
27}
28
29impl XP3FileIndex {
30
31    pub fn new(
32        info: XP3FileIndexInfo,
33        segments: Vec<XP3FileIndexSegment>,
34        adler: XP3FileIndexAdler,
35        time: Option<XP3FileIndexTime>
36    ) -> Self {
37        Self {
38            info,
39            segments,
40            adler,
41            time
42        }
43    }
44
45    /// File time
46    pub fn time(&self) -> Option<XP3FileIndexTime> {
47        self.time
48    }
49
50    /// File adler hash
51    pub fn adler(&self) -> XP3FileIndexAdler {
52        self.adler
53    }
54
55    /// File information
56    pub fn info(&self) -> &XP3FileIndexInfo {
57        &self.info
58    }
59
60    /// File segments
61    pub fn segments(&self) -> &Vec<XP3FileIndexSegment> {
62        &self.segments
63    }
64
65    /// Read xp3 file index from stream.
66    /// Returns read size, XP3FileIndex tuple.
67    pub fn from_bytes(file_size: u64, stream: &mut impl Read) -> Result<(u64, Self), XP3Error> {
68        let mut info: Option<XP3FileIndexInfo> = None;
69        let mut segm: Option<Vec<XP3FileIndexSegment>> = None;
70        let mut time: Option<XP3FileIndexTime> = None;
71        let mut adler32: Option<XP3FileIndexAdler> = None;
72
73        let mut total_read: u64 = 0;
74        while total_read < file_size {
75            let (read, index) = XP3Index::from_bytes(stream)?;
76            
77            let mut data_stream = Cursor::new(&index.data);
78
79            match index.identifier {
80
81                XP3_INDEX_INFO_IDENTIFIER => {
82                    if info.is_some() {
83                        return Err(XP3Error::new(XP3ErrorKind::InvalidFileIndex, None));
84                    }
85
86                    info = Some(XP3FileIndexInfo::from_bytes(&mut data_stream)?.1);
87                },
88
89                XP3_INDEX_SEGM_IDENTIFIER => {
90                    if segm.is_some() {
91                        return Err(XP3Error::new(XP3ErrorKind::InvalidFileIndex, None));
92                    }
93
94                    let count = data_stream.get_ref().len() / 28;
95
96                    let mut list: Vec<XP3FileIndexSegment> = Vec::new();
97                    for _ in 0..count {
98                        list.push(XP3FileIndexSegment::from_bytes(&mut data_stream)?.1);
99                    }
100
101                    segm = Some(list);
102                },
103
104                XP3_INDEX_ADLR_IDENTIFIER => {
105                    adler32 = Some(XP3FileIndexAdler::from_bytes(&mut data_stream)?.1);
106                },
107
108                XP3_INDEX_TIME_IDENTIFIER => {
109                    time = Some(XP3FileIndexTime::from_bytes(&mut data_stream)?.1);
110                },
111                
112                _ => {
113                    // SKIP infos we don't know yet.
114                }
115            }
116
117            
118            total_read += read;
119        }
120
121        if info.is_none() || adler32.is_none() || segm.is_none() {
122            return Err(XP3Error::new(XP3ErrorKind::InvalidFileIndex, None));
123        }
124
125        Ok((total_read, XP3FileIndex::new(info.unwrap(), segm.unwrap(), adler32.unwrap(), time)))
126    }
127
128    /// Write xp3 file index to stream.
129    pub fn write_bytes(&self, stream: &mut impl Write) -> Result<u64, XP3Error> {
130        let mut written = 0_u64;
131
132        if self.time.is_some() {
133            let mut buffer = Vec::<u8>::new();
134            self.time.unwrap().write_bytes(&mut buffer)?;
135            written += XP3Index { identifier: XP3_INDEX_TIME_IDENTIFIER, data: buffer }.write_bytes(stream)?;
136        }
137
138        {
139            let mut buffer = Vec::<u8>::new();
140            self.adler.write_bytes(&mut buffer)?;
141            written += XP3Index { identifier: XP3_INDEX_ADLR_IDENTIFIER, data: buffer }.write_bytes(stream)?;
142        }
143
144        {
145            let mut buffer = Vec::<u8>::new();
146            for segment in self.segments.iter() {
147                segment.write_bytes(&mut buffer)?;
148            }
149
150            written += XP3Index { identifier: XP3_INDEX_SEGM_IDENTIFIER, data: buffer }.write_bytes(stream)?;
151        }
152
153        {
154            let mut buffer = Vec::<u8>::new();
155            self.info.write_bytes(&mut buffer)?;
156            written += XP3Index { identifier: XP3_INDEX_INFO_IDENTIFIER, data: buffer }.write_bytes(stream)?;
157        }
158
159        Ok(written)
160    }
161
162}
163
164#[derive(Debug, Copy, Clone)]
165#[repr(u32)]
166pub enum IndexInfoFlag {
167
168    NotProtected = 0,
169    Protected = 2147483648
170
171}
172
173impl TryFrom<u32> for IndexInfoFlag {
174
175    type Error = XP3Error;
176
177    fn try_from(value: u32) -> Result<Self, Self::Error> {
178        match value {
179
180            value if value == Self::NotProtected as u32 => Ok(Self::NotProtected),
181            value if value == Self::Protected as u32 => Ok(Self::Protected),
182
183            _ => Err(XP3Error::new(XP3ErrorKind::InvalidFileIndexFlag, None))
184        }
185    }
186}
187
188#[derive(Debug, Clone)]
189/// Index info represents file metadatas like name.
190pub struct XP3FileIndexInfo {
191
192    flag: IndexInfoFlag,
193    file_size: u64,
194    saved_file_size: u64,
195    name: String
196
197}
198
199impl XP3FileIndexInfo {
200
201    pub fn new(
202        flag: IndexInfoFlag,
203        file_size: u64,
204        saved_file_size: u64,
205        name: String
206    ) -> Self {
207        Self {
208            flag,
209            file_size,
210            saved_file_size,
211            name
212        }
213    }
214
215    pub fn flag(&self) -> IndexInfoFlag {
216        self.flag
217    }
218
219    pub fn set_flag(&mut self, flag: IndexInfoFlag) {
220        self.flag = flag;
221    }
222
223    pub fn file_size(&self) -> u64 {
224        self.file_size
225    }
226
227    pub fn set_file_size(&mut self, file_size: u64) {
228        self.file_size = file_size;
229    }
230
231    pub fn saved_file_size(&self) -> u64 {
232        self.saved_file_size
233    }
234
235    pub fn set_saved_file_size(&mut self, saved_file_size: u64) {
236        self.saved_file_size = saved_file_size;
237    }
238    
239    pub fn name(&self) -> &String {
240        &self.name
241    }
242
243    pub fn set_name(&mut self, name: String) {
244        self.name = name;
245    }
246
247    /// Read xp3 file index info from stream.
248    pub fn from_bytes(stream: &mut impl Read) -> Result<(u64, Self), XP3Error> {
249        let flag = IndexInfoFlag::try_from(stream.read_u32::<LittleEndian>()?)?;
250
251        let file_size = stream.read_u64::<LittleEndian>()?;
252        let saved_file_size = stream.read_u64::<LittleEndian>()?;
253        
254        let name_byte_size = stream.read_u16::<LittleEndian>()? * 2;
255        let name = {
256            let mut name_buffer = Vec::with_capacity(name_byte_size as usize);
257            stream.take(name_byte_size as u64).read_to_end(&mut name_buffer)?;
258            
259            UTF_16LE.decode(&name_buffer, DecoderTrap::Replace).unwrap()
260        };
261
262        Ok((22 + name_byte_size as u64, Self::new(flag, file_size, saved_file_size, name)))
263    }
264
265    /// Write xp3 file index info to stream.
266    /// Returns written size.
267    pub fn write_bytes(&self, stream: &mut impl Write) -> Result<u64, XP3Error> {
268        stream.write_u32::<LittleEndian>(self.flag as u32)?;
269        
270        stream.write_u64::<LittleEndian>(self.file_size)?;
271        stream.write_u64::<LittleEndian>(self.saved_file_size)?;
272
273        let encoded = UTF_16LE.encode(&self.name, EncoderTrap::Replace).unwrap();
274        let name_byte_size = encoded.len().min(65536);
275        stream.write_u16::<LittleEndian>((name_byte_size / 2) as u16)?;
276        stream.write_all(&encoded[..name_byte_size])?;
277
278        Ok(22 + name_byte_size as u64)
279    }
280
281}
282
283#[derive(Debug, Copy, Clone)]
284#[repr(u32)]
285pub enum IndexSegmentFlag {
286
287    UnCompressed = 0,
288    Compressed = 1
289
290}
291
292#[derive(Debug, Copy, Clone)]
293/// Index segments representing data fragments of target file.
294/// Almost all archive have 1 segments each files but there can be more.
295pub struct XP3FileIndexSegment {
296
297    flag: IndexSegmentFlag,
298    data_offset: u64,
299    original_size: u64,
300    saved_size: u64
301
302}
303
304impl XP3FileIndexSegment {
305
306    pub fn new(
307        flag: IndexSegmentFlag,
308        data_offset: u64,
309        original_size: u64,
310        saved_size: u64
311    ) -> Self {
312        Self {
313            flag, data_offset, original_size, saved_size
314        }
315    }
316
317    pub fn flag(&self) -> IndexSegmentFlag {
318        self.flag
319    }
320
321    pub fn set_flag(&mut self, flag: IndexSegmentFlag) {
322        self.flag = flag;
323    }
324
325    pub fn data_offset(&self) -> u64 {
326        self.data_offset
327    }
328
329    pub fn set_data_offset(&mut self, data_offset: u64) {
330        self.data_offset = data_offset;
331    }
332
333    pub fn original_size(&self) -> u64 {
334        self.original_size
335    }
336
337    pub fn set_original_size(&mut self, original_size: u64) {
338        self.original_size = original_size;
339    }
340
341    pub fn saved_size(&self) -> u64 {
342        self.saved_size
343    }
344
345    pub fn set_saved_size(&mut self, saved_size: u64) {
346        self.saved_size = saved_size;
347    }
348
349    /// Read xp3 file index segment from stream.
350    pub fn from_bytes(stream: &mut impl Read) -> Result<(u64, Self), XP3Error> {
351        let flag = {
352            let raw_flag = stream.read_u32::<LittleEndian>()?;
353
354            if raw_flag == IndexSegmentFlag::UnCompressed as u32 {
355                Ok(IndexSegmentFlag::UnCompressed)
356            } else if raw_flag == IndexSegmentFlag::Compressed as u32 {
357                Ok(IndexSegmentFlag::Compressed)
358            } else {
359                Err(XP3Error::new(XP3ErrorKind::InvalidFileIndex, None))
360            }
361        }?;
362
363        let data_offset = stream.read_u64::<LittleEndian>()?;
364        let original_size = stream.read_u64::<LittleEndian>()?;
365        let saved_size = stream.read_u64::<LittleEndian>()?;
366
367        Ok((28, Self::new(flag, data_offset, original_size, saved_size)))
368    }
369
370    /// Write xp3 file index segment to stream.
371    /// Returns written size.
372    pub fn write_bytes(&self, stream: &mut impl Write) -> Result<u64, XP3Error> {
373        stream.write_u32::<LittleEndian>(self.flag as u32)?;
374
375        stream.write_u64::<LittleEndian>(self.data_offset)?;
376        stream.write_u64::<LittleEndian>(self.original_size)?;
377        stream.write_u64::<LittleEndian>(self.saved_size)?;
378
379        Ok(28)
380    }
381
382}
383
384#[derive(Debug, Copy, Clone)]
385/// Index time representing timestamp of target file.
386pub struct XP3FileIndexTime {
387
388    timestamp: u64
389
390}
391
392impl XP3FileIndexTime {
393
394    pub fn new(timestamp: u64) -> Self {
395        Self {
396            timestamp
397        }
398    }
399
400    pub fn timestamp(&self) -> u64 {
401        self.timestamp
402    }
403
404    pub fn set_timestamp(&mut self, timestamp: u64) {
405        self.timestamp = timestamp;
406    }
407
408    /// Read xp3 file index time from stream.
409    pub fn from_bytes(stream: &mut impl Read) -> Result<(u64, Self), XP3Error> {
410        let timestamp = stream.read_u64::<LittleEndian>()?;
411
412        Ok((8, Self::new(timestamp)))
413    }
414
415    /// Write xp3 file time to stream.
416    pub fn write_bytes(&self, stream: &mut impl Write) -> Result<u64, XP3Error> {
417        stream.write_u64::<LittleEndian>(self.timestamp)?;
418
419        Ok(8)
420    }
421
422}
423
424#[derive(Debug, Copy, Clone)]
425/// Index adler representing adler32 checksum of target file.
426pub struct XP3FileIndexAdler {
427
428    checksum: u32
429
430}
431
432impl XP3FileIndexAdler {
433
434    pub fn new(checksum: u32) -> Self {
435        Self {
436            checksum
437        }
438    }
439
440    pub fn checksum(&self) -> u32 {
441        self.checksum
442    }
443
444    pub fn set_checksum(&mut self, checksum: u32) {
445        self.checksum = checksum;
446    }
447
448    /// Read xp3 file index time from stream.
449    pub fn from_bytes(stream: &mut impl Read) -> Result<(u64, Self), XP3Error> {
450        let checksum = stream.read_u32::<LittleEndian>()?;
451
452        Ok((4, Self::new(checksum)))
453    }
454
455    /// Write xp3 file time to stream.
456    pub fn write_bytes(&self, stream: &mut impl Write) -> Result<u64, XP3Error> {
457        stream.write_u32::<LittleEndian>(self.checksum)?;
458
459        Ok(4)
460    }
461
462}