cat_dev/mion/proto/cgis/
setup.rs

1use crate::mion::proto::cgis::MionCGIErrors;
2#[cfg(feature = "clients")]
3use mac_address::MacAddress;
4use std::fmt::{Display, Formatter, Result as FmtResult};
5use std::net::Ipv4Addr;
6
7/// The parameters you can fetch from the `setup.cgi` page.
8///
9/// While this page is normally really more intended for humans, rather
10/// than an automated parsers. HOWEVER, `fsemul` & several other useful
11/// functions are only available on this page.
12///
13/// Why they're not available in the parameter space, or why there's not
14/// a `get_param` action, but is a `set_param` action I'll never know.
15/// It will make me sad though.
16#[allow(
17	// Clippy this is not a state machine, >:(
18	clippy::struct_excessive_bools,
19)]
20#[cfg_attr(docsrs, doc(cfg(feature = "clients")))]
21#[cfg(feature = "clients")]
22#[derive(Clone, Debug, Hash, PartialEq, Eq)]
23pub struct SetupParameters {
24	/// The 'ip address' of the bridge, default value is "192.168.0.1".
25	///
26	/// *note: this value doesn't get used when DHCP is on)*.
27	///
28	/// Internal HTML ID: `id_5`
29	static_ip_address: Ipv4Addr,
30	/// The Subnet mask of the bridge, default value is "255.255.255.0".
31	///
32	/// Internal HTML ID: `id_6`
33	subnet_mask: Ipv4Addr,
34	/// The default Gateway to reach out to for the bridge, default value is
35	/// "0.0.0.0".
36	///
37	/// Internal HTML ID: `id_7`
38	default_gateway: Ipv4Addr,
39	/// If the device should be using DHCP, default is TRUE.
40	///
41	/// Internal HTML ID: `id_4`
42	dhcp: bool,
43	/// If the device should query it's own DNS Servers, default is FALSE.
44	///
45	/// Internal HTML ID: `id_8`
46	dns: bool,
47	/// The Primary DNS Server to reach out too if DNS is flipped on, default is
48	/// "0.0.0.0".
49	///
50	/// Internal HTML ID: `id_9`
51	primary_dns_server: Ipv4Addr,
52	/// The Secondary DNS Server to reach out too if DNS is flipped on, default is
53	/// "0.0.0.0".
54	///
55	/// Internal HTML ID: `id_10`.
56	secondary_dns_server: Ipv4Addr,
57	/// If we support jumbo frames on the network, default is TRUE.
58	///
59	/// Internal HTML ID: `id_11`
60	jumbo_frame: bool,
61	/// The IP Address of the Host PC to reach out too, default is "0.0.0.0".
62	///
63	/// Internal HTML ID: `id_12`
64	host_pc_ip: Ipv4Addr,
65	/// The bank size (or hard-disk) size of the CAT-DEV.
66	///
67	/// Internal HTML ID is: `id_26`
68	bank_size: CatDevBankSize,
69	/// The Hard Disk "bank" number to use (which area to read from when booting
70	/// a game), default is "0".
71	///
72	/// Internal HTML ID: `id_27`
73	hdd_bank_no: u8,
74	/// The port of the ATAPI Emulator, default is `7974`.
75	///
76	/// Internal HTML ID is: `id_13`
77	atapi_emulator_port: u16,
78	/// The port of the SDIO Printf/Control traffic, default is `7975`.
79	///
80	/// Internal HTML ID: `id_14`
81	sdio_printf_port: u16,
82	/// The port of the SDIO Block Data traffic, default is `7976`.
83	///
84	/// Internal HTML ID: `id_15`
85	sdio_block_port: u16,
86	/// The port of the EXI traffic, default is `7977`.
87	///
88	/// Internal HTML ID: `id_16`
89	exi_port: u16,
90	/// The port for parameter-space traffic, default is `7978`.
91	///
92	/// *note: changing this will break official nintendo tools. Although our
93	/// tools do uspport it, it will require manually passing the port in. it
94	/// is unideal to change this port.*
95	///
96	/// Internal HTML ID: `id_17`
97	parameter_space_port: u16,
98	/// If drive timing emulation is on (make the disk slower on purpose),
99	/// default is FALSE.
100	///
101	/// Internal HTML ID: `id_33`
102	drive_timing_emulation: bool,
103	/// If the CAT-DEV device is operating in CAT-DEV mode, or H-READER mode,
104	/// default is FALSE.
105	///
106	/// Internal HTML ID: `id_32`
107	operational_mode_is_reader: bool,
108	/// Internal Hard-Drive product revision in hex.
109	///
110	/// Internal HTML ID: `id_28`
111	drive_product_revision: String,
112	/// Internal Hard-Drive vendor code in hex.
113	///
114	/// Internal HTML ID: `id_29`
115	drive_vendor_code: String,
116	/// Internal Hard-Drive device code in hex.
117	///
118	/// Internal HTML ID: `id_30`
119	drive_device_code: String,
120	/// Internal Hard-Drive release date in hex.
121	///
122	/// Internal HTML ID: `id_31`
123	drive_release_date: String,
124	/// The unique name of the cat-dev on the network.
125	///
126	/// Internal HTML ID: `id_2`
127	device_name: String,
128	/// The MAC Address of the machine.
129	///
130	/// Internal HTML ID: N/A
131	mac_address: MacAddress,
132}
133
134#[cfg_attr(docsrs, doc(cfg(feature = "clients")))]
135#[cfg(feature = "clients")]
136impl SetupParameters {
137	/// Create a new setup set of parameters.
138	#[allow(
139		// This is how many fields we have clippy.
140		clippy::too_many_arguments,
141		// This isn't a state machine, or anything.
142		clippy::fn_params_excessive_bools,
143	)]
144	#[must_use]
145	pub const fn new(
146		static_ip_address: Ipv4Addr,
147		subnet_mask: Ipv4Addr,
148		default_gateway: Ipv4Addr,
149		dhcp: bool,
150		dns: bool,
151		primary_dns_server: Ipv4Addr,
152		secondary_dns_server: Ipv4Addr,
153		jumbo_frame: bool,
154		host_pc_ip: Ipv4Addr,
155		bank_size: CatDevBankSize,
156		hdd_bank_no: u8,
157		atapi_emulator_port: u16,
158		sdio_printf_port: u16,
159		sdio_block_port: u16,
160		exi_port: u16,
161		parameter_space_port: u16,
162		drive_timing_emulation: bool,
163		operational_mode_is_reader: bool,
164		drive_product_revision: String,
165		drive_vendor_code: String,
166		drive_device_code: String,
167		drive_release_date: String,
168		device_name: String,
169		mac_address: MacAddress,
170	) -> Self {
171		Self {
172			static_ip_address,
173			subnet_mask,
174			default_gateway,
175			dhcp,
176			dns,
177			primary_dns_server,
178			secondary_dns_server,
179			jumbo_frame,
180			host_pc_ip,
181			bank_size,
182			hdd_bank_no,
183			atapi_emulator_port,
184			sdio_printf_port,
185			sdio_block_port,
186			exi_port,
187			parameter_space_port,
188			drive_timing_emulation,
189			operational_mode_is_reader,
190			drive_product_revision,
191			drive_vendor_code,
192			drive_device_code,
193			drive_release_date,
194			device_name,
195			mac_address,
196		}
197	}
198
199	/// Create a new set of default settings based on the device name, and mac.
200	#[must_use]
201	pub fn default_settings(device_name: String, mac_address: MacAddress) -> Self {
202		Self {
203			static_ip_address: Ipv4Addr::new(192, 168, 0, 1),
204			subnet_mask: Ipv4Addr::new(255, 255, 255, 0),
205			default_gateway: Ipv4Addr::UNSPECIFIED,
206			dhcp: true,
207			dns: false,
208			primary_dns_server: Ipv4Addr::UNSPECIFIED,
209			secondary_dns_server: Ipv4Addr::UNSPECIFIED,
210			jumbo_frame: true,
211			host_pc_ip: Ipv4Addr::UNSPECIFIED,
212			bank_size: CatDevBankSize::Blank,
213			hdd_bank_no: 0,
214			atapi_emulator_port: 7974,
215			sdio_printf_port: 7975,
216			sdio_block_port: 7976,
217			exi_port: 7977,
218			parameter_space_port: 7978,
219			drive_timing_emulation: false,
220			operational_mode_is_reader: false,
221			drive_product_revision: String::with_capacity(0),
222			drive_vendor_code: String::with_capacity(0),
223			drive_device_code: String::with_capacity(0),
224			drive_release_date: String::with_capacity(0),
225			device_name,
226			mac_address,
227		}
228	}
229
230	/// Get the static ip address being used.
231	///
232	/// This will return `None` if the console is using DHCP.
233	#[must_use]
234	pub const fn static_ip_address(&self) -> Option<Ipv4Addr> {
235		if self.dhcp {
236			None
237		} else {
238			Some(self.static_ip_address)
239		}
240	}
241	/// Get the value for the static ip address.
242	///
243	/// *note: may not be correct, and used if dhcp is on.*
244	#[must_use]
245	pub const fn raw_static_ip_address(&self) -> Ipv4Addr {
246		self.static_ip_address
247	}
248
249	/// Get the subnet mask for the CAT-DEV device.
250	#[must_use]
251	pub const fn subnet_mask(&self) -> Ipv4Addr {
252		self.subnet_mask
253	}
254
255	/// Get the gateway to use for the CAT-DEV.
256	#[must_use]
257	pub const fn default_gateway(&self) -> Ipv4Addr {
258		self.default_gateway
259	}
260
261	/// If we're using DHCP to get an IP Address.
262	#[must_use]
263	pub const fn using_dhcp(&self) -> bool {
264		self.dhcp
265	}
266
267	/// If we're using our own DNS ips set in our configuration.
268	#[must_use]
269	pub const fn using_self_managed_dns(&self) -> bool {
270		self.dns
271	}
272	/// Get the primary dns server the cat-dev will be using.
273	///
274	/// *note: this will return none if the cat-dev isn't using self managed
275	/// dns.*
276	#[must_use]
277	pub const fn primary_dns(&self) -> Option<Ipv4Addr> {
278		if self.dns {
279			Some(self.primary_dns_server)
280		} else {
281			None
282		}
283	}
284	/// Get the raw value of the Primary DNS Server for the cat-dev.
285	///
286	/// *note: this value will not be used if self managed dns is off.
287	#[must_use]
288	pub const fn raw_primary_dns(&self) -> Ipv4Addr {
289		self.primary_dns_server
290	}
291	/// Get the secondary dns server the cat-dev will be using.
292	///
293	/// *note: this will return none if the cat-dev isn't using self managed
294	/// dns.*
295	#[must_use]
296	pub const fn secondary_dns(&self) -> Option<Ipv4Addr> {
297		if self.dns {
298			Some(self.secondary_dns_server)
299		} else {
300			None
301		}
302	}
303	/// Get the raw value of the Secondary DNS Server for the cat-dev.
304	///
305	/// *note: this value will not be used if self managed dns is off.
306	#[must_use]
307	pub const fn raw_secondary_dns(&self) -> Ipv4Addr {
308		self.secondary_dns_server
309	}
310
311	/// If we're using jumbo frames.
312	#[must_use]
313	pub const fn jumbo_frame(&self) -> bool {
314		self.jumbo_frame
315	}
316
317	/// The potential IP Address of the host machine we're using.
318	#[must_use]
319	pub const fn host_pc_ip_address(&self) -> Ipv4Addr {
320		self.host_pc_ip
321	}
322
323	/// Get the hard disk size of the cat-dev.
324	#[must_use]
325	pub const fn hdd_bank_size(&self) -> CatDevBankSize {
326		self.bank_size
327	}
328
329	/// Get the bank number being used for the HDD.
330	#[must_use]
331	pub const fn hdd_bank_no(&self) -> u8 {
332		self.hdd_bank_no
333	}
334
335	/// The port for the ATAPI emulator.
336	#[must_use]
337	pub const fn atapi_emulator_port(&self) -> u16 {
338		self.atapi_emulator_port
339	}
340
341	/// The port for SDIO Printf/Control.
342	#[must_use]
343	pub const fn sdio_printf_port(&self) -> u16 {
344		self.sdio_printf_port
345	}
346
347	/// The port for SDIO Block Data.
348	#[must_use]
349	pub const fn sdio_block_port(&self) -> u16 {
350		self.sdio_block_port
351	}
352
353	/// Get the port for EXI traffic.
354	#[must_use]
355	pub const fn exi_port(&self) -> u16 {
356		self.exi_port
357	}
358
359	/// Get the port to use for parameter-space queries.
360	#[must_use]
361	pub const fn parameter_space_port(&self) -> u16 {
362		self.parameter_space_port
363	}
364
365	/// If we're using drive-timing emulation of a production console.
366	#[must_use]
367	pub const fn drive_timing_emulation_enabled(&self) -> bool {
368		self.drive_timing_emulation
369	}
370
371	/// Check if our operational mode is CAT-DEV mode.
372	#[must_use]
373	pub const fn is_cat_dev_mode(&self) -> bool {
374		!self.operational_mode_is_reader
375	}
376	/// Check if our operational mode is in H-Reader mode.
377	#[must_use]
378	pub const fn is_h_reader_mode(&self) -> bool {
379		self.operational_mode_is_reader
380	}
381
382	/// Get the hard-drive product revision.
383	#[must_use]
384	pub const fn drive_product_revision(&self) -> &String {
385		&self.drive_product_revision
386	}
387
388	/// Get the hard-drive vendor code.
389	#[must_use]
390	pub const fn drive_vendor_code(&self) -> &String {
391		&self.drive_vendor_code
392	}
393
394	/// Get the hard-drive device code.
395	#[must_use]
396	pub const fn drive_device_code(&self) -> &String {
397		&self.drive_device_code
398	}
399
400	/// Get the release date of the internal hard-drive.
401	#[must_use]
402	pub const fn drive_release_date(&self) -> &String {
403		&self.drive_release_date
404	}
405
406	/// Get the name of the cat-dev.
407	#[must_use]
408	pub const fn device_name(&self) -> &String {
409		&self.device_name
410	}
411
412	#[must_use]
413	pub const fn mac_address(&self) -> MacAddress {
414		self.mac_address
415	}
416}
417
418/// The bank sizes a cat-dev hard disk can have normally.
419///
420/// The blank value is a bit of a weird value, that i'm not sure why they
421/// ever had it, but it shows up in the `setup.cgi` page, so whatever they
422/// must have had a reason for it.
423#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
424pub enum CatDevBankSize {
425	/// Unknown choice present in `setup.cgi`
426	Blank,
427	/// The internal hard-disk has 25 Gigabytes of storage.
428	TwentyFiveGbs,
429	/// The internal hard-disk has 5 Gigabytes of storage.
430	FiveGbs,
431	/// The internal hard-disk has 9 Gigabytes of storage.
432	NineGbs,
433	/// The internal hard-disk has 12 Gigabytes of storage.
434	TwelveGbs,
435	/// The internal hard-disk has 14 Gigabytes of storage.
436	FourteenGbs,
437	/// The internal hard-disk has 16 Gigabytes of storage.
438	SixteenGbs,
439	/// The internal hard-disk has 18 Gigabytes of storage.
440	EighteenGbs,
441	/// The internal hard-disk has 21 Gigabytes of storage.
442	TwentyOneGbs,
443}
444impl From<&CatDevBankSize> for u32 {
445	fn from(size: &CatDevBankSize) -> u32 {
446		match *size {
447			CatDevBankSize::Blank => 4_294_967_295,
448			CatDevBankSize::TwentyFiveGbs => 0,
449			CatDevBankSize::FiveGbs => 1,
450			CatDevBankSize::NineGbs => 2,
451			CatDevBankSize::TwelveGbs => 3,
452			CatDevBankSize::FourteenGbs => 4,
453			CatDevBankSize::SixteenGbs => 5,
454			CatDevBankSize::EighteenGbs => 6,
455			CatDevBankSize::TwentyOneGbs => 7,
456		}
457	}
458}
459impl From<CatDevBankSize> for u32 {
460	fn from(value: CatDevBankSize) -> Self {
461		Self::from(&value)
462	}
463}
464impl TryFrom<u32> for CatDevBankSize {
465	type Error = MionCGIErrors;
466
467	fn try_from(value: u32) -> Result<Self, Self::Error> {
468		match value {
469			u32::MAX => Ok(Self::Blank),
470			0 => Ok(Self::TwentyFiveGbs),
471			1 => Ok(Self::FiveGbs),
472			2 => Ok(Self::NineGbs),
473			3 => Ok(Self::TwelveGbs),
474			4 => Ok(Self::FourteenGbs),
475			5 => Ok(Self::SixteenGbs),
476			6 => Ok(Self::EighteenGbs),
477			7 => Ok(Self::TwentyOneGbs),
478			_ => Err(MionCGIErrors::UnknownCatDevBankSizeId(value)),
479		}
480	}
481}
482impl Display for CatDevBankSize {
483	fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
484		write!(
485			fmt,
486			"{}",
487			match *self {
488				Self::Blank => " ",
489				Self::TwentyFiveGbs => "25GB",
490				Self::FiveGbs => "5GB",
491				Self::NineGbs => "9GB",
492				Self::TwelveGbs => "12GB",
493				Self::FourteenGbs => "14GB",
494				Self::SixteenGbs => "16GB",
495				Self::EighteenGbs => "18GB",
496				Self::TwentyOneGbs => "21GB",
497			}
498		)
499	}
500}
501
502#[cfg(test)]
503mod unit_tests {
504	use super::*;
505
506	#[test]
507	pub fn convert_bank_size() {
508		for bank_size in vec![
509			CatDevBankSize::Blank,
510			CatDevBankSize::TwentyFiveGbs,
511			CatDevBankSize::FiveGbs,
512			CatDevBankSize::NineGbs,
513			CatDevBankSize::TwelveGbs,
514			CatDevBankSize::FourteenGbs,
515			CatDevBankSize::SixteenGbs,
516			CatDevBankSize::EighteenGbs,
517			CatDevBankSize::TwentyOneGbs,
518		] {
519			assert_eq!(
520				Ok(bank_size),
521				CatDevBankSize::try_from(u32::from(bank_size)),
522				"bank size: {} was not the same after converting it back & forth",
523				bank_size,
524			);
525		}
526	}
527}