diff --git a/Contrib/VPatch/GenPat.exe b/Contrib/VPatch/GenPat.exe new file mode 100644 index 00000000..4161e8d5 Binary files /dev/null and b/Contrib/VPatch/GenPat.exe differ diff --git a/Contrib/VPatch/Readme.html b/Contrib/VPatch/Readme.html new file mode 100644 index 00000000..b174935e --- /dev/null +++ b/Contrib/VPatch/Readme.html @@ -0,0 +1,189 @@ + + + + +VPatch 2 + + + + + + + + + +
+ + +
+ + + + + + + + +

VPatch 2

+

Introduction

+

VPatch allows to create a patch file to update previous versions of +your software. The GenPat utitily generates the patch file. The plug-in can use the +patch to update a file. Using a patch, you can reduce the download size of your +updates, because only the differences between the files are included in the patch +file.

+

How to use

+

Generate the patch file

+

Make sure you have the source file (original version) and the target file (version + to update to). For example, DATA.DTA (currently on user system) and DATA_20.DTA + (version 2.0 of this data file). Now call the command line tool GenPat.exe:

+
GENPAT data.dta data_20.dta data.pat
+

Now, the patch will be generated, this will take some time.

+

Using the /B=(BlockSize) parameter of the GenPat utility (put it after +the filenames), you can use a different block size. A smaller block size may result in +a smaller patch, but the generation will take more time (the default blocksize is 64).

+

Update the file in your installer

+

Use the VPatch plug-in to update a file using a patch file:

+
vpatch::vpatchfile "pathfile.pat" "source.file" "new.file"
+

Check example.nsi for an example.

+

Multiple patches in one file

+

GenPat appends a patch to the file you specified. If there is already a patch +for the same orginal file in the patch file, the patch will be replaced. For example, if you want +to be able to upgrade version 1 and 2 to version 3, you can put a 1 > 3 and 2 > 3 patch in one +file.

+

You can also put patches for different files in one patch file, for example, a +patch from file A version 1 to file A version 2 and a patch from file B version 1 to +file B version 2. Just call the plug-in multiple times with the same patch file. It +will automatically select the right patch (based on the file CRC).

+

Version history

+
    +
  • 2.0 beta 2 +
      +
    • All new algorithm used in the patch generator: much faster (up to + 90%) while using smaller block sizes (higher compression)
    • +
    • Created a NSIS 2 plugin
    • +
    • Works with small files
    • +
    • Replaces existing patch in file if original file CRC is identical
    • +
  • +
+

Credits

+

Written by Koen van de Sande
+C plug-in by Edgewize
+New documentation and example by Joost Verburg

+

License

+
Copyright (C) 2001-2002 Koen van de Sande
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute
+it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; 
+   you must not claim that you wrote the original software.
+   If you use this software in a product, an acknowledgment in the
+   product documentation would be appreciated but is not required.
+2. Altered versions must be plainly marked as such,
+   and must not be misrepresented as being the original software.
+3. This notice may not be removed or altered from any distribution.
+
+Please note that this version contains the plug-in source only,
+not the source of the patch generator.
+
+
+
+
+ + + \ No newline at end of file diff --git a/Contrib/VPatch/example.nsi b/Contrib/VPatch/example.nsi new file mode 100644 index 00000000..0eeeb24a --- /dev/null +++ b/Contrib/VPatch/example.nsi @@ -0,0 +1,43 @@ +;VPatch example +;Written by Joost Verburg + +;-------------------------------- + +; The name of the installer +Name "VPatch Test" + +; The file to write +OutFile "vpatchtest.exe" + +; The default installation directory +InstallDir "$PROGRAMFILES\VPatch Test" + +; The text to prompt the user to enter a directory +DirText "Choose a folder in which to install the VPatch Test!" + +; Show details +ShowInstDetails show + +;-------------------------------- + +Section "" + + ; Set output path to the installation directory + SetOutPath $INSTDIR + + ; Extract the old file + File oldfile.txt + + ; Extract the patch to the plug-ins folder (temporary) + InitPluginsDir + File /oname=$PLUGINSDIR\patch.pat patch.pat + + ; Update the old file to the new file using the patch + DetailPrint "Updating oldfile.txt using patch..." + vpatch::vpatchfile "$PLUGINSDIR\patch.pat" "$INSTDIR\oldfile.txt" "$INSTDIR\newfile.txt" + + ; Show result + Pop $R0 + DetailPrint "Result: $R0" + +SectionEnd \ No newline at end of file diff --git a/Contrib/VPatch/newfile.txt b/Contrib/VPatch/newfile.txt new file mode 100644 index 00000000..2369a707 --- /dev/null +++ b/Contrib/VPatch/newfile.txt @@ -0,0 +1,6 @@ +*** THIS IS A TEST FILE FOR THE VPATCH EXAMPLE *** +*** COMPILE EXAMPLE.NSI TO TEST *** + +newfile - vpatch + +67890 - GHIJKL \ No newline at end of file diff --git a/Contrib/VPatch/oldfile.txt b/Contrib/VPatch/oldfile.txt new file mode 100644 index 00000000..a378cf30 --- /dev/null +++ b/Contrib/VPatch/oldfile.txt @@ -0,0 +1,6 @@ +*** THIS IS A TEST FILE FOR THE VPATCH EXAMPLE *** +*** COMPILE EXAMPLE.NSI TO TEST *** + +oldfile - vpatch + +12345 - ABCDEF \ No newline at end of file diff --git a/Contrib/VPatch/patch.pat b/Contrib/VPatch/patch.pat new file mode 100644 index 00000000..f66782cf Binary files /dev/null and b/Contrib/VPatch/patch.pat differ diff --git a/Contrib/VPatch/vpatchdll.c b/Contrib/VPatch/vpatchdll.c new file mode 100644 index 00000000..8140d492 --- /dev/null +++ b/Contrib/VPatch/vpatchdll.c @@ -0,0 +1,273 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include "..\..\ExDLL\exdll.h" + +int DoPatch(HANDLE hPatch, HANDLE hSource, HANDLE hDest); +void strcopy(char *tgt, const char *src); +char* chop_arg(char **args); + +#define PATCH_SUCCESS 0 +#define PATCH_ERROR 1 +#define PATCH_CORRUPT 2 +#define PATCH_NOMATCH 3 +#define PATCH_UPTODATE 4 +#define FILE_ERR_PATCH 5 +#define FILE_ERR_SOURCE 6 +#define FILE_ERR_DEST 7 + +HINSTANCE g_hInstance; + +HWND g_hwndParent; + +void __declspec(dllexport) vpatchfile(HWND hwndParent, int string_size, + char *variables, stack_t **stacktop) +{ + g_hwndParent=hwndParent; + + EXDLL_INIT(); + + + // note if you want parameters from the stack, pop them off in order. + // i.e. if you are called via exdll::myFunction file.dat poop.dat + // calling popstring() the first time would give you file.dat, + // and the second time would give you poop.dat. + // you should empty the stack of your parameters, and ONLY your + // parameters. + + // do your stuff here + { + static char source[1024]; + static char dest[1024]; + static char exename[1024]; + HANDLE hPatch, hSource, hDest; + int result; + + popstring(exename); + popstring(source); + popstring(dest); + + hPatch = CreateFile(exename, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (hPatch == INVALID_HANDLE_VALUE) { + pushstring("Unable to open patch file"); + return; + } + + hSource = CreateFile(source, GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + if (hSource == INVALID_HANDLE_VALUE) { + pushstring("Unable to open source file"); + return; + } + + hDest = CreateFile(dest, GENERIC_READ | GENERIC_WRITE, 0, NULL, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); + if (hDest == INVALID_HANDLE_VALUE) { + pushstring("Unable to open output file"); + return; + } + + result = DoPatch(hPatch, hSource, hDest); + + CloseHandle(hDest); + CloseHandle(hSource); + CloseHandle(hPatch); + + if ((result != PATCH_SUCCESS)) { + if (result == PATCH_ERROR) + pushstring("An error occured while patching"); + else if (result == PATCH_CORRUPT) + pushstring("Patch data is invalid or corrupt"); + else if (result == PATCH_NOMATCH) + pushstring("No suitable patches were found"); + else if (result == PATCH_UPTODATE) + pushstring("OK, new version already installed"); + DeleteFile(dest); + } else { + pushstring("OK"); + } + + return; + } +} + + + +BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) +{ + g_hInstance=hInst; + return TRUE; +} + + + +UINT CRCTable[256]; +BOOL bInitCRC = FALSE; + +_inline void InitCRC() { + int i, j; unsigned long c; + for (c = i = 0; i < 256; c = ++i) { + for (j = 0; j < 8; j++) { + if (c & 1) c = (c>>1) ^ 0xEDB88320; + else c >>= 1; + } + CRCTable[i] = c; + } + bInitCRC = TRUE; +} + +#define CRCBLOCKSIZE 4096 + +BOOL FileCRC(HANDLE hFile, DWORD *crc) { + static BYTE crcblock[CRCBLOCKSIZE]; + DWORD read; + BYTE *p; + + UINT c = 0xFFFFFFFF; + if (bInitCRC == FALSE) + InitCRC(); + + SetFilePointer(hFile, 0, NULL, FILE_BEGIN); + do { + if (ReadFile(hFile, crcblock, CRCBLOCKSIZE, &read, NULL) == FALSE) + return FALSE; + for (p = crcblock; p < crcblock + read; p++) + c = CRCTable[(c & 0xFF) ^ *p] ^ (c >> 8); + } while (read); + + *crc = (c ^ 0xFFFFFFFF); + + return TRUE; +} + +#define BLOCKSIZE 16384 + +int DoPatch(HANDLE hPatch, HANDLE hSource, HANDLE hDest) { + + static char block[BLOCKSIZE]; + + unsigned long temp = 0; + unsigned long read; + unsigned long source_crc = 0; + unsigned long patch_dest_crc = 0; + long patches = 0; + int already_uptodate = 0; + // special 'addition' for the dll: since the patch file is now + // in a seperate file, the VPAT header might be right at the start + // of the file, and a pointer at the end of the file is probably missing + // (because all patch generator versions don't append it, the linker/gui + // does this). + SetFilePointer(hPatch, 0, NULL, FILE_BEGIN); + ReadFile(hPatch, &temp, 4, &read, NULL); + // it's not at the start of file -> there must be a pointer at the end of + // file then + if (temp != 0x54415056) { + SetFilePointer(hPatch, -4, NULL, FILE_END); + ReadFile(hPatch, &temp, 4, &read, NULL); + + SetFilePointer(hPatch, temp, NULL, FILE_BEGIN); + ReadFile(hPatch, &temp, 4, &read, NULL); + if (temp != 0x54415056) + return PATCH_CORRUPT; + } + + if (!FileCRC(hSource, &source_crc)) + return PATCH_ERROR; + + + ReadFile(hPatch, &patches, 4, &read, NULL); + + while (patches--) { + long patch_blocks = 0, patch_size = 0; + unsigned long patch_source_crc = 0; + + ReadFile(hPatch, &patch_blocks, 4, &read, NULL); + ReadFile(hPatch, &patch_source_crc, 4, &read, NULL); + ReadFile(hPatch, &patch_dest_crc, 4, &read, NULL); + ReadFile(hPatch, &patch_size, 4, &read, NULL); + + //added by Koen - check to see if it's already up-to-date for some patch (so + //we can tell NSIS this isn't an error, but we already have the latest version) + if (source_crc == patch_dest_crc) { + already_uptodate = 1; + } + + if (source_crc == patch_source_crc) { + while (patch_blocks--) { + unsigned char blocktype = 0; + unsigned long blocksize = 0; + ReadFile(hPatch, &blocktype, 1, &read, NULL); + + switch (blocktype) { + case 1: + case 2: + case 3: + if (blocktype == 1) + { unsigned char x; blocksize = ReadFile(hPatch,&x,1,&read,NULL)? x:0; } + else if (blocktype == 2) + { unsigned short x; blocksize = ReadFile(hPatch,&x,2,&read,NULL)? x:0; } + else + { unsigned long x; blocksize = ReadFile(hPatch,&x,4,&read,NULL)? x:0; } + + if (!blocksize || !ReadFile(hPatch, &temp, 4, &read, NULL) || read != 4) + return PATCH_CORRUPT; + + SetFilePointer(hSource, temp, 0, FILE_BEGIN); + + do { + ReadFile(hSource, block, min(BLOCKSIZE, blocksize), &read, NULL); + WriteFile(hDest, block, read, &temp, NULL); + if (temp != min(BLOCKSIZE, blocksize)) + return PATCH_ERROR; + blocksize -= temp; + } while (temp); + + break; + + case 5: + case 6: + case 7: + if (blocktype == 5) + { unsigned char x; blocksize = ReadFile(hPatch,&x,1,&read,NULL)? x:0; } + else if (blocktype == 6) + { unsigned short x; blocksize = ReadFile(hPatch,&x,2,&read,NULL)? x:0; } + else + { unsigned long x; blocksize = ReadFile(hPatch,&x,4,&read,NULL)? x:0; } + + if (!blocksize) + return PATCH_CORRUPT; + + do { + ReadFile(hPatch, block, min(BLOCKSIZE, blocksize), &read, NULL); + WriteFile(hDest, block, read, &temp, NULL); + if (temp != min(BLOCKSIZE, blocksize)) + return PATCH_ERROR; + blocksize -= temp; + } while (temp); + + break; + + default: + return PATCH_CORRUPT; + } + } + { + unsigned long dest_crc = 0; + FileCRC(hDest, &dest_crc); + if (dest_crc != patch_dest_crc) + return PATCH_ERROR; + + return PATCH_SUCCESS; + } + } else { + SetFilePointer(hPatch, patch_size, NULL, FILE_CURRENT); + } + } + + //added by Koen - if already up to date, it doesn't matter that we didn't match + if(already_uptodate) { + return PATCH_UPTODATE; + } else { + return PATCH_NOMATCH; + } +} diff --git a/Contrib/VPatch/vpatchdll.dsp b/Contrib/VPatch/vpatchdll.dsp new file mode 100644 index 00000000..c89e8f2f --- /dev/null +++ b/Contrib/VPatch/vpatchdll.dsp @@ -0,0 +1,112 @@ +# Microsoft Developer Studio Project File - Name="vpatchdll" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=vpatchdll - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "vpatchdll.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "vpatchdll.mak" CFG="vpatchdll - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "vpatchdll - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "vpatchdll - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "vpatchdll - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "vpatchdll_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O1 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "vpatchdll_EXPORTS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /entry:"_DllMainCRTStartup" /dll /machine:I386 /nodefaultlib /out:"..\..\..\Plugins\VPatch.dll" /opt:nowin98 +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "vpatchdll - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "vpatchdll_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "vpatchdll_EXPORTS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"Debug/VPatch.dll" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "vpatchdll - Win32 Release" +# Name "vpatchdll - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\vpatchdll.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\vpatchdll.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/Contrib/VPatch/vpatchdll.dsw b/Contrib/VPatch/vpatchdll.dsw new file mode 100644 index 00000000..0c805517 --- /dev/null +++ b/Contrib/VPatch/vpatchdll.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "vpatchdll"=.\vpatchdll.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/Examples/makensis.nsi b/Examples/makensis.nsi index d04e97a8..c443b9a9 100644 --- a/Examples/makensis.nsi +++ b/Examples/makensis.nsi @@ -398,6 +398,18 @@ Section "Dialer" SecContribDialer File ..\Contrib\Dialer\Dialer.txt SectionEnd +Section "VPatch" SecContribVPatch + SectionIn 1 2 + SetOutPath $INSTDIR\Plugins + File ..\Plugins\VPatch.dll + SetOutPath $INSTDIR\Contrib\VPatch + File ..\Contrib\VPatch\Readme.html + File ..\Contrib\VPatch\example.nsi + File ..\Contrib\VPatch\oldfile.txt + File ..\Contrib\VPatch\newfile.txt + File ..\Contrib\VPatch\patch.pat +SectionEnd + SubSectionEnd Section "Zip2Exe" SecContribZ2E @@ -585,6 +597,14 @@ Section "Dialer Source" SecContribDialerS File ..\Contrib\Dialer\dialer.dsw SectionEnd +Section "VPatch Source" SecContribVPatchS + SectionIn 1 + SetOutPath $INSTDIR\Contrib\VPatch + File ..\Contrib\VPatch\vpatchdll.c + File ..\Contrib\VPatch\vpatchdll.dsp + File ..\Contrib\VPatch\vpatchdll.dsw +SectionEnd + SubSectionEnd ; plugins Section "Zip2Exe Source" SecContribZ2ES @@ -677,8 +697,8 @@ Section -post Call AddWorkspaceToStartMenu ; InstallOptions - Push "InstallOptions\install options.html" - Push "InstallOptions readme" + Push "InstallOptions\Readme.html" + Push "InstallOptions Readme" Call AddContribToStartMenu Push "InstallOptions\io.dsw" @@ -694,7 +714,7 @@ Section -post ; Modern UI Push "Modern UI\Readme.html" - Push "Modern UI readme" + Push "Modern UI Readme" Call AddContribToStartMenu ; Splash @@ -761,6 +781,11 @@ Section -post Push System\Source\System.sln Push "Source\System project workspace" Call AddContribToStartMenu + + ; VPatch + Push "VPatch\Readme.html" + Push "VPatch Readme" + Call AddContribToStartMenu nofunshit: !endif @@ -823,6 +848,8 @@ SectionEnd !insertmacro MUI_DESCRIPTION_TEXT ${SecContribNSISDL} "Plugin that lets you create a web based installer" !insertmacro MUI_DESCRIPTION_TEXT ${SecContribNSISDLS} "Source code to plugin that lets you create a web based installer" !insertmacro MUI_DESCRIPTION_TEXT ${SecContribUiHolderS} "Source code to the UI Holder where you can put your recources in to preview your user interface" + !insertmacro MUI_DESCRIPTION_TEXT ${SecContribVPatch} "Plugin that lets you create patches to upgrade older files" + !insertmacro MUI_DESCRIPTION_TEXT ${SecContribVPatchS} "Source code to plugin that lets you create patches to upgrade older files" !insertmacro MUI_DESCRIPTION_TEXT ${SecSrc} "Source code to NSIS and all related files" !insertmacro MUI_DESCRIPTION_TEXT ${SecSrcNSIS} "Source code to NSIS" !insertmacro MUI_DESCRIPTION_TEXT ${SecSrcContrib} "Source code to user contributed utilities" @@ -892,7 +919,7 @@ Function AddReadmeToStartMenu IfFileExists $INSTDIR\Contrib\$0\Readme.txt 0 done Push $0\Readme.txt create: - Push "$0 readme" + Push "$0 Readme" Call AddContribToStartMenu done: FunctionEnd diff --git a/Plugins/VPatch.dll b/Plugins/VPatch.dll new file mode 100644 index 00000000..1063501e Binary files /dev/null and b/Plugins/VPatch.dll differ