Subversion Repositories filter_foundry

Rev

Rev 171 | Rev 194 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. /*
  2.     This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
  3.     Copyright (C) 2003-2019 Toby Thain, toby@telegraphics.com.au
  4.  
  5.     This program is free software; you can redistribute it and/or modify
  6.     it under the terms of the GNU General Public License as published by
  7.     the Free Software Foundation; either version 2 of the License, or
  8.     (at your option) any later version.
  9.  
  10.     This program is distributed in the hope that it will be useful,
  11.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.     GNU General Public License for more details.
  14.  
  15.     You should have received a copy of the GNU General Public License
  16.     along with this program; if not, write to the Free Software
  17.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  18. */
  19.  
  20. #include "ff.h"
  21.  
  22. #include <windows.h>
  23. #include <stdbool.h>
  24. #include <stdio.h>
  25.  
  26. // This unit modifies the VersionInfo resource structure of a PE file.
  27. // Entries can be modified or deleted (not added).
  28. // Reference code by "RbMm" at StackOverflow:
  29. // https://stackoverflow.com/questions/53916682/programmatically-change-versioninfo-of-a-foreign-dll
  30. // Translated from C++ to C by Daniel Marschall and extended/modified to fit Filter Foundry's requirements.
  31.  
  32. typedef struct RsrcHeader {
  33.         WORD  wLength;
  34.         WORD  wValueLength;
  35.         WORD  wType;
  36.         WCHAR szKey[];
  37. } RsrcHeader;
  38.  
  39. typedef struct RsrcNode {
  40.         struct RsrcNode *first;
  41.         struct RsrcNode *next;
  42.         PCWSTR name;
  43.         const void *pvValue;
  44.         ULONG cbValue;
  45.         WORD wValueLength;
  46.         WORD wType;
  47. } RsrcNode;
  48.  
  49. typedef struct EnumVerData {
  50.         HANDLE hUpdate;
  51.         BOOL fDiscard;
  52.         PCWSTR changes;
  53. } EnumVerData;
  54.  
  55. RsrcNode* NewNode() {
  56.         RsrcNode* node = (RsrcNode*)calloc(1,sizeof(RsrcNode));
  57.         return node;
  58. }
  59.  
  60. bool NodeIsStringValue(RsrcNode* node) {
  61.         return node->wType;
  62. }
  63.  
  64. bool NodeParseResourse(RsrcNode* node, PVOID buf, ULONG size, ULONG* pLength) {
  65.         WORD wType;
  66.         ULONG wValueLength;
  67.         ULONG wLength;
  68.         ULONG cbValue;
  69.  
  70.         union {
  71.                 PVOID       pv;
  72.                 RsrcHeader* ph;
  73.                 ULONG_PTR   up;
  74.                 PCWSTR      sz;
  75.         } x;
  76.  
  77.         x.pv = buf;
  78.  
  79.         if (size < sizeof(RsrcHeader) || (x.up & 3))
  80.         {
  81.                 return false;
  82.         }
  83.  
  84.         wType = x.ph->wType;
  85.         wValueLength = x.ph->wValueLength, wLength = x.ph->wLength;
  86.         cbValue = 0;
  87.  
  88.         switch (wType)
  89.         {
  90.                 case 1:
  91.                         cbValue = wValueLength * sizeof(WCHAR);
  92.                         break;
  93.                 case 0:
  94.                         cbValue = wValueLength;
  95.                         break;
  96.                 default:
  97.                         return false;
  98.         }
  99.  
  100.         *pLength = wLength;
  101.  
  102.         if (wLength > size || wLength < sizeof(RsrcHeader) || cbValue >= (wLength -= sizeof(RsrcHeader)))
  103.         {
  104.                 return false;
  105.         }
  106.  
  107.         wLength -= cbValue;
  108.  
  109.         x.sz = x.ph->szKey, node->name = x.sz;
  110.  
  111.         do
  112.         {
  113.                 if (wLength < sizeof(WCHAR))
  114.                 {
  115.                         return false;
  116.                 }
  117.  
  118.                 wLength -= sizeof(WCHAR);
  119.         } while (*x.sz++);
  120.  
  121.         if (x.up & 3)
  122.         {
  123.                 if (wLength < 2)
  124.                 {
  125.                         return false;
  126.                 }
  127.                 x.up += 2, wLength -= 2;
  128.         }
  129.  
  130.         node->wType = wType, node->wValueLength = (WORD)wValueLength, node->cbValue = cbValue, node->pvValue = x.pv;
  131.  
  132.         if (wValueLength && wType)
  133.         {
  134.                 if (x.sz[wValueLength - 1])
  135.                 {
  136.                         return false;
  137.                 }
  138.         }
  139.  
  140.         if (wLength)
  141.         {
  142.                 x.up += wValueLength;
  143.  
  144.                 do
  145.                 {
  146.                         RsrcNode* lnode;
  147.  
  148.                         if (x.up & 3)
  149.                         {
  150.                                 if (wLength < 2)
  151.                                 {
  152.                                         return false;
  153.                                 }
  154.  
  155.                                 x.up += 2;
  156.  
  157.                                 if (!(wLength -= 2))
  158.                                 {
  159.                                         break;
  160.                                 }
  161.                         }
  162.  
  163.                         if (lnode = NewNode())
  164.                         {
  165.                                 lnode->next = node->first, node->first = lnode;
  166.  
  167.                                 if (NodeParseResourse(lnode, x.ph, wLength, &size))
  168.                                 {
  169.                                         continue;
  170.                                 }
  171.                         }
  172.  
  173.                         return false;
  174.  
  175.                 } while (x.up += size, wLength -= size);
  176.         }
  177.  
  178.         return true;
  179. }
  180.  
  181. const void* NodeGetValue(RsrcNode* node, ULONG* cb) {
  182.         *cb = node->cbValue;
  183.         return node->pvValue;
  184. }
  185.  
  186. bool NodeToBeDeleted(RsrcNode* node) {
  187.         return ((NodeIsStringValue(node)) && (!wcscmp((PCWSTR)node->pvValue, L"\b")));
  188. }
  189.  
  190. void NodeSetValue(RsrcNode* node, const void* pv, ULONG cb) {
  191.         node->pvValue = pv, node->cbValue = cb;
  192.         node->wValueLength = (WORD)(node->wType ? cb / sizeof(WCHAR) : cb);
  193. }
  194.  
  195. void FreeNode(RsrcNode* node) {
  196.         RsrcNode* next;
  197.  
  198.         if (next = node->first)
  199.         {
  200.                 do
  201.                 {
  202.                         RsrcNode* cur = next;
  203.                         next = next->next;
  204.                         FreeNode(cur);
  205.                 } while (next);
  206.         }
  207.  
  208.         free(node);
  209. }
  210.  
  211. RsrcNode* NodeFind(RsrcNode* node, const PCWSTR strings[], ULONG n) {
  212.         PCWSTR str;
  213.         RsrcNode* next;
  214.  
  215.         str = *strings++;
  216.  
  217.         if (!str || !wcscmp(str, node->name))
  218.         {
  219.                 if (!--n)
  220.                 {
  221.                         return node;
  222.                 }
  223.  
  224.                 if (next = node->first)
  225.                 {
  226.                         do
  227.                         {
  228.                                 RsrcNode* p;
  229.                                 if (p = NodeFind(next, strings, n))
  230.                                 {
  231.                                         return p;
  232.                                 }
  233.                         } while (next = next->next);
  234.                 }
  235.         }
  236.  
  237.         return NULL;
  238. }
  239.  
  240. ULONG NodeGetSize(RsrcNode* node) {
  241.         ULONG size;
  242.         RsrcNode* next;
  243.  
  244.         size = sizeof(RsrcHeader) + (1 + (ULONG)wcslen(node->name)) * sizeof(WCHAR);
  245.  
  246.         if (node->cbValue)
  247.         {
  248.                 size = ((size + 3) & ~3) + node->cbValue;
  249.         }
  250.  
  251.         if (next = node->first)
  252.         {
  253.                 do
  254.                 {
  255.                         size = ((size + 3) & ~3) + NodeGetSize(next);
  256.                 } while (next = next->next);
  257.         }
  258.  
  259.         return size;
  260. }
  261.  
  262. PVOID NodeStore(RsrcNode* node, PVOID buf, ULONG* pcb) {
  263.         ULONG size;
  264.         ULONG cb;
  265.         RsrcNode* next;
  266.  
  267.         union {
  268.                 RsrcHeader* ph;
  269.                 ULONG_PTR   up;
  270.                 PVOID       pv;
  271.         } x;
  272.  
  273.         x.pv = buf;
  274.  
  275.         x.ph->wType = node->wType;
  276.         x.ph->wValueLength = node->wValueLength;
  277.  
  278.         size = (1 + (ULONG)wcslen(node->name)) * sizeof(WCHAR);
  279.  
  280.         memcpy(x.ph->szKey, node->name, size);
  281.  
  282.         x.up += (size += sizeof(RsrcHeader));
  283.  
  284.         if (node->cbValue)
  285.         {
  286.                 x.up = (x.up + 3) & ~3;
  287.                 memcpy(x.pv, node->pvValue, node->cbValue);
  288.                 x.up += node->cbValue;
  289.                 size = ((size + 3) & ~3) + node->cbValue;
  290.         }
  291.  
  292.         if (next = node->first)
  293.         {
  294.                 do
  295.                 {
  296.                         if (!NodeToBeDeleted(next)) {
  297.                                 x.up = (x.up + 3) & ~3;
  298.                                 x.pv = NodeStore(next, x.pv, &cb);
  299.                                 size = ((size + 3) & ~3) + cb;
  300.                         }
  301.                 } while (next = next->next);
  302.         }
  303.  
  304.         ((RsrcHeader*)buf)->wLength = (WORD)size;
  305.  
  306.         *pcb = size;
  307.  
  308.         return x.pv;
  309. }
  310.  
  311. BOOL UpdateVersionRaw(PVOID pvVersion, ULONG cbVersion, PVOID* pvNewVersion, ULONG* cbNewVersion, PCWSTR changes) {
  312.         BOOL fOk = FALSE;
  313.         BOOL changesMade = FALSE;
  314.         RsrcNode* node;
  315.  
  316.         if (node = NewNode())
  317.         {
  318.                 // Parse VersionInfo (pvVersion) into a hierarchical structure with head "RsrcNode node"
  319.                 if (NodeParseResourse(node, pvVersion, cbVersion, &cbVersion))
  320.                 {
  321.                         // Loop through all elements of "PCWSTR changes" and apply the changes to the hierarchical structure
  322.                         while (1)
  323.                         {
  324.                                 PCWSTR change;
  325.                                 PCWSTR newValue;
  326.                                 PCWSTR str[4];
  327.                                 RsrcNode *p;
  328.  
  329.                                 change = changes;
  330.                                 if (wcslen(changes) == 0) break;
  331.                                 changes += (wcslen(changes)+1);
  332.  
  333.                                 newValue = changes;
  334.                                 changes += (wcslen(changes)+1);
  335.  
  336.                                 str[0] = L"VS_VERSION_INFO";
  337.                                 str[1] = L"StringFileInfo";
  338.                                 str[2] = NULL;
  339.                                 str[3] = change;
  340.  
  341.                                 if (p = NodeFind(node, str, 4))
  342.                                 {
  343.                                         if (NodeIsStringValue(p))
  344.                                         {
  345.                                                 ULONG cb;
  346.                                                 PCWSTR prevValue = (PCWSTR)NodeGetValue(p, &cb);
  347.  
  348.                                                 //printf("Change %S: %S -> %S\n", change, prevValue, newValue);
  349.  
  350.                                                 if (cb != (wcslen(newValue)+1)*sizeof(wchar_t) || (wcscmp(prevValue, newValue)))
  351.                                                 {
  352.                                                         NodeSetValue(p, newValue, (ULONG)((wcslen(newValue)+1)*sizeof(wchar_t)));
  353.                                                         changesMade = TRUE;
  354.                                                 }
  355.                                         }
  356.                                 }
  357.                         }
  358.  
  359.                         // Write back the hierarchical structure into the raw data pvVersion
  360.                         if (changesMade) {
  361.                                 cbVersion = NodeGetSize(node);
  362.  
  363.                                 if (pvVersion = LocalAlloc(0, cbVersion))
  364.                                 {
  365.                                         NodeStore(node, pvVersion, cbNewVersion);
  366.                                         *pvNewVersion = pvVersion;
  367.                                         fOk = TRUE;
  368.                                 }
  369.                         }
  370.                 }
  371.                 FreeNode(node);
  372.         }
  373.  
  374.         return fOk;
  375. }
  376.  
  377. BOOL CALLBACK EnumResLangProc(HMODULE hModule, PCTSTR lpszType, PCTSTR lpszName, WORD wIDLanguage, EnumVerData* Ctx) {
  378.         HRSRC hResInfo;
  379.         HGLOBAL hg;
  380.         ULONG size;
  381.         PVOID pv;
  382.  
  383.         if (hResInfo = FindResourceEx(hModule, lpszType, lpszName, wIDLanguage))
  384.         {
  385.                 if (hg = LoadResource(hModule, hResInfo))
  386.                 {
  387.                         if (size = SizeofResource(hModule, hResInfo))
  388.                         {
  389.                                 if (pv = LockResource(hg))
  390.                                 {
  391.                                         if (UpdateVersionRaw(pv, size, &pv, &size, Ctx->changes))
  392.                                         {
  393.                                                 if (_UpdateResource(Ctx->hUpdate, lpszType, lpszName, wIDLanguage, pv, size))
  394.                                                 {
  395.                                                         Ctx->fDiscard = FALSE;
  396.                                                 }
  397.  
  398.                                                 LocalFree(pv);
  399.                                         }
  400.                                 }
  401.                         }
  402.                 }
  403.         }
  404.  
  405.         return TRUE;
  406. }
  407.  
  408. // Format of argument "PCWSTR changes" is "<name>\0<value>\0<name>\0<value>\0....."
  409. // You can CHANGE values for any given name
  410. // You can DELETE entries by setting the value to "\b" (0x08 backspace character)
  411. // You cannot (yet) ADD entries.
  412. ULONG UpdateVersionInfo(PCTSTR FileName, PCWSTR changes) {
  413.         HMODULE hmod;
  414.         ULONG dwError;
  415.         EnumVerData ctx;
  416.  
  417.         dwError = NOERROR;
  418.  
  419.         ctx.changes = changes;
  420.  
  421.         if (ctx.hUpdate = _BeginUpdateResource(FileName, FALSE))
  422.         {
  423.                 ctx.fDiscard = TRUE;
  424.  
  425.                 if (hmod = LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE))
  426.                 {
  427.                         if (!EnumResourceLanguages(hmod, RT_VERSION,
  428.                                 MAKEINTRESOURCE(VS_VERSION_INFO),
  429.                                 (ENUMRESLANGPROC)EnumResLangProc, (LONG_PTR)&ctx))
  430.                         {
  431.                                 dwError = GetLastError();
  432.                         }
  433.  
  434.                         FreeLibrary(hmod);
  435.                 }
  436.                 else
  437.                 {
  438.                         dwError = GetLastError();
  439.                 }
  440.  
  441.                 if (!dwError && !_EndUpdateResource(ctx.hUpdate, ctx.fDiscard))
  442.                 {
  443.                         dwError = GetLastError();
  444.                 }
  445.         }
  446.         else
  447.         {
  448.                 dwError = GetLastError();
  449.         }
  450.  
  451.         return dwError;
  452. }
  453.  
  454. ULONG UpdateVersionInfoWithHandle(PCTSTR FileName, HANDLE hUpdate, PCWSTR changes) {
  455.         HMODULE hmod;
  456.         ULONG dwError;
  457.         EnumVerData ctx;
  458.  
  459.         dwError = NOERROR;
  460.  
  461.         ctx.changes = changes;
  462.         ctx.hUpdate = hUpdate;
  463.  
  464.         ctx.fDiscard = TRUE;
  465.  
  466.         if (hmod = LoadLibraryEx(FileName, 0, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE))
  467.         {
  468.                 if (!EnumResourceLanguages(hmod, RT_VERSION,
  469.                         MAKEINTRESOURCE(VS_VERSION_INFO),
  470.                         (ENUMRESLANGPROC)EnumResLangProc, (LONG_PTR)&ctx))
  471.                 {
  472.                         dwError = GetLastError();
  473.                 }
  474.  
  475.                 FreeLibrary(hmod);
  476.         }
  477.         else
  478.         {
  479.                 dwError = GetLastError();
  480.         }
  481.  
  482.         return dwError;
  483. }
  484.  
  485. /*
  486. Usage example:
  487.  
  488. int main(int argc, char** argv) {
  489.         static const PCWSTR changes = L"Blabla\0Tennis\0CompanyName\0NewCompany\0LegalCopyright\0\b\0OriginalFilename\0Tristan.bay\0";
  490.  
  491.         UpdateVersionInfoByFilename("E:\\_test\\rand_test\\test.dll", changes);
  492.         return 0;
  493. }
  494. */
  495.