1#![warn(missing_docs)]
2use binwrite::{BinWrite, WriterOption};
5use conv::ValueFrom;
6use sha2::{Digest, Sha256};
7use std::fmt;
8use std::fmt::Formatter;
9use std::io::Write;
10use thiserror::Error;
11
12#[derive(BinWrite, Debug, Default)]
13#[binwrite(little)]
14struct BootDatHeader {
28 inner: BootDatInner,
29 sha2_hdr: Sha2,
30}
31
32#[derive(BinWrite, Debug, Default)]
33#[binwrite(little)]
34struct BootDatInner {
35 ident: [u8; 0xc],
36 vers: [u8; 0x4],
37 sha2_s2: Sha2,
38 s2_dst: u32,
39 s2_size: u32,
40 s2_enc: u32,
41 pad: [u8; 0x10],
42 s3_size: u32,
43 pad2: Pad2,
44}
45
46#[derive(Error, Debug, Clone)]
48pub enum Error {
49 IoError(String),
51 HashError,
53 TruncationError,
55}
56
57impl std::convert::From<std::io::Error> for Error {
58 fn from(err: std::io::Error) -> Self {
59 Error::IoError(err.to_string())
60 }
61}
62
63impl std::fmt::Display for Error {
64 fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
65 match self {
66 Error::IoError(s) => write!(fmt, "IO Error: {}", s),
67 Error::HashError => write!(fmt, "Hash Error"),
68 Error::TruncationError => write!(fmt, "Number Truncation Error"),
69 }
70 }
71}
72
73struct Pad2([u8; 0x90]);
75
76impl Default for Pad2 {
77 fn default() -> Self {
78 Pad2([0; 0x90])
79 }
80}
81
82impl fmt::Debug for Pad2 {
83 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
84 write!(f, "{:?}", self.0)
85 }
86}
87
88impl BinWrite for Pad2 {
89 fn write_options<W: Write>(
90 &self,
91 writer: &mut W,
92 options: &WriterOption,
93 ) -> std::io::Result<()> {
94 for item in &self.0 {
95 BinWrite::write_options(item, writer, options)?;
96 }
97 Ok(())
98 }
99}
100
101#[derive(Default)]
102struct Sha2([u8; 0x20]);
103
104impl fmt::Debug for Sha2 {
105 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
106 write!(f, "{:?}", self.0)
107 }
108}
109
110impl BinWrite for Sha2 {
111 fn write_options<W: Write>(
112 &self,
113 writer: &mut W,
114 options: &WriterOption,
115 ) -> std::io::Result<()> {
116 for item in &self.0 {
117 BinWrite::write_options(item, writer, options)?;
118 }
119 Ok(())
120 }
121}
122
123#[must_use]
125pub fn get_version() -> &'static str {
126 env!("CARGO_PKG_VERSION")
127}
128
129pub fn generate_boot_dat(payload: &[u8]) -> Result<Vec<u8>, Error> {
137 let mut header = BootDatHeader::default();
138 header.inner.ident = [
139 0x49, 0x6e, 0x73, 0x61, 0x6e, 0x65, 0x20, 0x42, 0x4F, 0x4F, 0x54, 0x00,
140 ];
141 header.inner.vers = [0x56, 0x31, 0x2E, 0x30];
142
143 let stage_2_sha256 = sha256_digest(payload);
144 header.inner.sha2_s2 = Sha2(stage_2_sha256.try_into().map_err(|_| Error::HashError)?);
145 header.inner.s2_dst = 0x4001_0000;
146 header.inner.s2_size = u32::value_from(payload.len()).map_err(|_| Error::TruncationError)?;
147
148 let mut inner_serialized = vec![];
149 header.inner.write(&mut inner_serialized)?;
150
151 let header_inner_sha256 = sha256_digest(inner_serialized.as_slice());
152 header.sha2_hdr = Sha2(
153 header_inner_sha256
154 .try_into()
155 .map_err(|_| Error::HashError)?,
156 );
157
158 let mut serialized = vec![];
159 header.write(&mut serialized)?;
160
161 serialized.extend_from_slice(payload);
162 Ok(serialized)
163}
164
165fn sha256_digest(to_hash: &[u8]) -> Vec<u8> {
167 let mut hasher = Sha256::new();
168 hasher.update(to_hash);
169 hasher.finalize().to_vec()
170}
171
172#[cfg(test)]
173mod tests {
174 use hex_literal::hex;
175 #[test]
176 fn end_to_end_test() {
177 let generated = super::generate_boot_dat(&[0xa, 0xb, 0xc]).unwrap();
178 let hash = super::sha256_digest(generated.as_slice());
179 assert_eq!(
180 hash[..],
181 hex!("6ce4c88e604d351b0e14bca7dbf135b3c8c44428718b704883599f285eed984e")
182 );
183 }
184}