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