1use std::{fmt, str::FromStr};
10
11use crate::{D3d9, DEFAULT_VALUE, Dxgi, FieldError, OpenGl, Wined3d};
12#[cfg(doc)]
13use crate::{Exe, Rule, Setting, Table, metal_hud_env};
14
15macro_rules! summary {
18 (Exe) => {
19 "The image basename a rule matches, in any case, * for every process, or steam:440 for \
20 that Steam app."
21 };
22 (Company) => {
23 "A text the company name of the image's version resource must contain."
24 };
25 (Product) => {
26 "A text the product name of the image's version resource must contain."
27 };
28 (OriginalFilename) => {
29 "A text the original file name of the image's version resource must contain."
30 };
31 (Dxgi) => {
32 "The implementation of D3D10 to D3D12 the process loads."
33 };
34 (D3d9) => {
35 "The D3D9 implementation the process loads."
36 };
37 (Wined3d) => {
38 "The renderer wined3d draws with in the process."
39 };
40 (OpenGl) => {
41 "Which OpenGL the Mac driver gives the process."
42 };
43 (DpiAware) => {
44 "Whether the process is DPI-aware."
45 };
46 (LargeAddressAware) => {
47 "Whether a 32-bit process gets the 4 GB address space whatever its executable's flag \
48 says."
49 };
50 (Gcmouse) => {
51 "Whether raw mouse movement comes from GameController instead of the cursor."
52 };
53 (ForceNx) => {
54 "Whether no-execute stays on in a 32-bit process."
55 };
56 (MetalHud) => {
57 "Whether the Metal performance HUD shows."
58 };
59 (DllOverrides) => {
60 "DLL load-order overrides, one WINEDLLOVERRIDES element each."
61 };
62 (ArgumentsOverride) => {
63 "Arguments that replace those the rules folded before give the command line."
64 };
65 (Arguments) => {
66 "Text appended to the command line, one argument string each."
67 };
68 (DebugChannels) => {
69 "WINEDEBUG channels for the process, one entry each."
70 };
71 (Env) => {
72 "Environment variables the process gets; an empty value removes the variable."
73 };
74}
75
76#[non_exhaustive]
83#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
84pub enum Key {
85 #[doc = summary!(Exe)]
86 Exe,
89 #[doc = summary!(Company)]
90 Company,
95 #[doc = summary!(Product)]
96 Product,
101 #[doc = summary!(OriginalFilename)]
102 OriginalFilename,
108 #[doc = summary!(Dxgi)]
109 Dxgi,
113 #[doc = summary!(D3d9)]
114 D3d9,
118 #[doc = summary!(Wined3d)]
119 Wined3d,
124 #[doc = summary!(OpenGl)]
125 OpenGl,
129 #[doc = summary!(DpiAware)]
130 DpiAware,
134 #[doc = summary!(LargeAddressAware)]
135 LargeAddressAware,
139 #[doc = summary!(Gcmouse)]
140 Gcmouse,
144 #[doc = summary!(ForceNx)]
145 ForceNx,
149 #[doc = summary!(MetalHud)]
150 MetalHud,
155 #[doc = summary!(DllOverrides)]
156 DllOverrides,
160 #[doc = summary!(ArgumentsOverride)]
161 ArgumentsOverride,
165 #[doc = summary!(Arguments)]
166 Arguments,
170 #[doc = summary!(DebugChannels)]
171 DebugChannels,
178 #[doc = summary!(Env)]
179 Env,
183}
184
185#[non_exhaustive]
187#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
188pub enum KeyKind {
189 Matcher,
191 Scalar(ValueKind),
193 List,
195 Env,
197}
198
199#[non_exhaustive]
201#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
202pub enum ValueKind {
203 Word,
205 Bool,
207}
208
209impl Key {
210 pub const ALL: &'static [Self] = &[
212 Self::Exe,
213 Self::Company,
214 Self::Product,
215 Self::OriginalFilename,
216 Self::Dxgi,
217 Self::D3d9,
218 Self::Wined3d,
219 Self::OpenGl,
220 Self::DpiAware,
221 Self::LargeAddressAware,
222 Self::Gcmouse,
223 Self::ForceNx,
224 Self::MetalHud,
225 Self::DllOverrides,
226 Self::ArgumentsOverride,
227 Self::Arguments,
228 Self::DebugChannels,
229 Self::Env,
230 ];
231
232 #[must_use]
234 pub const fn as_str(self) -> &'static str {
235 match self {
236 Self::Exe => "exe",
237 Self::Company => "company",
238 Self::Product => "product",
239 Self::OriginalFilename => "original_filename",
240 Self::Dxgi => "dxgi",
241 Self::D3d9 => "d3d9",
242 Self::Wined3d => "wined3d",
243 Self::OpenGl => "opengl",
244 Self::DpiAware => "dpi_aware",
245 Self::LargeAddressAware => "large_address_aware",
246 Self::Gcmouse => "gcmouse",
247 Self::ForceNx => "force_nx",
248 Self::MetalHud => "metal_hud",
249 Self::DllOverrides => "dll_overrides",
250 Self::ArgumentsOverride => "arguments_override",
251 Self::Arguments => "arguments",
252 Self::DebugChannels => "debug_channels",
253 Self::Env => "env",
254 }
255 }
256
257 #[must_use]
260 pub const fn scalar(self) -> Option<Scalar> {
261 match self {
262 Self::Dxgi => Some(Scalar::Dxgi),
263 Self::D3d9 => Some(Scalar::D3d9),
264 Self::Wined3d => Some(Scalar::Wined3d),
265 Self::OpenGl => Some(Scalar::OpenGl),
266 Self::DpiAware => Some(Scalar::DpiAware),
267 Self::LargeAddressAware => Some(Scalar::LargeAddressAware),
268 Self::Gcmouse => Some(Scalar::Gcmouse),
269 Self::ForceNx => Some(Scalar::ForceNx),
270 Self::MetalHud => Some(Scalar::MetalHud),
271 Self::Exe
272 | Self::Company
273 | Self::Product
274 | Self::OriginalFilename
275 | Self::DllOverrides
276 | Self::ArgumentsOverride
277 | Self::Arguments
278 | Self::DebugChannels
279 | Self::Env => None,
280 }
281 }
282
283 #[must_use]
285 pub const fn kind(self) -> KeyKind {
286 if let Some(scalar) = self.scalar() {
287 return KeyKind::Scalar(scalar.value_kind());
288 }
289 match self {
290 Self::DllOverrides
291 | Self::ArgumentsOverride
292 | Self::Arguments
293 | Self::DebugChannels => KeyKind::List,
294 Self::Env => KeyKind::Env,
295 _ => KeyKind::Matcher,
296 }
297 }
298
299 #[must_use]
302 pub const fn label(self) -> &'static str {
303 match self {
304 Self::Exe => "Executable",
305 Self::Company => "Company",
306 Self::Product => "Product",
307 Self::OriginalFilename => "Original filename",
308 Self::Dxgi => "DXGI",
309 Self::D3d9 => "D3D9",
310 Self::Wined3d => "wined3d renderer",
311 Self::OpenGl => "OpenGL",
312 Self::DpiAware => "DPI-aware",
313 Self::LargeAddressAware => "Large-address-aware",
314 Self::Gcmouse => "GameController mouse",
315 Self::ForceNx => "Force no-execute",
316 Self::MetalHud => "Metal HUD",
317 Self::DllOverrides => "DLL overrides",
318 Self::ArgumentsOverride => "Arguments override",
319 Self::Arguments => "Arguments",
320 Self::DebugChannels => "Debug channels",
321 Self::Env => "Environment",
322 }
323 }
324
325 #[must_use]
330 pub const fn upstream(self) -> Option<&'static str> {
331 if let Some(scalar) = self.scalar() {
332 return Some(scalar.upstream());
333 }
334 match self {
335 Self::DllOverrides => Some("WINEDLLOVERRIDES and the registry's DllOverrides decide"),
336 Self::ArgumentsOverride | Self::Arguments => {
337 Some("the command line the process was started with")
338 }
339 Self::DebugChannels => Some("the WINEDEBUG the process was started with"),
340 Self::Env => Some("the environment the process was started with"),
341 _ => None,
342 }
343 }
344
345 #[must_use]
349 pub const fn values(self) -> &'static [&'static str] {
350 match self {
351 Self::Dxgi => &["dxmt", "wined3d", DEFAULT_VALUE],
352 Self::D3d9 => &["mtld3d", "wined3d", DEFAULT_VALUE],
353 Self::Wined3d => &["gl", "vulkan", DEFAULT_VALUE],
354 Self::OpenGl => &["egl", "cgl", DEFAULT_VALUE],
355 Self::DpiAware
356 | Self::LargeAddressAware
357 | Self::Gcmouse
358 | Self::ForceNx
359 | Self::MetalHud => &["true", "false", DEFAULT_VALUE],
360 Self::Exe
361 | Self::Company
362 | Self::Product
363 | Self::OriginalFilename
364 | Self::DllOverrides
365 | Self::ArgumentsOverride
366 | Self::Arguments
367 | Self::DebugChannels
368 | Self::Env => &[],
369 }
370 }
371
372 #[must_use]
376 pub const fn summary(self) -> &'static str {
377 match self {
378 Self::Exe => summary!(Exe),
379 Self::Company => summary!(Company),
380 Self::Product => summary!(Product),
381 Self::OriginalFilename => summary!(OriginalFilename),
382 Self::Dxgi => summary!(Dxgi),
383 Self::D3d9 => summary!(D3d9),
384 Self::Wined3d => summary!(Wined3d),
385 Self::OpenGl => summary!(OpenGl),
386 Self::DpiAware => summary!(DpiAware),
387 Self::LargeAddressAware => summary!(LargeAddressAware),
388 Self::Gcmouse => summary!(Gcmouse),
389 Self::ForceNx => summary!(ForceNx),
390 Self::MetalHud => summary!(MetalHud),
391 Self::DllOverrides => summary!(DllOverrides),
392 Self::ArgumentsOverride => summary!(ArgumentsOverride),
393 Self::Arguments => summary!(Arguments),
394 Self::DebugChannels => summary!(DebugChannels),
395 Self::Env => summary!(Env),
396 }
397 }
398
399 #[must_use]
408 pub const fn example(self) -> &'static str {
409 match self {
410 Self::Exe | Self::OriginalFilename => "Game.exe",
411 Self::Company => "Some Vendor",
412 Self::Product => "Some Game",
413 Self::Dxgi | Self::D3d9 => "wined3d",
414 Self::Wined3d => "vulkan",
415 Self::OpenGl => "cgl",
416 Self::DpiAware | Self::Gcmouse | Self::ForceNx => "false",
417 Self::LargeAddressAware | Self::MetalHud => "true",
418 Self::DllOverrides => "d3dcompiler_47=n",
419 Self::ArgumentsOverride | Self::Arguments => "-windowed",
420 Self::DebugChannels => "+relay",
421 Self::Env => "DXMT_CONFIG=d3d11.preferredMaxFrameRate=60",
422 }
423 }
424}
425
426impl FromStr for Key {
427 type Err = FieldError;
428
429 fn from_str(s: &str) -> Result<Self, Self::Err> {
431 Self::ALL
432 .iter()
433 .copied()
434 .find(|key| key.as_str().eq_ignore_ascii_case(s))
435 .ok_or_else(|| FieldError::UnknownKey(s.to_owned()))
436 }
437}
438
439impl fmt::Display for Key {
440 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
441 f.write_str(self.as_str())
442 }
443}
444
445#[non_exhaustive]
451#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
452pub enum Scalar {
453 Dxgi,
456 D3d9,
458 Wined3d,
461 OpenGl,
463 DpiAware,
466 LargeAddressAware,
469 Gcmouse,
472 ForceNx,
475 MetalHud,
478}
479
480impl Scalar {
481 pub const ALL: &'static [Self] = &[
483 Self::Dxgi,
484 Self::D3d9,
485 Self::Wined3d,
486 Self::OpenGl,
487 Self::DpiAware,
488 Self::LargeAddressAware,
489 Self::Gcmouse,
490 Self::ForceNx,
491 Self::MetalHud,
492 ];
493
494 #[must_use]
496 pub const fn key(self) -> Key {
497 match self {
498 Self::Dxgi => Key::Dxgi,
499 Self::D3d9 => Key::D3d9,
500 Self::Wined3d => Key::Wined3d,
501 Self::OpenGl => Key::OpenGl,
502 Self::DpiAware => Key::DpiAware,
503 Self::LargeAddressAware => Key::LargeAddressAware,
504 Self::Gcmouse => Key::Gcmouse,
505 Self::ForceNx => Key::ForceNx,
506 Self::MetalHud => Key::MetalHud,
507 }
508 }
509
510 #[must_use]
512 pub const fn as_str(self) -> &'static str {
513 self.key().as_str()
514 }
515
516 #[must_use]
518 pub const fn value_kind(self) -> ValueKind {
519 match self {
520 Self::Dxgi | Self::D3d9 | Self::Wined3d | Self::OpenGl => ValueKind::Word,
521 Self::DpiAware
522 | Self::LargeAddressAware
523 | Self::Gcmouse
524 | Self::ForceNx
525 | Self::MetalHud => ValueKind::Bool,
526 }
527 }
528
529 #[must_use]
532 pub const fn upstream(self) -> &'static str {
533 match self {
534 Self::Dxgi | Self::D3d9 => "wined3d",
535 Self::Wined3d => {
536 "the OpenGL renderer, unless WINE_D3D_CONFIG or wined3d's registry settings name \
537 another"
538 }
539 Self::OpenGl => "Apple's OpenGL",
540 Self::DpiAware => "the AppCompatFlags layer, then the manifest, otherwise unaware",
541 Self::LargeAddressAware => "the executable's large-address-aware flag",
542 Self::Gcmouse => "off, the cursor's movement",
543 Self::ForceNx => "no-execute off when the executable or a DLL lacks the NX_COMPAT flag",
544 Self::MetalHud => {
545 "the MTL_HUD_ variables of the environment the process was started with"
546 }
547 }
548 }
549
550 pub fn parse_value(self, text: &str) -> Result<ScalarValue, FieldError> {
560 let value = match self {
561 Self::Dxgi => text.parse().ok().map(ScalarValue::Dxgi),
562 Self::D3d9 => text.parse().ok().map(ScalarValue::D3d9),
563 Self::Wined3d => text.parse().ok().map(ScalarValue::Wined3d),
564 Self::OpenGl => text.parse().ok().map(ScalarValue::OpenGl),
565 Self::DpiAware
566 | Self::LargeAddressAware
567 | Self::Gcmouse
568 | Self::ForceNx
569 | Self::MetalHud => parse_bool(text).map(ScalarValue::Bool),
570 };
571 value.ok_or_else(|| FieldError::BadValue {
572 key: self.key(),
573 value: text.to_owned(),
574 expected: self.key().values(),
575 })
576 }
577}
578
579impl fmt::Display for Scalar {
580 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
581 f.write_str(self.as_str())
582 }
583}
584
585#[non_exhaustive]
587#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
588pub enum ScalarValue {
589 Dxgi(Dxgi),
591 D3d9(D3d9),
593 Wined3d(Wined3d),
595 OpenGl(OpenGl),
597 Bool(bool),
599}
600
601impl ScalarValue {
602 #[must_use]
604 pub const fn as_str(self) -> &'static str {
605 match self {
606 Self::Dxgi(value) => value.as_str(),
607 Self::D3d9(value) => value.as_str(),
608 Self::Wined3d(value) => value.as_str(),
609 Self::OpenGl(value) => value.as_str(),
610 Self::Bool(true) => "true",
611 Self::Bool(false) => "false",
612 }
613 }
614}
615
616impl fmt::Display for ScalarValue {
617 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
618 f.write_str(self.as_str())
619 }
620}
621
622const fn parse_bool(text: &str) -> Option<bool> {
624 if text.eq_ignore_ascii_case("true") {
625 Some(true)
626 } else if text.eq_ignore_ascii_case("false") {
627 Some(false)
628 } else {
629 None
630 }
631}
632
633#[cfg(test)]
634mod tests;