rs2cache/
js5_masterindex.rs1use crate::{
2 js5_compression::Js5Compression,
3 js5_index::{Js5Index, Js5Protocol},
4 store::{Store, ARCHIVESET},
5};
6use crc32fast::hash;
7use osrs_bytes::WriteExt;
8use std::{cmp, io::Write};
9
10const MASTERINDEXFORMAT_ORIGINAL: u8 = 0;
11const MASTERINDEXFORMAT_VERSIONED: u8 = 1;
12const MASTERINDEXFORMAT_DIGESTS: u8 = 2;
13const MASTERINDEXFORMAT_LENGTHS: u8 = 3;
14
15pub struct Js5MasterIndexEntry {
16 pub version: i32,
17 pub checksum: u32,
18 pub groups: usize,
19 pub total_uncompressed_length: u32,
20 pub digest: Option<[u8; 32]>,
21}
22
23pub struct Js5MasterIndex {
24 pub format: u8,
25 pub entries: Vec<Js5MasterIndexEntry>,
26}
27
28impl Js5MasterIndex {
29 pub fn write(&self) -> Vec<u8> {
30 let mut buf = Vec::new();
31
32 if self.format >= MASTERINDEXFORMAT_DIGESTS {
33 buf.write_u8(self.entries.len() as u8).unwrap();
34 }
35
36 for entry in &self.entries {
37 buf.write_u32(entry.checksum).unwrap();
38
39 if self.format >= MASTERINDEXFORMAT_VERSIONED {
40 buf.write_i32(entry.version).unwrap();
41 }
42
43 if self.format >= MASTERINDEXFORMAT_LENGTHS {
44 buf.write_i32(entry.groups as i32).unwrap();
45 buf.write_u32(entry.total_uncompressed_length).unwrap();
46 }
47
48 if self.format >= MASTERINDEXFORMAT_DIGESTS {
49 if let Some(digest) = &entry.digest {
50 buf.write_all(digest).unwrap();
51 } else {
52 buf.write_all(&[0; 32]).unwrap();
53 }
54 }
55 }
56
57 buf
60 }
61
62 pub fn create(store: &Box<dyn Store>) -> Js5MasterIndex {
63 let mut master_index = Js5MasterIndex {
64 format: MASTERINDEXFORMAT_ORIGINAL,
65 entries: Vec::new(),
66 };
67
68 let mut next_archive = 0;
69 for archive in store.list(ARCHIVESET).unwrap() {
70 let read = store.read(ARCHIVESET, archive).unwrap();
71
72 let checksum = hash(&read);
73
74 let uncompress = Js5Compression::uncompress(read, None).unwrap();
75
76 let index = Js5Index::read(uncompress).unwrap();
77
78 if index.has_lengths {
79 master_index.format = cmp::max(master_index.format, MASTERINDEXFORMAT_LENGTHS);
80 } else if index.has_digests {
81 master_index.format = cmp::max(master_index.format, MASTERINDEXFORMAT_DIGESTS);
82 } else if index.protocol >= Js5Protocol::Versioned as u8 {
83 master_index.format = cmp::max(master_index.format, MASTERINDEXFORMAT_VERSIONED);
84 }
85
86 let version = index.version;
87 let groups = index.groups.len();
88 let total_uncompressed_length: u32 = index
89 .groups
90 .iter()
91 .map(|group| group.1.uncompressed_length)
92 .sum();
93
94 for _ in next_archive..archive {
95 master_index.entries.push(Js5MasterIndexEntry {
96 version: 0,
97 checksum: 0,
98 groups: 0,
99 total_uncompressed_length: 0,
100 digest: None,
101 });
102 }
103
104 master_index.entries.push(Js5MasterIndexEntry {
105 version,
106 checksum,
107 groups,
108 total_uncompressed_length,
109 digest: None,
110 });
111
112 next_archive = archive + 1;
113 }
114
115 master_index
116 }
117}
118
119