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}