Skip to content

Commit

Permalink
Merge pull request Noxime#174 from LuisMayo/feature/return-icon-as-img
Browse files Browse the repository at this point in the history
feat: return icon as img
  • Loading branch information
Noxime authored Apr 27, 2024
2 parents 2041f33 + fbd88b9 commit a37a81e
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 5 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ rust-version = "1.71.1"
[features]
default = []
raw-bindings = []
image = ["dep:image"]

[workspace]
members = [
Expand All @@ -28,6 +29,7 @@ thiserror = "1.0"
bitflags = "1.2"
lazy_static = "1.4"
serde = { version = "1.0", features = ["derive"], optional = true }
image = { version = "0.25.1", optional = true, default-features = false }

[dev-dependencies]
serial_test = "1"
34 changes: 34 additions & 0 deletions src/user_stats/stat_callback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,37 @@ unsafe impl Callback for UserAchievementStored {
}
}
}

/// Result of a request to retrieve the achievement icon if the icon was not available at the time of the function call.
/// # Example
///
/// ```no_run
/// # use steamworks::*;
/// # let (client, single) = steamworks::Client::init().unwrap();
/// let callback_handle = client.register_callback(|val: UserAchievementIconFetched| {
/// // ...
/// });
/// ```
#[derive(Clone, Debug)]
pub struct UserAchievementIconFetched {
pub game_id: GameId,
pub achievement_name: String,
pub achieved: bool,
pub icon_handle: i32,
}

unsafe impl Callback for UserAchievementIconFetched {
const ID: i32 = CALLBACK_BASE_ID + 9;
const SIZE: i32 = std::mem::size_of::<sys::UserAchievementStored_t>() as i32;

unsafe fn from_raw(raw: *mut c_void) -> Self {
let val = &mut *(raw as *mut sys::UserAchievementIconFetched_t);
let name = CStr::from_ptr(val.m_rgchAchievementName.as_ptr()).to_owned();
Self {
game_id: GameId(val.m_nGameID.__bindgen_anon_1.m_ulGameID),
achievement_name: name.into_string().unwrap(),
achieved: val.m_bAchieved,
icon_handle: val.m_nIconHandle,
}
}
}
35 changes: 30 additions & 5 deletions src/user_stats/stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,15 @@ impl<M> AchievementHelper<'_, M> {
///
/// **Note: This is handled within the function. Returns a `Vec<u8>` buffer on success,
/// which can be converted into the image data and saved to disk (e.g. via external RGBA to image crate).*
/// ** Note: This may return None if Steam has not retrieved the icon yet. In that case an `UserAchievementIconFetched` callback will be processed

pub fn get_achievement_icon(&self) -> Option<Vec<u8>> {
Some(self.internal_get_achievement_icon(true)?.0)
}

fn internal_get_achievement_icon(&self, avoid_big_icons: bool) -> Option<(Vec<u8>, u32, u32)> {
unsafe {
let utils = sys::SteamAPI_SteamUtils_v010();
let utils: *mut sys::ISteamUtils = sys::SteamAPI_SteamUtils_v010();
let img = sys::SteamAPI_ISteamUserStats_GetAchievementIcon(
self.parent.user_stats,
self.name.as_ptr() as *const _,
Expand All @@ -207,14 +213,33 @@ impl<M> AchievementHelper<'_, M> {
if !sys::SteamAPI_ISteamUtils_GetImageSize(utils, img, &mut width, &mut height) {
return None;
}
if width != 64 || height != 64 {
if avoid_big_icons && (width != 64 || height != 64) {
return None;
}
let mut dest = vec![0; 64 * 64 * 4];
if !sys::SteamAPI_ISteamUtils_GetImageRGBA(utils, img, dest.as_mut_ptr(), 64 * 64 * 4) {
let mut dest = vec![0; (width * height * 4).try_into().unwrap()];
if !sys::SteamAPI_ISteamUtils_GetImageRGBA(
utils,
img,
dest.as_mut_ptr(),
(width * height * 4).try_into().unwrap(),
) {
return None;
}
Some(dest)
Some((dest, width, height))
}
}

/// Gets the icon for an achievement.
///
/// The image is returned as a handle to be used with `ISteamUtils::GetImageRGBA` to get
/// the actual image data.*
///
/// **Note: This is handled within the function. Returns a `ImageBuffer::<image::Rgba<u8>, Vec<u8>>` from the image crate on success**
/// ** Note: This may return None if Steam has not retrieved the icon yet. In that case an `UserAchievementIconFetched` callback will be processed
#[cfg(feature = "image")]
pub fn get_achievement_icon_v2(&self) -> Option<image::ImageBuffer<image::Rgba<u8>, Vec<u8>>> {
let (vec, width, height) = self.internal_get_achievement_icon(false)?;
let img = image::ImageBuffer::<image::Rgba<u8>, Vec<u8>>::from_vec(width, height, vec)?;
return Some(img);
}
}

0 comments on commit a37a81e

Please sign in to comment.