1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! Code for loading textures from RAM to VRAM
//!
//! Largely coppied from last year: https://github.com/Ewpratten/ludum-dare-49/blob/master/game/src/utilities/datastore.rs

use std::path::Path;

use raylib::{
    audio::{Music, RaylibAudio, Sound},
    texture::Texture2D,
    RaylibHandle, RaylibThread,
};
use tempfile::tempdir;

use crate::asset_manager::InternalData;

#[derive(Debug, thiserror::Error)]
pub enum ResourceLoadError {
    #[error(transparent)]
    Io(#[from] std::io::Error),
    #[error("Could not load embedded asset: {0}")]
    AssetNotFound(String),
    #[error("Generic error: {0}")]
    Generic(String),
}

/// Loads an embedded texture into VRAM.
///
/// # Technical Info
/// In this application, we are using `rust_embed` to embed static assets directly inside the executable.
/// This has the limitation of none of the assets being "real files", which causes an issue with Raylib.
/// Raylib requires a "real file" in order to load data into VRAM (without digging into `unsafe` dark magic).
/// The solution is to temporarily write the assets to disk, and then load them from disk.
/// We must also preserve the file extension, so the Raylib file loader can parse them correctly.
pub fn load_texture_from_internal_data(
    raylib_handle: &mut RaylibHandle,
    thread: &RaylibThread,
    path: &str,
) -> Result<Texture2D, ResourceLoadError> {
    // Create a temp file path to work with
    let temp_dir = tempdir()?;
    debug!(
        "Created temporary directory for passing embedded data to Raylib: {}",
        temp_dir.path().display()
    );
    let tmp_path = temp_dir.path().join(Path::new(path).file_name().unwrap());

    // Unpack the raw image data to a real file on the local filesystem so raylib will read it correctly
    std::fs::write(
        &tmp_path,
        &InternalData::get(path)
            .ok_or(ResourceLoadError::AssetNotFound(path.to_string()))?
            .data,
    )?;

    // Call through via FFI to re-load the file
    let texture = raylib_handle
        .load_texture(thread, tmp_path.to_str().unwrap())
        .map_err(ResourceLoadError::Generic)?;

    // Close the file
    debug!(
        "Dropping temporary directory: {}",
        temp_dir.path().display()
    );
    temp_dir.close()?;

    Ok(texture)
}

pub fn load_music_from_internal_data(
    thread: &RaylibThread,
    path: &str,
) -> Result<Music, ResourceLoadError> {
    // Create a temp file path to work with
    let temp_dir = tempdir()?;
    debug!(
        "Created temporary directory for passing embedded data to Raylib: {}",
        temp_dir.path().display()
    );
    let tmp_path = temp_dir.path().join(Path::new(path).file_name().unwrap());

    // Unpack the raw sound data to a real file on the local filesystem so raylib will read it correctly
    std::fs::write(
        &tmp_path,
        &InternalData::get(path)
            .ok_or(ResourceLoadError::AssetNotFound(path.to_string()))?
            .data,
    )?;

    // Call through via FFI to re-load the file
    let texture = Music::load_music_stream(thread, tmp_path.to_str().unwrap())
        .map_err(ResourceLoadError::Generic)?;

    // Close the file
    debug!(
        "Dropping temporary directory: {}",
        temp_dir.path().display()
    );
    // temp_dir.close()?;

    Ok(texture)
}

pub fn load_sound_from_internal_data(
    path: &str,
) -> Result<Sound, ResourceLoadError> {
    // Create a temp file path to work with
    let temp_dir = tempdir()?;
    debug!(
        "Created temporary directory for passing embedded data to Raylib: {}",
        temp_dir.path().display()
    );
    let tmp_path = temp_dir.path().join(Path::new(path).file_name().unwrap());

    // Unpack the raw sound data to a real file on the local filesystem so raylib will read it correctly
    std::fs::write(
        &tmp_path,
        &InternalData::get(path)
            .ok_or(ResourceLoadError::AssetNotFound(path.to_string()))?
            .data,
    )?;

    // Call through via FFI to re-load the file
    let texture =
        Sound::load_sound(tmp_path.to_str().unwrap()).map_err(ResourceLoadError::Generic)?;

    // Close the file
    debug!(
        "Dropping temporary directory: {}",
        temp_dir.path().display()
    );
    temp_dir.close()?;

    Ok(texture)
}