Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / sdk / tools / mkshelllink / mkshelllink.c
1 /* COPYRIGHT: See COPYING in the top level directory
2 * PROJECT: ReactOS Shell Link maker
3 * FILE: tools/mkshelllink/mkshelllink.c
4 * PURPOSE: Shell Link maker
5 * PROGRAMMER: Rafal Harabien
6 */
7
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <ctype.h>
12
13 #ifndef _MSC_VER
14 #include <stdint.h>
15 #else
16 typedef unsigned __int8 uint8_t;
17 typedef unsigned __int16 uint16_t;
18 typedef unsigned __int32 uint32_t;
19 #endif
20
21 #define SW_SHOWNORMAL 1
22 #define SW_SHOWMINNOACTIVE 7
23
24 typedef struct _GUID
25 {
26 uint32_t Data1;
27 uint16_t Data2;
28 uint16_t Data3;
29 uint8_t Data4[8];
30 } GUID;
31
32 typedef struct _FILETIME {
33 uint32_t dwLowDateTime;
34 uint32_t dwHighDateTime;
35 } FILETIME, *PFILETIME;
36
37 #define DEFINE_GUID2(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) const GUID name = { l,w1,w2,{ b1,b2,b3,b4,b5,b6,b7,b8 } }
38 DEFINE_GUID2(CLSID_ShellLink,0x00021401L,0,0,0xC0,0,0,0,0,0,0,0x46);
39 DEFINE_GUID2(CLSID_MyComputer,0x20D04FE0,0x3AEA,0x1069,0xA2,0xD8,0x08,0x00,0x2B,0x30,0x30,0x9D);
40
41 #define LINK_ID_LIST 0x01
42 #define LINK_FILE 0x02
43 #define LINK_DESCRIPTION 0x04
44 #define LINK_RELATIVE_PATH 0x08
45 #define LINK_WORKING_DIR 0x10
46 #define LINK_CMD_LINE_ARGS 0x20
47 #define LINK_ICON 0x40
48 #define LINK_UNICODE 0x80
49
50 #define LOCATOR_LOCAL 0x1
51 #define LOCATOR_NETWORK 0x2
52
53 #pragma pack(push, 1)
54
55 /* Specification: http://ithreats.files.wordpress.com/2009/05/lnk_the_windows_shortcut_file_format.pdf */
56
57 typedef struct _LNK_HEADER
58 {
59 uint32_t Signature;
60 GUID Guid;
61 uint32_t Flags;
62 uint32_t Attributes;
63 FILETIME CreationTime;
64 FILETIME ModificationTime;
65 FILETIME LastAccessTime;
66 uint32_t FileSize;
67 uint32_t IconNr;
68 uint32_t Show;
69 uint32_t Hotkey;
70 uint32_t Unknown;
71 uint32_t Unknown2;
72 } LNK_HEADER;
73
74 typedef struct _LNK_LOCATOR_INFO
75 {
76 uint32_t Size;
77 uint32_t DataOffset;
78 uint32_t Flags;
79 uint32_t LocalVolumeInfoOffset;
80 uint32_t LocalBasePathnameOffset;
81 uint32_t NetworkVolumeInfoOffset;
82 uint32_t RemainingPathnameOffset;
83 char Data[0];
84 } LNK_LOCATOR_INFO;
85
86 typedef struct _LNK_LOCAL_VOLUME_INFO
87 {
88 uint32_t Size;
89 uint32_t VolumeType; /* See GetDriveType */
90 uint32_t SerialNumber;
91 uint32_t VolumeNameOffset;
92 char VolumeLabel[0];
93 } LNK_LOCAL_VOLUME_INFO;
94
95 #define PT_GUID 0x1F
96 #define PT_DRIVE1 0x2F
97 #define PT_FOLDER 0x31
98 #define PT_VALUE 0x32
99
100 typedef struct _ID_LIST_FILE
101 {
102 uint16_t Size;
103 uint8_t Type;
104 uint8_t dummy;
105 uint32_t dwFileSize;
106 uint16_t uFileDate;
107 uint16_t uFileTime;
108 uint16_t uFileAttribs;
109 char szName[0];
110 } ID_LIST_FILE;
111
112 typedef struct _ID_LIST_GUID
113 {
114 uint16_t Size;
115 uint8_t Type;
116 uint8_t dummy;
117 GUID guid;
118 } ID_LIST_GUID;
119
120 typedef struct _ID_LIST_DRIVE
121 {
122 uint16_t Size;
123 uint8_t Type;
124 char szDriveName[20];
125 uint16_t unknown;
126 } ID_LIST_DRIVE;
127
128 #pragma pack(pop)
129
130 int main(int argc, const char *argv[])
131 {
132 unsigned i;
133 const char *pszOutputPath = "shortcut.lnk";
134 const char *pszTarget = NULL;
135 const char *pszDescription = "Description";
136 const char *pszWorkingDir = NULL;
137 const char *pszCmdLineArgs = NULL;
138 const char *pszIcon = NULL;
139 int IconNr = 0;
140 GUID Guid = CLSID_MyComputer;
141 int bHelp = 0, bMinimized = 0;
142 FILE *pFile;
143 LNK_HEADER Header;
144 uint16_t uhTmp;
145 uint32_t dwTmp;
146
147 for (i = 1; i < argc; ++i)
148 {
149 if (argv[i][0] != '-' && argv[i][0] != '/')
150 pszTarget = argv[i];
151 else if (!strcmp(argv[i] + 1, "h"))
152 bHelp = 1;
153 else if (!strcmp(argv[i] + 1, "o") && i + 1 < argc)
154 pszOutputPath = argv[++i];
155 else if (!strcmp(argv[i] + 1, "d") && i + 1 < argc)
156 pszDescription = argv[++i];
157 else if (!strcmp(argv[i] + 1, "w") && i + 1 < argc)
158 pszWorkingDir = argv[++i];
159 else if (!strcmp(argv[i] + 1, "c") && i + 1 < argc)
160 pszCmdLineArgs = argv[++i];
161 else if (!strcmp(argv[i] + 1, "i") && i + 1 < argc)
162 {
163 pszIcon = argv[++i];
164 if (i + 1 < argc && isdigit(argv[i + 1][0]))
165 IconNr = atoi(argv[++i]);
166 }
167 else if (!strcmp(argv[i] + 1, "m"))
168 bMinimized = 1;
169 else if (!strcmp(argv[i] + 1, "g") && i + 1 < argc)
170 {
171 unsigned Data4Tmp[8], j;
172
173 sscanf(argv[++i], "{%8x-%4hx-%4hx-%2x%2x-%2x%2x%2x%2x%2x%2x}",
174 &Guid.Data1, &Guid.Data2, &Guid.Data3,
175 &Data4Tmp[0], &Data4Tmp[1], &Data4Tmp[2], &Data4Tmp[3],
176 &Data4Tmp[4], &Data4Tmp[5], &Data4Tmp[6], &Data4Tmp[7]);
177 for (j = 0; j < 8; ++j)
178 Guid.Data4[j] = (uint8_t)Data4Tmp[j];
179 }
180 else
181 printf("Invalid option: %s\n", argv[i]);
182 }
183
184 if (!pszTarget || bHelp)
185 {
186 printf("Usage: %s [-o path][-d descr][-w path][-c cmd_line_args][-i icon_path [nr]][-h][-g guid] target\n"
187 "-o path\tSets output path\n"
188 "-d descr\tSets shortcut description\n"
189 "-w path\tSets working directory for executable\n"
190 "-c cmd_line_args\tSets command line arguments passed to program\n"
191 "-i icon_path [nr]\tSets icon file and optionally icon index\n"
192 "-m\tStart minimized\n"
193 "-g guid\tSets GUID to which target path is relative. Default value is MyComputer GUID.\n"
194 "target\tAbsolute or relative to guid specified with -g option path\n", argv[0]);
195 return 0;
196 }
197
198 pFile = fopen(pszOutputPath, "wb");
199 if (!pFile)
200 {
201 printf("Failed to open %s\n", pszOutputPath);
202 return -1;
203 }
204
205 // Header
206 memset(&Header, 0, sizeof(Header));
207 Header.Signature = (uint32_t)'L';
208 Header.Guid = CLSID_ShellLink;
209 Header.Flags = LINK_ID_LIST;
210 if (pszDescription)
211 Header.Flags |= LINK_DESCRIPTION;
212 if (pszWorkingDir)
213 Header.Flags |= LINK_WORKING_DIR;
214 if (pszCmdLineArgs)
215 Header.Flags |= LINK_CMD_LINE_ARGS;
216 if (pszIcon)
217 Header.Flags |= LINK_ICON;
218 Header.IconNr = IconNr;
219 Header.Show = bMinimized ? SW_SHOWMINNOACTIVE : SW_SHOWNORMAL;
220 fwrite(&Header, sizeof(Header), 1, pFile);
221
222 if (Header.Flags & LINK_ID_LIST)
223 {
224 ID_LIST_FILE IdListFile;
225 ID_LIST_GUID IdListGuid;
226 ID_LIST_DRIVE IdListDrive;
227 unsigned cbListSize = sizeof(IdListGuid) + sizeof(uint16_t), cchName;
228 const char *pszName = pszTarget;
229
230 // ID list
231 // It seems explorer does not accept links without id list. List is relative to desktop.
232
233 pszName = pszTarget;
234
235 if (pszName[0] && pszName[1] == ':')
236 {
237 cbListSize += sizeof(IdListDrive);
238 pszName += 2;
239 while (*pszName == '\\' || *pszName == '/')
240 ++pszName;
241 }
242
243 while (*pszName)
244 {
245 cchName = 0;
246 while (pszName[cchName] && pszName[cchName] != '\\' && pszName[cchName] != '/')
247 ++cchName;
248
249 if (cchName != 1 || pszName[0] != '.')
250 cbListSize += sizeof(IdListFile) + 2 * (cchName + 1);
251
252 pszName += cchName;
253 while (*pszName == '\\' || *pszName == '/')
254 ++pszName;
255 }
256
257 uhTmp = cbListSize;
258 fwrite(&uhTmp, sizeof(uhTmp), 1, pFile); // size
259
260 IdListGuid.Size = sizeof(IdListGuid);
261 IdListGuid.Type = PT_GUID;
262 IdListGuid.dummy = 0x50;
263 IdListGuid.guid = Guid;
264 fwrite(&IdListGuid, sizeof(IdListGuid), 1, pFile);
265
266 pszName = pszTarget;
267
268 if (isalpha(pszName[0]) && pszName[1] == ':')
269 {
270 memset(&IdListDrive, 0, sizeof(IdListDrive));
271 IdListDrive.Size = sizeof(IdListDrive);
272 IdListDrive.Type = PT_DRIVE1;
273 sprintf(IdListDrive.szDriveName, "%c:\\", pszName[0]);
274 fwrite(&IdListDrive, sizeof(IdListDrive), 1, pFile);
275 pszName += 2;
276 while(*pszName == '\\' || *pszName == '/')
277 ++pszName;
278 }
279
280 while (*pszName)
281 {
282 cchName = 0;
283 while (pszName[cchName] && pszName[cchName] != '\\' && pszName[cchName] != '/')
284 ++cchName;
285
286 if (cchName != 1 || pszName[0] != '.')
287 {
288 memset(&IdListFile, 0, sizeof(IdListFile));
289 IdListFile.Size = sizeof(IdListFile) + 2 * (cchName + 1);
290 if (!pszName[cchName])
291 IdListFile.Type = PT_VALUE; // File
292 else
293 IdListFile.Type = PT_FOLDER;
294 fwrite(&IdListFile, sizeof(IdListFile), 1, pFile);
295 fwrite(pszName, cchName, 1, pFile);
296 fputc(0, pFile);
297 fwrite(pszName, cchName, 1, pFile);
298 fputc(0, pFile);
299 }
300
301 pszName += cchName;
302 while (*pszName == '\\' || *pszName == '/')
303 ++pszName;
304 }
305
306 uhTmp = 0; // list end
307 fwrite(&uhTmp, sizeof(uhTmp), 1, pFile);
308 }
309
310 if (Header.Flags & LINK_DESCRIPTION)
311 {
312 // Dscription
313 uhTmp = strlen(pszDescription);
314 fwrite(&uhTmp, sizeof(uhTmp), 1, pFile);
315 fputs(pszDescription, pFile);
316 }
317
318 if (Header.Flags & LINK_RELATIVE_PATH)
319 {
320 // Relative Path
321 uhTmp = strlen(pszTarget);
322 fwrite(&uhTmp, sizeof(uhTmp), 1, pFile);
323 fputs(pszTarget, pFile);
324 }
325
326 if (Header.Flags & LINK_WORKING_DIR)
327 {
328 // Working Dir
329 uhTmp = strlen(pszWorkingDir);
330 fwrite(&uhTmp, sizeof(uhTmp), 1, pFile);
331 fputs(pszWorkingDir, pFile);
332 }
333
334 if (Header.Flags & LINK_CMD_LINE_ARGS)
335 {
336 // Command line arguments
337 uhTmp = strlen(pszCmdLineArgs);
338 fwrite(&uhTmp, sizeof(uhTmp), 1, pFile);
339 fputs(pszCmdLineArgs, pFile);
340 }
341
342 if (Header.Flags & LINK_ICON)
343 {
344 // Command line arguments
345 uhTmp = strlen(pszIcon);
346 fwrite(&uhTmp, sizeof(uhTmp), 1, pFile);
347 fputs(pszIcon, pFile);
348 }
349
350 // Extra stuff
351 dwTmp = 0;
352 fwrite(&dwTmp, sizeof(dwTmp), 1, pFile);
353
354 fclose(pFile);
355
356 return 0;
357 }