120 lines
3.1 KiB
Go
120 lines
3.1 KiB
Go
package binmeta
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/tc-hib/winres"
|
|
"github.com/tc-hib/winres/version"
|
|
)
|
|
|
|
// GetIconAndName returns an icon and name of the given binary path.
|
|
// Providing the home directory of the user running the process of that binary can improve results.
|
|
// Even if an error is returned, the other return values are valid, if set.
|
|
func GetIconAndName(ctx context.Context, binPath string, homeDir string) (icon *Icon, name string, err error) {
|
|
// Get name and png from exe.
|
|
png, name, err := getIconAndNamefromRSS(ctx, binPath)
|
|
|
|
// Fall back to name generation if name is not set.
|
|
if name == "" {
|
|
name = GenerateBinaryNameFromPath(binPath)
|
|
}
|
|
|
|
// Handle previous error.
|
|
if err != nil {
|
|
return nil, name, err
|
|
}
|
|
|
|
// Update profile icon and return icon object.
|
|
filename, err := UpdateProfileIcon(png, "png")
|
|
if err != nil {
|
|
return nil, name, fmt.Errorf("failed to store icon: %w", err)
|
|
}
|
|
|
|
return &Icon{
|
|
Type: IconTypeAPI,
|
|
Value: filename,
|
|
Source: IconSourceCore,
|
|
}, name, nil
|
|
}
|
|
|
|
func getIconAndNamefromRSS(ctx context.Context, binPath string) (png []byte, name string, err error) {
|
|
// Open .exe file.
|
|
exeFile, err := os.Open(binPath)
|
|
if err != nil {
|
|
if errors.Is(err, os.ErrNotExist) {
|
|
return nil, "", nil
|
|
}
|
|
return nil, "", fmt.Errorf("failed to open exe %s to get icon: %w", binPath, err)
|
|
}
|
|
defer exeFile.Close() //nolint:errcheck
|
|
|
|
// Load .exe resources.
|
|
rss, err := winres.LoadFromEXE(exeFile)
|
|
if err != nil {
|
|
return nil, "", fmt.Errorf("failed to get rss: %w", err)
|
|
}
|
|
|
|
// DEBUG: Print all available resources:
|
|
// rss.Walk(func(typeID, resID winres.Identifier, langID uint16, data []byte) bool {
|
|
// fmt.Printf("typeID=%d resID=%d langID=%d\n", typeID, resID, langID)
|
|
// return true
|
|
// })
|
|
|
|
// Get name from version record.
|
|
var (
|
|
versionInfo *version.Info
|
|
versionInfoErr error
|
|
)
|
|
rss.WalkType(winres.RT_VERSION, func(resID winres.Identifier, langID uint16, data []byte) bool {
|
|
versionInfo, versionInfoErr = version.FromBytes(data)
|
|
switch {
|
|
case versionInfoErr != nil:
|
|
return true
|
|
case versionInfo == nil:
|
|
return true
|
|
}
|
|
|
|
// Get metadata table and main language.
|
|
table := versionInfo.Table().GetMainTranslation()
|
|
if table == nil {
|
|
return true
|
|
}
|
|
|
|
name = table[version.ProductName]
|
|
return name == ""
|
|
})
|
|
name = cleanFileDescription(name)
|
|
|
|
// Get first icon.
|
|
var (
|
|
icon *winres.Icon
|
|
iconErr error
|
|
)
|
|
rss.WalkType(winres.RT_GROUP_ICON, func(resID winres.Identifier, langID uint16, _ []byte) bool {
|
|
icon, iconErr = rss.GetIconTranslation(resID, langID)
|
|
return iconErr != nil
|
|
})
|
|
if iconErr != nil {
|
|
return nil, name, fmt.Errorf("failed to get icon: %w", err)
|
|
}
|
|
if icon == nil {
|
|
return nil, name, errors.New("no icon in resources")
|
|
}
|
|
// Convert icon, if it exists.
|
|
icoBuf := &bytes.Buffer{}
|
|
err = icon.SaveICO(icoBuf)
|
|
if err != nil {
|
|
return nil, name, fmt.Errorf("failed to save ico: %w", err)
|
|
}
|
|
png, err = ConvertICOtoPNG(icoBuf.Bytes())
|
|
if err != nil {
|
|
return nil, name, fmt.Errorf("failed to convert ico to png: %w", err)
|
|
}
|
|
|
|
return png, name, nil
|
|
}
|