diff --git a/include/eepp/system/sys.hpp b/include/eepp/system/sys.hpp index c0dc2c540..5ba9e5020 100644 --- a/include/eepp/system/sys.hpp +++ b/include/eepp/system/sys.hpp @@ -130,6 +130,9 @@ class EE_API Sys { /** @returns The unix timestamp of the process creation time */ static Int64 getProcessCreationTime( Uint64 pid ); + + /** @returns The target destination of a windows shortcut path (.lnk files) */ + static std::string getShortcutTarget( const std::string& lnkFilePath ); }; }} // namespace EE::System diff --git a/src/eepp/system/sys.cpp b/src/eepp/system/sys.cpp index 937f88b80..08a356c39 100644 --- a/src/eepp/system/sys.cpp +++ b/src/eepp/system/sys.cpp @@ -1559,4 +1559,62 @@ std::vector Sys::pidof( const std::string& processName ) { #endif } +#pragma pack( push, 1 ) + +// Basic structure of the Shell Link Header (size = 76 bytes) +struct ShellLinkHeader { + uint32_t headerSize; + uint8_t guid[16]; + uint32_t linkFlags; + uint32_t fileAttributes; + uint64_t creationTime; + uint64_t accessTime; + uint64_t writeTime; + uint32_t fileSize; + uint32_t iconIndex; + uint32_t showCommand; + uint16_t hotKey; + uint16_t reserved1; + uint32_t reserved2; + uint32_t reserved3; +}; + +#pragma pack( pop ) + +std::string Sys::getShortcutTarget( const std::string& lnkFilePath ) { + if ( FileSystem::fileExtension( lnkFilePath ) != "lnk" ) + return ""; + + std::vector data; + FileSystem::fileGet( lnkFilePath, data ); + + ShellLinkHeader* header = reinterpret_cast( data.data() ); + + if ( header->headerSize != 76 ) + return ""; + + size_t offset = sizeof( ShellLinkHeader ); + + if ( header->linkFlags & 0x01 ) { + uint16_t idListSize = *reinterpret_cast( &data[offset] ); + offset += 2 + idListSize; // Skip the IDList section + } + + if ( header->linkFlags & 0x02 ) { + uint32_t linkInfoSize = *reinterpret_cast( &data[offset] ); + + if ( linkInfoSize > 0 ) { + uint32_t localBasePathOffset = *reinterpret_cast( &data[offset + 16] ); + + if ( localBasePathOffset != 0 && ( offset + localBasePathOffset ) < data.size() ) { + std::string targetPath( + reinterpret_cast( &data[offset + localBasePathOffset] ) ); + return targetPath; + } + } + } + + return ""; +} + }} // namespace EE::System diff --git a/src/tools/ecode/ecode.cpp b/src/tools/ecode/ecode.cpp index 43a9a6671..f3b08ce68 100644 --- a/src/tools/ecode/ecode.cpp +++ b/src/tools/ecode/ecode.cpp @@ -2045,7 +2045,7 @@ void App::loadImageFromPath( const std::string& path ) { } void App::loadFileFromPath( - const std::string& path, bool inNewTab, UICodeEditor* codeEditor, + std::string path, bool inNewTab, UICodeEditor* codeEditor, std::function onLoaded ) { if ( Image::isImageExtension( path ) && Image::isImage( path ) && FileSystem::fileExtension( path ) != "svg" ) { @@ -3307,6 +3307,15 @@ void App::init( const LogLevel& logLevel, std::string file, const Float& pidelDe switch ( event->Type ) { case InputEvent::FileDropped: { std::string file( event->file.file ); + if ( FileSystem::fileExtension( file ) == "lnk" ) { + auto target = Sys::getShortcutTarget( file ); + if ( !target.empty() ) { + if ( FileSystem::fileExists( target ) ) + file = target; + else + return; + } + } mPathsToLoad.emplace_back( std::move( file ) ); break; } diff --git a/src/tools/ecode/ecode.hpp b/src/tools/ecode/ecode.hpp index 19e16bf19..650ff493a 100644 --- a/src/tools/ecode/ecode.hpp +++ b/src/tools/ecode/ecode.hpp @@ -85,7 +85,7 @@ class App : public UICodeEditorSplitter::Client { std::shared_ptr getThreadPool() const; - void loadFileFromPath( const std::string& path, bool inNewTab = true, + void loadFileFromPath( std::string path, bool inNewTab = true, UICodeEditor* codeEditor = nullptr, std::function onLoaded = std::function() );