-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
layout.rs
215 lines (187 loc) · 5.32 KB
/
layout.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
use std::{collections::HashMap, fs, path::Path};
use serde::Deserialize;
use serde_xml_rs;
#[derive(Debug, Deserialize)]
pub struct MameLayout {
pub element: Vec<NameElement>,
pub view: Vec<View>,
}
#[derive(Debug, Deserialize)]
pub struct NameElement {
pub name: String,
#[serde(rename = "$value")]
pub items: Vec<NameElementChildren>,
}
#[derive(PartialEq, Debug, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum NameElementChildren {
Image(Image),
Rect(Rect),
}
#[derive(PartialEq, Debug, Deserialize)]
pub struct Image {}
#[derive(PartialEq, Debug, Deserialize)]
pub struct Rect {}
#[derive(Clone, Debug, Deserialize)]
pub struct View {
pub name: String,
// element: Vec<RefElement>,
// pub screen: Vec<Screen>,
#[serde(rename = "$value")]
pub items: Vec<ViewElement>,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ViewElement {
Bounds(CompleteBounds),
#[serde(alias = "bezel")]
Element(Element),
Overlay(Element),
Screen(Screen),
}
#[derive(Clone, Debug, Deserialize)]
pub struct XYBounds {
pub x: i32,
pub y: i32,
pub width: i32,
pub height: i32,
}
#[derive(Clone, Debug, Deserialize)]
pub struct CompleteBounds {
// Standard XY
pub x: Option<i32>,
pub y: Option<i32>,
pub width: Option<i32>,
pub height: Option<i32>,
// Center
pub xc: Option<i32>,
pub yc: Option<i32>,
// LeftRight
pub left: Option<i32>,
pub right: Option<i32>,
pub top: Option<i32>,
pub bottom: Option<i32>,
}
#[derive(Clone, Debug)]
pub struct Bounds {
pub x: i32,
pub y: i32,
pub width: i32,
pub height: i32,
}
// This is written in such garbage form because serde_xml_rs doesn't support untagged enums, so I can't get it to
// properly build enums with the different Bounds variants
impl CompleteBounds {
pub fn to_xy(&self) -> Bounds {
if let (Some(x), Some(y), Some(width), Some(height)) =
(self.x, self.y, self.width, self.height)
{
// XY
Bounds {
x,
y,
width,
height,
}
} else if let (Some(xc), Some(yc), Some(width), Some(height)) =
(self.xc, self.yc, self.width, self.height)
{
// Center
Bounds {
x: xc - width,
y: yc - height,
width: width,
height: height,
}
} else if let (Some(left), Some(right), Some(top), Some(bottom)) =
(self.left, self.right, self.top, self.bottom)
{
Bounds {
x: left,
y: top,
width: right - left,
height: bottom - top,
}
} else {
panic!("Bounds appear to have an invalid format {self:?}");
}
}
}
#[derive(Clone, Debug, Deserialize)]
pub struct Element {
#[serde(rename = "ref")]
#[serde(alias = "element")]
pub ref_name: String,
pub bounds: CompleteBounds,
pub blend: Option<BlendType>,
}
#[derive(Clone, PartialEq, Debug, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum BlendType {
Add,
Alpha,
Multiply,
}
#[derive(Clone, Debug, Deserialize)]
pub struct Screen {
pub index: i32,
pub bounds: CompleteBounds,
pub blend: Option<BlendType>,
}
pub fn parse_layout(
temp_dir: &Path,
specified_layout: Option<&String>,
) -> Result<(MameLayout, View), String> {
let layout_path = temp_dir.join("default.lay");
let layout_file = match fs::read(&layout_path) {
Ok(layout_file) => layout_file,
Err(_) => {
return Err(format!(
"Could not find default.lay file at path {layout_path:?}"
))
}
};
// let output: MameLayout = match serde_xml_rs::from_reader(layout_file.as_slice()) {
// Ok(output) => output,
// Err(err) => {
// return Err(format!("Could not parse layout: \"{err}\""))},
// };
let output: MameLayout = serde_xml_rs::from_reader(layout_file.as_slice()).unwrap();
let mut map = HashMap::<String, View>::new();
for view in output.view.iter() {
map.insert(view.name.to_lowercase(), view.clone());
}
if let Some(specified_layout) = specified_layout {
if let Some(view) = map.remove(&specified_layout.trim().to_lowercase()) {
return Ok((output, view));
} else {
return Err(format!("Could not find view named \"{specified_layout}\""));
}
}
guard!(let Some(view) = select_view(&mut map) else {
return Err("Could not find suitable view".to_string());
});
Ok((output, view))
}
fn select_view<'a>(views: &mut HashMap<String, View>) -> Option<View> {
// Constructed this way to give ordered priority to each view name we want
let desired_names = vec![
"backgrounds only (no frame)",
"background only (no frame)",
"backgrounds only (no shadow)",
"background only (no shadow)",
"backgrounds only",
"background only",
"background",
"handheld layout",
"external layout",
"screen focus",
"unit only",
];
for name in desired_names {
if let Some(view) = views.remove(name) {
return Some(view);
}
}
None
}