mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
Windows: When using "Create shortcut", use the game's icon instead of PPSSPP's.
Since on Windows, shortcuts can't embed icons, we first save the game's icon .png as an .ico in the SAVESTATE folder (there might be a better place, but it also doesn't seem worth it to create a new folder for this). Part of #10885 (Android functionality still missing, for example).
This commit is contained in:
@@ -6,6 +6,8 @@
|
||||
#include <thread>
|
||||
|
||||
#include "Common/Data/Encoding/Utf8.h"
|
||||
#include "Common/File/FileUtil.h"
|
||||
#include "Common/Data/Format/PNGLoad.h"
|
||||
#include "ShellUtil.h"
|
||||
|
||||
#include <shlobj.h>
|
||||
@@ -188,7 +190,7 @@ namespace W32Util {
|
||||
|
||||
|
||||
// http://msdn.microsoft.com/en-us/library/aa969393.aspx
|
||||
HRESULT CreateLink(LPCWSTR lpszPathObj, LPCWSTR lpszArguments, LPCWSTR lpszPathLink, LPCWSTR lpszDesc) {
|
||||
static HRESULT CreateLink(LPCWSTR lpszPathObj, LPCWSTR lpszArguments, LPCWSTR lpszPathLink, LPCWSTR lpszDesc, LPCWSTR lpszIcon, int iconIndex) {
|
||||
HRESULT hres;
|
||||
IShellLink *psl = nullptr;
|
||||
hres = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
@@ -205,7 +207,9 @@ HRESULT CreateLink(LPCWSTR lpszPathObj, LPCWSTR lpszArguments, LPCWSTR lpszPathL
|
||||
psl->SetPath(lpszPathObj);
|
||||
psl->SetArguments(lpszArguments);
|
||||
psl->SetDescription(lpszDesc);
|
||||
// psl->SetIconLocation(..)
|
||||
if (lpszIcon) {
|
||||
psl->SetIconLocation(lpszIcon, iconIndex);
|
||||
}
|
||||
|
||||
// Query IShellLink for the IPersistFile interface, used for saving the
|
||||
// shortcut in persistent storage.
|
||||
@@ -223,7 +227,7 @@ HRESULT CreateLink(LPCWSTR lpszPathObj, LPCWSTR lpszArguments, LPCWSTR lpszPathL
|
||||
return hres;
|
||||
}
|
||||
|
||||
bool CreateDesktopShortcut(std::string_view argumentPath, std::string_view gameTitleStr) {
|
||||
bool CreateDesktopShortcut(std::string_view argumentPath, std::string_view gameTitleStr, const Path &icoFile) {
|
||||
// Get the desktop folder
|
||||
wchar_t *pathbuf = new wchar_t[4096];
|
||||
SHGetFolderPath(0, CSIDL_DESKTOPDIRECTORY, NULL, SHGFP_TYPE_CURRENT, pathbuf);
|
||||
@@ -264,7 +268,12 @@ bool CreateDesktopShortcut(std::string_view argumentPath, std::string_view gameT
|
||||
|
||||
sanitizedArgument = "\"" + sanitizedArgument + "\"";
|
||||
|
||||
CreateLink(moduleFilename.c_str(), ConvertUTF8ToWString(sanitizedArgument).c_str(), pathbuf, ConvertUTF8ToWString(gameTitle).c_str());
|
||||
std::wstring icon;
|
||||
if (!icoFile.empty()) {
|
||||
icon = icoFile.ToWString();
|
||||
}
|
||||
|
||||
CreateLink(moduleFilename.c_str(), ConvertUTF8ToWString(sanitizedArgument).c_str(), pathbuf, ConvertUTF8ToWString(gameTitle).c_str(), icon.empty() ? nullptr : icon.c_str(), 0);
|
||||
|
||||
// TODO: Also extract the game icon and convert to .ico, put it somewhere under Memstick, and set it.
|
||||
|
||||
@@ -272,4 +281,56 @@ bool CreateDesktopShortcut(std::string_view argumentPath, std::string_view gameT
|
||||
return false;
|
||||
}
|
||||
|
||||
// Function to create an icon file from PNG image data (these icons require Windows Vista).
|
||||
// The Old New Thing comes to the rescue again! ChatGPT failed miserably.
|
||||
// https://devblogs.microsoft.com/oldnewthing/20101018-00/?p=12513
|
||||
// https://devblogs.microsoft.com/oldnewthing/20101022-00/?p=12473
|
||||
bool CreateICOFromPNGData(const uint8_t *imageData, size_t imageDataSize, const Path &icoPath) {
|
||||
if (imageDataSize <= sizeof(PNGHeaderPeek)) {
|
||||
return false;
|
||||
}
|
||||
// Parse the PNG
|
||||
PNGHeaderPeek pngHeader;
|
||||
memcpy(&pngHeader, imageData, sizeof(PNGHeaderPeek));
|
||||
if (pngHeader.Width() > 256 || pngHeader.Height() > 256) {
|
||||
// Reject the png as an icon.
|
||||
return false;
|
||||
}
|
||||
|
||||
struct IconHeader {
|
||||
uint16_t reservedZero;
|
||||
uint16_t type; // should be 1
|
||||
uint16_t imageCount;
|
||||
};
|
||||
IconHeader hdr{ 0, 1, 1 };
|
||||
struct IconDirectoryEntry {
|
||||
BYTE bWidth;
|
||||
BYTE bHeight;
|
||||
BYTE bColorCount;
|
||||
BYTE bReserved;
|
||||
WORD wPlanes;
|
||||
WORD wBitCount;
|
||||
DWORD dwBytesInRes;
|
||||
DWORD dwImageOffset;
|
||||
};
|
||||
IconDirectoryEntry entry{};
|
||||
entry.bWidth = pngHeader.Width();
|
||||
entry.bHeight = pngHeader.Height();
|
||||
entry.bColorCount = 0;
|
||||
entry.dwBytesInRes = (DWORD)imageDataSize;
|
||||
entry.wPlanes = 32;
|
||||
entry.wBitCount = 32;
|
||||
entry.dwImageOffset = sizeof(hdr) + sizeof(entry);
|
||||
|
||||
FILE *file = File::OpenCFile(icoPath, "wb");
|
||||
if (!file) {
|
||||
return false;
|
||||
}
|
||||
fwrite(&hdr, sizeof(hdr), 1, file);
|
||||
fwrite(&entry, sizeof(entry), 1, file);
|
||||
fwrite(imageData, 1, imageDataSize, file);
|
||||
fclose(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Reference in New Issue
Block a user