Subversion Repositories filter_foundry

Rev

Rev 207 | Rev 209 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
184 dmarschall 1
/*
207 daniel-mar 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
184 dmarschall 5
 
207 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.
184 dmarschall 10
 
207 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.
184 dmarschall 15
 
207 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
184 dmarschall 19
*/
20
 
21
#include <stddef.h>
22
#include <stdint.h>
185 dmarschall 23
#include <assert.h>
184 dmarschall 24
 
25
#include "ff.h"
26
#include "symtab.h"
27
 
28
#include "PIActions.h"
29
#include "PITerminology.h"
30
 
31
#include "compat_string.h"
32
 
207 daniel-mar 33
//#define PRINTABLE_HASH_FF16
34
 
184 dmarschall 35
/*
207 daniel-mar 36
Find a printable 4-character key, remembering (see Photoshop API guide):
37
- All IDs starting with an uppercase letter are reserved by Adobe.
38
- All IDs that are all uppercase are reserved by Apple.
39
- All IDs that are all lowercase are reserved by Apple.
40
- This leaves all IDs that begin with a lowercase letter and have at least
41
  one uppercase letter for you and other plug-in developers.
42
Note: It is questionable if "a!!!" or "A!!!" are also reseved by Apple. We don't risk it.
184 dmarschall 43
*/
44
unsigned long printablehash(unsigned long hash) {
207 daniel-mar 45
#ifdef PRINTABLE_HASH_FF16
46
        // FilterFoundry version 1.6 hashing by Toby Thain
47
        // Only accepts upper case at the last character
48
        // 0       = 'a  A'
49
        // 6100899 = 'z~~Z'
50
        unsigned long key = 'a' + (hash % 26);  hash /= 26; // first lower case
51
        key = (key << 8) | (' ' + (hash % 95)); hash /= 95; // any printable
52
        key = (key << 8) | (' ' + (hash % 95)); hash /= 95; // any printable
53
        return  (key << 8) | ('A' + (hash % 26));           // last upper case
54
#else
55
        // FilterFoundry version 1.7 hashing by Daniel Marschall
56
        // Accepts upper case at character 2, 3 or 4
57
        // Spaces are only set the right as padding to make a code shorter
58
        // 0        = 'aA  '
59
        // 13530139 = 'zZZZ'
60
        // The key-space is ~2.22 times larger
61
        long lowlim;
62
        long uplim;
63
        int upperCaseInfo;
64
        int length;
65
        int lastThreeCharInfo;
66
        int firstChar;
67
        unsigned long key;
68
        int found;
69
        int i,j,k;
70
 
71
        uplim = 0;
72
        for (k=1; k<4; k++) {
73
                int mask;
74
                if (k == 1) mask = 1;//0b1;
75
                if (k == 2) mask = 2;//0b11;
76
                if (k == 3) mask = 4;//0b111;
77
                for (i=1; i<=mask; i++) {
78
                        // 'k' characters
79
                        long test = 1;
80
                        for (j=0; j<k; ++j) {
81
                                test *= ((i&(1<<j)) != 0) ? 26 : 94-26;
82
                        }
83
                        uplim += test;
84
                }
85
        }
86
 
87
        lastThreeCharInfo = hash % uplim; hash /= uplim;
88
        firstChar = hash%26; hash /= 26;
89
 
90
        lowlim = 0;
91
        uplim = 0;
92
        found = 0;
93
        length = -1; // avoid compiler warning
94
        upperCaseInfo = -1; // avoid compiler warning
95
        for (k=1; k<4; k++) {
96
                int mask;
97
                if (k == 1) mask = 1;//0b1;
98
                if (k == 2) mask = 2;//0b11;
99
                if (k == 3) mask = 4;//0b111;
100
                if (!found) for (i=1; i<=mask; i++) {
101
                        // 'k' characters
102
                        long test = 1;
103
                        for (j=0; j<k; ++j) {
104
                                test *= ((i&(1<<j)) != 0) ? 26 : 94-26;
105
                        }
106
                        uplim += test;
107
                        if ((lastThreeCharInfo >= lowlim) && (lastThreeCharInfo < uplim)) {
108
                                lastThreeCharInfo -= lowlim;
109
                                found = 1;
110
                                length = k;
111
                                upperCaseInfo = i;
112
                                break;
113
                        }
114
                        lowlim = uplim;
115
                }
116
        }
117
 
118
        key = ('a' + firstChar) << 24; // first char is lower-case
119
        for (i=0; i<length; ++i) {
120
                char res;
121
                if ((upperCaseInfo&(1<<i)) == 0) {
122
                        res = '!' + (lastThreeCharInfo % (94-26));
123
                        if (res >= 'A') res += 26;
124
                        lastThreeCharInfo /= (94-26);
125
                } else {
126
                        res = 'A' + (lastThreeCharInfo % 26);
127
                        lastThreeCharInfo /= 26;
128
                }
129
                key |= res << (8*(i+(4-length-1))); // 2nd, 3rd, 4th char are either upper-case or any printable char
130
        }
131
        if (length == 1) {
132
                key &= 0xFFFF0000;
133
                key |= ' ' << 8;
134
                key |= ' ';
135
        }
136
        if (length == 2) {
137
                key &= 0xFFFFFF00;
138
                key |= ' ';
139
        }
140
        return key;
141
#endif
184 dmarschall 142
}
143
 
144
long roundToNext4(long x) {
207 daniel-mar 145
        int pad = 4 - (x % 4);
146
        if (pad == 0) pad = 4;
147
        return x + pad;
184 dmarschall 148
}
149
 
208 daniel-mar 150
size_t fixpipl(PIPropertyList *pipl, size_t origsize, StringPtr title, long *event_id) {
207 daniel-mar 151
        PIProperty *prop;
152
        char *p;
153
        struct hstm_data {
154
                /* this structure must be 14+1 bytes long, to match PiPL structure */
155
                long version; /* = 0 */
156
                long class_id;
157
                long event_id;
158
                short aete_resid;
159
                char scope[1];
160
        };
161
        struct hstm_data *hstm;
162
        int scopelen;
163
        unsigned long hash;
184 dmarschall 164
 
207 daniel-mar 165
        pipl->count += 3; // 3 more keys in PiPL: name, catg, hstm
184 dmarschall 166
 
207 daniel-mar 167
        p = (char*)pipl + origsize;
168
        prop = (PIProperty*)p;
184 dmarschall 169
 
207 daniel-mar 170
        /* add Title/Name property key */
184 dmarschall 171
 
207 daniel-mar 172
        prop->vendorID = kPhotoshopSignature;
173
        prop->propertyKey = PINameProperty;
174
        prop->propertyID = 0;
175
        prop->propertyLength = roundToNext4(title[0] + 1);
176
        PLstrcpy((StringPtr)prop->propertyData, title);
184 dmarschall 177
 
207 daniel-mar 178
        // skip past new property record, and any padding
179
        p += offsetof(PIProperty, propertyData) + prop->propertyLength;
180
        prop = (PIProperty*)p;
184 dmarschall 181
 
207 daniel-mar 182
        /* add Category property key */
184 dmarschall 183
 
207 daniel-mar 184
        prop->vendorID = kPhotoshopSignature;
185
        prop->propertyKey = PICategoryProperty;
186
        prop->propertyID = 0;
187
        prop->propertyLength = roundToNext4(gdata->parm.category[0] + 1);
188
        PLstrcpy((StringPtr)prop->propertyData, gdata->parm.category);
184 dmarschall 189
 
207 daniel-mar 190
        p += offsetof(PIProperty, propertyData) + prop->propertyLength;
191
        prop = (PIProperty*)p;
184 dmarschall 192
 
207 daniel-mar 193
        /* add HasTerminology property key */
184 dmarschall 194
 
207 daniel-mar 195
        /* construct scope string by concatenating Category and Title - hopefully unique! */
196
        hstm = (struct hstm_data*)prop->propertyData;
197
        scopelen = sprintf(hstm->scope, "%s %s",
198
                INPLACEP2CSTR(gdata->parm.category),
199
                INPLACEP2CSTR(title));
184 dmarschall 200
 
207 daniel-mar 201
        /* make up a new event ID for this aete, based on printable base-95 hash of scope */
202
        hash = djb2(hstm->scope);
208 daniel-mar 203
        *event_id = printablehash(hash); /* this is used by aete_generate() later... */
184 dmarschall 204
 
207 daniel-mar 205
        prop->vendorID = kPhotoshopSignature;
206
        prop->propertyKey = PIHasTerminologyProperty;
207
        prop->propertyID = 0;
208
        prop->propertyLength = roundToNext4(offsetof(struct hstm_data, scope) + scopelen);
184 dmarschall 209
 
207 daniel-mar 210
        hstm->version = 0;
211
        hstm->class_id = plugInClassID;
212
        hstm->event_id = event_id;
213
        hstm->aete_resid = AETE_ID;
184 dmarschall 214
 
207 daniel-mar 215
        p += offsetof(PIProperty, propertyData) + prop->propertyLength;
184 dmarschall 216
 
207 daniel-mar 217
        return p - (char*)pipl;  // figure how many bytes were added
184 dmarschall 218
}
219
 
187 dmarschall 220
void _aete_write_byte(void** aeteptr, uint8_t val) {
207 daniel-mar 221
        uint8_t* tmp = *((uint8_t**)aeteptr);
222
        *tmp = val;
223
        *aeteptr = (void*)((unsigned char*)tmp + 1);
187 dmarschall 224
}
225
#define AETE_WRITE_BYTE(i) _aete_write_byte(&aeteptr, (i));
184 dmarschall 226
 
187 dmarschall 227
void _aete_write_word(void** aeteptr, uint16_t val) {
207 daniel-mar 228
        uint16_t* tmp = *((uint16_t**)aeteptr);
229
        *tmp = val;
230
        *aeteptr = (void*)((unsigned char*)tmp + 2);
187 dmarschall 231
}
232
#define AETE_WRITE_WORD(i) _aete_write_word(&aeteptr, (i));
233
 
234
void _aete_write_dword(void** aeteptr, uint32_t val) {
207 daniel-mar 235
        uint32_t* tmp = *((uint32_t**)aeteptr);
236
        *tmp = val;
237
        *aeteptr = (void*)((unsigned char*)tmp + 4);
187 dmarschall 238
}
239
#define AETE_WRITE_DWORD(i) _aete_write_dword(&aeteptr, (i));
240
 
241
void _aete_write_pstr(void** aeteptr, char* str) {
207 daniel-mar 242
        char* tmp;
187 dmarschall 243
 
207 daniel-mar 244
        assert(strlen(str) <= 255);
187 dmarschall 245
 
207 daniel-mar 246
        _aete_write_byte(aeteptr, (uint8_t)strlen(str));
187 dmarschall 247
 
207 daniel-mar 248
        tmp = *((char**)aeteptr);
249
        strcpy(tmp, str);
250
        *aeteptr = (void*)((unsigned char*)tmp + strlen(str));
187 dmarschall 251
}
252
#define AETE_WRITE_PSTR(s) _aete_write_pstr(&aeteptr, (s));
253
 
254
void _aete_align_word(void** aeteptr) {
207 daniel-mar 255
        #ifdef MAC_ENV
256
        unsigned char* tmp = *((unsigned char**)aeteptr);
257
        tmp += (intptr_t)tmp & 1;
258
        *aeteptr = (void*)tmp;
259
        #endif
187 dmarschall 260
}
261
#define AETE_ALIGN_WORD() _aete_align_word(&aeteptr);
262
 
185 dmarschall 263
void* _aete_property(void* aeteptr, PARM_T *pparm, int ctlidx, int mapidx, OSType key) {
207 daniel-mar 264
        char tmp[256];
185 dmarschall 265
 
207 daniel-mar 266
        if (pparm->ctl_used[ctlidx] || pparm->map_used[mapidx]) {
267
                if (pparm->map_used[mapidx]) {
268
                        if (ctlidx & 1) {
269
                                sprintf(tmp, "... %s", (char*)pparm->map[mapidx]);
270
                        } else {
271
                                sprintf(tmp, "%s ...", (char*)pparm->map[mapidx]);
272
                        }
273
                        AETE_WRITE_PSTR(tmp);
274
                } else {
275
                        AETE_WRITE_PSTR((char*)pparm->ctl[ctlidx]);
276
                }
277
                AETE_ALIGN_WORD();
278
                AETE_WRITE_DWORD(key);
279
                AETE_WRITE_DWORD(typeSInt32);
280
                AETE_WRITE_PSTR(_strdup(""));
281
                AETE_ALIGN_WORD();
282
                AETE_WRITE_WORD(0x8000); /* FLAGS_1_OPT_PARAM / flagsOptionalSingleParameter */
283
        }
185 dmarschall 284
 
207 daniel-mar 285
        return aeteptr;
185 dmarschall 286
}
287
 
208 daniel-mar 288
size_t aete_generate(void* aeteptr, PARM_T *pparm, long event_id) {
207 daniel-mar 289
        int numprops;
290
        void *beginptr = aeteptr;
184 dmarschall 291
 
207 daniel-mar 292
        // Attention!
293
        // - On some systems (e.g. ARM based CPUs) this will cause an unaligned memory access exception.
294
        //   For X86, memory access just becomes slower.
295
        // - If you change something here, please also change it in Scripting.rc (Windows) and scripting.r (Mac OS)
184 dmarschall 296
 
207 daniel-mar 297
        // Note:
298
        // - The 'aete' resource for Mac OS has word alignments after strings (but not if the next element is also a string)
299
        //   see https://developer.apple.com/library/archive/documentation/mac/pdf/Interapplication_Communication/AE_Term_Resources.pdf page 8-9
184 dmarschall 300
 
301
#ifdef WIN_ENV
207 daniel-mar 302
        AETE_WRITE_WORD(0x0001); /* Reserved (for Photoshop) */
184 dmarschall 303
#endif
207 daniel-mar 304
        AETE_WRITE_BYTE(0x01); /* aete version */
305
        AETE_WRITE_BYTE(0x00); /* aete version */
306
        AETE_WRITE_WORD(english); /* language specifiers */
307
        AETE_WRITE_WORD(roman);
308
        AETE_WRITE_WORD(1); /* 1 suite */
309
        {
310
                AETE_WRITE_PSTR(/*"Telegraphics"*/(char*)pparm->author); /* vendor suite name */
311
                AETE_WRITE_PSTR(_strdup("")); /* optional description */
312
                AETE_ALIGN_WORD();
313
                AETE_WRITE_DWORD(plugInSuiteID); /* suite ID */
314
                AETE_WRITE_WORD(1); /* suite code, must be 1. Attention: Filters like 'Pointillize' have set this to 0! */
315
                AETE_WRITE_WORD(1); /* suite level, must be 1. Attention: Filters like 'Pointillize' have set this to 0! */
316
                AETE_WRITE_WORD(1); /* 1 event (structure for filters) */
317
                {
318
                        AETE_WRITE_PSTR(/*"FilterFoundry"*/(char*)pparm->title); /* event name */
319
                        AETE_WRITE_PSTR(_strdup("")); /* event description */
320
                        AETE_ALIGN_WORD();
321
                        AETE_WRITE_DWORD(plugInClassID); /* event class */
322
                        AETE_WRITE_DWORD(/*plugInEventID*/event_id); /* event ID */
323
                        /* NO_REPLY: */
324
                        AETE_WRITE_DWORD(noReply); /* noReply='null' */
325
                        AETE_WRITE_PSTR(_strdup("")); /* reply description */
326
                        AETE_ALIGN_WORD();
327
                        AETE_WRITE_WORD(0);
328
                        /* IMAGE_DIRECT_PARAM: */
329
                        AETE_WRITE_DWORD(typeImageReference); /* typeImageReference='#ImR' */
330
                        AETE_WRITE_PSTR(_strdup("")); /* direct parm description */
331
                        AETE_ALIGN_WORD();
332
                        AETE_WRITE_WORD(0xB000);
184 dmarschall 333
 
207 daniel-mar 334
                        numprops = 0;
335
                        if (pparm->ctl_used[0] || pparm->map_used[0]) numprops++;
336
                        if (pparm->ctl_used[1] || pparm->map_used[0]) numprops++;
337
                        if (pparm->ctl_used[2] || pparm->map_used[1]) numprops++;
338
                        if (pparm->ctl_used[3] || pparm->map_used[1]) numprops++;
339
                        if (pparm->ctl_used[4] || pparm->map_used[2]) numprops++;
340
                        if (pparm->ctl_used[5] || pparm->map_used[2]) numprops++;
341
                        if (pparm->ctl_used[6] || pparm->map_used[3]) numprops++;
342
                        if (pparm->ctl_used[7] || pparm->map_used[3]) numprops++;
343
                        AETE_WRITE_WORD(numprops);
344
                        {
345
                                // Standalone filters don't need RGBA expressions
346
                                /*
347
                                AETE_WRITE_PSTR("R");
348
                                AETE_ALIGN_WORD();
349
                                AETE_WRITE_DWORD(PARAM_R_KEY);
350
                                AETE_WRITE_DWORD(typeText);
351
                                AETE_WRITE_PSTR("R channel expression");
352
                                AETE_ALIGN_WORD();
353
                                AETE_WRITE_WORD(0x8000);
184 dmarschall 354
 
207 daniel-mar 355
                                AETE_WRITE_PSTR("G");
356
                                AETE_ALIGN_WORD();
357
                                AETE_WRITE_DWORD(PARAM_G_KEY);
358
                                AETE_WRITE_DWORD(typeText);
359
                                AETE_WRITE_PSTR("G channel expression");
360
                                AETE_ALIGN_WORD();
361
                                AETE_WRITE_WORD(0x8000);
184 dmarschall 362
 
207 daniel-mar 363
                                AETE_WRITE_PSTR("B");
364
                                AETE_ALIGN_WORD();
365
                                AETE_WRITE_DWORD(PARAM_B_KEY);
366
                                AETE_WRITE_DWORD(typeText);
367
                                AETE_WRITE_PSTR("B channel expression");
368
                                AETE_ALIGN_WORD();
369
                                AETE_WRITE_WORD(0x8000);
184 dmarschall 370
 
207 daniel-mar 371
                                AETE_WRITE_PSTR("A");
372
                                AETE_ALIGN_WORD();
373
                                AETE_WRITE_DWORD(PARAM_A_KEY);
374
                                AETE_WRITE_DWORD(typeText);
375
                                AETE_WRITE_PSTR("A channel expression");
376
                                AETE_ALIGN_WORD();
377
                                AETE_WRITE_WORD(0x8000);
378
                                */
184 dmarschall 379
 
207 daniel-mar 380
                                aeteptr = _aete_property(aeteptr, pparm, 0, 0, PARAM_CTL0_KEY);
381
                                aeteptr = _aete_property(aeteptr, pparm, 1, 0, PARAM_CTL1_KEY);
382
                                aeteptr = _aete_property(aeteptr, pparm, 2, 1, PARAM_CTL2_KEY);
383
                                aeteptr = _aete_property(aeteptr, pparm, 3, 1, PARAM_CTL3_KEY);
384
                                aeteptr = _aete_property(aeteptr, pparm, 4, 2, PARAM_CTL4_KEY);
385
                                aeteptr = _aete_property(aeteptr, pparm, 5, 2, PARAM_CTL5_KEY);
386
                                aeteptr = _aete_property(aeteptr, pparm, 6, 3, PARAM_CTL6_KEY);
387
                                aeteptr = _aete_property(aeteptr, pparm, 7, 3, PARAM_CTL7_KEY);
388
                        }
389
                }
184 dmarschall 390
 
207 daniel-mar 391
                /* non-filter plug-in class here */
392
                AETE_WRITE_WORD(0); /* 0 classes */
393
                {}
394
                AETE_WRITE_WORD(0); /* 0 comparison ops (not supported) */
395
                {}
396
                AETE_WRITE_WORD(0); /* 0 enumerations */
397
                {}
398
        }
399
        AETE_WRITE_DWORD(0); /* padding (FIXME: do we need that? Adobe's Windows filters don't) */
184 dmarschall 400
 
207 daniel-mar 401
        return (unsigned char*)aeteptr - (unsigned char*)beginptr; // length of stuff written
184 dmarschall 402
}
403
 
404
void obfusc(unsigned char *pparm, size_t size) {
207 daniel-mar 405
        size_t i;
406
        unsigned char *p;
407
        uint32_t x32;
184 dmarschall 408
 
207 daniel-mar 409
        x32 = 0x95D4A68F; // Hardcoded seed
410
        for (i = size, p = pparm; i--;) {
411
                // https://de.wikipedia.org/wiki/Xorshift
412
                x32 ^= x32 << 13;
413
                x32 ^= x32 >> 17;
414
                x32 ^= x32 << 5;
415
                *p++ ^= x32;
416
        }
184 dmarschall 417
}