Subversion Repositories filter_foundry

Rev

Rev 292 | Rev 314 | 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-2009 Toby Thain, toby@telegraphics.com.au
  4.     Copyright (C) 2018-2021 Daniel Marschall, ViaThinkSoft
  5.  
  6.     This program is free software; you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License as published by
  8.     the Free Software Foundation; either version 2 of the License, or
  9.     (at your option) any later version.
  10.  
  11.     This program is distributed in the hope that it will be useful,
  12.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.     GNU General Public License for more details.
  15.  
  16.     You should have received a copy of the GNU General Public License
  17.     along with this program; if not, write to the Free Software
  18.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19. */
  20.  
  21. #include "ff.h"
  22.  
  23. #include <time.h>
  24.  
  25. #include "file_compat.h"
  26. #include "compat_string.h"
  27. #include "versioninfo_modify_win.h"
  28. #include "version.h"
  29.  
  30. extern HINSTANCE hDllInstance;
  31.  
  32. Boolean doresources(HMODULE srcmod,char *dstname, int bits);
  33.  
  34. void dbglasterror(char *func){
  35.         char s[0x100];
  36.  
  37.         strcpy(s,func);
  38.         strcat(s," failed: ");
  39.         FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM,NULL,GetLastError(),0,s+strlen(s),0x100,NULL );
  40.         dbg(s);
  41. }
  42.  
  43. /*
  44. BOOL CALLBACK enumfunc(HMODULE hModule,LPCTSTR lpszType,LPCTSTR lpszName,WORD wIDLanguage,LONG lParam){
  45.         char s[0x100];
  46.         sprintf(s,"EnumResourceLanguages callback: module=%#x type=%s name=%s lang=%d",
  47.                 hModule,lpszType,lpszName,wIDLanguage);
  48.         dbg(s);
  49.         return TRUE;
  50. }
  51. */
  52.  
  53. int domanifest(char *newmanifest, const char *manifestp, PARM_T* pparm, int bits) {
  54.         char name[1024];
  55.         char description[1024];
  56.         size_t i;
  57.         size_t iname = 0;
  58.         int idescription = 0;
  59.  
  60.         // Description
  61.         for (i = 0; i < strlen((char*)pparm->category); i++) {
  62.                 char c = pparm->category[i];
  63.                 if ((c != '<') && (c != '>')) {
  64.                         description[idescription++] = c;
  65.                 }
  66.         }
  67.         description[idescription++] = ' ';
  68.         description[idescription++] = '-';
  69.         description[idescription++] = ' ';
  70.         for (i = 0; i < strlen((char*)pparm->title); i++) {
  71.                 char c = pparm->title[i];
  72.                 if ((c != '<') && (c != '>')) {
  73.                         description[idescription++] = c;
  74.                 }
  75.         }
  76.         description[idescription++] = '\0';
  77.  
  78.         // Name
  79.         strcpy(name, "Telegraphics.FilterFoundry.");
  80.         iname = strlen("Telegraphics.FilterFoundry.");
  81.         for (i = 0; i < strlen((char*)pparm->category); i++) {
  82.                 char c = pparm->category[i];
  83.                 if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'))) {
  84.                         name[iname++] = c;
  85.                 }
  86.         }
  87.         name[iname++] = '.';
  88.         for (i = 0; i < strlen((char*)pparm->title); i++) {
  89.                 char c = pparm->title[i];
  90.                 if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'))) {
  91.                         name[iname++] = c;
  92.                 }
  93.         }
  94.         name[iname++] = '\0';
  95.  
  96.         if (bits == 64) {
  97.                 return sprintf(newmanifest, manifestp, (char*)name, "amd64", VERSION_STR, (char*)description);
  98.         }
  99.         else {
  100.                 return sprintf(newmanifest, manifestp, (char*)name, "x86", VERSION_STR, (char*)description);
  101.         }
  102. }
  103.  
  104. void changeVersionInfo(char* dstname, PARM_T* pparm, HGLOBAL hupdate) {
  105.         char* soleFilename;
  106.         LPWSTR changeRequestStr, tmp;
  107.  
  108.         if (soleFilename = strrchr(dstname, '\\')) {
  109.                 ++soleFilename;
  110.         }
  111.         else {
  112.                 soleFilename = dstname;
  113.         }
  114.  
  115.         // Format of argument "PCWSTR changes" is "<name>\0<value>\0<name>\0<value>\0....."
  116.         // You can CHANGE values for any given name
  117.         // You can DELETE entries by setting the value to "\b" (0x08 backspace character)
  118.         // You cannot (yet) ADD entries.
  119.         changeRequestStr = (LPWSTR)malloc(6 * 2 * 100 + 1);
  120.  
  121.         tmp = changeRequestStr;
  122.  
  123.         tmp += mbstowcs(tmp, "Comments", 100);
  124.         tmp++;
  125.         tmp += mbstowcs(tmp, "Built using Filter Foundry " VERSION_STR, 100);
  126.         tmp++;
  127.  
  128.         tmp += mbstowcs(tmp, "CompanyName", 100);
  129.         tmp++;
  130.         if (strlen((char*)pparm->author) > 0) {
  131.                 tmp += mbstowcs(tmp, (char*)pparm->author, 100);
  132.         }
  133.         else {
  134.                 tmp += mbstowcs(tmp, "\b", 100); // \b = remove
  135.         }
  136.         tmp++;
  137.  
  138.         tmp += mbstowcs(tmp, "LegalCopyright", 100);
  139.         tmp++;
  140.         if (strlen((char*)pparm->copyright) > 0) {
  141.                 tmp += mbstowcs(tmp, (char*)pparm->copyright, 100);
  142.         }
  143.         else {
  144.                 tmp += mbstowcs(tmp, "\b", 100); // \b = remove
  145.         }
  146.         tmp++;
  147.  
  148.         tmp += mbstowcs(tmp, "FileDescription", 100);
  149.         tmp++;
  150.         if (strlen((char*)pparm->title) > 0) {
  151.                 tmp += mbstowcs(tmp, (char*)pparm->title, 100);
  152.         }
  153.         else {
  154.                 tmp += mbstowcs(tmp, "Untitled filter", 100);
  155.         }
  156.         tmp++;
  157.  
  158.         tmp += mbstowcs(tmp, "OriginalFilename", 100);
  159.         tmp++;
  160.         tmp += mbstowcs(tmp, soleFilename, 100);
  161.         tmp++;
  162.  
  163.         tmp += mbstowcs(tmp, "License", 100);
  164.         tmp++;
  165.         tmp += mbstowcs(tmp, "\b", 100); // \b = remove, since filter is standalone and might have its own license
  166.         tmp++;
  167.  
  168.         tmp += mbstowcs(tmp, "", 1);
  169.  
  170.         if (UpdateVersionInfoWithHandle(dstname, hupdate, changeRequestStr) != NOERROR) {
  171.                 alertuser(_strdup("UpdateVersionInfoWithHandle failed"), _strdup(""));
  172.         }
  173.  
  174.         free(changeRequestStr);
  175. }
  176.  
  177. Boolean update_pe_timestamp(const char* filename, time_t timestamp) {
  178.         size_t peoffset;
  179.         FILE* fptr;
  180.        
  181.         fptr = fopen(filename, "rb+");
  182.         if (fptr == NULL) return false;
  183.  
  184.         fseek(fptr, 0x3C, SEEK_SET);
  185.         fread(&peoffset, sizeof(peoffset), 1, fptr);
  186.  
  187.         fseek(fptr, (long)peoffset + 8, SEEK_SET);
  188.         fwrite(&timestamp, sizeof(time_t), 1, fptr);
  189.  
  190.         fclose(fptr);
  191.  
  192.         return true;
  193. }
  194.  
  195. int binary_replace_file(const char* filename, unsigned int search, unsigned int replace) {
  196.         unsigned int srecord = 0;
  197.         int found = 0;
  198.  
  199.         FILE* fptr = fopen(filename, "rb+");
  200.         if (fptr == NULL) return -1;
  201.  
  202.         while ((fread(&srecord, sizeof(srecord), 1, fptr) == 1))
  203.         {
  204.                 if (srecord == search) {
  205.                         srecord = replace;
  206.                         fseek(fptr, -1*(long)sizeof(srecord), SEEK_CUR);
  207.                         fwrite(&srecord, (int)sizeof(srecord), 1, fptr);
  208.                         fseek(fptr, 0, SEEK_CUR); // important!
  209.                         found++;
  210.                 }
  211.                 else {
  212.                         fseek(fptr, -1*(long)(sizeof(srecord) - 1), SEEK_CUR);
  213.                 }
  214.         }
  215.         fclose(fptr);
  216.  
  217.         return found;
  218. }
  219.  
  220. //DOS .EXE header
  221. struct image_dos_header
  222. {
  223.         uint16_t e_magic;                     // Magic number
  224.         uint16_t e_cblp;                      // Bytes on last page of file
  225.         uint16_t e_cp;                        // Pages in file
  226.         uint16_t e_crlc;                      // Relocations
  227.         uint16_t e_cparhdr;                   // Size of header in paragraphs
  228.         uint16_t e_minalloc;                  // Minimum extra paragraphs needed
  229.         uint16_t e_maxalloc;                  // Maximum extra paragraphs needed
  230.         uint16_t e_ss;                        // Initial (relative) SS value
  231.         uint16_t e_sp;                        // Initial SP value
  232.         uint16_t e_csum;                      // Checksum
  233.         uint16_t e_ip;                        // Initial IP value
  234.         uint16_t e_cs;                        // Initial (relative) CS value
  235.         uint16_t e_lfarlc;                    // File address of relocation table
  236.         uint16_t e_ovno;                      // Overlay number
  237.         uint16_t e_res[4];                    // Reserved words
  238.         uint16_t e_oemid;                     // OEM identifier (for e_oeminfo)
  239.         uint16_t e_oeminfo;                   // OEM information; e_oemid specific
  240.         uint16_t e_res2[10];                  // Reserved words
  241.         int32_t  e_lfanew;                    // File address of new exe header
  242. };
  243.  
  244. struct image_file_header
  245. {
  246.         uint16_t Machine;
  247.         uint16_t NumberOfSections;
  248.         uint32_t TimeDateStamp;
  249.         uint32_t PointerToSymbolTable;
  250.         uint32_t NumberOfSymbols;
  251.         uint16_t SizeOfOptionalHeader;
  252.         uint16_t Characteristics;
  253. };
  254.  
  255. uint32_t calculate_checksum(const char* filename) {
  256.         //Calculate checksum of image
  257.         // Taken from "PE Bliss" Cross-Platform Portable Executable C++ Library
  258.         // https://github.com/mrexodia/portable-executable-library/blob/master/pe_lib/pe_checksum.cpp
  259.         // Converted from C++ to C by Daniel Marschall
  260.  
  261.         FILE* fptr;
  262.         unsigned long long checksum = 0;
  263.         struct image_dos_header header;
  264.         size_t filesize;
  265.         unsigned long long top;
  266.         unsigned long pe_checksum_pos;
  267.         static const unsigned long checksum_pos_in_optional_headers = 64;
  268.         size_t i;
  269.  
  270.         fptr = fopen(filename, "rb");
  271.         if (fptr == NULL) return 0x00000000;
  272.  
  273.         //Read DOS header
  274.         fseek(fptr, 0, SEEK_SET);
  275.         fread(&header, sizeof(struct image_dos_header), 1, fptr);
  276.  
  277.         //Calculate PE checksum
  278.         fseek(fptr, 0, SEEK_SET);
  279.         top = 0xFFFFFFFF;
  280.         top++;
  281.  
  282.         //"CheckSum" field position in optional PE headers - it's always 64 for PE and PE+
  283.         //Calculate real PE headers "CheckSum" field position
  284.         //Sum is safe here
  285.         pe_checksum_pos = header.e_lfanew + sizeof(struct image_file_header) + sizeof(uint32_t) + checksum_pos_in_optional_headers;
  286.  
  287.         //Calculate checksum for each byte of file
  288.         fseek(fptr, 0L, SEEK_END);
  289.         filesize = ftell(fptr);
  290.         fseek(fptr, 0L, SEEK_SET);
  291.         for (i = 0; i < filesize; i += 4)
  292.         {
  293.                 unsigned long dw = 0;
  294.  
  295.                 //Read DWORD from file
  296.                 fread(&dw, sizeof(dw), 1, fptr);
  297.                 //Skip "CheckSum" DWORD
  298.                 if (i == pe_checksum_pos)
  299.                         continue;
  300.  
  301.                 //Calculate checksum
  302.                 checksum = (checksum & 0xffffffff) + dw + (checksum >> 32);
  303.                 if (checksum > top)
  304.                         checksum = (checksum & 0xffffffff) + (checksum >> 32);
  305.         }
  306.  
  307.         //Finish checksum
  308.         checksum = (checksum & 0xffff) + (checksum >> 16);
  309.         checksum = (checksum)+(checksum >> 16);
  310.         checksum = checksum & 0xffff;
  311.  
  312.         checksum += (unsigned long)(filesize);
  313.  
  314.         fclose(fptr);
  315.  
  316.         //Return checksum
  317.         return (uint32_t)checksum;
  318. }
  319.  
  320. Boolean repair_pe_checksum(const char* filename) {
  321.         size_t peoffset;
  322.         FILE* fptr;
  323.  
  324.         uint32_t checksum = calculate_checksum(filename);
  325.         //if (checksum == 0x00000000) return false;
  326.  
  327.         fptr = fopen(filename, "rb+");
  328.         if (fptr == NULL) return false;
  329.  
  330.         fseek(fptr, 0x3C, SEEK_SET);
  331.         fread(&peoffset, sizeof(peoffset), 1, fptr);
  332.  
  333.         fseek(fptr, (long)peoffset + 88, SEEK_SET);
  334.         fwrite(&checksum, sizeof(uint32_t), 1, fptr);
  335.  
  336.         fclose(fptr);
  337.  
  338.         return true;
  339. }
  340.  
  341. Boolean doresources(HMODULE srcmod,char *dstname, int bits){
  342.         HRSRC datarsrc,aetersrc,manifestsrc;
  343.         HGLOBAL datah,aeteh,hupdate,manifesth;
  344.         Ptr newpipl = NULL, newaete = NULL;
  345.         LPVOID datap, aetep, manifestp;
  346.         PARM_T *pparm = NULL;
  347.         size_t piplsize,aetesize,origsize,manifestsize;
  348.         Str255 title;
  349.         LPCTSTR parm_type;
  350.         int i,parm_id;
  351.         Boolean discard = true;
  352.         unsigned int obfuscseed = 0;
  353.         long event_id;
  354.  
  355.         if( (hupdate = _BeginUpdateResource(dstname,false)) ){
  356.                 DBG("BeginUpdateResource OK");
  357.                 if( (datarsrc = FindResource(srcmod,MAKEINTRESOURCE(16000),"TPLT"))
  358.                         && (datah = LoadResource(srcmod,datarsrc))
  359.                         && (datap = (Ptr)LockResource(datah))
  360.                         && (aetersrc = FindResource(srcmod, MAKEINTRESOURCE(16000), "AETE"))
  361.                         && (aeteh = LoadResource(srcmod, aetersrc))
  362.                         && (aetep = (Ptr)LockResource(aeteh))
  363.                         && (manifestsrc = FindResource(srcmod, MAKEINTRESOURCE(50), "TPLT"))
  364.                         && (manifesth = LoadResource(srcmod, manifestsrc))
  365.                         && (manifestp = (Ptr)LockResource(manifesth)) )
  366.                 {
  367.                         char newmanifest[5000];
  368.  
  369.                         DBG("loaded DATA, PiPL");
  370.  
  371.                         PLstrcpy(title,gdata->parm.title);
  372.                         if(gdata->parm.popDialog)
  373.                                 PLstrcat(title,(StringPtr)"\003...");
  374.  
  375.                         origsize = SizeofResource(srcmod,datarsrc);
  376.  
  377.                         if( (newpipl = (Ptr)malloc(origsize+0x300))
  378.                          && (newaete = (Ptr)malloc(4096))
  379.                          && (pparm = (PARM_T*)malloc(sizeof(PARM_T))) )
  380.                         {
  381.                                 // ====== Generate AETE and PIPL
  382.  
  383.                                 /* add user-specified title and category to new PiPL */
  384.                                 memcpy(newpipl,datap,origsize);
  385.                                 /* note that Windows PiPLs have 2 byte version datum in front
  386.                                    that isn't reflected in struct definition or Mac resource template: */
  387.                                 piplsize = fixpipl((PIPropertyList*)(newpipl+2),origsize-2,title, &event_id) + 2;
  388.  
  389.                                 /* set up the PARM resource with saved parameters */
  390.                                 memcpy(pparm,&gdata->parm,sizeof(PARM_T));
  391.  
  392.                                 /* Generate 'aete' resource (contains names of the parameters for the "Actions" tab in Photoshop) */
  393.                                 aetesize = aete_generate(newaete, pparm, event_id);
  394.  
  395.                                 // ====== Change Pascal strings to C-Strings
  396.  
  397.                                 /* convert to C strings for Windows PARM resource */
  398.                                 // Don't do it before aete_generate, because they need Pascal strings
  399.                                 myp2cstr(pparm->category);
  400.                                 myp2cstr(pparm->title);
  401.                                 myp2cstr(pparm->copyright);
  402.                                 myp2cstr(pparm->author);
  403.                                 for (i = 0; i < 4; ++i)
  404.                                         myp2cstr(pparm->map[i]);
  405.                                 for (i = 0; i < 8; ++i)
  406.                                         myp2cstr(pparm->ctl[i]);
  407.  
  408.                                 manifestsize = domanifest(newmanifest, (const char*)manifestp, pparm, bits);
  409.  
  410.                                 // ====== Change version attributes
  411.  
  412.                                 changeVersionInfo(dstname, pparm, hupdate);
  413.  
  414.                                 // ====== Obfuscate pparm!
  415.  
  416.                                 if (gdata->obfusc) {
  417.                                         parm_type = RT_RCDATA;
  418.                                         parm_id = OBFUSCDATA_ID;
  419.  
  420.                                         // Note: After we have finished updating the resources, we will write <obfuscseed> into the binary code of the 8BF file
  421.                                         obfuscseed = obfusc(pparm);
  422.                                 }else{
  423.                                         parm_type = "PARM";
  424.                                         parm_id = PARM_ID;
  425.                                 }
  426.  
  427.                                 // ====== Save AETE, PIPL, Manifest and PARM/RCDATA
  428.  
  429.                                 /* Attention: The resource we have found using FindResource() might have a different
  430.                                    language than the resource we are saving (Neutral), so we might end up having
  431.                                    multiple languages for the same resource. Therefore, the language "Neutral" was
  432.                                    set in the Scripting.rc file for the resource AETE and PIPL.rc for the resources PIPL. */
  433.  
  434.                                 if(_UpdateResource(hupdate, "TPLT" /* note: caps!! */, MAKEINTRESOURCE(50), MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), NULL, 0)
  435.                                         && _UpdateResource(hupdate, "TPLT" /* note: caps!! */, MAKEINTRESOURCE(16000), MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), NULL, 0)
  436.                                         && _UpdateResource(hupdate, RT_DIALOG, MAKEINTRESOURCE(ID_BUILDDLG), MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), NULL, 0)
  437.                                         && _UpdateResource(hupdate,"PIPL" /* note: caps!! */,MAKEINTRESOURCE(16000), MAKELANGID(LANG_NEUTRAL,SUBLANG_NEUTRAL),newpipl,(DWORD)piplsize)
  438.                                         && _UpdateResource(hupdate, "AETE" /* note: caps!! */, MAKEINTRESOURCE(16000), MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), newaete, (DWORD)aetesize)
  439.                                         && _UpdateResource(hupdate, RT_MANIFEST, MAKEINTRESOURCE(1), MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), newmanifest, (DWORD)manifestsize)
  440.                                         && _UpdateResource(hupdate,parm_type,MAKEINTRESOURCE(parm_id), MAKELANGID(LANG_NEUTRAL,SUBLANG_NEUTRAL),pparm,sizeof(PARM_T)) )
  441.                                 {
  442.                                         discard = false;
  443.                                 } else {
  444.                                         dbglasterror(_strdup("UpdateResource"));
  445.                                 }
  446.                         }
  447.  
  448.                 }else dbglasterror(_strdup("Find-, Load- or LockResource"));
  449.  
  450.                 // Here, the file will be saved
  451.                 if(!_EndUpdateResource(hupdate,discard))
  452.                         dbglasterror(_strdup("EndUpdateResource"));
  453.  
  454.                 if (gdata->obfusc) {
  455.                         // We modify the binary code to replace the deobfuscate-seed from <cObfuscV4Seed> to <obfuscseed>
  456.                         if (binary_replace_file(dstname, cObfuscV4Seed, obfuscseed) != 1) {
  457.                                 // The seed must only be exactly 1 time inside the 8BF file,
  458.                                 // since "const volatile" makes sure that the compiler won't place
  459.                                 // it at several locations in the code.
  460.                                 dbg("binary_replace_file failed");
  461.                                 discard = true;
  462.                         }
  463.                 }
  464.  
  465.                 update_pe_timestamp(dstname, time(0));
  466.  
  467.                 repair_pe_checksum(dstname);
  468.  
  469.                 if(pparm) free(pparm);
  470.                 if(newpipl) free(newpipl);
  471.                 if(newaete) free(newaete);
  472.         }else
  473.                 dbglasterror(_strdup("BeginUpdateResource"));
  474.         return !discard;
  475. }
  476.  
  477. Boolean remove_64_filename_prefix(char* dstname) {
  478.         // foobar.8bf => foobar.8bf
  479.         // foobar64.8bf => foobar.8bf
  480.         size_t i;
  481.         for (i = strlen(dstname); i > 2; i--) {
  482.                 if (dstname[i] == '.') {
  483.                         if ((dstname[i - 2] == '6') && (dstname[i - 1] == '4')) {
  484.                                 size_t tmp = strlen(dstname);
  485.                                 memcpy(&dstname[i - 2], &dstname[i], strlen(dstname) - i + 1);
  486.                                 dstname[tmp - 2] = 0;
  487.                                 return true;
  488.                         }
  489.                 }
  490.         }
  491.         return false;
  492. }
  493.  
  494. Boolean add_64_filename_prefix(char* dstname) {
  495.         // foobar.8bf => foobar64.8bf
  496.         size_t i;
  497.         for (i = strlen(dstname); i > 2; i--) {
  498.                 if (dstname[i] == '.') {
  499.                         size_t tmp = strlen(dstname);
  500.                         memcpy(&dstname[i + 2], &dstname[i], strlen(dstname) - i + 1);
  501.                         dstname[i] = '6';
  502.                         dstname[i + 1] = '4';
  503.                         dstname[tmp + 2] = 0;
  504.                         return true;
  505.                 }
  506.         }
  507.         return false;
  508. }
  509.  
  510. BOOL FileExists(LPCTSTR szPath) {
  511.         DWORD dwAttrib = GetFileAttributes(szPath);
  512.         return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
  513.                 !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
  514. }
  515.  
  516. OSErr do_make_standalone(char* srcname, char* dstname, int bits) {
  517.         Boolean res;
  518.         char err[MAX_PATH + 200];
  519.  
  520.         //DeleteFile(dstname);
  521.         if (CopyFile(srcname, dstname, false)) {
  522.                 HMODULE hSrcmod;
  523.                 hSrcmod = LoadLibraryEx(srcname, NULL, LOAD_LIBRARY_AS_DATAFILE);
  524.                 if (hSrcmod) {
  525.                         res = doresources(hSrcmod, dstname, bits);
  526.                         if (!res) {
  527.                                 DeleteFile(dstname);
  528.                                 sprintf(err, "Could not create %d bit standalone plugin (doresources failed).", bits);
  529.                                 alertuser(_strdup(&err[0]), _strdup(""));
  530.                         }
  531.                         FreeLibrary(hSrcmod);
  532.                 }
  533.                 else {
  534.                         DWORD dwErr = GetLastError();
  535.                         res = false;
  536.                         DeleteFile(dstname);
  537.                         // TODO: Also translate Win32 error to use readable text ( https://docs.microsoft.com/en-us/windows/win32/debug/retrieving-the-last-error-code )
  538.                         sprintf(err, "Could not create %d bit standalone plugin (LoadLibraryEx failed, Win32 error %lu).", bits, dwErr);
  539.                         alertuser(_strdup(&err[0]), _strdup(""));
  540.                 }
  541.         }
  542.         else {
  543.                 DWORD dwErr = GetLastError();
  544.                 res = false;
  545.                 //DeleteFile(dstname);
  546.                 // TODO: Also translate Win32 error to use readable text ( https://docs.microsoft.com/en-us/windows/win32/debug/retrieving-the-last-error-code )
  547.                 sprintf(err, "Could not create %d bit standalone plugin (CopyFile failed, Win32 error %lu).", bits, dwErr);
  548.                 alertuser(_strdup(&err[0]), _strdup(""));
  549.         }
  550.  
  551.         return res ? noErr : ioErr;
  552. }
  553.  
  554. OSErr make_standalone(StandardFileReply *sfr){
  555.         OSErr tmpErr, outErr;
  556.         char dstname[0x100],srcname[MAX_PATH+1];
  557.  
  558.         if (!isWin32NT()) {
  559.                 HMODULE hLib;
  560.  
  561.                 hLib = LoadLibraryA("UNICOWS.DLL");
  562.                 if (!hLib) {
  563.                         char* sysdir;
  564.  
  565.                         // Unicows.dll is required to implement the BeginUpdateResource functionalities in Win9x
  566.  
  567.                         sysdir = (char*)malloc(MAX_PATH);
  568.                         GetSystemDirectoryA(sysdir, MAX_PATH);
  569.                         alertuser(_strdup("To build standalone plugins using this version of\nWindows, you need to install UNICOWS.DLL\n\nPlease download it from the Internet\nand place it into following directory:"), sysdir);
  570.                         free(sysdir);
  571.  
  572.                         return false;
  573.                 }
  574.                 else {
  575.                         FreeLibrary(hLib);
  576.                 }
  577.         }
  578.  
  579.         outErr = noErr;
  580.  
  581. #ifdef _WIN64
  582.  
  583.         //64 bit DLL makes 64 bit:
  584.         // Source file = module filename
  585.         GetModuleFileName(hDllInstance, srcname, MAX_PATH);
  586.         // Destfile = no64_or_32(chosenname) + 64
  587.         myp2cstrcpy(dstname, sfr->sfFile.name);
  588.         remove_64_filename_prefix(dstname);
  589.         add_64_filename_prefix(dstname);
  590.         tmpErr = do_make_standalone(&srcname[0], &dstname[0], 64);
  591.         if (tmpErr != noErr)
  592.                 outErr = tmpErr;
  593.         else
  594.                 showmessage(_strdup("64 bit standalone filter was successfully created"));
  595.  
  596.         //64 bit DLL makes 32 bit:
  597.         // Source file = no32(modulefilename)
  598.         GetModuleFileName(hDllInstance, srcname, MAX_PATH);
  599.         if (!remove_64_filename_prefix(srcname)) {
  600.                 char err[MAX_PATH + 200];
  601.                 sprintf(err, "Cannot create the %d bit version of this filter, because the 32-bit variant of this plugin could not be found", 32);
  602.                 alertuser(_strdup(&err[0]), _strdup(""));
  603.         }
  604.         else if (!FileExists(srcname)) {
  605.                 char err[MAX_PATH + 200];
  606.                 sprintf(err, "%s was not found. Therefore, the %d bit version of the standalone filter could not be created!", srcname, 32);
  607.                 alertuser(_strdup(&err[0]), _strdup(""));
  608.         }
  609.         else {
  610.                 // Destfile = no64_or_32(chosenname)
  611.                 myp2cstrcpy(dstname, sfr->sfFile.name);
  612.                 remove_64_filename_prefix(dstname);
  613.                 tmpErr = do_make_standalone(&srcname[0], &dstname[0], 32);
  614.                 if (tmpErr != noErr)
  615.                         outErr = tmpErr;
  616.                 else
  617.                         showmessage(_strdup("32 bit standalone filter was successfully created"));
  618.         }
  619.  
  620. #else
  621.        
  622.         //32 bit DLL makes 32 bit:
  623.         // Source file = module filename
  624.         GetModuleFileName(hDllInstance, srcname, MAX_PATH);
  625.         // Destfile = no64_or_32(chosenname)
  626.         myp2cstrcpy(dstname, sfr->sfFile.name);
  627.         remove_64_filename_prefix(dstname);
  628.         tmpErr = do_make_standalone(&srcname[0], &dstname[0], 32);
  629.         if (tmpErr != noErr)
  630.                 outErr = tmpErr;
  631.         else
  632.                 showmessage(_strdup("32 bit standalone filter was successfully created"));
  633.  
  634.         if (isWin32NT()) {
  635.                 //32 bit DLL makes 64 bit:
  636.                 // Source file = module filename + 64
  637.                 GetModuleFileName(hDllInstance, srcname, MAX_PATH);
  638.                 add_64_filename_prefix(srcname);
  639.                 if (!FileExists(srcname)) {
  640.                         char err[MAX_PATH + 200];
  641.                         sprintf(err, "%s was not found. Therefore, the %d bit version of the standalone filter could not be created!", srcname, 64);
  642.                         alertuser(_strdup(&err[0]), _strdup(""));
  643.                 }
  644.                 else {
  645.                         // Destfile = no64_or_32(chosenname) + 64
  646.                         myp2cstrcpy(dstname, sfr->sfFile.name);
  647.                         remove_64_filename_prefix(dstname);
  648.                         add_64_filename_prefix(dstname);
  649.                         tmpErr = do_make_standalone(&srcname[0], &dstname[0], 64);
  650.                         if (tmpErr != noErr)
  651.                                 outErr = tmpErr;
  652.                         else
  653.                                 showmessage(_strdup("64 bit standalone filter was successfully created"));
  654.                 }
  655.         }
  656.         else {
  657.                 // Unicows.dll cannot edit resources of 64 bit DLLs.
  658.                 // The normal Kernel function BeginUpdateResource can edit 64 bit DLLs, even in NT4.0 SP6
  659.                 simplealert(_strdup("Note: A 64 bit standalone filter cannot be created with your Windows version"));
  660.         }
  661. #endif
  662.  
  663.         return outErr;
  664. }
  665.