esrtool/
lib.rs

1use std::fmt;
2use std::fs::OpenOptions;
3use std::io;
4use std::io::{BufReader, Read, Write};
5
6mod defines;
7
8pub struct Iso {
9    udf: bool,
10    patched: bool,
11    path: String,
12    data: Vec<u8>,
13}
14
15pub enum ActionResult {
16    PatchOK,
17    UnpatchOK,
18    WriteOK,
19}
20// Public Functions
21impl Iso {
22    pub fn new(path: &str) -> Result<Iso, io::Error> {
23        let iso_f = OpenOptions::new()
24            .read(true)
25            .write(true)
26            .create(false)
27            .open(path)?;
28        let mut reader = BufReader::new(iso_f);
29        let mut data = [0u8; 500 * 1024]; // 500KB
30        reader.read_exact(&mut data)?;
31        let data: Vec<u8> = data.to_vec();
32        let mut iso = Iso {
33            udf: false,
34            patched: false,
35            path: path.to_string(),
36            data,
37        };
38        iso.check_iso();
39        Ok(iso)
40    }
41    pub fn check_iso(&mut self) {
42        self.check_udf();
43        self.check_patched();
44    }
45    pub fn check_udf(&mut self) -> bool {
46        self.udf = false;
47        for i in 1..64 {
48            let offset = ((LBA_SIZE * i) + 32768 + 1) as usize;
49            if self.data[offset..offset + 3] == b"NSR".to_owned() {
50                self.udf = true;
51                break;
52            }
53        }
54        return self.udf;
55    }
56
57    pub fn check_patched(&mut self) -> bool {
58        let offset = ((LBA_SIZE * 14) + 25) as usize;
59        self.patched = self.data[offset..offset + 4] == b"+NSR".to_owned();
60        self.patched
61    }
62
63    pub fn patch(&mut self) -> Result<ActionResult, &'static str> {
64        if !self.udf {
65            return Err("No UDF descriptor found. Is this really an ISO?");
66        }
67        if self.patched {
68            return Err("Already patched! Did you want to unpatch?");
69        }
70        self.copy_lba(34, 14);
71        self.copy_lba(50, 15);
72        self.patch_lba(34);
73        self.patch_lba(50);
74        self.write_dvd_data();
75        self.check_iso();
76        Ok(ActionResult::PatchOK)
77    }
78
79    pub fn unpatch(&mut self) -> Result<ActionResult, &'static str> {
80        if !self.udf {
81            return Err("No UDF descriptor found. Is this really an ISO?");
82        }
83        if !self.patched {
84            return Err("File isn't patched! Did you want to patch?");
85        }
86        self.copy_lba(14, 34);
87        self.copy_lba(15, 50);
88        let zeros = vec![0u8; LBA_SIZE as usize];
89        self.write_lba(&zeros, 14);
90        self.write_lba(&zeros, 15);
91        for i in 0..12 {
92            self.write_lba(&zeros, 128 + i);
93        }
94        self.check_iso();
95        Ok(ActionResult::UnpatchOK)
96    }
97    pub fn write(&self) -> Result<ActionResult, io::Error> {
98        let mut iso_f = OpenOptions::new()
99            .read(false)
100            .write(true)
101            .create(false)
102            .open(&self.path)?;
103        iso_f.write_all(&self.data)?;
104        Ok(ActionResult::WriteOK)
105    }
106}
107
108// Private Functions
109impl Iso {
110    fn patch_lba(&mut self, dst_lba: u64) {
111        let dst_s = (dst_lba * LBA_SIZE) as usize;
112        let dst_e = dst_s + LBA_SIZE as usize;
113        let mut lba = self.data[dst_s..dst_e].to_vec();
114
115        lba[188] = 128;
116        lba[189] = 0;
117
118        let desc_crc_len: u16 = (lba[10] as u16) | ((lba[11] as u16) << 8);
119        let desc: Vec<u8> = lba[16..2048].to_vec();
120        let desc_crc = Self::crc(&desc, desc_crc_len as usize);
121        let desc_crc_bytes = desc_crc.to_le_bytes();
122        lba[8] = desc_crc_bytes[0];
123        lba[9] = desc_crc_bytes[1];
124
125        let mut checksum = 0u8;
126        for i in 0..16 {
127            checksum = checksum.wrapping_add(lba[i]);
128        }
129        checksum = checksum.wrapping_sub(lba[4]);
130        lba[4] = checksum;
131        self.write_lba(&lba, dst_lba);
132    }
133
134    fn write_lba(&mut self, lba: &Vec<u8>, dst_lba: u64) {
135        let dst_s = (dst_lba * LBA_SIZE) as usize;
136        let dst_e = dst_s + (LBA_SIZE as usize);
137        self.data[dst_s..dst_e].copy_from_slice(&lba);
138    }
139
140    fn copy_lba(&mut self, slba: u64, dlba: u64) {
141        let src_start = (LBA_SIZE * slba) as usize;
142        let src_end = src_start + (LBA_SIZE as usize);
143        let lba = self.data[src_start..src_end].to_vec();
144        let dest_start = (LBA_SIZE * dlba) as usize;
145        let dest_end = dest_start + (LBA_SIZE as usize);
146        self.data[dest_start..dest_end].copy_from_slice(&lba[..])
147    }
148
149    fn write_dvd_data(&mut self) {
150        let dst_s = (128 * LBA_SIZE) as usize;
151        let dst_e = dst_s + 12 * (LBA_SIZE as usize);
152        self.data[dst_s..dst_e].copy_from_slice(&defines::DVD_DATA);
153    }
154}
155
156// Associated functions
157impl Iso {
158    fn crc(block: &[u8], desc_crc_len: usize) -> u16 {
159        let mut crc = 0u16;
160        for i in 0..desc_crc_len {
161            let crc_bytes = crc.to_le_bytes();
162            let crc_h = crc_bytes[0];
163            let crc_l = crc_bytes[1];
164            crc = (crc_h as u16) << 8;
165            let j: usize = (crc_l ^ block[i]).into();
166            crc ^= defines::CRC_LOOKUP[j];
167        }
168        crc
169    }
170}
171
172impl fmt::Display for Iso {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        write!(
175            f,
176            "Path:\t{}\nUDF:\t{}\nPatch:\t{}",
177            self.path, self.udf, self.patched
178        )
179    }
180}
181
182const LBA_SIZE: u64 = 2048;