Subversion Repositories filter_foundry

Rev

Rev 462 | Rev 476 | 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 <stdio.h>
  22. //#include <sound.h>
  23.  
  24. #include "ff.h"
  25.  
  26. #include "str.h"
  27. #include "node.h"
  28. #include "funcs.h"
  29. #include "y.tab.h"
  30. #include "scripting.h"
  31. #include <math.h>
  32. #include "PIBufferSuite.h"
  33.  
  34. // GIMP (PSPI) and IrfanView preserve neither *data(gdata), nor pb->parameters between invocations!
  35. // For debugging, we can simulate it here
  36. //#define DEBUG_SIMULATE_GIMP
  37.  
  38. // Used to find out which host signatures and memory settings a plugin host has
  39. //#define SHOW_HOST_DEBUG
  40.  
  41. struct node *tree[4];
  42. char *err[4];
  43. int errpos[4],errstart[4],nplanes,cnvused,chunksize,toprow;
  44. uint8_t slider[8]; // this is the "working data". We cannot always use gdata->parm, because parm will not be loaded if a AFS file is read
  45. char* expr[4]; // this is the "working data". We cannot always use gdata->parm, because parm will not be loaded if a AFS file is read
  46. value_type cell[NUM_CELLS];
  47. // long maxSpace;
  48. globals_t *gdata;
  49. FilterRecordPtr gpb;
  50.  
  51. #ifdef MAC_ENV
  52.         #define HINSTANCE HANDLE
  53.         #define hDllInstance NULL /* fake this Windows-only global */
  54. #endif
  55.  
  56. #ifdef WIN_ENV
  57. #include "manifest.h"
  58. #endif
  59.  
  60. extern struct sym_rec predefs[];
  61. extern int nplanes,varused[];
  62.  
  63. int checkandinitparams(Handle params);
  64.  
  65. // MPW MrC requires prototype
  66. DLLEXPORT MACPASCAL
  67. void ENTRYPOINT(short selector,FilterRecordPtr pb,intptr_t *data,short *result);
  68.  
  69. unsigned long get_parm_hash(PARM_T *parm) {
  70.         unsigned long hash;
  71.         int i;
  72.  
  73.         hash = djb2(parm->szCategory);
  74.         hash += djb2(parm->szTitle);
  75.         hash += djb2(parm->szCopyright);
  76.         hash += djb2(parm->szAuthor);
  77.         for (i = 0; i < 4; i++) hash += djb2(parm->szMap[i]);
  78.         for (i = 0; i < 8; i++) hash += djb2(parm->szCtl[i]);
  79.         for (i = 0; i < 4; i++) hash += djb2(parm->szFormula[i]);
  80.  
  81.         return hash;
  82. }
  83.  
  84. size_t get_temp_afs(LPTSTR outfilename, Boolean isStandalone, PARM_T *parm) {
  85.         char* atempdir;
  86.         int hash;
  87.         size_t i, j;
  88.         TCHAR out[MAX_PATH + 1];
  89.         char ahash[20];
  90.  
  91.         // out = getenv("TMP")
  92.         atempdir = getenv("TMP");
  93.         for (i = 0; i < strlen(atempdir); i++) {
  94.                 out[i] = (TCHAR)atempdir[i];
  95.                 out[i + 1] = 0;
  96.         }
  97.  
  98.         #ifdef WIN_ENV
  99.         if (xstrlen(out) > 0) xstrcat(out, TEXT("\\"));
  100.         #else
  101.         if (xstrlen(out) > 0) xstrcat(out, TEXT("/"));
  102.         #endif
  103.  
  104.         hash = (isStandalone) ? get_parm_hash(parm) : 0;
  105.  
  106.         // sprintf(outfilename, "%sFilterFoundry%d.afs", atempdir, hash);
  107.         xstrcat(out, TEXT("FilterFoundry"));
  108.         _itoa(hash, &ahash[0], 10);
  109.         for (i = 0; i < strlen(ahash); i++) {
  110.                 j = xstrlen(out);
  111.                 out[j] = (TCHAR)ahash[i];
  112.                 out[j + 1] = 0;
  113.         }
  114.         xstrcat(out, TEXT(".afs"));
  115.         if (outfilename != NULL) {
  116.                 xstrcpy(outfilename, out);
  117.         }
  118.         return xstrlen(out);
  119. }
  120.  
  121. void CALLBACK FakeRundll32(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) {
  122.         UNREFERENCED_PARAMETER(hwnd);
  123.         UNREFERENCED_PARAMETER(hinst);
  124.         UNREFERENCED_PARAMETER(lpszCmdLine);
  125.         UNREFERENCED_PARAMETER(nCmdShow);
  126.  
  127.         /*
  128.         char* tmp = (char*)malloc(512);
  129.         if (tmp != 0) {
  130.                 sprintf(tmp, "hwnd: %p\nhinst: %p\nlpszCmdLine: %s\nCmdShow: %d", (void*)(hwnd), (void*)(hinst), lpszCmdLine, nCmdShow);
  131.                 MessageBoxA(0, tmp, 0, 0);
  132.                 free(tmp);
  133.         }
  134.         */
  135.  
  136.         simplealert((TCHAR*)TEXT("You tried to execute this method with RunDLL32. This is a Photoshop plugin!"));
  137.         return;
  138. }
  139.  
  140. DLLEXPORT MACPASCAL
  141. void ENTRYPOINT(short selector, FilterRecordPtr pb, intptr_t *data, short *result){
  142.         static Boolean wantdialog = false;
  143.         static Boolean premiereWarnedOnce = false;
  144.         OSErr e = noErr;
  145.         char *reason;
  146.  
  147.         #ifdef SHOW_HOST_DEBUG
  148.         char* tmp;
  149.         #endif
  150.  
  151.         #ifdef WIN_ENV
  152.         // For Windows, we use an activation context to enforce that our Manifest resource will
  153.         // be used. This allows us to use Visual Styles, even if the host application does not
  154.         // support it.
  155.         ManifestActivationCtx manifestVars;
  156.         BOOL activationContextUsed;
  157.         #endif
  158.  
  159.         // ---------------------------------------------------------------------
  160.  
  161.         EnterCodeResource();
  162.  
  163.         #ifdef WIN_ENV
  164.         if ((intptr_t)result == SW_SHOWDEFAULT) {
  165.                 // When the 8BF file is analyzed with VirusTotal.com, it will invoke each
  166.                 // exported function by calling
  167.                 // loaddll64.exe 'C:\Users\user\Desktop\attachment.dll'
  168.                 //        ==>  rundll32.exe C:\Users\user\Desktop\attachment.dll,PluginMain
  169.                 //           ==> C:\Windows\system32\WerFault.exe -u -p 6612 -s 480
  170.                 //
  171.                 // But RunDLL32 requires following signature:
  172.                 //    void __stdcall EntryPoint(HWND hwnd,      HINSTANCE hinst,    LPSTR lpszCmdLine, int nCmdShow);
  173.                 // Our signature is:
  174.                 //    void           PluginMain(short selector, FilterRecordPtr pb, intptr_t *data,    short *result);
  175.                 //
  176.                 // Obviously, this will cause an Exception. (It crashes at *result=e because result is 0xA)
  177.                 // Here is the problem: The crash will be handled by WerFault.exe inside the
  178.                 // VirusTotal virtual machine. WerFault connects to various servers (9 DNS resolutions!) and does
  179.                 // a lot of weird things, but VirusTotal thinks that our plugin does all that stuff,
  180.                 // and so they mark our plugin as "malware"!
  181.                 // This is a problem with VirusTotal! It shall not assume that WerFault.exe actions are our actions!
  182.                 // Even processes like "MicrosoftEdgeUpdate.exe" and "SpeechRuntime.exe" are reported to be our
  183.                 // actions, although they have nothing to do with us!
  184.                 // See https://www.virustotal.com/gui/file/1f1012c567208186be455b81afc1ee407ae6476c197d633c70cc70929113223a/behavior
  185.                 //
  186.                 // TODO: Usually, The first 64KB of address space are always invalid. However, in Win32s (Windows 3.11), the
  187.                 //       variable "result" is <=0xFFFF ! Let's just hope that it is never 0x000A (SW_SHOWDEFAULT),
  188.                 //       otherwise we have a problem here!
  189.                 // I don't understand why this works! Aren't we __cdecl and rundll expected __stdcall? But why is the parameter order correct and not reversed?
  190.                 FakeRundll32((HWND)(intptr_t)selector, (HINSTANCE)pb, (LPSTR)data, (int)(intptr_t)result);
  191.                 return;
  192.         }
  193.         #endif
  194.  
  195.         #ifdef SHOW_HOST_DEBUG
  196.         tmp = (char*)malloc(512);
  197.         sprintf(tmp, "Host signature: '%c%c%c%c' (%d)\nMaxSpace32 = %d\nMaxSpace64 = %lld\nNum buffer procs: %d", (pb->hostSig >> 24) & 0xFF, (pb->hostSig >> 16) & 0xFF, (pb->hostSig >> 8) & 0xFF, pb->hostSig & 0xFF, pb->hostSig, pb->maxSpace, pb->maxSpace64, pb->bufferProcs == 0 ? -999/*About has no BufferProcs*/ : pb->bufferProcs->numBufferProcs);
  198.         simplealert(tmp);
  199.         free(tmp);
  200.         #endif
  201.  
  202.         if (pb->hostSig == HOSTSIG_ADOBE_PREMIERE) {
  203.                 // DM 19.07.2021 : Tried running the 8BF file in Adobe Premiere 5 (yes, that's possible,
  204.                 // and there is even a FilterFactory for Premiere!),
  205.                 // but it crashes in evalpixel() where there is write-access to the "outp".
  206.                 // Probably the canvas structure is different (maybe it contains frames to achieve transitions?)
  207.                 if (!premiereWarnedOnce) {
  208.                         simplealert((TCHAR*)TEXT("This version of Filter Foundry is not compatible with Adobe Premiere!"));
  209.                 }
  210.                 premiereWarnedOnce = true;
  211.                 *result = errPlugInHostInsufficient;
  212.                 return;
  213.         }
  214.  
  215.         #ifdef DEBUG_SIMULATE_GIMP
  216.         *data = 0;
  217.         pb->parameters = pb->handleProcs->newProc(1);
  218.         #endif
  219.  
  220.         // Register "gdata" that contains the PARM information and other things which need to be persistant
  221.         // and preserve then in *data
  222.         // TODO: memory leak? where is the stuff freed?
  223.         if (selector != filterSelectorAbout && !*data) {
  224.                 /*
  225.                 PSBufferSuite1* pSBufferSuite32 = NULL;
  226.  
  227.                 if ((pb->sSPBasic == 0) ||
  228.                         (pb->sSPBasic->AcquireSuite(kPSBufferSuite, kPSBufferSuiteVersion1, (const void**)&pSBufferSuite32)) ||
  229.                         (pSBufferSuite32 == NULL))
  230.                 {
  231.                                 // Old deprecated buffer suite
  232.                                 BufferID tempId;
  233.                                 if ((*result = pb->bufferProcs->allocateProc(sizeof(globals_t), &tempId))) return;
  234.                                 *data = (intptr_t)pb->bufferProcs->lockProc(tempId, true);
  235.                                 gdata = (globals_t*)*data;
  236.                 }
  237.                 else
  238.                 {
  239.                                 // New buffer suite (but only 32-bit version 1, because version 2 has problems with old Photoshop versions)
  240.                                 // Windows Photoshop 7 and CS 2 accepts kPSBufferSuiteVersion2, but doesn't correctly implement it:
  241.                                 // The symbols "New" and "GetSpace64" point to memory memory addresses outside the Photoshop.exe address range.
  242.                                 // (Other Photoshop versions were not tested.)
  243.                                 // 64-bit support for Windows was established in Photoshop CS 4,
  244.                                 // and PSBufferSuite2 was first documented in SDK CS 6.
  245.                                 // So, kPSBufferSuiteVersion2 probably was partically implemented as hidden "Work in progress" version
  246.                                 // before it was publicly documented.
  247.                                 // Side note:  pb->bufferSpace64/pb->maxSpace64 was documented in SDK CC 2017.
  248.                                 //             pb->bufferProcs->allocateProc64/spaceProc64 was documented in SDK CS 6.
  249.                                 unsigned32 siz = sizeof(globals_t);
  250.                                 *data = (intptr_t)pSBufferSuite32->New(&siz, siz);
  251.                                 if ((*data == 0) || (siz == 0)) {
  252.                                                 *result = errPlugInHostInsufficient; // TODO: what is the correct error code for "out of memory"?
  253.                                                 return;
  254.                                 }
  255.                                 gdata = (globals_t*)*data;
  256.                                 pb->sSPBasic->ReleaseSuite(kPSBufferSuite, kPSBufferSuiteVersion1);
  257.                 }
  258.                 gdata->standalone = gdata->parmloaded = false;
  259.                 */
  260.  
  261.                 // We have 3 options:
  262.                 // - The deprecated buffer suite (pb->bufferProcs), works fine
  263.                 // - The recommended buffer suite (kPSBufferSuite), does NOT work (causes memory corruption?) and is not available on some hosts!
  264.                 //   Either I do something wrong, or maybe it cannot be used to store data between invocations?
  265.                 // - Using malloc(), which works also fine and is more independent from the host and easier
  266.                 *data = (intptr_t)malloc(sizeof(globals_t));
  267.                 if (*data == 0) return;
  268.                 gdata = (globals_t*)*data;
  269.                 //#ifdef _WIN32
  270.                 //gdata->pluginDllSliderMessageId = 0;
  271.                 //#endif
  272.                 //gdata->standalone = gdata->parmloaded = false; // they will be set later
  273.                 memset(gdata, 0, sizeof(globals_t));
  274.         }
  275.         else {
  276.                 // We have data from the previous invocation. Use it instead
  277.                 gdata = (globals_t*)*data;
  278.         }
  279.  
  280.         #ifdef WIN_ENV
  281.         activationContextUsed = ActivateManifest((HMODULE)hDllInstance, 1, &manifestVars);
  282.         #endif
  283.  
  284.         gpb = pb;
  285.  
  286.         nplanes = MIN(pb->planes,4);
  287.  
  288.         switch (selector){
  289.         case filterSelectorAbout:
  290.                 if (!gdata) {
  291.                         gdata = (globals_t*)malloc(sizeof(globals_t));
  292.                         if (!gdata) break;
  293.                         gdata->hWndMainDlg = (HWND)((PlatformData*)((AboutRecordPtr)pb)->platformData)->hwnd; // so that simplealert() works
  294.                         gdata->standalone = gdata->parmloaded = readPARMresource((HMODULE)hDllInstance,&reason);
  295.                         if (gdata->parmloaded && (gdata->parm.cbSize != PARM_SIZE) && (gdata->parm.cbSize != PARM_SIZE_PREMIERE) && (gdata->parm.cbSize != PARM_SIG_MAC)) {
  296.                                 if (gdata->obfusc) {
  297.                                         simplealert((TCHAR*)TEXT("Incompatible obfuscation."));
  298.                                 }
  299.                                 else {
  300.                                         simplealert((TCHAR*)TEXT("Invalid parameter data."));
  301.                                 }
  302.                         }
  303.                         else {
  304.                                 DoAbout((AboutRecordPtr)pb);
  305.                         }
  306.                         free(gdata);
  307.                         gdata = NULL;
  308.                 } else {
  309.                         DoAbout((AboutRecordPtr)pb);
  310.                 }
  311.                 break;
  312.         case filterSelectorParameters:
  313.                 wantdialog = true;
  314.                 break;
  315.         case filterSelectorPrepare:
  316.                 gdata->hWndMainDlg = 0;
  317.                 DoPrepare(pb);
  318.                 init_symtab(predefs); // ready for parser calls
  319.                 init_trigtab();
  320.                 break;
  321.         case filterSelectorStart:
  322.                 if (HAS_BIG_DOC(pb)) {
  323.                         // The BigDocument structure is required if the document is larger than 30,000 pixels
  324.                         // It deprecates imageSize, filterRect, inRect, outRect, maskRect, floatCoord, and wholeSize.
  325.                         // By setting it to nonzero, we communicate to Photoshop that we support the BigDocument structure.
  326.                         pb->bigDocumentData->PluginUsing32BitCoordinates = true;
  327.                 }
  328.  
  329.                 /* initialise the parameter handle that Photoshop keeps for us */
  330.                 if(!pb->parameters)
  331.                         pb->parameters = PINEWHANDLE(1); // don't set initial size to 0, since some hosts (e.g. GIMP/PSPI) are incompatible with that.
  332.  
  333.                 wantdialog |= checkandinitparams(pb->parameters);
  334.  
  335.                 /* wantdialog = false means that we never got a Parameters call, so we're not supposed to ask user */
  336.                 if( wantdialog && (!gdata->standalone || gdata->parm.popDialog) ){
  337.                         if( maindialog(pb) ){
  338.                                 if (!host_preserves_parameters()) {
  339.                                         /* Workaround for GIMP/PSPI, to avoid that formulas vanish when you re-open the main window.
  340.                                            The reason is a bug in PSPI: The host should preserve the value of pb->parameters, which PSPI does not do.
  341.                                            Also, all global variables are unloaded, so the plugin cannot preserve any data.
  342.                                            Workaround in FF 1.7: If the host GIMP is detected, then a special mode will be activated.
  343.                                            This mode saves the filter data into a temporary file "FilterFoundryXX.afs" and loads it
  344.                                            when the window is opened again. */
  345.                                         // Workaround: Save settings in "FilterFoundryXX.afs" if the host does not preserve pb->parameters
  346.                                         TCHAR outfilename[MAX_PATH+1];
  347.                                         StandardFileReply sfr;
  348.                                         char* bakexpr[4];
  349.                                         int i;
  350.  
  351.                                         sfr.sfGood = true;
  352.                                         sfr.sfReplacing = true;
  353.                                         sfr.sfType = PS_FILTER_FILETYPE;
  354.  
  355.                                         get_temp_afs(&outfilename[0], gdata->standalone, &gdata->parm);
  356.  
  357.                                         xstrcpy(sfr.sfFile.szName, outfilename);
  358.                                         #ifdef WIN_ENV
  359.                                         sfr.nFileExtension = (WORD)(xstrlen(outfilename) - strlen(".afs") + 1);
  360.                                         #endif
  361.                                         sfr.sfScript = 0; // FIXME: is that ok?
  362.  
  363.                                         // We only want the parameters (ctl,map) in the temporary .afs file
  364.                                         // It is important to remove the expressions, otherwise they would be
  365.                                         // revealed in the temporary files.
  366.                                         for (i = 0; i < 4; i++) {
  367.                                                 bakexpr[i] = expr[i]; // moved out of the if-definition to make the compiler happy
  368.                                         }
  369.                                         if (gdata->standalone) {
  370.                                                 expr[0] = _strdup("r");
  371.                                                 expr[1] = _strdup("g");
  372.                                                 expr[2] = _strdup("b");
  373.                                                 expr[3] = _strdup("a");
  374.                                         }
  375.  
  376.                                         savefile_afs_pff_picotxt(&sfr);
  377.  
  378.                                         if (gdata->standalone) {
  379.                                                 for (i = 0; i < 4; i++) {
  380.                                                         if (expr[i]) free(expr[i]);
  381.                                                         expr[i] = bakexpr[i];
  382.                                                 }
  383.                                         }
  384.                                 } else {
  385.                                         /* update stored parameters from new user settings */
  386.                                         saveparams_afs_pff(pb->parameters);
  387.                                 }
  388.                         }else
  389.                                 e = userCanceledErr;
  390.                 }
  391.                 wantdialog = false;
  392.  
  393.                 if(!e){
  394.                         if(setup(pb)){
  395.                                 DoStart(pb);
  396.                         }else{
  397.                                 SYSBEEP(1);
  398.                                 e = filterBadParameters;
  399.                         }
  400.                 }
  401.                 break;
  402.         case filterSelectorContinue:
  403.                 e = DoContinue(pb);
  404.                 break;
  405.         case filterSelectorFinish:
  406.                 DoFinish(pb);
  407.                 break;
  408.         default:
  409.                 e = filterBadParameters;
  410.         }
  411.  
  412.         *result = e;
  413.  
  414.         #ifdef WIN_ENV
  415.         if (activationContextUsed) DeactivateManifest(&manifestVars);
  416.         #endif
  417.  
  418.         ExitCodeResource();
  419. }
  420.  
  421. int checkandinitparams(Handle params){
  422.         char *reasonstr,*reason;
  423.         int i;
  424.         Boolean bUninitializedParams;
  425.         Boolean showdialog;
  426.  
  427.         if (!host_preserves_parameters()) {
  428.                 // Workaround: Load settings in "FilterFoundryXX.afs" if host does not preserve pb->parameters
  429.                 TCHAR outfilename[MAX_PATH + 1];
  430.                 Boolean isStandalone;
  431.                 StandardFileReply sfr;
  432.                 char* bakexpr[4];
  433.  
  434.                 sfr.sfGood = true;
  435.                 sfr.sfReplacing = true;
  436.                 sfr.sfType = PS_FILTER_FILETYPE;
  437.  
  438.                 // We need to set gdata->standalone after loadfile(), but we must call readPARMresource() before loadfile()
  439.                 // Reason: readPARMresource() reads parameters from the DLL while loadfile() reads parameters from the AFS file
  440.                 // But loadfile() will reset gdata->standalone ...
  441.                 isStandalone = readPARMresource((HMODULE)hDllInstance, &reason);
  442.                 if (isStandalone && (gdata->parm.cbSize != PARM_SIZE) && (gdata->parm.cbSize != PARM_SIZE_PREMIERE) && (gdata->parm.cbSize != PARM_SIG_MAC)) {
  443.                         if (gdata->obfusc) {
  444.                                 simplealert((TCHAR*)TEXT("Incompatible obfuscation."));
  445.                         }
  446.                         else {
  447.                                 simplealert((TCHAR*)TEXT("Invalid parameter data."));
  448.                         }
  449.                         gdata->parmloaded = false;
  450.                         return false;
  451.                 }
  452.  
  453.                 get_temp_afs(&outfilename[0], isStandalone, &gdata->parm);
  454.  
  455.                 xstrcpy(sfr.sfFile.szName, outfilename);
  456.                 #ifdef WIN_ENV
  457.                 sfr.nFileExtension = (WORD)(xstrlen(outfilename) - strlen(".afs") + 1);
  458.                 #endif
  459.                 sfr.sfScript = 0; // FIXME: is that ok?
  460.  
  461.                 // We only want the parameters (ctl,map) in the temporary .afs file
  462.                 if (isStandalone) {
  463.                         for (i = 0; i < 4; i++) {
  464.                                 bakexpr[i] = my_strdup(expr[i]);
  465.                         }
  466.                 }
  467.  
  468.                 if (loadfile(&sfr, &reason)) {
  469.                         gdata->standalone = gdata->parmloaded = isStandalone;
  470.  
  471.                         if (isStandalone) {
  472.                                 for (i = 0; i < 4; i++) {
  473.                                         if (expr[i]) free(expr[i]);
  474.                                         expr[i] = bakexpr[i];
  475.                                 }
  476.                         }
  477.  
  478.                         return true;
  479.                 }
  480.         }
  481.  
  482.         if( (bUninitializedParams = !(params && readparams_afs_pff(params,&reasonstr))) ){
  483.                 /* either the parameter handle was uninitialised,
  484.                    or the parameter data couldn't be read; set default values */
  485.  
  486.                 // see if saved parameters exist
  487.                 gdata->standalone = gdata->parmloaded = readPARMresource((HMODULE)hDllInstance,&reason);
  488.                 if (gdata->parmloaded && (gdata->parm.cbSize != PARM_SIZE) && (gdata->parm.cbSize != PARM_SIZE_PREMIERE) && (gdata->parm.cbSize != PARM_SIG_MAC)) {
  489.                         if (gdata->obfusc) {
  490.                                 simplealert((TCHAR*)TEXT("Incompatible obfuscation."));
  491.                         }
  492.                         else {
  493.                                 simplealert((TCHAR*)TEXT("Invalid parameter data."));
  494.                         }
  495.                         gdata->parmloaded = false;
  496.                         return false;
  497.                 }
  498.  
  499.                 if(!gdata->standalone){
  500.                         // no saved settings (not standalone)
  501.                         for(i = 0; i < 8; ++i)
  502.                                 slider[i] = (uint8_t)(i*10+100);
  503.                         for(i = 0; i < 4; ++i)
  504.                                 if(expr[i])
  505.                                         free(expr[i]);
  506.                         if(gpb->imageMode == plugInModeRGBColor){
  507.                                 expr[0] = _strdup("r");
  508.                                 expr[1] = _strdup("g");
  509.                                 expr[2] = _strdup("b");
  510.                                 expr[3] = _strdup("a");
  511.                         }else{
  512.                                 expr[0] = _strdup("c");
  513.                                 expr[1] = _strdup("c");
  514.                                 expr[2] = _strdup("c");
  515.                                 expr[3] = _strdup("c");
  516.                         }
  517.                 }
  518.         }
  519.  
  520.         // let scripting system change parameters, if we're scripted;
  521.         // user may want to force display of dialog during scripting playback
  522.         switch (ReadScriptParamsOnRead()) {
  523.         case SCR_SHOW_DIALOG:
  524.                 showdialog = true;
  525.                 break;
  526.         case SCR_HIDE_DIALOG:
  527.                 showdialog = false;
  528.                 break;
  529.         default:
  530.         case SCR_NO_SCRIPT:
  531.                 showdialog = bUninitializedParams;
  532.                 break;
  533.         }
  534.  
  535.         saveparams_afs_pff(params);
  536.  
  537.         return showdialog;
  538. }
  539.  
  540. Boolean host_preserves_parameters() {
  541.         #ifdef DEBUG_SIMULATE_GIMP
  542.         return false;
  543.         #endif
  544.  
  545.         if (gpb->hostSig == HOSTSIG_GIMP) return false;
  546.         if (gpb->hostSig == HOSTSIG_IRFANVIEW) return false;
  547.  
  548.         // We just assume the other hosts preserve the parameters
  549.         return true;
  550. }
  551.  
  552. int64_t maxspace(){
  553.         // Please see "Hosts.md" for details about the MaxSpace implementations of tested plugins
  554.  
  555.         // Plugins that don't support MaxSpace64 shall set the field to zero; then we will use MaxSpace instead.
  556.         // Also check "gpb->bufferProcs->numBufferProcs" to see if 64 bit API is available
  557.         if ((gpb->bufferProcs->numBufferProcs >= 8) && (gpb->maxSpace64 > 0)) {
  558.                 uint64_t maxSpace64 = gpb->maxSpace64;
  559.  
  560.                 return maxSpace64;
  561.         } else {
  562.                 // Note: If maxSpace gets converted from Int32 to unsigned int, we can reach up to 4 GB RAM. However, after this, there will be a wrap to 0 GB again.
  563.                 unsigned int maxSpace32 = (unsigned int) gpb->maxSpace;
  564.                 uint64_t maxSpace64 = maxSpace32;
  565.  
  566.                 if (gpb->hostSig == HOSTSIG_IRFANVIEW) maxSpace64 *= 1024; // IrfanView is giving Kilobytes instead of Bytes
  567.                 //if (gpb->hostSig == HOSTSIG_SERIF_PHOTOPLUS) maxSpace64 *= 1024; // TODO: Serif PhotoPlus also gives Kilobytes instead of bytes. But since it uses not a unique host signature, there is nothing we can do???
  568.  
  569.                 return maxSpace64;
  570.         }
  571. }
  572.  
  573. Boolean maxspace_available() {
  574.         // Please see "Hosts.md" for details about the MaxSpace implementations of tested plugins
  575.  
  576.         // GIMP PSPI sets MaxSpace to hardcoded 100 MB
  577.         if (gpb->hostSig == HOSTSIG_GIMP) return false;
  578.  
  579.         // HOSTSIG_PAINT_NET sets MaxSpace to hardcoded 1 GB, see https://github.com/0xC0000054/PSFilterPdn/issues/5
  580.         // Comment by the host author "This was done to avoid any compatibility issues with plugins handling 2 GB - 1"
  581.         if (gpb->hostSig == HOSTSIG_PAINT_NET) return false;
  582.  
  583.         return true;
  584. }
  585.  
  586. void DoPrepare(FilterRecordPtr pb){
  587.         int i;
  588.  
  589.         for(i = 4; i--;){
  590.                 if(expr[i]||tree[i]) DBG("expr[] or tree[] non-NULL in Prepare!");
  591.                 expr[i] = NULL;
  592.                 tree[i] = NULL;
  593.         }
  594.  
  595.         // Commented out by DM, 18 Dec 2018:
  596.         // This code did not work on systems with 8 GB RAM:
  597.         /*
  598.         long space = (pb->maxSpace*9)/10; // don't ask for more than 90% of available memory
  599.  
  600.         maxSpace = 512L<<10; // this is a wild guess, actually
  601.         if(maxSpace > space)
  602.                         maxSpace = space;
  603.         pb->maxSpace = maxSpace;
  604.         */
  605.  
  606.         // New variant:
  607.         if (maxspace_available()) {
  608.                 pb->maxSpace = (int32)ceil((maxspace()/10.)*9); // don't ask for more than 90% of available memory
  609.                 // FIXME: Also maxSpace64
  610.         }
  611. }
  612.  
  613. void RequestNext(FilterRecordPtr pb){
  614.         /* Request next block of the image */
  615.  
  616.         pb->inLoPlane = pb->outLoPlane = 0;
  617.         pb->inHiPlane = pb->outHiPlane = nplanes-1;
  618.  
  619.         if (HAS_BIG_DOC(pb)) {
  620.                 // if any of the formulae involve random access to image pixels,
  621.                 // ask for the entire image
  622.                 if (needall) {
  623.                         SETRECT(BIGDOC_IN_RECT(pb), 0, 0, BIGDOC_IMAGE_SIZE(pb).h, BIGDOC_IMAGE_SIZE(pb).v);
  624.                 } else {
  625.                         // TODO: This does not work with GIMP. So, if we are using GIMP, we should
  626.                         //       somehow always use "needall=true", and/or find out why this doesn't work
  627.                         //       with GIMP.
  628.  
  629.                         // otherwise, process the filtered area, by chunksize parts
  630.                         BIGDOC_IN_RECT(pb).left = BIGDOC_FILTER_RECT(pb).left;
  631.                         BIGDOC_IN_RECT(pb).right = BIGDOC_FILTER_RECT(pb).right;
  632.                         BIGDOC_IN_RECT(pb).top = (int32)toprow;
  633.                         BIGDOC_IN_RECT(pb).bottom = (int32)MIN(toprow + chunksize, BIGDOC_FILTER_RECT(pb).bottom);
  634.  
  635.                         if (cnvused) {
  636.                                 // cnv() needs one extra pixel in each direction
  637.                                 if (BIGDOC_IN_RECT(pb).left > 0)
  638.                                         --BIGDOC_IN_RECT(pb).left;
  639.                                 if (BIGDOC_IN_RECT(pb).right < BIGDOC_IMAGE_SIZE(pb).h)
  640.                                         ++BIGDOC_IN_RECT(pb).right;
  641.                                 if (BIGDOC_IN_RECT(pb).top > 0)
  642.                                         --BIGDOC_IN_RECT(pb).top;
  643.                                 if (BIGDOC_IN_RECT(pb).bottom < BIGDOC_IMAGE_SIZE(pb).v)
  644.                                         ++BIGDOC_IN_RECT(pb).bottom;
  645.                         }
  646.                 }
  647.                 BIGDOC_OUT_RECT(pb) = BIGDOC_FILTER_RECT(pb);
  648.                 /*
  649.                 {char s[0x100];sprintf(s,"RequestNext needall=%d inRect=(%d,%d,%d,%d) filterRect=(%d,%d,%d,%d)",
  650.                                 needall,
  651.                                 BIGDOC_IN_RECT(pb).left,BIGDOC_IN_RECT(pb).top,BIGDOC_IN_RECT(pb).right,BIGDOC_IN_RECT(pb).bottom,
  652.                                 BIGDOC_FILTER_RECT(pb).left,BIGDOC_FILTER_RECT(pb).top,BIGDOC_FILTER_RECT(pb).right,BIGDOC_FILTER_RECT(pb).bottom);dbg(s);}
  653.                 */
  654.         } else {
  655.                 // if any of the formulae involve random access to image pixels,
  656.                 // ask for the entire image
  657.                 if (needall) {
  658.                         SETRECT(IN_RECT(pb), 0, 0, IMAGE_SIZE(pb).h, IMAGE_SIZE(pb).v);
  659.                 }
  660.                 else {
  661.                         // TODO: This does not work with GIMP. So, if we are using GIMP, we should
  662.                         //       somehow always use "needall=true", and/or find out why this doesn't work
  663.                         //       with GIMP.
  664.  
  665.                         // otherwise, process the filtered area, by chunksize parts
  666.                         IN_RECT(pb).left = FILTER_RECT(pb).left;
  667.                         IN_RECT(pb).right = FILTER_RECT(pb).right;
  668.                         IN_RECT(pb).top = (int16)toprow;
  669.                         IN_RECT(pb).bottom = (int16)MIN(toprow + chunksize, FILTER_RECT(pb).bottom);
  670.  
  671.                         if (cnvused) {
  672.                                 // cnv() needs one extra pixel in each direction
  673.                                 if (IN_RECT(pb).left > 0)
  674.                                         --IN_RECT(pb).left;
  675.                                 if (IN_RECT(pb).right < IMAGE_SIZE(pb).h)
  676.                                         ++IN_RECT(pb).right;
  677.                                 if (IN_RECT(pb).top > 0)
  678.                                         --IN_RECT(pb).top;
  679.                                 if (IN_RECT(pb).bottom < IMAGE_SIZE(pb).v)
  680.                                         ++IN_RECT(pb).bottom;
  681.                         }
  682.                 }
  683.                 OUT_RECT(pb) = FILTER_RECT(pb);
  684.                 /*
  685.                 {char s[0x100];sprintf(s,"RequestNext needall=%d inRect=(%d,%d,%d,%d) filterRect=(%d,%d,%d,%d)",
  686.                                 needall,
  687.                                 IN_RECT(pb).left,IN_RECT(pb).top,IN_RECT(pb).right,IN_RECT(pb).bottom,
  688.                                 FILTER_RECT(pb).left,FILTER_RECT(pb).top,FILTER_RECT(pb).right,FILTER_RECT(pb).bottom);dbg(s);}
  689.                 */
  690.         }
  691. }
  692.  
  693. void DoStart(FilterRecordPtr pb){
  694.         /* Global variable "needall": if src() or rad() functions are used, random access to the image data is required,
  695.            so we must request the entire image in a single chunk, otherwise we will use chunksize "CHUNK_ROWS". */
  696.         if (HAS_BIG_DOC(pb)) {
  697.                 chunksize = needall ? (BIGDOC_FILTER_RECT(pb).bottom - BIGDOC_FILTER_RECT(pb).top) : CHUNK_ROWS;
  698.                 toprow = BIGDOC_FILTER_RECT(pb).top;
  699.         } else {
  700.                 chunksize = needall ? (FILTER_RECT(pb).bottom - FILTER_RECT(pb).top) : CHUNK_ROWS;
  701.                 toprow = FILTER_RECT(pb).top;
  702.         }
  703.         RequestNext(pb);
  704. }
  705.  
  706. OSErr DoContinue(FilterRecordPtr pb){
  707.         OSErr e = noErr;
  708.         long outoffset;
  709.  
  710.         if (HAS_BIG_DOC(pb)) {
  711.                 VRect fr;
  712.                 if (needall) {
  713.                         fr = BIGDOC_FILTER_RECT(pb);  // filter whole selection at once
  714.                 } else if (cnvused) {
  715.                         // we've requested one pixel extra all around
  716.                         // (see RequestNext()), just for access purposes. But filter
  717.                         // original selection only.
  718.                         fr.left = BIGDOC_FILTER_RECT(pb).left;
  719.                         fr.right = BIGDOC_FILTER_RECT(pb).right;
  720.                         fr.top = toprow;
  721.                         fr.bottom = MIN(toprow + chunksize, BIGDOC_FILTER_RECT(pb).bottom);
  722.                 } else {  // filter whatever portion we've been given
  723.                         fr = BIGDOC_IN_RECT(pb);
  724.                 }
  725.  
  726.                 outoffset = (long)pb->outRowBytes * (fr.top - BIGDOC_OUT_RECT(pb).top)
  727.                         + (long)nplanes * (fr.left - BIGDOC_OUT_RECT(pb).left);
  728.  
  729.                 if (!(e = process_scaled_bigdoc(pb, true, fr, fr,
  730.                         (Ptr)pb->outData + outoffset, pb->outRowBytes, 1.)))
  731.                 {
  732.                         toprow += chunksize;
  733.                         if (toprow < BIGDOC_FILTER_RECT(pb).bottom)
  734.                                 RequestNext(pb);
  735.                         else {
  736.                                 SETRECT(BIGDOC_IN_RECT(pb), 0, 0, 0, 0);
  737.                                 BIGDOC_OUT_RECT(pb) = BIGDOC_MASK_RECT(pb) = BIGDOC_IN_RECT(pb);
  738.                         }
  739.                 }
  740.         } else {
  741.                 Rect fr;
  742.                 if (needall) {
  743.                         fr = FILTER_RECT(pb);  // filter whole selection at once
  744.                 } else if (cnvused) {
  745.                         // we've requested one pixel extra all around
  746.                         // (see RequestNext()), just for access purposes. But filter
  747.                         // original selection only.
  748.                         fr.left = FILTER_RECT(pb).left;
  749.                         fr.right = FILTER_RECT(pb).right;
  750.                         fr.top = toprow;
  751.                         fr.bottom = MIN(toprow + chunksize, FILTER_RECT(pb).bottom);
  752.                 } else {  // filter whatever portion we've been given
  753.                         fr = IN_RECT(pb);
  754.                 }
  755.  
  756.                 outoffset = (long)pb->outRowBytes*(fr.top - OUT_RECT(pb).top)
  757.                         + (long)nplanes*(fr.left - OUT_RECT(pb).left);
  758.  
  759.                 if(!(e = process_scaled_olddoc(pb, true, fr, fr,
  760.                         (Ptr)pb->outData+outoffset, pb->outRowBytes, 1.)))
  761.                 {
  762.                         toprow += chunksize;
  763.                         if(toprow < FILTER_RECT(pb).bottom)
  764.                                 RequestNext(pb);
  765.                         else{
  766.                                 SETRECT(IN_RECT(pb),0,0,0,0);
  767.                                 OUT_RECT(pb) = MASK_RECT(pb) = IN_RECT(pb);
  768.                         }
  769.                 }
  770.         }
  771.         return e;
  772. }
  773.  
  774. void DoFinish(FilterRecordPtr pb){
  775.         int i;
  776.  
  777.         UNREFERENCED_PARAMETER(pb);
  778.  
  779.         WriteScriptParamsOnRead();
  780.  
  781.         for(i = 4; i--;){
  782.                 freetree(tree[i]);
  783.                 if(expr[i]) free(expr[i]);
  784.         }
  785. }
  786.  
  787. InternalState saveInternalState() {
  788.         InternalState ret;
  789.         int i;
  790.  
  791.         ret.bak_obfusc = gdata->obfusc;
  792.         ret.bak_standalone = gdata->standalone;
  793.         ret.bak_parmloaded = gdata->parmloaded;
  794.         memcpy(&ret.bak_parm, &gdata->parm, sizeof(PARM_T));
  795.         for (i = 0; i < 4; i++) ret.bak_expr[i] = my_strdup(expr[i]);
  796.         for (i = 0; i < 8; i++) ret.bak_slider[i] = slider[i];
  797.  
  798.         return ret;
  799. }
  800.  
  801. void restoreInternalState(InternalState state) {
  802.         int i;
  803.         gdata->obfusc = state.bak_obfusc;
  804.         gdata->standalone = state.bak_standalone;
  805.         gdata->parmloaded = state.bak_parmloaded;
  806.         memcpy(&gdata->parm, &state.bak_parm, sizeof(PARM_T));
  807.         for (i = 0; i < 4; i++) {
  808.                 if (expr[i]) free(expr[i]);
  809.                 expr[i] = state.bak_expr[i];
  810.         }
  811.         for (i = 0; i < 8; i++) {
  812.                 slider[i] = state.bak_slider[i];
  813.         }
  814. }
  815.