// Give the produced bundle the icon of the application it carries.
//
// The icon is read from the application executable with the PE reader and
// written into the bundle with the Windows resource API. That API creates the
// resource section when the target file does not have one yet, which is the
// case for the MoonBit launcher stub.
//
// The feature is Windows only; on other platforms the bundle simply keeps
// the icon of the launcher.
///|
/// Encode a String as a NUL terminated UTF-16LE buffer, for the wide
/// Windows APIs.
fn utf16_z(s : String) -> Bytes {
let arr = []
for ch in s.iter() {
let cp = ch.to_int()
if cp < 0x10000 {
arr.push((cp & 0xff).to_byte())
arr.push(((cp >> 8) & 0xff).to_byte())
} else {
// Surrogate pair for the code points above the basic plane.
let v = cp - 0x10000
let hi = 0xd800 + (v >> 10)
let lo = 0xdc00 + (v & 0x3ff)
arr.push((hi & 0xff).to_byte())
arr.push(((hi >> 8) & 0xff).to_byte())
arr.push((lo & 0xff).to_byte())
arr.push(((lo >> 8) & 0xff).to_byte())
}
}
arr.push(b'\x00')
arr.push(b'\x00')
Bytes::from_array(arr)
}
///|
/// Copy the icon of `source_exe` into `target_exe`.
///
/// Returns true when the icon was copied.
#cfg(platform="windows")
pub async fn copy_icon(source_exe : String, target_exe : String) -> Bool {
let data = @fs.read_binary(source_exe)
// A source that is not a PE image, or that carries no icon, simply means
// there is nothing to copy.
let pe = parse_pe(data) catch { _ => return false }
let (group_id, group, icons) = match extract_icon(pe) {
Some(x) => x
None => return false
}
let handle = begin_update_resource_w(utf16_z(target_exe), 0)
if handle == 0 {
return false
}
let mut ok = true
for i in icons {
let r = update_resource_w(handle, RT_ICON, i.0, 0, i.1, i.1.length())
if r == 0 {
ok = false
}
}
let r = update_resource_w(
handle,
RT_GROUP_ICON,
group_id,
0,
group,
group.length(),
)
if r == 0 {
ok = false
}
let _ = end_update_resource_w(handle, 0)
ok
}
///|
/// Copy the icon of `source_exe` into `target_exe`.
///
/// The icon is a Windows resource, so this is a no-op elsewhere.
#cfg(not(platform="windows"))
pub async fn copy_icon(source_exe : String, target_exe : String) -> Bool {
false
}
///|
#cfg(platform="windows")
#borrow(path)
extern "C" fn begin_update_resource_w(
path : Bytes,
delete_existing : Int32,
) -> Int64 = "BeginUpdateResourceW"
///|
#cfg(platform="windows")
#borrow(data)
extern "C" fn update_resource_w(
handle : Int64,
rtype : Int64,
name : Int64,
lang : Int32,
data : Bytes,
size : Int32,
) -> Int32 = "UpdateResourceW"
///|
#cfg(platform="windows")
extern "C" fn end_update_resource_w(handle : Int64, discard : Int32) -> Int32 = "EndUpdateResourceW"