1use crate::id::*;
2use crate::result::*;
3use crate::utils::*;
4use crate::version::*;
5use std::path::Path;
6use std::{
7 fmt,
8 path::PathBuf,
9 sync::{Arc, Mutex},
10};
11use windows::{
12 core::PCSTR,
13 Win32::Foundation::{BOOL, HANDLE, HINSTANCE},
14 Win32::System::LibraryLoader::*,
15};
16
17pub mod resource_type {
18 use super::Id;
23 pub const UNKNOWN: Id = Id::Integer(0);
24 pub const ACCELERATOR: Id = Id::Integer(9);
25 pub const ANICURSOR: Id = Id::Integer(21);
26 pub const ANIICON: Id = Id::Integer(22);
27 pub const BITMAP: Id = Id::Integer(2);
28 pub const CURSOR: Id = Id::Integer(1);
29 pub const DIALOG: Id = Id::Integer(5);
30 pub const DLGINCLUDE: Id = Id::Integer(17);
31 pub const FONT: Id = Id::Integer(8);
32 pub const FONTDIR: Id = Id::Integer(7);
33 pub const HTML: Id = Id::Integer(23);
34 pub const ICON: Id = Id::Integer(3);
35 pub const MANIFEST: Id = Id::Integer(24);
36 pub const MENU: Id = Id::Integer(4);
37 pub const MESSAGETABLE: Id = Id::Integer(11);
38 pub const PLUGPLAY: Id = Id::Integer(19);
39 pub const VERSION: Id = Id::Integer(16);
40 pub const VXD: Id = Id::Integer(20);
41}
42
43#[derive(Debug, Clone)]
45pub struct ResourceDataInner {
46 }
48
49#[derive(Debug, Clone)]
51pub enum ResourceData {
52 Accelerator(ResourceDataInner),
53 AniCursor(ResourceDataInner),
54 AniIcon(ResourceDataInner),
55 Bitmap(ResourceDataInner),
56 Cursor(ResourceDataInner),
57 Dialog(ResourceDataInner),
58 DialogInclude(ResourceDataInner),
59 Font(ResourceDataInner),
60 FontDirectory(ResourceDataInner),
61 Html(ResourceDataInner),
62 Icon(ResourceDataInner),
63 Manifest(ResourceDataInner),
64 Menu(ResourceDataInner),
65 MessageTable(ResourceDataInner),
66 PlugPlay(ResourceDataInner),
67 Version(VersionInfo),
68 VxD(ResourceDataInner),
69 Unknown(ResourceDataInner),
70}
71
72#[derive(Clone)]
74pub struct Resource {
75 pub kind: Id,
77 pub name: Id,
79 pub lang: u16,
81 pub encoded: Arc<Mutex<Vec<u8>>>,
83 pub decoded: Arc<Mutex<Option<ResourceData>>>,
85 module_handle: Arc<Mutex<Option<HANDLE>>>,
87}
88
89impl std::fmt::Debug for Resource {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 f.debug_struct("")
92 .field("kind", &self.kind)
93 .field("name", &self.name)
94 .field("lang", &self.lang)
95 .field("data len", &self.encoded.lock().unwrap().len())
96 .finish()
99 }
100}
101
102impl Resource {
103 pub fn new(
105 resources: &Resources,
106 rtype: PCSTR,
107 rname: PCSTR,
108 rlang: u16,
109 data: &[u8],
110 ) -> Resource {
111 let typeid: Id = rtype.into();
112 Resource {
113 kind: typeid,
114 name: rname.into(),
115 lang: rlang,
116 encoded: Arc::new(Mutex::new(data.to_vec())),
117 decoded: Arc::new(Mutex::new(None)),
118 module_handle: resources.module_handle(),
119 }
120 }
121
122 pub fn remove(&self) -> Result<&Self> {
124 if let Some(handle) = self.module_handle.lock().unwrap().as_ref() {
125 let success = unsafe {
126 UpdateResourceA(
127 *handle,
128 self.kind.clone(),
129 self.name.clone(),
130 self.lang,
131 None,
132 0,
133 )
134 .as_bool()
135 };
136
137 if !success {
138 return Err(format!(
139 "Resources::load(): Error removing resources: {:?}",
140 get_last_error()
141 )
142 .into());
143 }
144 } else {
145 return Err("Resource::replace(): resource file is not open".into());
146 };
147
148 Ok(self)
149 }
150
151 pub fn replace(&self, data: &[u8]) -> Result<&Self> {
155 *self.encoded.lock().unwrap() = data.to_vec();
156 Ok(self)
157 }
158
159 pub fn update(&self) -> Result<&Self> {
161 if let Some(handle) = self.module_handle.lock().unwrap().as_ref() {
162 let encoded = self.encoded.lock().unwrap();
163 let success = unsafe {
164 UpdateResourceA(
165 *handle,
166 self.kind.clone(),
167 self.name.clone(),
168 self.lang,
169 Some(std::mem::transmute(encoded.as_ptr())),
170 encoded.len() as u32,
171 )
172 .as_bool()
173 };
174
175 if !success {
176 return Err(format!(
177 "Resources::load(): Error removing resources: {:?}",
178 get_last_error()
179 )
180 .into());
181 }
182 } else {
183 return Err("Resource::replace(): resource file is not open".into());
184 };
185
186 Ok(self)
187 }
188}
189
190#[derive(Debug)]
194pub struct Resources {
195 file: PathBuf,
196 module_handle: Arc<Mutex<Option<HANDLE>>>,
197 pub list: Arc<Mutex<Vec<Arc<Resource>>>>,
199}
200
201impl Resources {
202 pub fn new(file: &Path) -> Resources {
205 Resources {
206 file: file.to_path_buf(),
207 module_handle: Arc::new(Mutex::new(None)),
208 list: Arc::new(Mutex::new(Vec::new())),
209 }
210 }
211
212 pub fn load(&self) -> Result<()> {
216 unsafe {
217 let handle = LoadLibraryExA(
218 pcstr!(self.file.to_str().unwrap()),
219 None,
220 DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE,
222 )?;
223
224 let ptr: *const Resources = std::mem::transmute(&*self);
225 let success =
226 EnumResourceTypesA(handle, Some(enum_types), std::mem::transmute(ptr)).as_bool();
227
228 FreeLibrary(handle);
229
230 if !success {
231 return Err(format!(
232 "Resources::load(): Error enumerating resources: {:?}",
233 get_last_error()
234 )
235 .into());
236 }
237 }
238
239 Ok(())
240 }
241
242 pub fn module_handle(&self) -> Arc<Mutex<Option<HANDLE>>> {
243 self.module_handle.clone()
244 }
245
246 pub fn is_open(&self) -> bool {
248 self.module_handle.lock().unwrap().is_some()
249 }
250
251 pub fn open(&mut self) -> Result<&Self> {
254 self.open_impl(false)
255 }
256
257 pub fn open_delete_existing_resources(&mut self) -> Result<&Self> {
260 self.open_impl(true)
261 }
262
263 fn open_impl(&mut self, delete_existing_resources: bool) -> Result<&Self> {
264 if self.is_open() {
265 return Err(
266 format!("resource '{}' is already open", self.file.to_str().unwrap()).into(),
267 );
268 }
269
270 self.load()?;
271
272 let handle = unsafe {
273 BeginUpdateResourceA(
274 pcstr!(self.file.to_str().unwrap()),
275 delete_existing_resources,
276 )?
277 };
278
279 self.module_handle.lock().unwrap().replace(handle);
280 Ok(self)
283 }
284
285 pub fn remove(&self, resource: &Resource) -> Result<&Self> {
287 self.remove_with_args(&resource.kind, &resource.name, resource.lang)?;
288 Ok(self)
289 }
290
291 pub fn remove_with_args(&self, kind: &Id, name: &Id, lang: u16) -> Result<&Self> {
297 if let Some(handle) = self.module_handle.lock().unwrap().as_ref() {
299 let success = unsafe { UpdateResourceA(*handle, kind, name, lang, None, 0).as_bool() };
300
301 if !success {
302 return Err(format!(
303 "Resources::load(): Error removing resources: {:?}",
304 get_last_error()
305 )
306 .into());
307 }
308 } else {
309 return Err(format!("resource '{}' is not open", self.file.to_str().unwrap()).into());
310 };
311
312 Ok(self)
313 }
314
315 pub fn try_replace(&self, resource: &Resource) -> Result<&Self> {
318 self.replace_with_args(
319 &resource.kind,
320 &resource.name,
321 resource.lang,
322 &resource.encoded.lock().unwrap(),
323 )?;
324 Ok(self)
325 }
326
327 pub fn replace_with_args(&self, kind: &Id, name: &Id, lang: u16, data: &[u8]) -> Result<&Self> {
331 if let Some(handle) = self.module_handle.lock().unwrap().as_ref() {
332 let success = unsafe {
333 UpdateResourceA(
334 *handle,
335 kind,
336 name,
337 lang,
338 Some(std::mem::transmute(data.as_ptr())),
339 data.len() as u32,
340 )
341 .as_bool()
342 };
343
344 if !success {
345 return Err(format!(
346 "Resources::load(): Error updating resources: {:?}",
347 get_last_error()
348 )
349 .into());
350 }
351 } else {
352 return Err(format!(
353 "resource file '{}' is not open",
354 self.file.to_str().unwrap()
355 )
356 .into());
357 };
358
359 Ok(self)
360 }
361
362 pub fn close(&mut self) {
364 if let Some(handle) = self.module_handle.lock().unwrap().take() {
365 unsafe {
366 EndUpdateResourceA(handle, false);
367 };
368 }
369 }
370
371 pub fn discard(&mut self) {
373 if let Some(handle) = self.module_handle.lock().unwrap().take() {
374 unsafe {
375 EndUpdateResourceA(handle, true);
376 };
377 }
378 }
379
380 pub fn insert(&self, r: Resource) {
384 self.list.lock().unwrap().push(Arc::new(r))
385 }
386
387 pub fn find(&self, typeid: Id, nameid: Id) -> Option<Arc<Resource>> {
389 for item in self.list.lock().unwrap().iter() {
390 if item.kind == typeid && item.name == nameid {
391 return Some(item.clone());
392 }
393 }
394
395 None
396 }
397
398 pub fn get_version_info(&self) -> Result<Option<VersionInfo>> {
400 for item in self.list.lock().unwrap().iter() {
401 if item.kind == resource_type::VERSION {
402 return Ok(Some(item.clone().try_into()?));
403 }
404 }
405
406 Ok(None)
407 }
408}
409
410impl Drop for Resources {
411 fn drop(&mut self) {
412 self.close();
413 }
414}
415
416unsafe extern "system" fn enum_languages(
417 hmodule: HINSTANCE,
418 lptype: PCSTR,
419 lpname: PCSTR,
420 lang: u16,
421 lparam: isize,
422) -> BOOL {
423 let rptr: *const Resources = std::mem::transmute(lparam);
424 let hresinfo = match FindResourceExA(hmodule, lptype, lpname, lang) {
425 Ok(hresinfo) => hresinfo,
426 Err(e) => panic!("Unable to find resource {hmodule:?} {lptype:?} {lpname:?} {lang}: {e}"),
427 };
428 let resource = LoadResource(hmodule, hresinfo);
429 let len = SizeofResource(hmodule, hresinfo);
430 let data_ptr = LockResource(resource);
431 let data = std::slice::from_raw_parts(std::mem::transmute(data_ptr), len as usize);
432 let resources = &*rptr;
433 resources.insert(Resource::new(resources, lptype, lpname, lang, data));
434 BOOL(1)
435}
436
437unsafe extern "system" fn enum_names(
438 hmodule: HINSTANCE,
439 lptype: PCSTR,
440 lpname: PCSTR,
441 lparam: isize,
442) -> BOOL {
443 EnumResourceLanguagesA(hmodule, lptype, lpname, Some(enum_languages), lparam);
444 BOOL(1)
445}
446
447unsafe extern "system" fn enum_types(hmodule: HINSTANCE, lptype: PCSTR, lparam: isize) -> BOOL {
448 EnumResourceNamesA(hmodule, lptype, Some(enum_names), lparam);
449 BOOL(1)
450}