pn_editor_core/editor/input/files/
load.rs1use super::Editor;
2
3use crate::{net::Net, text_content::TextContent, vector::Vector};
4
5use header_parsing::parse_header;
6use text_editing::TextLine;
7
8use std::{
9 collections::HashMap,
10 fs::File,
11 io::{BufRead, BufReader, Error as IOError},
12 path::{Path, PathBuf},
13};
14
15pub enum Error {
16 IO(IOError),
17 NoDirectorySpecified,
18 SubheaderWithoutHeader(usize),
19}
20
21impl From<IOError> for Error {
22 fn from(err: IOError) -> Self {
23 Self::IO(err)
24 }
25}
26
27fn load_keys(path: &Path) -> Result<Vec<String>, IOError> {
28 let Ok(file) = File::open(path) else {
29 return Ok(Vec::new());
30 };
31
32 let mut result = Vec::new();
33 for line in BufReader::new(file).lines() {
34 result.push(line?);
35 }
36
37 Ok(result)
38}
39
40struct SortedTextMap {
41 names: Vec<Vec<Box<str>>>,
42 contents: HashMap<Vec<Box<str>>, Vec<TextLine>>,
43}
44
45impl SortedTextMap {
46 fn new(names: Vec<Vec<Box<str>>>) -> Self {
47 Self {
48 names,
49 contents: HashMap::new(),
50 }
51 }
52
53 fn add_content(&mut self, key: &Vec<Box<str>>, content: Vec<TextLine>) {
54 if self.names.contains(key) {
55 self.contents.insert(key.clone(), content);
56 }
57 }
58}
59
60impl Editor {
61 fn load_layout(&mut self, file: &mut File) -> Result<(), Error> {
62 let mut removed_tids = Vec::new();
63 for (&tid, node) in &mut self.nodes.transitions.nodes {
64 use std::io::Read;
65 let mut buffer: [u8; 8] = unsafe { std::mem::zeroed() };
66 file.read_exact(&mut buffer)?;
67 if 0x7FFFFFFF == u64::from_le_bytes(buffer) {
68 removed_tids.push(tid);
69 } else {
70 node.position = Vector::new([
71 f32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]),
72 f32::from_le_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]),
73 ]);
74 }
75 }
76
77 let mut removed_pids = Vec::new();
78 for (&pid, node) in &mut self.nodes.places.nodes {
79 use std::io::Read;
80 node.positions.clear();
81 let mut buffer: [u8; 8] = unsafe { std::mem::zeroed() };
82 file.read_exact(&mut buffer)?;
83 if 0x7FFFFFFF != u64::from_le_bytes(buffer) {
84 let (first, count) = (
85 f32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]),
86 u32::from_le_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]),
87 );
88
89 if first.is_nan() {
90 for _i in 0..count {
91 file.read_exact(&mut buffer)?;
92 node.positions.push(Vector::new([
93 f32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]),
94 f32::from_le_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]),
95 ]));
96 }
97 } else {
98 node.positions.push(Vector::new([
99 f32::from_le_bytes([buffer[0], buffer[1], buffer[2], buffer[3]]),
100 f32::from_le_bytes([buffer[4], buffer[5], buffer[6], buffer[7]]),
101 ]));
102 }
103 }
104 if node.positions.is_empty() {
105 removed_pids.push(pid);
106 }
107 }
108
109 for tid in removed_tids {
110 self.nodes.transitions.remove(tid);
111 }
112 for pid in removed_pids {
113 self.nodes.places.remove(pid, self.graph.net());
114 }
115
116 Ok(())
117 }
118
119 fn load_story(file: &File, text_map: &mut SortedTextMap) -> Result<(), Error> {
120 let mut first = false;
121 let mut empty_count = 0;
122 let mut current_key = Vec::new();
123 let mut current_content = Vec::new();
124
125 for (line_number, line) in BufReader::new(file).lines().enumerate() {
126 let line = line?;
127
128 if let Some(success) = parse_header(&mut current_key, &line) {
129 let Ok(changes) = success else {
130 return Err(Error::SubheaderWithoutHeader(line_number));
131 };
132
133 first = true;
134 if !changes.path.is_empty() {
135 text_map.add_content(changes.path, current_content);
136 current_content = Vec::new();
137 empty_count = 0;
138 }
139
140 changes.apply();
141
142 continue;
143 }
144
145 if line.is_empty() {
146 if first {
147 first = false;
148 } else {
149 empty_count += 1;
150 }
151
152 continue;
153 }
154
155 if !current_key.is_empty() {
156 current_content.extend((0..empty_count).map(|_| TextLine::new()));
157 empty_count = 0;
158 current_content.push(TextLine::from_string(line));
159 }
160 }
161
162 if !current_key.is_empty() {
163 text_map.add_content(¤t_key, current_content);
164 }
165
166 Ok(())
167 }
168
169 pub fn load_net(&mut self, folder_path: PathBuf) -> Result<(), Error> {
171 let Some((pns, pnl, pnk, pnkp, story)) = Self::project_paths(&folder_path) else {
172 return Err(Error::NoDirectorySpecified);
173 };
174
175 let net = Net::load(pns.as_ref()).unwrap_or_default();
176
177 let names: Vec<Vec<Box<str>>> = load_keys(&pnk)?
178 .into_iter()
179 .map(|name| name.split('#').map(Into::into).collect())
180 .collect();
181 let place_names: Vec<(Option<Box<str>>, Box<str>)> = load_keys(&pnkp)?
182 .into_iter()
183 .map(|name| {
184 if let Some((aspect, name)) = name.split_once('#') {
185 (Some(aspect.into()), name.into())
186 } else {
187 (None, name.into())
188 }
189 })
190 .collect();
191
192 let mut text_map = SortedTextMap::new(names);
193 if let Ok(file) = File::open(story) {
194 Self::load_story(&file, &mut text_map)?;
195 }
196 let SortedTextMap {
197 names,
198 mut contents,
199 } = text_map;
200
201 self.project_path = Some(folder_path);
202 self.reset(net, names, place_names);
203
204 let mut added_contents = HashMap::new();
205 for (&tid, node) in &self.nodes.transitions.nodes {
206 let mut path = node.path.clone();
207 path.push(node.name.as_str().into());
208 let Some(content) = contents.remove(&path) else {
209 continue;
210 };
211
212 if !content.is_empty() {
213 added_contents.insert(tid, content);
214 }
215 }
216 for (tid, content) in added_contents {
217 unsafe {
218 self.nodes
219 .transitions
220 .nodes
221 .get_mut(&tid)
222 .unwrap_unchecked()
223 }
224 .content = TextContent::new(content);
225 }
226
227 if let Ok(mut file) = File::open(pnl) {
228 self.load_layout(&mut file)?;
229 } else {
230 self.auto_layout();
231 }
232
233 Ok(())
234 }
235
236 pub fn load(&mut self, force_path: bool, ignore_save_warning: bool) {
238 if !ignore_save_warning && !self.ensure_saved(false) {
239 return;
240 }
241 let path = if !force_path && let Some(path) = &self.project_path {
242 path.clone()
243 } else if let Some(path) = self.file_dialog().pick_folder() {
244 path
245 } else {
246 return;
247 };
248
249 let _ = self.load_net(path);
250 }
251}