Subversion Repositories filter_foundry

Rev

Rev 557 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
259 daniel-mar 1
/*
268 daniel-mar 2
    This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
536 daniel-mar 3
    Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.net
550 daniel-mar 4
    Copyright (C) 2018-2023 Daniel Marschall, ViaThinkSoft
259 daniel-mar 5
 
312 daniel-mar 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.
259 daniel-mar 10
 
312 daniel-mar 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.
259 daniel-mar 15
 
312 daniel-mar 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
259 daniel-mar 19
*/
20
 
21
//#include <stdio.h>
22
//#include <sound.h>
23
 
24
#include "ff.h"
25
 
444 daniel-mar 26
#include "str.h"
259 daniel-mar 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
 
264 daniel-mar 38
// Used to find out which host signatures and memory settings a plugin host has
39
//#define SHOW_HOST_DEBUG
40
 
480 daniel-mar 41
// Here are working variables:
259 daniel-mar 42
struct node *tree[4];
492 daniel-mar 43
TCHAR *err[4];
259 daniel-mar 44
int errpos[4],errstart[4],nplanes,cnvused,chunksize,toprow;
301 daniel-mar 45
value_type cell[NUM_CELLS];
480 daniel-mar 46
 
47
// this is the only memory area that keeps preserved by Photoshop:
259 daniel-mar 48
globals_t *gdata;
49
FilterRecordPtr gpb;
50
 
51
#ifdef MAC_ENV
483 daniel-mar 52
#define HINSTANCE HANDLE
53
#define hDllInstance NULL /* fake this Windows-only global */
259 daniel-mar 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
 
550 daniel-mar 69
unsigned long parm_hash(PARM_T *parm) {
259 daniel-mar 70
        unsigned long hash;
71
        int i;
72
 
393 daniel-mar 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]);
259 daniel-mar 80
 
81
        return hash;
82
}
83
 
559 daniel-mar 84
size_t get_temp_afs(TCHAR* outfilename, Boolean isStandalone, PARM_T *parm) {
444 daniel-mar 85
        char* atempdir;
373 daniel-mar 86
        int hash;
444 daniel-mar 87
        size_t i, j;
88
        TCHAR out[MAX_PATH + 1];
89
        char ahash[20];
373 daniel-mar 90
 
444 daniel-mar 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
 
373 daniel-mar 98
        #ifdef WIN_ENV
444 daniel-mar 99
        if (xstrlen(out) > 0) xstrcat(out, TEXT("\\"));
373 daniel-mar 100
        #else
444 daniel-mar 101
        if (xstrlen(out) > 0) xstrcat(out, TEXT("/"));
373 daniel-mar 102
        #endif
103
 
550 daniel-mar 104
        hash = (isStandalone) ? parm_hash(parm) : 0;
444 daniel-mar 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);
373 daniel-mar 119
}
120
 
533 daniel-mar 121
char* stristr(const char* str, const char* strSearch) {
122
        // Source: https://stackoverflow.com/questions/27303062/strstr-function-like-that-ignores-upper-or-lower-case
123
        char *sors, *subs, *res = NULL;
124
        if ((sors = _strdup(str)) != NULL) {
125
                if ((subs = _strdup(strSearch)) != NULL) {
126
                        res = strstr(_strlwr(sors), _strlwr(subs));
127
                        if (res != NULL)
128
                                res = (char*)str + (res - sors);
129
                        free(subs);
130
                }
131
                free(sors);
132
        }
133
        return res;
134
}
135
 
559 daniel-mar 136
#ifdef WIN_ENV
533 daniel-mar 137
BOOL CalledFromRunDLL32(HINSTANCE hinst) {
138
        char exename[MAX_PATH];
534 daniel-mar 139
        if (GetModuleFileNameA(hinst, exename, MAX_PATH) == 0) return false;
533 daniel-mar 140
        return stristr(exename, "rundll32") != NULL;
141
}
559 daniel-mar 142
#endif
533 daniel-mar 143
 
559 daniel-mar 144
#ifdef WIN_ENV
461 daniel-mar 145
void CALLBACK FakeRundll32(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow) {
463 daniel-mar 146
        UNREFERENCED_PARAMETER(hwnd);
147
        UNREFERENCED_PARAMETER(hinst);
148
        UNREFERENCED_PARAMETER(lpszCmdLine);
149
        UNREFERENCED_PARAMETER(nCmdShow);
461 daniel-mar 150
 
462 daniel-mar 151
        /*
463 daniel-mar 152
        char* tmp = (char*)malloc(512);
461 daniel-mar 153
        if (tmp != 0) {
154
                sprintf(tmp, "hwnd: %p\nhinst: %p\nlpszCmdLine: %s\nCmdShow: %d", (void*)(hwnd), (void*)(hinst), lpszCmdLine, nCmdShow);
155
                MessageBoxA(0, tmp, 0, 0);
156
                free(tmp);
157
        }
462 daniel-mar 158
        */
461 daniel-mar 159
 
492 daniel-mar 160
        simplealert_id(MSG_RUNDLL_ERR_ID);
161
 
461 daniel-mar 162
        return;
163
}
559 daniel-mar 164
#endif
461 daniel-mar 165
 
479 daniel-mar 166
void CreateDataPointer(intptr_t* data) {
167
        // Register "gdata" that contains the PARM information and other things which need to be persistant
168
        // and preserve them in *data
169
        // This memory allocation is never freed, because the filter can always be invoked again.
170
        // The memory allocation will be kept for the lifetime of the Photoshop process and
171
        // freed together with the application termination.
172
 
173
        // We have at least 5 options to allocate memory:
480 daniel-mar 174
 
175
        // (Method 1)
481 daniel-mar 176
        // 1. The deprecated Standard Buffer Suite (pb->bufferProcs) - works fine!
486 daniel-mar 177
        /*
178
        BufferID tempId;
179
        if (gpb->bufferProcs->allocateProc(sizeof(globals_t), &tempId) != noErr) {
180
                *data = NULL;
181
        }
182
        else {
183
                *data = (void*)gpb->bufferProcs->lockProc(tempId, true);
184
                if (*data) memset((void*)*data, 0, sizeof(globals_t));
185
        }
186
        */
480 daniel-mar 187
 
188
        // (Method 2) *DOES NOT WORK*
189
        // 2. The recommended buffer suite (kPSBufferSuite),
190
        //    It does not work, since it causes memory corruption when the filter is invoked a second time.
191
        //    Probably the BufferSuite cannot be used to share data between filter invocations?
529 daniel-mar 192
        //    Also, the buffer suite is only available on the Adobe Photoshop host application.
486 daniel-mar 193
        /*
194
        FFBuffer buf;
195
        newBuffer(&buf, sizeof(globals_t));
196
        *data = (intptr_t)lockBuffer(&buf);
197
        if (*data) memset((void*)*data, 0, sizeof(globals_t));
198
        */
480 daniel-mar 199
 
200
        // (Method 3)
479 daniel-mar 201
        // 3. Using malloc(), which works also fine and is more independent from the host. It is also easier.
202
        //    However, we do not know how malloc() is implemented, and it might cause problems if the
203
        //    DLL is unloaded between invocations.
486 daniel-mar 204
        /*
205
        *data = (intptr_t)malloc(sizeof(globals_t));
206
        if (*data) memset((void*)*data, 0, sizeof(globals_t));
207
        */
480 daniel-mar 208
 
209
        // (Method 4)
479 daniel-mar 210
        // 4. Using PLUGIN.DLL:NewPtr(). This does FilterFactory 3.0.4, but requires an Adobe host.
480 daniel-mar 211
        //    In Plugin.dll, the function NewPtr() and NewPtrClear() are implemented as GlobalAlloc/GlobalLock.
212
        //    Nothing special.
213
 
214
        // (Method 5)
479 daniel-mar 215
        // 5. Using GlobalAlloc/GlobalLock. This does FilterFactory 3.00 (Flags GHND = GMEM_MOVEABLE and GMEM_ZEROINIT).
216
        //    This is Windows dependant. (However, on Mac we will just call NewPtr.)
217
        //    GlobalAlloc and LocalAlloc are equal in 32-bit windows. The memory allocation is NOT global anymore,
218
        //    instead it is on the private heap of the application. (Therefore we do not cause a leak when
219
        //    Photoshop is closed).
480 daniel-mar 220
        // In Filter Foundry 1.7.0.17 we define a function called NewPtrClear, which is natively supported by Mac,
221
        // and in Windows it will be implemented using GlobalAlloc/GlobalLock.
479 daniel-mar 222
        *data = (intptr_t)NewPtrClear(sizeof(globals_t));
223
}
224
 
536 daniel-mar 225
DLLEXPORT MACPASCAL
259 daniel-mar 226
void ENTRYPOINT(short selector, FilterRecordPtr pb, intptr_t *data, short *result){
227
        static Boolean wantdialog = false;
228
        static Boolean premiereWarnedOnce = false;
393 daniel-mar 229
 
284 daniel-mar 230
        #ifdef SHOW_HOST_DEBUG
231
        char* tmp;
232
        #endif
268 daniel-mar 233
 
234
        #ifdef WIN_ENV
259 daniel-mar 235
        // For Windows, we use an activation context to enforce that our Manifest resource will
236
        // be used. This allows us to use Visual Styles, even if the host application does not
237
        // support it.
238
        ManifestActivationCtx manifestVars;
239
        BOOL activationContextUsed;
268 daniel-mar 240
        #endif
259 daniel-mar 241
 
284 daniel-mar 242
        // ---------------------------------------------------------------------
243
 
244
        EnterCodeResource();
245
 
283 daniel-mar 246
        #ifdef WIN_ENV
483 daniel-mar 247
        activationContextUsed = ActivateManifest((HMODULE)hDllInstance, 1, &manifestVars);
248
        #endif
249
 
250
        #ifdef WIN_ENV
533 daniel-mar 251
        if ((intptr_t)result == SW_SHOWDEFAULT && CalledFromRunDLL32((HINSTANCE)pb)) {
283 daniel-mar 252
                // When the 8BF file is analyzed with VirusTotal.com, it will invoke each
253
                // exported function by calling
461 daniel-mar 254
                // loaddll64.exe 'C:\Users\user\Desktop\attachment.dll'
255
                //        ==>  rundll32.exe C:\Users\user\Desktop\attachment.dll,PluginMain
256
                //           ==> C:\Windows\system32\WerFault.exe -u -p 6612 -s 480
257
                //
533 daniel-mar 258
                // But RunDLL32 requires the following signature:
461 daniel-mar 259
                //    void __stdcall EntryPoint(HWND hwnd,      HINSTANCE hinst,    LPSTR lpszCmdLine, int nCmdShow);
260
                // Our signature is:
261
                //    void           PluginMain(short selector, FilterRecordPtr pb, intptr_t *data,    short *result);
536 daniel-mar 262
                //
533 daniel-mar 263
                // Obviously, this will cause an Exception. (It crashes at *result=e because result is 0xA, which is SW_SHOWDEFAULT)
283 daniel-mar 264
                // Here is the problem: The crash will be handled by WerFault.exe inside the
265
                // VirusTotal virtual machine. WerFault connects to various servers (9 DNS resolutions!) and does
266
                // a lot of weird things, but VirusTotal thinks that our plugin does all that stuff,
267
                // and so they mark our plugin as "malware"!
268
                // This is a problem with VirusTotal! It shall not assume that WerFault.exe actions are our actions!
533 daniel-mar 269
                // Even actions from processes like "MicrosoftEdgeUpdate.exe" and "SpeechRuntime.exe" are reported to be our
283 daniel-mar 270
                // actions, although they have nothing to do with us!
271
                // See https://www.virustotal.com/gui/file/1f1012c567208186be455b81afc1ee407ae6476c197d633c70cc70929113223a/behavior
393 daniel-mar 272
                //
533 daniel-mar 273
                // Note in re "*result": Usually, The first 64KB of address space are always invalid. However, in Win32s (Windows 3.11), the
274
                // variable "result" is <=0xFFFF. So we cannot assume that result<=0xFFFF means that the call came from RunDLL32.
275
 
463 daniel-mar 276
                FakeRundll32((HWND)(intptr_t)selector, (HINSTANCE)pb, (LPSTR)data, (int)(intptr_t)result);
533 daniel-mar 277
                // (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?)
278
 
483 daniel-mar 279
                goto endmain;
283 daniel-mar 280
        }
281
        #endif
393 daniel-mar 282
 
559 daniel-mar 283
        // will be changed if an error happens
284
        *result = noErr;
285
 
264 daniel-mar 286
        #ifdef SHOW_HOST_DEBUG
284 daniel-mar 287
        tmp = (char*)malloc(512);
459 daniel-mar 288
        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);
284 daniel-mar 289
        simplealert(tmp);
424 daniel-mar 290
        free(tmp);
264 daniel-mar 291
        #endif
259 daniel-mar 292
 
293
        if (pb->hostSig == HOSTSIG_ADOBE_PREMIERE) {
529 daniel-mar 294
                // DM 19.07.2021 : Tried running the 8BF file in Adobe Premiere 5 + Win98
295
                // (yes, that's possible, and there is even a FilterFactory for Premiere!),
259 daniel-mar 296
                // but it crashes in evalpixel() where there is write-access to the "outp".
491 daniel-mar 297
                // DM 24.04.2022 : On Adobe Premiere 6 + Win10, the filter opens sometimes (and sometimes crashes inside DialogBoxParam),
298
                // but the filter is not applied when you click "OK" (because it crashes internally; only the debugger sees it)...
529 daniel-mar 299
                // Maybe the canvas structure is different (maybe it contains frames to achieve transitions?)
300
                // TODO: make Filter Foundry compatible with Premiere!
259 daniel-mar 301
                if (!premiereWarnedOnce) {
492 daniel-mar 302
                        simplealert_id(MSG_PREMIERE_COMPAT_ID);
259 daniel-mar 303
                }
304
                premiereWarnedOnce = true;
305
                *result = errPlugInHostInsufficient;
483 daniel-mar 306
                goto endmain;
259 daniel-mar 307
        }
308
 
309
        #ifdef DEBUG_SIMULATE_GIMP
310
        *data = 0;
311
        pb->parameters = pb->handleProcs->newProc(1);
312
        #endif
313
 
483 daniel-mar 314
        gpb = pb; // required for CreateDataPointer()
480 daniel-mar 315
 
259 daniel-mar 316
        if (selector != filterSelectorAbout && !*data) {
479 daniel-mar 317
                // The filter was never called before. We allocate (zeroed) memory now.
318
                CreateDataPointer(data);
319
                if (!*data) {
320
                        *result = memFullErr;
483 daniel-mar 321
                        goto endmain;
259 daniel-mar 322
                }
323
        }
324
 
479 daniel-mar 325
        gdata = (globals_t*)*data;
326
 
259 daniel-mar 327
        nplanes = MIN(pb->planes,4);
328
 
329
        switch (selector){
330
        case filterSelectorAbout:
331
                if (!gdata) {
550 daniel-mar 332
                        Boolean parmReadOk;
479 daniel-mar 333
                        // This is a temporary gdata structure just for the About dialog!
334
                        // Not to be confused with the "real" gdata during the filter invocation (containing more data).
259 daniel-mar 335
                        gdata = (globals_t*)malloc(sizeof(globals_t));
336
                        if (!gdata) break;
411 daniel-mar 337
                        gdata->hWndMainDlg = (HWND)((PlatformData*)((AboutRecordPtr)pb)->platformData)->hwnd; // so that simplealert() works
557 daniel-mar 338
                        parmReadOk = (LOADING_OK == readPARMresource((HMODULE)hDllInstance));
550 daniel-mar 339
                        if (!parmReadOk) gdata->parm.standalone = false;
340
                        if (parmReadOk && (gdata->parm.cbSize != PARM_SIZE) && (gdata->parm.cbSize != PARM_SIZE_PREMIERE) && (gdata->parm.cbSize != PARM_SIG_MAC)) {
341
                                parm_reset(true, true, true, true);
309 daniel-mar 342
                                if (gdata->obfusc) {
492 daniel-mar 343
                                        simplealert_id(MSG_INCOMPATIBLE_OBFUSCATION_ID);
309 daniel-mar 344
                                }
345
                                else {
492 daniel-mar 346
                                        simplealert_id(MSG_INVALID_PARAMETER_DATA_ID);
309 daniel-mar 347
                                }
348
                        }
349
                        else {
350
                                DoAbout((AboutRecordPtr)pb);
351
                        }
259 daniel-mar 352
                        free(gdata);
353
                        gdata = NULL;
354
                } else {
355
                        DoAbout((AboutRecordPtr)pb);
356
                }
357
                break;
358
        case filterSelectorParameters:
359
                wantdialog = true;
360
                break;
361
        case filterSelectorPrepare:
410 daniel-mar 362
                gdata->hWndMainDlg = 0;
259 daniel-mar 363
                DoPrepare(pb);
364
                init_symtab(predefs); // ready for parser calls
365
                init_trigtab();
366
                break;
367
        case filterSelectorStart:
368
                if (HAS_BIG_DOC(pb)) {
369
                        // The BigDocument structure is required if the document is larger than 30,000 pixels
370
                        // It deprecates imageSize, filterRect, inRect, outRect, maskRect, floatCoord, and wholeSize.
371
                        // By setting it to nonzero, we communicate to Photoshop that we support the BigDocument structure.
372
                        pb->bigDocumentData->PluginUsing32BitCoordinates = true;
373
                }
374
 
375
                /* initialise the parameter handle that Photoshop keeps for us */
376
                if(!pb->parameters)
377
                        pb->parameters = PINEWHANDLE(1); // don't set initial size to 0, since some hosts (e.g. GIMP/PSPI) are incompatible with that.
378
 
482 daniel-mar 379
                if (!pb->parameters) {
483 daniel-mar 380
                        *result = memFullErr;
482 daniel-mar 381
                }
382
                else
383
                {
384
                        wantdialog |= checkandinitparams(pb->parameters);
259 daniel-mar 385
 
482 daniel-mar 386
                        /* wantdialog = false means that we never got a Parameters call, so we're not supposed to ask user */
550 daniel-mar 387
                        if (wantdialog && (!gdata->parm.standalone || gdata->parm.popDialog)) {
482 daniel-mar 388
                                if (maindialog(pb)) {
389
                                        if (!host_preserves_parameters()) {
390
                                                /* Workaround for GIMP/PSPI, to avoid that formulas vanish when you re-open the main window.
391
                                                   The reason is a bug in PSPI: The host should preserve the value of pb->parameters, which PSPI does not do.
392
                                                   Also, all global variables are unloaded, so the plugin cannot preserve any data.
393
                                                   Workaround in FF 1.7: If the host GIMP is detected, then a special mode will be activated.
394
                                                   This mode saves the filter data into a temporary file "FilterFoundryXX.afs" and loads it
395
                                                   when the window is opened again. */
396
                                                   // Workaround: Save settings in "FilterFoundryXX.afs" if the host does not preserve pb->parameters
397
                                                TCHAR outfilename[MAX_PATH + 1];
398
                                                StandardFileReply sfr;
550 daniel-mar 399
                                                InternalState tmpState;
555 daniel-mar 400
                                                FFSavingResult saveres;
259 daniel-mar 401
 
482 daniel-mar 402
                                                sfr.sfGood = true;
403
                                                sfr.sfReplacing = true;
404
                                                sfr.sfType = PS_FILTER_FILETYPE;
259 daniel-mar 405
 
550 daniel-mar 406
                                                get_temp_afs(&outfilename[0], gdata->parm.standalone, &gdata->parm);
259 daniel-mar 407
 
482 daniel-mar 408
                                                xstrcpy(sfr.sfFile.szName, outfilename);
409
                                                #ifdef WIN_ENV
410
                                                sfr.nFileExtension = (WORD)(xstrlen(outfilename) - strlen(".afs") + 1);
411
                                                #endif
546 daniel-mar 412
                                                sfr.sfScript = smSystemScript;
264 daniel-mar 413
 
482 daniel-mar 414
                                                // We only want the parameters (ctl,map) in the temporary .afs file
415
                                                // It is important to remove the expressions, otherwise they would be
550 daniel-mar 416
                                                // revealed in the temporary files (That might be bad for obfuscated filters).
417
                                                if (gdata->parm.standalone) {
418
                                                        tmpState = saveInternalState();
419
                                                        parm_reset(false, false, false, true);
482 daniel-mar 420
                                                }
264 daniel-mar 421
 
555 daniel-mar 422
                                                saveres = savefile_afs_pff_picotxt_guf(&sfr);
264 daniel-mar 423
 
550 daniel-mar 424
                                                if (gdata->parm.standalone) {
425
                                                        restoreInternalState(tmpState);
377 daniel-mar 426
                                                }
555 daniel-mar 427
 
557 daniel-mar 428
                                                if (saveres != SAVING_OK) {
555 daniel-mar 429
                                                        TCHAR* reason = FF_GetMsg_Cpy(saveres);
430
                                                        alertuser_id(MSG_CANNOT_SAVE_SETTINGS_ID, reason);
431
                                                        FF_GetMsg_Free(reason);
432
                                                        *result = filterBadParameters;
433
                                                }
264 daniel-mar 434
                                        }
482 daniel-mar 435
                                        else {
436
                                                /* update stored parameters from new user settings */
437
                                                if (pb->parameters)
555 daniel-mar 438
                                                        saveparams_afs_pff(pb->parameters, false);
482 daniel-mar 439
                                        }
259 daniel-mar 440
                                }
482 daniel-mar 441
                                else
483 daniel-mar 442
                                        *result = userCanceledErr;
482 daniel-mar 443
                        }
444
                        wantdialog = false;
259 daniel-mar 445
                }
446
 
483 daniel-mar 447
                if(*result == noErr){
259 daniel-mar 448
                        if(setup(pb)){
449
                                DoStart(pb);
450
                        }else{
510 daniel-mar 451
                                simplealert_id(MSG_SAVED_EXPR_ERR_ID);
483 daniel-mar 452
                                *result = filterBadParameters;
259 daniel-mar 453
                        }
454
                }
455
                break;
456
        case filterSelectorContinue:
483 daniel-mar 457
                *result = DoContinue(pb);
259 daniel-mar 458
                break;
459
        case filterSelectorFinish:
460
                DoFinish(pb);
461
                break;
462
        default:
483 daniel-mar 463
                *result = filterBadParameters;
259 daniel-mar 464
        }
465
 
483 daniel-mar 466
endmain:
259 daniel-mar 467
 
476 daniel-mar 468
        // TODO: Question: Is that OK to call this every invocation, or should it be only around UI stuff?
259 daniel-mar 469
        #ifdef WIN_ENV
470
        if (activationContextUsed) DeactivateManifest(&manifestVars);
471
        #endif
472
 
473
        ExitCodeResource();
474
}
475
 
550 daniel-mar 476
void parm_reset(Boolean resetMetadata, Boolean resetSliderValues, Boolean resetSliderNames, Boolean resetFormulas) {
477
        gdata->parm.cbSize = PARM_SIZE;
478
        if (resetMetadata) {
479
                strcpy(gdata->parm.szCategory, "Filter Foundry");
480
                strcpy(gdata->parm.szTitle, "Untitled");
481
                strcpy(gdata->parm.szCopyright, ""); //"Filter Foundry Copyright (C) 2003-2009 Toby Thain, 2018-" RELEASE_YEAR " Daniel Marschall"
482
                strcpy(gdata->parm.szAuthor, "Anonymous");
483
        }
484
        if (resetSliderValues) {
485
                int i;
486
                for (i = 0; i < 8; ++i) {
487
                        gdata->parm.val[i] = (uint8_t)(i * 10 + 100);
488
                }
489
        }
490
        if (resetSliderNames) {
491
                int i;
492
                for (i = 0; i < 8; ++i) {
493
                        strcpy(gdata->parm.szCtl[i], "ctl(X)");
494
                        gdata->parm.szCtl[i][4] = '0' + i;
495
                }
496
                for (i = 0; i < 4; ++i) {
497
                        strcpy(gdata->parm.szMap[i], "Map X");
498
                        gdata->parm.szMap[i][4] = '0' + i;
499
                }
500
        }
501
        if (resetFormulas) {
502
                if (gpb->imageMode == plugInModeRGBColor) {
503
                        strcpy(gdata->parm.szFormula[0], "r");
504
                        strcpy(gdata->parm.szFormula[1], "g");
505
                        strcpy(gdata->parm.szFormula[2], "b");
506
                        strcpy(gdata->parm.szFormula[3], "a");
507
                }
508
                else {
509
                        strcpy(gdata->parm.szFormula[0], "c");
510
                        strcpy(gdata->parm.szFormula[1], "c");
511
                        strcpy(gdata->parm.szFormula[2], "c");
512
                        strcpy(gdata->parm.szFormula[3], "c");
513
                }
514
        }
515
}
516
 
517
void parm_cleanup() {
518
        // Cleanup "PARM" resource by removing stuff after the null terminators, to avoid that parts of confidential formulas are leaked
519
        int i;
520
 
521
        {
522
                char tmp[256];
523
 
524
                strcpy(tmp, gdata->parm.szCategory);
525
                memset(gdata->parm.szCategory, 0, sizeof(gdata->parm.szCategory));
526
                strcpy(gdata->parm.szCategory, tmp);
527
 
528
                strcpy(tmp, gdata->parm.szTitle);
529
                memset(gdata->parm.szTitle, 0, sizeof(gdata->parm.szTitle));
530
                strcpy(gdata->parm.szTitle, tmp);
531
 
532
                strcpy(tmp, gdata->parm.szCopyright);
533
                memset(gdata->parm.szCopyright, 0, sizeof(gdata->parm.szCopyright));
534
                strcpy(gdata->parm.szCopyright, tmp);
535
 
536
                strcpy(tmp, gdata->parm.szAuthor);
537
                memset(gdata->parm.szAuthor, 0, sizeof(gdata->parm.szAuthor));
538
                strcpy(gdata->parm.szAuthor, tmp);
539
        }
540
 
541
        for (i = 0; i < 4; i++) {
542
                char tmp[256];
543
                strcpy(tmp, gdata->parm.szMap[i]);
544
                memset(gdata->parm.szMap[i], 0, sizeof(gdata->parm.szMap[i]));
545
                strcpy(gdata->parm.szMap[i], tmp);
546
        }
547
 
548
        for (i = 0; i < 8; i++) {
549
                char tmp[256];
550
                strcpy(tmp, gdata->parm.szCtl[i]);
551
                memset(gdata->parm.szCtl[i], 0, sizeof(gdata->parm.szCtl[i]));
552
                strcpy(gdata->parm.szCtl[i], tmp);
553
        }
554
 
555
        for (i = 0; i < 4; i++) {
556
                char tmp[1024];
557
                strcpy(tmp, gdata->parm.szFormula[i]);
558
                memset(gdata->parm.szFormula[i], 0, sizeof(gdata->parm.szFormula[i]));
559
                strcpy(gdata->parm.szFormula[i], tmp);
560
        }
561
}
562
 
259 daniel-mar 563
int checkandinitparams(Handle params){
456 daniel-mar 564
        int i;
565
        Boolean bUninitializedParams;
259 daniel-mar 566
        Boolean showdialog;
550 daniel-mar 567
        InternalState tmpState;
259 daniel-mar 568
 
569
        if (!host_preserves_parameters()) {
570
                // Workaround: Load settings in "FilterFoundryXX.afs" if host does not preserve pb->parameters
444 daniel-mar 571
                TCHAR outfilename[MAX_PATH + 1];
550 daniel-mar 572
                Boolean parmReadOk;
259 daniel-mar 573
                StandardFileReply sfr;
550 daniel-mar 574
                char bakexpr[4][MAXEXPR];
264 daniel-mar 575
 
259 daniel-mar 576
                sfr.sfGood = true;
577
                sfr.sfReplacing = true;
578
                sfr.sfType = PS_FILTER_FILETYPE;
579
 
557 daniel-mar 580
                parmReadOk = (LOADING_OK == readPARMresource((HMODULE)hDllInstance));
550 daniel-mar 581
                if (!parmReadOk) gdata->parm.standalone = false;
582
                if (parmReadOk && (gdata->parm.cbSize != PARM_SIZE) && (gdata->parm.cbSize != PARM_SIZE_PREMIERE) && (gdata->parm.cbSize != PARM_SIG_MAC)) {
583
                        parm_reset(true, true, true, true);
309 daniel-mar 584
                        if (gdata->obfusc) {
492 daniel-mar 585
                                simplealert_id(MSG_INCOMPATIBLE_OBFUSCATION_ID);
309 daniel-mar 586
                        }
587
                        else {
492 daniel-mar 588
                                simplealert_id(MSG_INVALID_PARAMETER_DATA_ID);
309 daniel-mar 589
                        }
590
                        return false;
591
                }
259 daniel-mar 592
 
550 daniel-mar 593
                get_temp_afs(&outfilename[0], parmReadOk, &gdata->parm);
259 daniel-mar 594
 
444 daniel-mar 595
                xstrcpy(sfr.sfFile.szName, outfilename);
264 daniel-mar 596
                #ifdef WIN_ENV
444 daniel-mar 597
                sfr.nFileExtension = (WORD)(xstrlen(outfilename) - strlen(".afs") + 1);
264 daniel-mar 598
                #endif
546 daniel-mar 599
                sfr.sfScript = smSystemScript;
259 daniel-mar 600
 
550 daniel-mar 601
                if (parmReadOk) {
602
                        tmpState = saveInternalState();
264 daniel-mar 603
                }
604
 
557 daniel-mar 605
                if (LOADING_OK == loadfile(&sfr)) {
550 daniel-mar 606
                        if (parmReadOk) {
607
                                // In the standalone filter, we only want the parameters (ctl,map) in the temporary .afs file, not the formulas
608
                                // We do not need to care about the metadata, because the AFS does not touch the metadata anyway
377 daniel-mar 609
                                for (i = 0; i < 4; i++) {
550 daniel-mar 610
                                        strcpy(bakexpr[i], gdata->parm.szFormula[i]);
377 daniel-mar 611
                                }
550 daniel-mar 612
                                restoreInternalState(tmpState);
613
                                for (i = 0; i < 4; i++) {
614
                                        strcpy(gdata->parm.szFormula[i],bakexpr[i]);
615
                                }
259 daniel-mar 616
                        }
264 daniel-mar 617
 
618
                        return true;
259 daniel-mar 619
                }
620
        }
621
 
557 daniel-mar 622
        if( (bUninitializedParams = !(params && (LOADING_OK == readparams_afs_pff(params, false)))) ){
259 daniel-mar 623
                /* either the parameter handle was uninitialised,
624
                   or the parameter data couldn't be read; set default values */
625
 
550 daniel-mar 626
                Boolean parmReadOk;
627
 
259 daniel-mar 628
                // see if saved parameters exist
557 daniel-mar 629
                parmReadOk = (LOADING_OK == readPARMresource((HMODULE)hDllInstance));
550 daniel-mar 630
                if (!parmReadOk) gdata->parm.standalone = false;
631
                if (parmReadOk && (gdata->parm.cbSize != PARM_SIZE) && (gdata->parm.cbSize != PARM_SIZE_PREMIERE) && (gdata->parm.cbSize != PARM_SIG_MAC)) {
632
                        parm_reset(true, true, true, true);
309 daniel-mar 633
                        if (gdata->obfusc) {
492 daniel-mar 634
                                simplealert_id(MSG_INCOMPATIBLE_OBFUSCATION_ID);
309 daniel-mar 635
                        }
636
                        else {
492 daniel-mar 637
                                simplealert_id(MSG_INVALID_PARAMETER_DATA_ID);
309 daniel-mar 638
                        }
639
                        return false;
640
                }
259 daniel-mar 641
 
550 daniel-mar 642
                if(!gdata->parm.standalone){
259 daniel-mar 643
                        // no saved settings (not standalone)
550 daniel-mar 644
                        parm_reset(true, true, true, true);
259 daniel-mar 645
                }
646
        }
647
 
648
        // let scripting system change parameters, if we're scripted;
649
        // user may want to force display of dialog during scripting playback
650
        switch (ReadScriptParamsOnRead()) {
651
        case SCR_SHOW_DIALOG:
652
                showdialog = true;
653
                break;
654
        case SCR_HIDE_DIALOG:
655
                showdialog = false;
656
                break;
657
        default:
658
        case SCR_NO_SCRIPT:
659
                showdialog = bUninitializedParams;
660
                break;
661
        }
662
 
555 daniel-mar 663
        if (params) saveparams_afs_pff(params, false);
259 daniel-mar 664
 
665
        return showdialog;
666
}
667
 
532 daniel-mar 668
Boolean host_preserves_parameters(void) {
259 daniel-mar 669
        #ifdef DEBUG_SIMULATE_GIMP
670
        return false;
671
        #endif
672
 
673
        if (gpb->hostSig == HOSTSIG_GIMP) return false;
674
        if (gpb->hostSig == HOSTSIG_IRFANVIEW) return false;
675
 
676
        // We just assume the other hosts preserve the parameters
677
        return true;
678
}
679
 
532 daniel-mar 680
int64_t maxspace(void){
259 daniel-mar 681
        // Please see "Hosts.md" for details about the MaxSpace implementations of tested plugins
682
 
683
        // Plugins that don't support MaxSpace64 shall set the field to zero; then we will use MaxSpace instead.
684
        // Also check "gpb->bufferProcs->numBufferProcs" to see if 64 bit API is available
685
        if ((gpb->bufferProcs->numBufferProcs >= 8) && (gpb->maxSpace64 > 0)) {
686
                uint64_t maxSpace64 = gpb->maxSpace64;
687
 
688
                return maxSpace64;
689
        } else {
690
                // 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.
691
                unsigned int maxSpace32 = (unsigned int) gpb->maxSpace;
692
                uint64_t maxSpace64 = maxSpace32;
693
 
694
                if (gpb->hostSig == HOSTSIG_IRFANVIEW) maxSpace64 *= 1024; // IrfanView is giving Kilobytes instead of Bytes
695
                //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???
696
 
697
                return maxSpace64;
698
        }
699
}
700
 
532 daniel-mar 701
Boolean maxspace_available(void) {
259 daniel-mar 702
        // Please see "Hosts.md" for details about the MaxSpace implementations of tested plugins
703
 
704
        // GIMP PSPI sets MaxSpace to hardcoded 100 MB
705
        if (gpb->hostSig == HOSTSIG_GIMP) return false;
706
 
707
        // HOSTSIG_PAINT_NET sets MaxSpace to hardcoded 1 GB, see https://github.com/0xC0000054/PSFilterPdn/issues/5
708
        // Comment by the host author "This was done to avoid any compatibility issues with plugins handling 2 GB - 1"
709
        if (gpb->hostSig == HOSTSIG_PAINT_NET) return false;
710
 
711
        return true;
712
}
713
 
714
void DoPrepare(FilterRecordPtr pb){
715
        int i;
716
 
717
        for(i = 4; i--;){
718
                tree[i] = NULL;
482 daniel-mar 719
                err[i] = NULL;
259 daniel-mar 720
        }
721
 
722
        // Commented out by DM, 18 Dec 2018:
723
        // This code did not work on systems with 8 GB RAM:
724
        /*
725
        long space = (pb->maxSpace*9)/10; // don't ask for more than 90% of available memory
726
 
727
        maxSpace = 512L<<10; // this is a wild guess, actually
728
        if(maxSpace > space)
729
                        maxSpace = space;
730
        pb->maxSpace = maxSpace;
731
        */
732
 
733
        // New variant:
734
        if (maxspace_available()) {
481 daniel-mar 735
                // don't ask for more than 90% of available memory
736
                int64 ninetyPercent = (int64)ceil((maxspace() / 10.) * 9);
737
                if ((gpb->bufferProcs->numBufferProcs >= 8) && (gpb->maxSpace64 > 0)) {
738
                        pb->maxSpace64 = ninetyPercent;
739
                }
740
                if (ninetyPercent <= 0x7FFFFFFF) {
741
                        pb->maxSpace = (int32)ninetyPercent;
742
                }
259 daniel-mar 743
        }
744
}
745
 
456 daniel-mar 746
void RequestNext(FilterRecordPtr pb){
259 daniel-mar 747
        /* Request next block of the image */
748
 
749
        pb->inLoPlane = pb->outLoPlane = 0;
750
        pb->inHiPlane = pb->outHiPlane = nplanes-1;
751
 
752
        if (HAS_BIG_DOC(pb)) {
753
                // if any of the formulae involve random access to image pixels,
754
                // ask for the entire image
755
                if (needall) {
756
                        SETRECT(BIGDOC_IN_RECT(pb), 0, 0, BIGDOC_IMAGE_SIZE(pb).h, BIGDOC_IMAGE_SIZE(pb).v);
757
                } else {
758
                        // TODO: This does not work with GIMP. So, if we are using GIMP, we should
759
                        //       somehow always use "needall=true", and/or find out why this doesn't work
760
                        //       with GIMP.
761
 
762
                        // otherwise, process the filtered area, by chunksize parts
763
                        BIGDOC_IN_RECT(pb).left = BIGDOC_FILTER_RECT(pb).left;
764
                        BIGDOC_IN_RECT(pb).right = BIGDOC_FILTER_RECT(pb).right;
765
                        BIGDOC_IN_RECT(pb).top = (int32)toprow;
766
                        BIGDOC_IN_RECT(pb).bottom = (int32)MIN(toprow + chunksize, BIGDOC_FILTER_RECT(pb).bottom);
767
 
768
                        if (cnvused) {
769
                                // cnv() needs one extra pixel in each direction
770
                                if (BIGDOC_IN_RECT(pb).left > 0)
771
                                        --BIGDOC_IN_RECT(pb).left;
772
                                if (BIGDOC_IN_RECT(pb).right < BIGDOC_IMAGE_SIZE(pb).h)
773
                                        ++BIGDOC_IN_RECT(pb).right;
774
                                if (BIGDOC_IN_RECT(pb).top > 0)
775
                                        --BIGDOC_IN_RECT(pb).top;
776
                                if (BIGDOC_IN_RECT(pb).bottom < BIGDOC_IMAGE_SIZE(pb).v)
777
                                        ++BIGDOC_IN_RECT(pb).bottom;
778
                        }
779
                }
780
                BIGDOC_OUT_RECT(pb) = BIGDOC_FILTER_RECT(pb);
781
                /*
782
                {char s[0x100];sprintf(s,"RequestNext needall=%d inRect=(%d,%d,%d,%d) filterRect=(%d,%d,%d,%d)",
783
                                needall,
784
                                BIGDOC_IN_RECT(pb).left,BIGDOC_IN_RECT(pb).top,BIGDOC_IN_RECT(pb).right,BIGDOC_IN_RECT(pb).bottom,
785
                                BIGDOC_FILTER_RECT(pb).left,BIGDOC_FILTER_RECT(pb).top,BIGDOC_FILTER_RECT(pb).right,BIGDOC_FILTER_RECT(pb).bottom);dbg(s);}
786
                */
787
        } else {
788
                // if any of the formulae involve random access to image pixels,
789
                // ask for the entire image
790
                if (needall) {
791
                        SETRECT(IN_RECT(pb), 0, 0, IMAGE_SIZE(pb).h, IMAGE_SIZE(pb).v);
792
                }
793
                else {
794
                        // TODO: This does not work with GIMP. So, if we are using GIMP, we should
795
                        //       somehow always use "needall=true", and/or find out why this doesn't work
796
                        //       with GIMP.
797
 
798
                        // otherwise, process the filtered area, by chunksize parts
799
                        IN_RECT(pb).left = FILTER_RECT(pb).left;
800
                        IN_RECT(pb).right = FILTER_RECT(pb).right;
801
                        IN_RECT(pb).top = (int16)toprow;
802
                        IN_RECT(pb).bottom = (int16)MIN(toprow + chunksize, FILTER_RECT(pb).bottom);
803
 
804
                        if (cnvused) {
805
                                // cnv() needs one extra pixel in each direction
806
                                if (IN_RECT(pb).left > 0)
807
                                        --IN_RECT(pb).left;
808
                                if (IN_RECT(pb).right < IMAGE_SIZE(pb).h)
809
                                        ++IN_RECT(pb).right;
810
                                if (IN_RECT(pb).top > 0)
811
                                        --IN_RECT(pb).top;
812
                                if (IN_RECT(pb).bottom < IMAGE_SIZE(pb).v)
813
                                        ++IN_RECT(pb).bottom;
814
                        }
815
                }
816
                OUT_RECT(pb) = FILTER_RECT(pb);
817
                /*
818
                {char s[0x100];sprintf(s,"RequestNext needall=%d inRect=(%d,%d,%d,%d) filterRect=(%d,%d,%d,%d)",
819
                                needall,
820
                                IN_RECT(pb).left,IN_RECT(pb).top,IN_RECT(pb).right,IN_RECT(pb).bottom,
821
                                FILTER_RECT(pb).left,FILTER_RECT(pb).top,FILTER_RECT(pb).right,FILTER_RECT(pb).bottom);dbg(s);}
822
                */
823
        }
824
}
825
 
826
void DoStart(FilterRecordPtr pb){
264 daniel-mar 827
        /* Global variable "needall": if src() or rad() functions are used, random access to the image data is required,
828
           so we must request the entire image in a single chunk, otherwise we will use chunksize "CHUNK_ROWS". */
259 daniel-mar 829
        if (HAS_BIG_DOC(pb)) {
830
                chunksize = needall ? (BIGDOC_FILTER_RECT(pb).bottom - BIGDOC_FILTER_RECT(pb).top) : CHUNK_ROWS;
831
                toprow = BIGDOC_FILTER_RECT(pb).top;
832
        } else {
833
                chunksize = needall ? (FILTER_RECT(pb).bottom - FILTER_RECT(pb).top) : CHUNK_ROWS;
834
                toprow = FILTER_RECT(pb).top;
835
        }
456 daniel-mar 836
        RequestNext(pb);
259 daniel-mar 837
}
838
 
839
OSErr DoContinue(FilterRecordPtr pb){
840
        OSErr e = noErr;
841
        long outoffset;
842
 
843
        if (HAS_BIG_DOC(pb)) {
844
                VRect fr;
845
                if (needall) {
846
                        fr = BIGDOC_FILTER_RECT(pb);  // filter whole selection at once
847
                } else if (cnvused) {
848
                        // we've requested one pixel extra all around
849
                        // (see RequestNext()), just for access purposes. But filter
850
                        // original selection only.
851
                        fr.left = BIGDOC_FILTER_RECT(pb).left;
852
                        fr.right = BIGDOC_FILTER_RECT(pb).right;
853
                        fr.top = toprow;
854
                        fr.bottom = MIN(toprow + chunksize, BIGDOC_FILTER_RECT(pb).bottom);
855
                } else {  // filter whatever portion we've been given
856
                        fr = BIGDOC_IN_RECT(pb);
857
                }
858
 
859
                outoffset = (long)pb->outRowBytes * (fr.top - BIGDOC_OUT_RECT(pb).top)
860
                        + (long)nplanes * (fr.left - BIGDOC_OUT_RECT(pb).left);
861
 
862
                if (!(e = process_scaled_bigdoc(pb, true, fr, fr,
863
                        (Ptr)pb->outData + outoffset, pb->outRowBytes, 1.)))
864
                {
865
                        toprow += chunksize;
866
                        if (toprow < BIGDOC_FILTER_RECT(pb).bottom)
456 daniel-mar 867
                                RequestNext(pb);
259 daniel-mar 868
                        else {
869
                                SETRECT(BIGDOC_IN_RECT(pb), 0, 0, 0, 0);
870
                                BIGDOC_OUT_RECT(pb) = BIGDOC_MASK_RECT(pb) = BIGDOC_IN_RECT(pb);
871
                        }
872
                }
873
        } else {
874
                Rect fr;
875
                if (needall) {
876
                        fr = FILTER_RECT(pb);  // filter whole selection at once
877
                } else if (cnvused) {
878
                        // we've requested one pixel extra all around
879
                        // (see RequestNext()), just for access purposes. But filter
880
                        // original selection only.
881
                        fr.left = FILTER_RECT(pb).left;
882
                        fr.right = FILTER_RECT(pb).right;
883
                        fr.top = toprow;
884
                        fr.bottom = MIN(toprow + chunksize, FILTER_RECT(pb).bottom);
885
                } else {  // filter whatever portion we've been given
886
                        fr = IN_RECT(pb);
887
                }
888
 
889
                outoffset = (long)pb->outRowBytes*(fr.top - OUT_RECT(pb).top)
890
                        + (long)nplanes*(fr.left - OUT_RECT(pb).left);
891
 
892
                if(!(e = process_scaled_olddoc(pb, true, fr, fr,
893
                        (Ptr)pb->outData+outoffset, pb->outRowBytes, 1.)))
894
                {
895
                        toprow += chunksize;
896
                        if(toprow < FILTER_RECT(pb).bottom)
456 daniel-mar 897
                                RequestNext(pb);
259 daniel-mar 898
                        else{
899
                                SETRECT(IN_RECT(pb),0,0,0,0);
900
                                OUT_RECT(pb) = MASK_RECT(pb) = IN_RECT(pb);
901
                        }
902
                }
903
        }
904
        return e;
905
}
906
 
907
void DoFinish(FilterRecordPtr pb){
908
        int i;
909
 
433 daniel-mar 910
        UNREFERENCED_PARAMETER(pb);
911
 
259 daniel-mar 912
        WriteScriptParamsOnRead();
913
 
914
        for(i = 4; i--;){
915
                freetree(tree[i]);
916
        }
917
}
379 daniel-mar 918
 
532 daniel-mar 919
InternalState saveInternalState(void) {
430 daniel-mar 920
        InternalState ret;
379 daniel-mar 921
        ret.bak_obfusc = gdata->obfusc;
922
        memcpy(&ret.bak_parm, &gdata->parm, sizeof(PARM_T));
923
 
924
        return ret;
925
}
926
 
430 daniel-mar 927
void restoreInternalState(InternalState state) {
379 daniel-mar 928
        gdata->obfusc = state.bak_obfusc;
929
        memcpy(&gdata->parm, &state.bak_parm, sizeof(PARM_T));
930
}