cat_dev/mion/proto/cgis/
setup.rs

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