Skip to main content

fyrox_resource/
builtin.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Built-in resource is a resource embedded in the application executable file. It is a very useful
22//! mechanism when you need to bundle all game resources and put them in the executable file. See
23//! [`BuiltInResource`] docs for more info.
24
25use crate::{core::Uuid, untyped::UntypedResource, Resource, TypedResourceData};
26use fxhash::FxHashMap;
27use std::{
28    borrow::Cow,
29    ops::Deref,
30    path::{Path, PathBuf},
31};
32
33/// Data source of a built-in resource.
34#[derive(Clone)]
35pub struct DataSource {
36    /// File extension, associated with the data source.
37    pub extension: Cow<'static, str>,
38    /// The actual data.
39    pub bytes: Cow<'static, [u8]>,
40}
41
42impl DataSource {
43    /// Creates a new data source with the given path and the data. The data must be embedded in
44    /// the application binary using [`include_bytes`] macro or similar. Use [`crate::embedded_data_source`]
45    /// macro to combine a call to this method with [`include_bytes`] macro.
46    pub fn new(path: &'static str, data: &'static [u8]) -> Self {
47        Self {
48            extension: Cow::Borrowed(
49                Path::new(path)
50                    .extension()
51                    .and_then(|ext| ext.to_str())
52                    .unwrap_or(""),
53            ),
54            bytes: Cow::Borrowed(data),
55        }
56    }
57}
58
59/// Combines a call of [`include_bytes`] with the call of [`DataSource::new`]. Prevents you from
60/// typing the same path twice.
61#[macro_export]
62macro_rules! embedded_data_source {
63    ($path:expr) => {
64        $crate::manager::DataSource::new($path, include_bytes!($path))
65    };
66}
67
68/// Untyped built-in resource.
69#[derive(Clone)]
70pub struct UntypedBuiltInResource {
71    /// Id of the built-in resource.
72    pub id: PathBuf,
73    /// Initial data, from which the resource is created from.
74    pub data_source: Option<DataSource>,
75    /// Uuid of the resource.
76    pub resource_uuid: Uuid,
77    /// Ready-to-use ("loaded") resource.
78    pub resource: UntypedResource,
79}
80
81/// Built-in resource is a resource embedded in the application executable file. It is a very useful
82/// mechanism when you need to bundle all game resources and put them in the executable file.
83///
84/// ## Registration
85///
86/// Every built-in resource must be registered in a resource manager to be accessible via standard
87/// [`crate::manager::ResourceManager::request`] method. It could be done pretty easily:
88///
89/// ```rust
90/// use fyrox_resource::{
91///     builtin::BuiltInResource,
92///     core::{reflect::prelude::*, type_traits::prelude::*, visitor::prelude::*, Uuid},
93///     manager::ResourceManager,
94///     Resource, ResourceData,
95/// };
96/// use std::{error::Error, path::Path};
97///
98/// #[derive(TypeUuidProvider, Default, Debug, Clone, Visit, Reflect)]
99/// #[type_uuid(id = "00d036bb-fbed-47f7-94e3-b3fce93dee17")]
100/// struct MyResource {
101///     some_data: String,
102/// }
103///
104/// impl ResourceData for MyResource {
105///     fn type_uuid(&self) -> Uuid {
106///         <Self as TypeUuidProvider>::type_uuid()
107///     }
108///
109///     fn save(&mut self, _path: &Path) -> Result<(), Box<dyn Error>> {
110///         Ok(())
111///     }
112///
113///     fn can_be_saved(&self) -> bool {
114///         false
115///     }
116///
117///     fn try_clone_box(&self) -> Option<Box<dyn ResourceData>> {
118///         Some(Box::new(self.clone()))
119///     }
120/// }
121///
122/// fn register_built_in_resource(resource_manager: &ResourceManager) {
123///     let id = "MyResourceId";
124///     let some_data = "This string is a built-in resource with MyResourceId id.";
125///
126///     let resource = Resource::new_embedded(MyResource {
127///         some_data: some_data.into(),
128///     });
129///
130///     resource_manager.register_built_in_resource(BuiltInResource::new_no_source(id, resource));
131///
132///     assert_eq!(
133///         resource_manager
134///             .request::<MyResource>(id)
135///             .data_ref()
136///             .some_data,
137///         some_data,
138///     )
139/// }
140/// ```
141pub struct BuiltInResource<T>
142where
143    T: TypedResourceData,
144{
145    /// Id of the built-in resource.
146    pub id: PathBuf,
147    /// Initial data, from which the resource is created from.
148    pub data_source: Option<DataSource>,
149    /// Ready-to-use ("loaded") resource.
150    pub resource: Resource<T>,
151}
152
153impl<T: TypedResourceData> Clone for BuiltInResource<T> {
154    fn clone(&self) -> Self {
155        Self {
156            id: self.id.clone(),
157            data_source: self.data_source.clone(),
158            resource: self.resource.clone(),
159        }
160    }
161}
162
163impl<T: TypedResourceData> BuiltInResource<T> {
164    /// Creates a new built-in resource with an id, a data source and function that creates the
165    /// resource from the given data source.
166    pub fn new<F>(id: impl AsRef<Path>, data_source: DataSource, make: F) -> Self
167    where
168        F: FnOnce(&[u8]) -> Resource<T>,
169    {
170        let resource = make(&data_source.bytes);
171        Self {
172            id: id.as_ref().to_path_buf(),
173            resource,
174            data_source: Some(data_source),
175        }
176    }
177
178    /// Creates a new built-in resource from an id and arbitrary resource.
179    pub fn new_no_source(id: impl AsRef<Path>, resource: Resource<T>) -> Self {
180        Self {
181            id: id.as_ref().to_path_buf(),
182            data_source: None,
183            resource,
184        }
185    }
186
187    /// Returns the wrapped resource instance.
188    pub fn resource(&self) -> Resource<T> {
189        self.resource.clone()
190    }
191}
192
193impl<T: TypedResourceData> From<BuiltInResource<T>> for UntypedBuiltInResource {
194    fn from(value: BuiltInResource<T>) -> Self {
195        Self {
196            id: value.id,
197            data_source: value.data_source,
198            resource_uuid: value.resource.resource_uuid(),
199            resource: value.resource.into(),
200        }
201    }
202}
203
204/// A container for built-in resources. Every built-in resource is registered using its id defined
205/// at the creation stage.
206#[derive(Default, Clone)]
207pub struct BuiltInResourcesContainer {
208    inner: FxHashMap<PathBuf, UntypedBuiltInResource>,
209}
210
211impl BuiltInResourcesContainer {
212    /// Adds a new typed built-in resource and removes the previous one with the same id (if any).
213    pub fn add<T>(&mut self, resource: BuiltInResource<T>) -> Option<UntypedBuiltInResource>
214    where
215        T: TypedResourceData,
216    {
217        self.add_untyped(resource.into())
218    }
219
220    /// Adds a new untyped built-in resource and removes the previous one with the same id (if any).
221    pub fn add_untyped(
222        &mut self,
223        resource: UntypedBuiltInResource,
224    ) -> Option<UntypedBuiltInResource> {
225        let id = resource.id.clone();
226        self.inner.insert(id, resource)
227    }
228
229    /// Tries to remove a built-in resource by its path.
230    pub fn remove(&mut self, id: impl AsRef<Path>) -> Option<UntypedBuiltInResource> {
231        self.inner.remove(id.as_ref())
232    }
233
234    /// Tries to find a built-in resource by its uuid.
235    pub fn find_by_uuid(&self, uuid: Uuid) -> Option<&UntypedBuiltInResource> {
236        self.inner.values().find(|r| r.resource_uuid == uuid)
237    }
238
239    /// Checks whether the given resource path corresponds to a built-in resource or not.
240    pub fn is_built_in_resource_path(&self, resource: impl AsRef<Path>) -> bool {
241        self.inner.contains_key(resource.as_ref())
242    }
243
244    /// Checks whether the given resource is a built-in resource instance or not.
245    pub fn is_built_in_resource(&self, resource: impl AsRef<UntypedResource>) -> bool {
246        self.inner
247            .values()
248            .any(|built_in| &built_in.resource == resource.as_ref())
249    }
250}
251
252impl Deref for BuiltInResourcesContainer {
253    type Target = FxHashMap<PathBuf, UntypedBuiltInResource>;
254
255    fn deref(&self) -> &Self::Target {
256        &self.inner
257    }
258}