hadris_iso/write/options.rs
1use alloc::string::String;
2
3use super::super::boot::options::BootOptions;
4use super::super::read::PathSeparator;
5use super::super::rrip::RripOptions;
6use crate::joliet::JolietLevel;
7
8/// Hybrid boot options for creating bootable ISO images from USB/disk.
9///
10/// This enables the ISO to be bootable when written directly to a USB drive
11/// or other storage media, in addition to being bootable as a CD/DVD.
12#[derive(Debug, Clone, Default)]
13pub struct HybridBootOptions {
14 /// The type of partition table to write.
15 pub partition_scheme: PartitionScheme,
16 /// Optional MBR bootstrap code to inject (must be 446 bytes or less).
17 /// This is typically the first stage of a bootloader like GRUB or Syslinux.
18 pub mbr_bootstrap: Option<alloc::vec::Vec<u8>>,
19 /// Whether to mark the ISO partition as bootable in the MBR.
20 pub bootable: bool,
21 /// Path (in the ISO tree, using the configured path separator) of the
22 /// El Torito UEFI boot image to additionally expose as a GPT EFI System
23 /// Partition.
24 ///
25 /// Applies to [`PartitionScheme::Gpt`] and [`PartitionScheme::Hybrid`].
26 /// When `None`, and the El Torito options contain exactly one
27 /// `PlatformId::UEFI` section entry, that entry's boot image is exposed
28 /// automatically. Formatting fails with a not-found error when the
29 /// configured path does not resolve to a file in the ISO tree.
30 pub efi_boot_partition: Option<String>,
31}
32
33impl HybridBootOptions {
34 /// Create options for MBR-only hybrid boot (BIOS systems).
35 pub fn mbr() -> Self {
36 Self {
37 partition_scheme: PartitionScheme::Mbr,
38 mbr_bootstrap: None,
39 bootable: true,
40 efi_boot_partition: None,
41 }
42 }
43
44 /// Create options for GPT-only boot (UEFI systems).
45 pub fn gpt() -> Self {
46 Self {
47 partition_scheme: PartitionScheme::Gpt,
48 mbr_bootstrap: None,
49 bootable: false,
50 efi_boot_partition: None,
51 }
52 }
53
54 /// Create options for hybrid MBR+GPT boot (dual BIOS/UEFI systems).
55 pub fn hybrid() -> Self {
56 Self {
57 partition_scheme: PartitionScheme::Hybrid,
58 mbr_bootstrap: None,
59 bootable: true,
60 efi_boot_partition: None,
61 }
62 }
63
64 /// Set the MBR bootstrap code.
65 pub fn bootstrap(mut self, bootstrap: alloc::vec::Vec<u8>) -> Self {
66 self.mbr_bootstrap = Some(bootstrap);
67 self
68 }
69
70 /// Set the path of the El Torito UEFI boot image to expose as a GPT EFI
71 /// System Partition. See [`HybridBootOptions::efi_boot_partition`].
72 pub fn with_efi_boot_partition(mut self, path: impl Into<String>) -> Self {
73 self.efi_boot_partition = Some(path.into());
74 self
75 }
76}
77
78/// The partition scheme to use for hybrid boot.
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
80pub enum PartitionScheme {
81 /// No partition table (CD/DVD only, not USB bootable).
82 #[default]
83 None,
84 /// MBR partition table only (for BIOS USB boot).
85 Mbr,
86 /// GPT partition table only (for UEFI boot).
87 Gpt,
88 /// Hybrid MBR + GPT (for dual BIOS/UEFI boot).
89 /// Creates a protective MBR with GPT, plus MBR entries mirroring key partitions.
90 Hybrid,
91}
92
93#[derive(Debug, Clone)]
94/// Represents IsoFormatOptions.
95pub struct IsoFormatOptions {
96 /// The `volume_name` field.
97 pub volume_name: String,
98 /// The `system_id` field.
99 pub system_id: Option<String>,
100 /// The `volume_set_id` field.
101 pub volume_set_id: Option<String>,
102 /// The `publisher_id` field.
103 pub publisher_id: Option<String>,
104 /// The `preparer_id` field.
105 pub preparer_id: Option<String>,
106 /// The `application_id` field.
107 pub application_id: Option<String>,
108 /// The `sector_size` field.
109 pub sector_size: usize,
110 /// The `features` field.
111 pub features: CreationFeatures,
112 /// The `path_separator` field.
113 pub path_separator: PathSeparator,
114 /// When false (default), PVD string fields are stored as-is without charset
115 /// validation (matching xorriso/genisoimage behavior). When true, auto-converts
116 /// lowercase to uppercase and substitutes invalid characters for ECMA-119 compliance.
117 pub strict_charset: bool,
118}
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121/// Identifies a BaseIsoLevel value.
122pub enum BaseIsoLevel {
123 /// L1 Filenames
124 /// Supports only uppercase and using the 8.3 format
125 Level1 {
126 /// The `supports_lowercase` field.
127 supports_lowercase: bool,
128 /// The `supports_rrip` field.
129 supports_rrip: bool,
130 },
131 /// L2 Filenames
132 /// Supports up to 30 characters
133 Level2 {
134 /// The `supports_lowercase` field.
135 supports_lowercase: bool,
136 /// The `supports_rrip` field.
137 supports_rrip: bool,
138 },
139 /// ISO 9660 interchange level 3.
140 ///
141 /// Level 3 retains the Level 2 filename rules and permits a logical file
142 /// to be represented by multiple consecutive directory records/extents.
143 Level3 {
144 /// Whether lowercase ASCII names are accepted.
145 supports_lowercase: bool,
146 /// Whether Rock Ridge system-use fields are emitted.
147 supports_rrip: bool,
148 },
149}
150
151#[derive(Debug, Clone)]
152/// Represents CreationFeatures.
153pub struct CreationFeatures {
154 /// The base Filename Level
155 /// This only supports ASCII uppercase, numbers, and '_' for compatibility reasons.
156 pub filenames: BaseIsoLevel,
157 /// The L3 Filename Level
158 /// This supports filenames up to 207 characters, without using Joliet or Rock Ridge
159 pub long_filenames: bool,
160 /// The Joliet Extension for Unicode filenames
161 pub joliet: Option<JolietLevel>,
162 /// Rock Ridge extension options for POSIX filesystem semantics
163 pub rock_ridge: Option<RripOptions>,
164 /// El-Torito boot options (for CD/DVD boot)
165 pub el_torito: Option<BootOptions>,
166 /// Hybrid boot options (for USB/disk boot)
167 /// Enables the ISO to be bootable when written directly to a USB drive.
168 pub hybrid_boot: Option<HybridBootOptions>,
169}
170
171impl Default for CreationFeatures {
172 fn default() -> Self {
173 Self {
174 filenames: BaseIsoLevel::Level1 {
175 supports_lowercase: false,
176 supports_rrip: false,
177 },
178 long_filenames: false,
179 joliet: None,
180 rock_ridge: None,
181 el_torito: None,
182 hybrid_boot: None,
183 }
184 }
185}
186
187impl CreationFeatures {
188 /// Create features with Rock Ridge enabled (default settings)
189 pub fn rock_ridge() -> Self {
190 Self {
191 filenames: BaseIsoLevel::Level1 {
192 supports_lowercase: false,
193 supports_rrip: true,
194 },
195 rock_ridge: Some(RripOptions::default()),
196 ..Default::default()
197 }
198 }
199
200 /// Create features with Joliet enabled
201 pub fn joliet(level: JolietLevel) -> Self {
202 Self {
203 joliet: Some(level),
204 ..Default::default()
205 }
206 }
207
208 /// Create features with both Rock Ridge and Joliet enabled
209 pub fn extensions() -> Self {
210 Self {
211 filenames: BaseIsoLevel::Level1 {
212 supports_lowercase: false,
213 supports_rrip: true,
214 },
215 joliet: Some(JolietLevel::Level3),
216 rock_ridge: Some(RripOptions::default()),
217 ..Default::default()
218 }
219 }
220
221 /// Create features with hybrid boot enabled (MBR for USB boot)
222 pub fn hybrid_boot(scheme: PartitionScheme) -> Self {
223 Self {
224 hybrid_boot: Some(HybridBootOptions {
225 partition_scheme: scheme,
226 mbr_bootstrap: None,
227 bootable: true,
228 efi_boot_partition: None,
229 }),
230 ..Default::default()
231 }
232 }
233}
234
235impl From<BaseIsoLevel> for crate::file::EntryType {
236 fn from(value: BaseIsoLevel) -> Self {
237 match value {
238 BaseIsoLevel::Level1 {
239 supports_lowercase,
240 supports_rrip,
241 } => Self::Level1 {
242 supports_lowercase,
243 supports_rrip,
244 },
245 BaseIsoLevel::Level2 {
246 supports_lowercase,
247 supports_rrip,
248 } => Self::Level2 {
249 supports_lowercase,
250 supports_rrip,
251 },
252 BaseIsoLevel::Level3 {
253 supports_lowercase,
254 supports_rrip,
255 } => Self::Level2 {
256 supports_lowercase,
257 supports_rrip,
258 },
259 }
260 }
261}