Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/examples/asset/asset_decompression.rs
6592 views
1
//! Implements loader for a Gzip compressed asset.
2
3
use bevy::{
4
asset::{
5
io::{Reader, VecReader},
6
AssetLoader, ErasedLoadedAsset, LoadContext, LoadDirectError,
7
},
8
prelude::*,
9
reflect::TypePath,
10
};
11
use flate2::read::GzDecoder;
12
use std::{io::prelude::*, marker::PhantomData};
13
use thiserror::Error;
14
15
#[derive(Asset, TypePath)]
16
struct GzAsset {
17
uncompressed: ErasedLoadedAsset,
18
}
19
20
#[derive(Default)]
21
struct GzAssetLoader;
22
23
/// Possible errors that can be produced by [`GzAssetLoader`]
24
#[non_exhaustive]
25
#[derive(Debug, Error)]
26
enum GzAssetLoaderError {
27
/// An [IO](std::io) Error
28
#[error("Could not load asset: {0}")]
29
Io(#[from] std::io::Error),
30
/// An error caused when the asset path cannot be used to determine the uncompressed asset type.
31
#[error("Could not determine file path of uncompressed asset")]
32
IndeterminateFilePath,
33
/// An error caused by the internal asset loader.
34
#[error("Could not load contained asset: {0}")]
35
LoadDirectError(#[from] LoadDirectError),
36
}
37
38
impl AssetLoader for GzAssetLoader {
39
type Asset = GzAsset;
40
type Settings = ();
41
type Error = GzAssetLoaderError;
42
43
async fn load(
44
&self,
45
reader: &mut dyn Reader,
46
_settings: &(),
47
load_context: &mut LoadContext<'_>,
48
) -> Result<Self::Asset, Self::Error> {
49
let compressed_path = load_context.path();
50
let file_name = compressed_path
51
.file_name()
52
.ok_or(GzAssetLoaderError::IndeterminateFilePath)?
53
.to_string_lossy();
54
let uncompressed_file_name = file_name
55
.strip_suffix(".gz")
56
.ok_or(GzAssetLoaderError::IndeterminateFilePath)?;
57
let contained_path = compressed_path.join(uncompressed_file_name);
58
59
let mut bytes_compressed = Vec::new();
60
61
reader.read_to_end(&mut bytes_compressed).await?;
62
63
let mut decoder = GzDecoder::new(bytes_compressed.as_slice());
64
65
let mut bytes_uncompressed = Vec::new();
66
67
decoder.read_to_end(&mut bytes_uncompressed)?;
68
69
// Now that we have decompressed the asset, let's pass it back to the
70
// context to continue loading
71
72
let mut reader = VecReader::new(bytes_uncompressed);
73
74
let uncompressed = load_context
75
.loader()
76
.with_unknown_type()
77
.immediate()
78
.with_reader(&mut reader)
79
.load(contained_path)
80
.await?;
81
82
Ok(GzAsset { uncompressed })
83
}
84
85
fn extensions(&self) -> &[&str] {
86
&["gz"]
87
}
88
}
89
90
#[derive(Component, Default)]
91
struct Compressed<T> {
92
compressed: Handle<GzAsset>,
93
_phantom: PhantomData<T>,
94
}
95
96
fn main() {
97
App::new()
98
.add_plugins(DefaultPlugins)
99
.init_asset::<GzAsset>()
100
.init_asset_loader::<GzAssetLoader>()
101
.add_systems(Startup, setup)
102
.add_systems(Update, decompress::<Sprite, Image>)
103
.run();
104
}
105
106
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
107
commands.spawn(Camera2d);
108
109
commands.spawn(Compressed::<Image> {
110
compressed: asset_server.load("data/compressed_image.png.gz"),
111
..default()
112
});
113
}
114
115
fn decompress<T: Component + From<Handle<A>>, A: Asset>(
116
mut commands: Commands,
117
asset_server: Res<AssetServer>,
118
mut compressed_assets: ResMut<Assets<GzAsset>>,
119
query: Query<(Entity, &Compressed<A>)>,
120
) {
121
for (entity, Compressed { compressed, .. }) in query.iter() {
122
let Some(GzAsset { uncompressed }) = compressed_assets.remove(compressed) else {
123
continue;
124
};
125
126
let uncompressed = uncompressed.take::<A>().unwrap();
127
128
commands
129
.entity(entity)
130
.remove::<Compressed<A>>()
131
.insert(T::from(asset_server.add(uncompressed)));
132
}
133
}
134
135