Rev 516 | Rev 526 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 516 | Rev 517 | ||
---|---|---|---|
1 | /* |
1 | /* |
2 | This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop |
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 |
3 | Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.com.au |
4 | Copyright (C) 2018-2022 Daniel Marschall, ViaThinkSoft |
4 | Copyright (C) 2018-2022 Daniel Marschall, ViaThinkSoft |
5 | 5 | ||
6 | This program is free software; you can redistribute it and/or modify |
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 |
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 |
8 | the Free Software Foundation; either version 2 of the License, or |
9 | (at your option) any later version. |
9 | (at your option) any later version. |
10 | 10 | ||
11 | This program is distributed in the hope that it will be useful, |
11 | This program is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | GNU General Public License for more details. |
14 | GNU General Public License for more details. |
15 | 15 | ||
16 | You should have received a copy of the GNU General Public License |
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 |
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 |
18 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
19 | */ |
19 | */ |
20 | 20 | ||
21 | #include <stddef.h> |
21 | #include <stddef.h> |
22 | #include <stdint.h> |
22 | #include <stdint.h> |
23 | #include <assert.h> |
23 | #include <assert.h> |
24 | #include <time.h> |
24 | #include <time.h> |
25 | 25 | ||
26 | #include "ff.h" |
26 | #include "ff.h" |
27 | #include "symtab.h" |
27 | #include "symtab.h" |
28 | 28 | ||
29 | #include "scripting.h" |
29 | #include "scripting.h" |
30 | #include "PIActions.h" |
30 | #include "PIActions.h" |
31 | #include "PITerminology.h" |
31 | #include "PITerminology.h" |
32 | 32 | ||
33 | #include "compat_string.h" |
33 | #include "compat_string.h" |
34 | 34 | ||
35 | //#define PRINTABLE_HASH_FF16 |
35 | //#define PRINTABLE_HASH_FF16 |
36 | #define ENABLE_APPLESCRIPT |
36 | #define ENABLE_APPLESCRIPT |
37 | 37 | ||
38 | /* |
38 | /* |
39 | Find a printable 4-character key, remembering (see Photoshop API guide): |
39 | Find a printable 4-character key, remembering (see Photoshop API guide): |
40 | - All IDs starting with an uppercase letter are reserved by Adobe. |
40 | - All IDs starting with an uppercase letter are reserved by Adobe. |
41 | - All IDs that are all uppercase are reserved by Apple. |
41 | - All IDs that are all uppercase are reserved by Apple. |
42 | - All IDs that are all lowercase are reserved by Apple. |
42 | - All IDs that are all lowercase are reserved by Apple. |
43 | - This leaves all IDs that begin with a lowercase letter and have at least |
43 | - This leaves all IDs that begin with a lowercase letter and have at least |
44 | one uppercase letter for you and other plug-in developers. |
44 | one uppercase letter for you and other plug-in developers. |
45 | Note: It is questionable if "a!!!" or "A!!!" are also reseved by Apple. We don't risk it. |
45 | Note: It is questionable if "a!!!" or "A!!!" are also reseved by Apple. We don't risk it. |
46 | */ |
46 | */ |
47 | unsigned long printablehash(unsigned long hash) { |
47 | unsigned long printablehash(unsigned long hash) { |
48 | #ifdef PRINTABLE_HASH_FF16 |
48 | #ifdef PRINTABLE_HASH_FF16 |
49 | 49 | ||
50 | // FilterFoundry version 1.6 hashing by Toby Thain |
50 | // FilterFoundry version 1.6 hashing by Toby Thain |
51 | // Only accepts upper-case at the last character |
51 | // Only accepts upper-case at the last character |
52 | // 0 = 'a A' |
52 | // 0 = 'a A' |
53 | // 6100899 = 'z~~Z' |
53 | // 6100899 = 'z~~Z' |
54 | unsigned long key = 'a' + (hash % 26); hash /= 26; // first lower-case |
54 | unsigned long key = 'a' + (hash % 26); hash /= 26; // first lower-case |
55 | key = (key << 8) | (' ' + (hash % 95)); hash /= 95; // any printable |
55 | key = (key << 8) | (' ' + (hash % 95)); hash /= 95; // any printable |
56 | key = (key << 8) | (' ' + (hash % 95)); hash /= 95; // any printable |
56 | key = (key << 8) | (' ' + (hash % 95)); hash /= 95; // any printable |
57 | return (key << 8) | ('A' + (hash % 26)); // last upper-case |
57 | return (key << 8) | ('A' + (hash % 26)); // last upper-case |
58 | 58 | ||
59 | #else |
59 | #else |
60 | 60 | ||
61 | // FilterFoundry version 1.7 hashing by Daniel Marschall |
61 | // FilterFoundry version 1.7 hashing by Daniel Marschall |
62 | // Accepts upper-case at character 2, 3 or 4 |
62 | // Accepts upper-case at character 2, 3 or 4 |
63 | // Spaces are only set the right as padding to make a code shorter |
63 | // Spaces are only set the right as padding to make a code shorter |
64 | // 0 = 'aA ' |
64 | // 0 = 'aA ' |
65 | // 13530139 = 'zZZZ' |
65 | // 13530139 = 'zZZZ' |
66 | // The key-space is ~2.22 times larger |
66 | // The key-space is ~2.22 times larger |
67 | long lowlim; |
67 | long lowlim; |
68 | long uplim; |
68 | long uplim; |
69 | int upperCaseInfo; |
69 | int upperCaseInfo; |
70 | int length; |
70 | int length; |
71 | int lastThreeCharInfo; |
71 | int lastThreeCharInfo; |
72 | int firstChar; |
72 | int firstChar; |
73 | unsigned long key; |
73 | unsigned long key; |
74 | int found; |
74 | int found; |
75 | int i,j,k; |
75 | int i,j,k; |
76 | 76 | ||
77 | uplim = 0; |
77 | uplim = 0; |
78 | for (k=1; k<4; k++) { |
78 | for (k=1; k<4; k++) { |
79 | int mask = 0; |
79 | int mask = 0; |
80 | if (k == 1) mask = 1;//0b1; |
80 | if (k == 1) mask = 1;//0b1; |
81 | if (k == 2) mask = 2;//0b11; |
81 | if (k == 2) mask = 2;//0b11; |
82 | if (k == 3) mask = 4;//0b111; |
82 | if (k == 3) mask = 4;//0b111; |
83 | for (i=1; i<=mask; i++) { |
83 | for (i=1; i<=mask; i++) { |
84 | // 'k' characters |
84 | // 'k' characters |
85 | long test = 1; |
85 | long test = 1; |
86 | for (j=0; j<k; ++j) { |
86 | for (j=0; j<k; ++j) { |
87 | test *= ((i&(1<<j)) != 0) ? 26 : 94-26; |
87 | test *= ((i&(1<<j)) != 0) ? 26 : 94-26; |
88 | } |
88 | } |
89 | uplim += test; |
89 | uplim += test; |
90 | } |
90 | } |
91 | } |
91 | } |
92 | 92 | ||
93 | lastThreeCharInfo = hash % uplim; hash /= uplim; |
93 | lastThreeCharInfo = hash % uplim; hash /= uplim; |
94 | firstChar = hash%26; hash /= 26; |
94 | firstChar = hash%26; hash /= 26; |
95 | 95 | ||
96 | lowlim = 0; |
96 | lowlim = 0; |
97 | uplim = 0; |
97 | uplim = 0; |
98 | found = 0; |
98 | found = 0; |
99 | length = -1; // avoid compiler warning |
99 | length = -1; // avoid compiler warning |
100 | upperCaseInfo = -1; // avoid compiler warning |
100 | upperCaseInfo = -1; // avoid compiler warning |
101 | for (k=1; k<4; k++) { |
101 | for (k=1; k<4; k++) { |
102 | int mask; |
102 | int mask; |
103 | if (k == 1) mask = 1;//0b1; |
103 | if (k == 1) mask = 1;//0b1; |
104 | if (k == 2) mask = 2;//0b11; |
104 | if (k == 2) mask = 2;//0b11; |
105 | if (k == 3) mask = 4;//0b111; |
105 | if (k == 3) mask = 4;//0b111; |
106 | if (!found) for (i=1; i<=mask; i++) { |
106 | if (!found) for (i=1; i<=mask; i++) { |
107 | // 'k' characters |
107 | // 'k' characters |
108 | long test = 1; |
108 | long test = 1; |
109 | for (j=0; j<k; ++j) { |
109 | for (j=0; j<k; ++j) { |
110 | test *= ((i&(1<<j)) != 0) ? 26 : 94-26; |
110 | test *= ((i&(1<<j)) != 0) ? 26 : 94-26; |
111 | } |
111 | } |
112 | uplim += test; |
112 | uplim += test; |
113 | if ((lastThreeCharInfo >= lowlim) && (lastThreeCharInfo < uplim)) { |
113 | if ((lastThreeCharInfo >= lowlim) && (lastThreeCharInfo < uplim)) { |
114 | lastThreeCharInfo -= lowlim; |
114 | lastThreeCharInfo -= lowlim; |
115 | found = 1; |
115 | found = 1; |
116 | length = k; |
116 | length = k; |
117 | upperCaseInfo = i; |
117 | upperCaseInfo = i; |
118 | break; |
118 | break; |
119 | } |
119 | } |
120 | lowlim = uplim; |
120 | lowlim = uplim; |
121 | } |
121 | } |
122 | } |
122 | } |
123 | 123 | ||
124 | key = ('a' + firstChar) << 24; // first char is lower-case |
124 | key = ('a' + firstChar) << 24; // first char is lower-case |
125 | for (i=0; i<length; ++i) { |
125 | for (i=0; i<length; ++i) { |
126 | char res; |
126 | char res; |
127 | if ((upperCaseInfo&(1<<i)) == 0) { |
127 | if ((upperCaseInfo&(1<<i)) == 0) { |
128 | res = '!' + (lastThreeCharInfo % (94-26)); |
128 | res = '!' + (lastThreeCharInfo % (94-26)); |
129 | if (res >= 'A') res += 26; |
129 | if (res >= 'A') res += 26; |
130 | lastThreeCharInfo /= (94-26); |
130 | lastThreeCharInfo /= (94-26); |
131 | } else { |
131 | } else { |
132 | res = 'A' + (lastThreeCharInfo % 26); |
132 | res = 'A' + (lastThreeCharInfo % 26); |
133 | lastThreeCharInfo /= 26; |
133 | lastThreeCharInfo /= 26; |
134 | } |
134 | } |
135 | key |= res << (8*(i+(4-length-1))); // 2nd, 3rd, 4th char are either upper-case or any printable char |
135 | key |= res << (8*(i+(4-length-1))); // 2nd, 3rd, 4th char are either upper-case or any printable char |
136 | } |
136 | } |
137 | if (length == 1) { |
137 | if (length == 1) { |
138 | key &= 0xFFFF0000; |
138 | key &= 0xFFFF0000; |
139 | key |= ' ' << 8; |
139 | key |= ' ' << 8; |
140 | key |= ' '; |
140 | key |= ' '; |
141 | } |
141 | } |
142 | if (length == 2) { |
142 | if (length == 2) { |
143 | key &= 0xFFFFFF00; |
143 | key &= 0xFFFFFF00; |
144 | key |= ' '; |
144 | key |= ' '; |
145 | } |
145 | } |
146 | return key; |
146 | return key; |
147 | 147 | ||
148 | #endif |
148 | #endif |
149 | } |
149 | } |
150 | 150 | ||
151 | size_t roundToNext4(size_t x) { |
151 | size_t roundToNext4(size_t x) { |
152 | int pad = 4 - (x % 4); |
152 | int pad = 4 - (x % 4); |
153 | if (pad == 0) pad = 4; |
153 | if (pad == 0) pad = 4; |
154 | return x + pad; |
154 | return x + pad; |
155 | } |
155 | } |
156 | 156 | ||
157 | size_t fixpipl(PIPropertyList *pipl, size_t origsize, char* title, char* component, char* category, long *event_id) { |
157 | size_t fixpipl(PIPropertyList *pipl, size_t origsize, char* title, char* component, char* category, long *event_id) { |
158 | PIProperty *prop; |
158 | PIProperty *prop; |
159 | char *p; |
159 | char *p; |
160 | struct hstm_data { |
160 | struct hstm_data { |
161 | /* this structure must be 14+1 bytes long, to match PiPL structure */ |
161 | /* this structure must be 14+1 bytes long, to match PiPL structure */ |
162 | long version; /* = 0 */ |
162 | long version; /* = 0 */ |
163 | long class_id; |
163 | long class_id; |
164 | long event_id; |
164 | long event_id; |
165 | short aete_resid; |
165 | short aete_resid; |
166 | char scope[1]; |
166 | char scope[1]; |
167 | }; |
167 | }; |
168 | struct hstm_data *hstm; |
168 | struct hstm_data *hstm; |
169 | int scopelen; |
169 | int scopelen; |
170 | unsigned long hash; |
170 | unsigned long hash; |
171 | size_t realLength; |
171 | size_t realLength; |
172 | size_t roundedLength; |
172 | size_t roundedLength; |
173 | unsigned long componentVersion; |
173 | unsigned long componentVersion; |
174 | time_t curTime; |
174 | time_t curTime; |
175 | char szScope[0x300]; |
175 | char szScope[0x300], szOID[0x50]; |
176 | 176 | ||
177 | pipl->count += 4; // 4 more keys in PiPL: catg, name, cmpt, hstm |
177 | pipl->count += 5; // 5 more keys in PiPL: catg, name, cmpt, hstm, ObId |
178 | 178 | ||
179 | p = (char*)pipl + origsize; |
179 | p = (char*)pipl + origsize; |
180 | prop = (PIProperty*)p; |
180 | prop = (PIProperty*)p; |
181 | 181 | ||
182 | /* Important not about proptyLength: |
182 | /* Important not about proptyLength: |
183 | |
183 | |
184 | In regards propertyLength, Adobe writes in SPPiPL.h: |
184 | In regards propertyLength, Adobe writes in SPPiPL.h: |
185 | "Number of characters in the data array. Rounded to a multiple of 4." |
185 | "Number of characters in the data array. Rounded to a multiple of 4." |
186 | 186 | ||
187 | On the other hand, the 1997 PICA documentation(page 23) and |
187 | On the other hand, the 1997 PICA documentation(page 23) and |
188 | 1996 "Cross-Application Plug-in Development Resource Guide" describes : |
188 | 1996 "Cross-Application Plug-in Development Resource Guide" describes : |
189 | "[propertyLength] contains the length of the propertyData field. It does not include any padding bytes after |
189 | "[propertyLength] contains the length of the propertyData field. It does not include any padding bytes after |
190 | propertyData to achieve four byte alignment.This field may be zero." |
190 | propertyData to achieve four byte alignment.This field may be zero." |
191 | 191 | ||
192 | I think this is not correct, since even official plugins of Adobe(e.g. "3D Transform.8bf") and cnvtpipl |
192 | I think this is not correct, since even official plugins of Adobe(e.g. "3D Transform.8bf") and cnvtpipl |
193 | are rounding the length to a multiple of 4 (actually, rounding to the next possible multiple 4, |
193 | are rounding the length to a multiple of 4 (actually, rounding to the next possible multiple 4, |
194 | so that padding is always guaranteed). |
194 | so that padding is always guaranteed). |
195 | Photoshop (tested with Photoshop 7) will crash if the propertyLength follows the definition of PICA. |
195 | Photoshop (tested with Photoshop 7) will crash if the propertyLength follows the definition of PICA. |
196 | */ |
196 | */ |
197 | 197 | ||
198 | /* add Category property key */ |
198 | /* add Category property key */ |
199 | 199 | ||
200 | prop->vendorID = kPhotoshopSignature; |
200 | prop->vendorID = kPhotoshopSignature; |
201 | prop->propertyKey = PICategoryProperty; |
201 | prop->propertyKey = PICategoryProperty; |
202 | prop->propertyID = 0; |
202 | prop->propertyID = 0; |
203 | prop->propertyLength = (SPInt32)roundToNext4(strlen(category) + 1); |
203 | prop->propertyLength = (SPInt32)roundToNext4(strlen(category) + 1); |
204 | memset(prop->propertyData, 0x00, prop->propertyLength); // fill padding with 00h bytes (cosmetics) |
204 | memset(prop->propertyData, 0x00, prop->propertyLength); // fill padding with 00h bytes (cosmetics) |
205 | myc2pstrcpy((StringPtr)prop->propertyData, category); |
205 | myc2pstrcpy((StringPtr)prop->propertyData, category); |
206 | p += offsetof(PIProperty, propertyData) + prop->propertyLength; // skip past new property record, and any padding |
206 | p += offsetof(PIProperty, propertyData) + prop->propertyLength; // skip past new property record, and any padding |
207 | prop = (PIProperty*)p; |
207 | prop = (PIProperty*)p; |
208 | 208 | ||
209 | /* add Title/Name property key */ |
209 | /* add Title/Name property key */ |
210 | 210 | ||
211 | prop->vendorID = kPhotoshopSignature; |
211 | prop->vendorID = kPhotoshopSignature; |
212 | prop->propertyKey = PINameProperty; |
212 | prop->propertyKey = PINameProperty; |
213 | prop->propertyID = 0; |
213 | prop->propertyID = 0; |
214 | prop->propertyLength = (SPInt32)roundToNext4(strlen(title) + 1); |
214 | prop->propertyLength = (SPInt32)roundToNext4(strlen(title) + 1); |
215 | memset(prop->propertyData, 0x00, prop->propertyLength); // fill padding with 00h bytes (cosmetics) |
215 | memset(prop->propertyData, 0x00, prop->propertyLength); // fill padding with 00h bytes (cosmetics) |
216 | myc2pstrcpy((StringPtr)prop->propertyData, title); |
216 | myc2pstrcpy((StringPtr)prop->propertyData, title); |
217 | p += offsetof(PIProperty, propertyData) + prop->propertyLength; // skip past new property record, and any padding |
217 | p += offsetof(PIProperty, propertyData) + prop->propertyLength; // skip past new property record, and any padding |
218 | prop = (PIProperty*)p; |
218 | prop = (PIProperty*)p; |
219 | 219 | ||
220 | /* add Component property key */ |
220 | /* add Component property key */ |
221 | 221 | ||
222 | prop->vendorID = kPhotoshopSignature; |
222 | prop->vendorID = kPhotoshopSignature; |
223 | prop->propertyKey = PIComponentProperty; |
223 | prop->propertyKey = PIComponentProperty; |
224 | prop->propertyID = 0; |
224 | prop->propertyID = 0; |
225 | time(&curTime); |
225 | time(&curTime); |
226 | componentVersion = (unsigned long)(curTime - (time_t)946681200)/*01.Jan.2000 00:00:00*/; |
226 | componentVersion = (unsigned long)(curTime - (time_t)946681200)/*01.Jan.2000 00:00:00*/; |
227 | prop->propertyLength = (SPInt32)roundToNext4(strlen(component) + 1 + sizeof(componentVersion)); |
227 | prop->propertyLength = (SPInt32)roundToNext4(strlen(component) + 1 + sizeof(componentVersion)); |
228 | memset(prop->propertyData, 0x00, prop->propertyLength); // fill padding with 00h bytes (cosmetics) |
228 | memset(prop->propertyData, 0x00, prop->propertyLength); // fill padding with 00h bytes (cosmetics) |
229 | memcpy(prop->propertyData, &componentVersion, sizeof(componentVersion)); |
229 | memcpy(prop->propertyData, &componentVersion, sizeof(componentVersion)); |
230 | strcpy((char*)(prop->propertyData+sizeof(componentVersion)), component); |
230 | strcpy((char*)(prop->propertyData+sizeof(componentVersion)), component); |
231 | p += offsetof(PIProperty, propertyData) + prop->propertyLength; // skip past new property record, and any padding |
231 | p += offsetof(PIProperty, propertyData) + prop->propertyLength; // skip past new property record, and any padding |
232 | prop = (PIProperty*)p; |
232 | prop = (PIProperty*)p; |
233 | 233 | ||
- | 234 | /* add OID property key */ |
|
- | 235 | ||
- | 236 | sprintf(szScope, "%s %s", category, component); |
|
- | 237 | hash = djb2(szScope); |
|
- | 238 | // max 47 chars ("1.3.6.1.4.1.37476.2.72.1.4294967295.4294967295\0") |
|
- | 239 | sprintf(szOID, "1.3.6.1.4.1.37476.2.72.1.%lu.%lu", hash, componentVersion); |
|
- | 240 | ||
- | 241 | prop->vendorID = kViaThinkSoftSignature; |
|
- | 242 | prop->propertyKey = PIOIDProperty; |
|
- | 243 | prop->propertyID = 0; |
|
- | 244 | prop->propertyLength = (SPInt32)roundToNext4(strlen(szOID) + 1); |
|
- | 245 | memset(prop->propertyData, 0x00, prop->propertyLength); // fill padding with 00h bytes (cosmetics) |
|
- | 246 | strcpy((char*)prop->propertyData, szOID); |
|
- | 247 | p += offsetof(PIProperty, propertyData) + prop->propertyLength; // skip past new property record, and any padding |
|
- | 248 | prop = (PIProperty*)p; |
|
- | 249 | ||
234 | /* add HasTerminology property key */ |
250 | /* add HasTerminology property key */ |
235 | 251 | ||
236 | hstm = (struct hstm_data*)prop->propertyData; |
252 | hstm = (struct hstm_data*)prop->propertyData; |
237 | 253 | ||
238 | sprintf(szScope, "%s %s", category, title); |
- | |
239 | - | ||
240 | #ifdef ENABLE_APPLESCRIPT |
254 | #ifdef ENABLE_APPLESCRIPT |
241 | // If the uniqueString/scope is set, the plugin will only communicate with Photoshop. |
255 | // If the uniqueString/scope is set, the plugin will only communicate with Photoshop. |
242 | // Otherwise it can be accessed with AppleScript, but the AETE keys need to be unique then. |
256 | // Otherwise it can be accessed with AppleScript, but the AETE keys need to be unique then. |
243 | // This is achieved with getAeteKey(). |
257 | // This is achieved with getAeteKey(). |
244 | scopelen = 0; |
258 | scopelen = 0; |
245 | hstm->scope[0] = '\0'; |
259 | hstm->scope[0] = '\0'; |
246 | #else |
260 | #else |
247 | // Construct scope string by concatenating Category and Title - hopefully unique! |
261 | // Construct scope string by concatenating Category and Title - hopefully unique! |
248 | // Note: In doresources() we malloc'ed 300h additional bytes, |
262 | // Note: In doresources() we malloc'ed 300h additional bytes, |
249 | // and in the build dialog, title and category are size-limited, |
263 | // and in the build dialog, title and category are size-limited, |
250 | // so we can write here without malloc. |
264 | // so we can write here without malloc. |
251 | scopelen = strlen(&szScope); |
265 | scopelen = strlen(&szScope); |
252 | memcpy(&hstm->scope, &szScope, scopelen); |
266 | memcpy(&hstm->scope, &szScope, scopelen); |
253 | #endif |
267 | #endif |
254 | 268 | ||
255 | /* make up a new event ID for this aete, based on printable base-95 hash of scope */ |
269 | /* make up a new event ID for this aete, based on printable base-95 hash of scope */ |
256 | hash = djb2(szScope); |
- | |
257 | *event_id = printablehash(hash); /* this is used by aete_generate() later... */ |
270 | *event_id = printablehash(hash); /* this is used by aete_generate() later... */ |
258 | 271 | ||
259 | prop->vendorID = kPhotoshopSignature; |
272 | prop->vendorID = kPhotoshopSignature; |
260 | prop->propertyKey = PIHasTerminologyProperty; |
273 | prop->propertyKey = PIHasTerminologyProperty; |
261 | prop->propertyID = 0; |
274 | prop->propertyID = 0; |
262 | 275 | ||
263 | realLength = offsetof(struct hstm_data, scope) + scopelen + 1/*null-term*/; |
276 | realLength = offsetof(struct hstm_data, scope) + scopelen + 1/*null-term*/; |
264 | roundedLength = roundToNext4(realLength); |
277 | roundedLength = roundToNext4(realLength); |
265 | prop->propertyLength = (SPInt32)roundedLength; |
278 | prop->propertyLength = (SPInt32)roundedLength; |
266 | memset(prop->propertyData + realLength, 0x00, roundedLength - realLength); // fill padding with 00h bytes (cosmetics) |
279 | memset(prop->propertyData + realLength, 0x00, roundedLength - realLength); // fill padding with 00h bytes (cosmetics) |
267 | 280 | ||
268 | hstm->version = 0; |
281 | hstm->version = 0; |
269 | hstm->class_id = plugInClassID; |
282 | hstm->class_id = plugInClassID; |
270 | hstm->event_id = *event_id; |
283 | hstm->event_id = *event_id; |
271 | hstm->aete_resid = AETE_ID; |
284 | hstm->aete_resid = AETE_ID; |
272 | 285 | ||
273 | p += offsetof(PIProperty, propertyData) + prop->propertyLength; |
286 | p += offsetof(PIProperty, propertyData) + prop->propertyLength; |
274 | 287 | ||
275 | return p - (char*)pipl; // figure how many bytes were added |
288 | return p - (char*)pipl; // figure how many bytes were added |
276 | } |
289 | } |
277 | 290 | ||
278 | void _aete_write_byte(void** aeteptr, uint8_t val) { |
291 | void _aete_write_byte(void** aeteptr, uint8_t val) { |
279 | uint8_t* tmp = *((uint8_t**)aeteptr); |
292 | uint8_t* tmp = *((uint8_t**)aeteptr); |
280 | *tmp = val; |
293 | *tmp = val; |
281 | *aeteptr = (void*)((unsigned char*)tmp + 1); |
294 | *aeteptr = (void*)((unsigned char*)tmp + 1); |
282 | } |
295 | } |
283 | #define AETE_WRITE_BYTE(i) _aete_write_byte(&aeteptr, (i)); |
296 | #define AETE_WRITE_BYTE(i) _aete_write_byte(&aeteptr, (i)); |
284 | 297 | ||
285 | void _aete_write_word(void** aeteptr, uint16_t val) { |
298 | void _aete_write_word(void** aeteptr, uint16_t val) { |
286 | uint16_t* tmp = *((uint16_t**)aeteptr); |
299 | uint16_t* tmp = *((uint16_t**)aeteptr); |
287 | *tmp = val; |
300 | *tmp = val; |
288 | *aeteptr = (void*)((unsigned char*)tmp + 2); |
301 | *aeteptr = (void*)((unsigned char*)tmp + 2); |
289 | } |
302 | } |
290 | #define AETE_WRITE_WORD(i) _aete_write_word(&aeteptr, (i)); |
303 | #define AETE_WRITE_WORD(i) _aete_write_word(&aeteptr, (i)); |
291 | 304 | ||
292 | void _aete_write_dword(void** aeteptr, uint32_t val) { |
305 | void _aete_write_dword(void** aeteptr, uint32_t val) { |
293 | uint32_t* tmp = *((uint32_t**)aeteptr); |
306 | uint32_t* tmp = *((uint32_t**)aeteptr); |
294 | *tmp = val; |
307 | *tmp = val; |
295 | *aeteptr = (void*)((unsigned char*)tmp + 4); |
308 | *aeteptr = (void*)((unsigned char*)tmp + 4); |
296 | } |
309 | } |
297 | #define AETE_WRITE_DWORD(i) _aete_write_dword(&aeteptr, (i)); |
310 | #define AETE_WRITE_DWORD(i) _aete_write_dword(&aeteptr, (i)); |
298 | 311 | ||
299 | void _aete_write_c2pstr(void** aeteptr, char* str) { |
312 | void _aete_write_c2pstr(void** aeteptr, char* str) { |
300 | char* tmp; |
313 | char* tmp; |
301 | 314 | ||
302 | assert(strlen(str) <= 255); |
315 | assert(strlen(str) <= 255); |
303 | 316 | ||
304 | _aete_write_byte(aeteptr, (uint8_t)strlen(str)); |
317 | _aete_write_byte(aeteptr, (uint8_t)strlen(str)); |
305 | 318 | ||
306 | tmp = *((char**)aeteptr); |
319 | tmp = *((char**)aeteptr); |
307 | strcpy(tmp, str); |
320 | strcpy(tmp, str); |
308 | *aeteptr = (void*)((unsigned char*)tmp + strlen(str)); |
321 | *aeteptr = (void*)((unsigned char*)tmp + strlen(str)); |
309 | } |
322 | } |
310 | #define AETE_WRITE_C2PSTR(s) _aete_write_c2pstr(&aeteptr, (char*)(s)); |
323 | #define AETE_WRITE_C2PSTR(s) _aete_write_c2pstr(&aeteptr, (char*)(s)); |
311 | 324 | ||
312 | void _aete_write_p2pstr(void** aeteptr, char* str) { |
325 | void _aete_write_p2pstr(void** aeteptr, char* str) { |
313 | char* tmp; |
326 | char* tmp; |
314 | 327 | ||
315 | if (strlen(str) == 0) { |
328 | if (strlen(str) == 0) { |
316 | _aete_write_byte(aeteptr, 0); |
329 | _aete_write_byte(aeteptr, 0); |
317 | } else { |
330 | } else { |
318 | tmp = *((char**)aeteptr); |
331 | tmp = *((char**)aeteptr); |
319 | strcpy(tmp, str); |
332 | strcpy(tmp, str); |
320 | *aeteptr = (void*)((unsigned char*)tmp + strlen(str)); |
333 | *aeteptr = (void*)((unsigned char*)tmp + strlen(str)); |
321 | } |
334 | } |
322 | } |
335 | } |
323 | #define AETE_WRITE_P2PSTR(s) _aete_write_p2pstr(&aeteptr, (s)); |
336 | #define AETE_WRITE_P2PSTR(s) _aete_write_p2pstr(&aeteptr, (s)); |
324 | 337 | ||
325 | void _aete_align_word(void** aeteptr) { |
338 | void _aete_align_word(void** aeteptr) { |
326 | #ifdef MAC_ENV |
339 | #ifdef MAC_ENV |
327 | unsigned char* tmp = *((unsigned char**)aeteptr); |
340 | unsigned char* tmp = *((unsigned char**)aeteptr); |
328 | tmp += (intptr_t)tmp & 1; |
341 | tmp += (intptr_t)tmp & 1; |
329 | *aeteptr = (void*)tmp; |
342 | *aeteptr = (void*)tmp; |
330 | #else |
343 | #else |
331 | UNREFERENCED_PARAMETER(aeteptr); |
344 | UNREFERENCED_PARAMETER(aeteptr); |
332 | #endif |
345 | #endif |
333 | } |
346 | } |
334 | #define AETE_ALIGN_WORD() _aete_align_word(&aeteptr); |
347 | #define AETE_ALIGN_WORD() _aete_align_word(&aeteptr); |
335 | 348 | ||
336 | void* _aete_property(void* aeteptr, PARM_T *pparm, int ctlidx, int mapidx, OSType key) { |
349 | void* _aete_property(void* aeteptr, PARM_T *pparm, int ctlidx, int mapidx, OSType key) { |
337 | char tmp[256]; |
350 | char tmp[256]; |
338 | 351 | ||
339 | if (pparm->ctl_used[ctlidx] || pparm->map_used[mapidx]) { |
352 | if (pparm->ctl_used[ctlidx] || pparm->map_used[mapidx]) { |
340 | if (pparm->map_used[mapidx]) { |
353 | if (pparm->map_used[mapidx]) { |
341 | if (ctlidx & 1) { |
354 | if (ctlidx & 1) { |
342 | sprintf(tmp, "... %s", pparm->szMap[mapidx]); |
355 | sprintf(tmp, "... %s", pparm->szMap[mapidx]); |
343 | } else { |
356 | } else { |
344 | sprintf(tmp, "%s ...", pparm->szMap[mapidx]); |
357 | sprintf(tmp, "%s ...", pparm->szMap[mapidx]); |
345 | } |
358 | } |
346 | AETE_WRITE_C2PSTR(tmp); |
359 | AETE_WRITE_C2PSTR(tmp); |
347 | } else { |
360 | } else { |
348 | AETE_WRITE_C2PSTR(pparm->szCtl[ctlidx]); |
361 | AETE_WRITE_C2PSTR(pparm->szCtl[ctlidx]); |
349 | } |
362 | } |
350 | AETE_ALIGN_WORD(); |
363 | AETE_ALIGN_WORD(); |
351 | AETE_WRITE_DWORD(key); |
364 | AETE_WRITE_DWORD(key); |
352 | AETE_WRITE_DWORD(typeSInt32); |
365 | AETE_WRITE_DWORD(typeSInt32); |
353 | AETE_WRITE_C2PSTR(""); |
366 | AETE_WRITE_C2PSTR(""); |
354 | AETE_ALIGN_WORD(); |
367 | AETE_ALIGN_WORD(); |
355 | AETE_WRITE_WORD(0x8000); /* FLAGS_1_OPT_PARAM / flagsOptionalSingleParameter */ |
368 | AETE_WRITE_WORD(0x8000); /* FLAGS_1_OPT_PARAM / flagsOptionalSingleParameter */ |
356 | } |
369 | } |
357 | 370 | ||
358 | return aeteptr; |
371 | return aeteptr; |
359 | } |
372 | } |
360 | 373 | ||
361 | size_t aete_generate(void* aeteptr, PARM_T *pparm, long event_id) { |
374 | size_t aete_generate(void* aeteptr, PARM_T *pparm, long event_id) { |
362 | int numprops; |
375 | int numprops; |
363 | void *beginptr = aeteptr; |
376 | void *beginptr = aeteptr; |
364 | 377 | ||
365 | // Attention! |
378 | // Attention! |
366 | // - On some systems (e.g. ARM based CPUs) this will cause an unaligned memory access exception. |
379 | // - On some systems (e.g. ARM based CPUs) this will cause an unaligned memory access exception. |
367 | // For X86, memory access just becomes slower. |
380 | // For X86, memory access just becomes slower. |
368 | // - If you change something here, please also change it in scripting.rc (Windows) and scripting.r (Mac OS) |
381 | // - If you change something here, please also change it in scripting.rc (Windows) and scripting.r (Mac OS) |
369 | 382 | ||
370 | // Note: |
383 | // Note: |
371 | // - The 'aete' resource for Mac OS has word alignments after strings (but not if the next element is also a string) |
384 | // - The 'aete' resource for Mac OS has word alignments after strings (but not if the next element is also a string) |
372 | // see https://developer.apple.com/library/archive/documentation/mac/pdf/Interapplication_Communication/AE_Term_Resources.pdf page 8-9 |
385 | // see https://developer.apple.com/library/archive/documentation/mac/pdf/Interapplication_Communication/AE_Term_Resources.pdf page 8-9 |
373 | 386 | ||
374 | #ifdef WIN_ENV |
387 | #ifdef WIN_ENV |
375 | AETE_WRITE_WORD(0x0001); /* Reserved (for Photoshop) */ |
388 | AETE_WRITE_WORD(0x0001); /* Reserved (for Photoshop) */ |
376 | #endif |
389 | #endif |
377 | 390 | ||
378 | AETE_WRITE_BYTE(0x01); /* aete version */ |
391 | AETE_WRITE_BYTE(0x01); /* aete version */ |
379 | AETE_WRITE_BYTE(0x00); /* aete version */ |
392 | AETE_WRITE_BYTE(0x00); /* aete version */ |
380 | AETE_WRITE_WORD(english); /* language specifiers */ |
393 | AETE_WRITE_WORD(english); /* language specifiers */ |
381 | AETE_WRITE_WORD(roman); |
394 | AETE_WRITE_WORD(roman); |
382 | AETE_WRITE_WORD(1); /* 1 suite */ |
395 | AETE_WRITE_WORD(1); /* 1 suite */ |
383 | { |
396 | { |
384 | AETE_WRITE_C2PSTR(pparm->szAuthor); /* vendor suite name */ |
397 | AETE_WRITE_C2PSTR(pparm->szAuthor); /* vendor suite name */ |
385 | AETE_WRITE_C2PSTR(""); /* optional description */ |
398 | AETE_WRITE_C2PSTR(""); /* optional description */ |
386 | AETE_ALIGN_WORD(); |
399 | AETE_ALIGN_WORD(); |
387 | AETE_WRITE_DWORD(plugInSuiteID); /* suite ID */ |
400 | AETE_WRITE_DWORD(plugInSuiteID); /* suite ID */ |
388 | AETE_WRITE_WORD(1); /* suite code, must be 1. Attention: Filters like 'Pointillize' have set this to 0! */ |
401 | AETE_WRITE_WORD(1); /* suite code, must be 1. Attention: Filters like 'Pointillize' have set this to 0! */ |
389 | AETE_WRITE_WORD(1); /* suite level, must be 1. Attention: Filters like 'Pointillize' have set this to 0! */ |
402 | AETE_WRITE_WORD(1); /* suite level, must be 1. Attention: Filters like 'Pointillize' have set this to 0! */ |
390 | AETE_WRITE_WORD(1); /* 1 event (structure for filters) */ |
403 | AETE_WRITE_WORD(1); /* 1 event (structure for filters) */ |
391 | { |
404 | { |
392 | AETE_WRITE_C2PSTR(pparm->szTitle); /* event name */ |
405 | AETE_WRITE_C2PSTR(pparm->szTitle); /* event name */ |
393 | AETE_WRITE_C2PSTR(""); /* event description */ |
406 | AETE_WRITE_C2PSTR(""); /* event description */ |
394 | AETE_ALIGN_WORD(); |
407 | AETE_ALIGN_WORD(); |
395 | AETE_WRITE_DWORD(plugInClassID); /* event class */ |
408 | AETE_WRITE_DWORD(plugInClassID); /* event class */ |
396 | AETE_WRITE_DWORD(/*plugInEventID*/event_id); /* event ID */ |
409 | AETE_WRITE_DWORD(/*plugInEventID*/event_id); /* event ID */ |
397 | /* NO_REPLY: */ |
410 | /* NO_REPLY: */ |
398 | AETE_WRITE_DWORD(noReply); /* noReply='null' */ |
411 | AETE_WRITE_DWORD(noReply); /* noReply='null' */ |
399 | AETE_WRITE_C2PSTR(""); /* reply description */ |
412 | AETE_WRITE_C2PSTR(""); /* reply description */ |
400 | AETE_ALIGN_WORD(); |
413 | AETE_ALIGN_WORD(); |
401 | AETE_WRITE_WORD(0); |
414 | AETE_WRITE_WORD(0); |
402 | /* IMAGE_DIRECT_PARAM: */ |
415 | /* IMAGE_DIRECT_PARAM: */ |
403 | AETE_WRITE_DWORD(typeImageReference); /* typeImageReference='#ImR' */ |
416 | AETE_WRITE_DWORD(typeImageReference); /* typeImageReference='#ImR' */ |
404 | AETE_WRITE_C2PSTR(""); /* direct parm description */ |
417 | AETE_WRITE_C2PSTR(""); /* direct parm description */ |
405 | AETE_ALIGN_WORD(); |
418 | AETE_ALIGN_WORD(); |
406 | AETE_WRITE_WORD(0xB000); |
419 | AETE_WRITE_WORD(0xB000); |
407 | 420 | ||
408 | numprops = 0; |
421 | numprops = 0; |
409 | if (pparm->ctl_used[0] || pparm->map_used[0]) numprops++; |
422 | if (pparm->ctl_used[0] || pparm->map_used[0]) numprops++; |
410 | if (pparm->ctl_used[1] || pparm->map_used[0]) numprops++; |
423 | if (pparm->ctl_used[1] || pparm->map_used[0]) numprops++; |
411 | if (pparm->ctl_used[2] || pparm->map_used[1]) numprops++; |
424 | if (pparm->ctl_used[2] || pparm->map_used[1]) numprops++; |
412 | if (pparm->ctl_used[3] || pparm->map_used[1]) numprops++; |
425 | if (pparm->ctl_used[3] || pparm->map_used[1]) numprops++; |
413 | if (pparm->ctl_used[4] || pparm->map_used[2]) numprops++; |
426 | if (pparm->ctl_used[4] || pparm->map_used[2]) numprops++; |
414 | if (pparm->ctl_used[5] || pparm->map_used[2]) numprops++; |
427 | if (pparm->ctl_used[5] || pparm->map_used[2]) numprops++; |
415 | if (pparm->ctl_used[6] || pparm->map_used[3]) numprops++; |
428 | if (pparm->ctl_used[6] || pparm->map_used[3]) numprops++; |
416 | if (pparm->ctl_used[7] || pparm->map_used[3]) numprops++; |
429 | if (pparm->ctl_used[7] || pparm->map_used[3]) numprops++; |
417 | AETE_WRITE_WORD(numprops); |
430 | AETE_WRITE_WORD(numprops); |
418 | { |
431 | { |
419 | // Standalone filters don't need RGBA expressions |
432 | // Standalone filters don't need RGBA expressions |
420 | /* |
433 | /* |
421 | AETE_WRITE_C2PSTR("R"); |
434 | AETE_WRITE_C2PSTR("R"); |
422 | AETE_ALIGN_WORD(); |
435 | AETE_ALIGN_WORD(); |
423 | AETE_WRITE_DWORD(PARAM_R_KEY); |
436 | AETE_WRITE_DWORD(PARAM_R_KEY); |
424 | AETE_WRITE_DWORD(typeText); |
437 | AETE_WRITE_DWORD(typeText); |
425 | AETE_WRITE_C2PSTR("R channel expression"); |
438 | AETE_WRITE_C2PSTR("R channel expression"); |
426 | AETE_ALIGN_WORD(); |
439 | AETE_ALIGN_WORD(); |
427 | AETE_WRITE_WORD(0x8000); |
440 | AETE_WRITE_WORD(0x8000); |
428 | 441 | ||
429 | AETE_WRITE_C2PSTR("G"); |
442 | AETE_WRITE_C2PSTR("G"); |
430 | AETE_ALIGN_WORD(); |
443 | AETE_ALIGN_WORD(); |
431 | AETE_WRITE_DWORD(PARAM_G_KEY); |
444 | AETE_WRITE_DWORD(PARAM_G_KEY); |
432 | AETE_WRITE_DWORD(typeText); |
445 | AETE_WRITE_DWORD(typeText); |
433 | AETE_WRITE_C2PSTR("G channel expression"); |
446 | AETE_WRITE_C2PSTR("G channel expression"); |
434 | AETE_ALIGN_WORD(); |
447 | AETE_ALIGN_WORD(); |
435 | AETE_WRITE_WORD(0x8000); |
448 | AETE_WRITE_WORD(0x8000); |
436 | 449 | ||
437 | AETE_WRITE_C2PSTR("B"); |
450 | AETE_WRITE_C2PSTR("B"); |
438 | AETE_ALIGN_WORD(); |
451 | AETE_ALIGN_WORD(); |
439 | AETE_WRITE_DWORD(PARAM_B_KEY); |
452 | AETE_WRITE_DWORD(PARAM_B_KEY); |
440 | AETE_WRITE_DWORD(typeText); |
453 | AETE_WRITE_DWORD(typeText); |
441 | AETE_WRITE_C2PSTR("B channel expression"); |
454 | AETE_WRITE_C2PSTR("B channel expression"); |
442 | AETE_ALIGN_WORD(); |
455 | AETE_ALIGN_WORD(); |
443 | AETE_WRITE_WORD(0x8000); |
456 | AETE_WRITE_WORD(0x8000); |
444 | 457 | ||
445 | AETE_WRITE_C2PSTR("A"); |
458 | AETE_WRITE_C2PSTR("A"); |
446 | AETE_ALIGN_WORD(); |
459 | AETE_ALIGN_WORD(); |
447 | AETE_WRITE_DWORD(PARAM_A_KEY); |
460 | AETE_WRITE_DWORD(PARAM_A_KEY); |
448 | AETE_WRITE_DWORD(typeText); |
461 | AETE_WRITE_DWORD(typeText); |
449 | AETE_WRITE_C2PSTR("A channel expression"); |
462 | AETE_WRITE_C2PSTR("A channel expression"); |
450 | AETE_ALIGN_WORD(); |
463 | AETE_ALIGN_WORD(); |
451 | AETE_WRITE_WORD(0x8000); |
464 | AETE_WRITE_WORD(0x8000); |
452 | */ |
465 | */ |
453 | 466 | ||
454 | aeteptr = _aete_property(aeteptr, pparm, 0, 0, getAeteKey('0', pparm)); |
467 | aeteptr = _aete_property(aeteptr, pparm, 0, 0, getAeteKey('0', pparm)); |
455 | aeteptr = _aete_property(aeteptr, pparm, 1, 0, getAeteKey('1', pparm)); |
468 | aeteptr = _aete_property(aeteptr, pparm, 1, 0, getAeteKey('1', pparm)); |
456 | aeteptr = _aete_property(aeteptr, pparm, 2, 1, getAeteKey('2', pparm)); |
469 | aeteptr = _aete_property(aeteptr, pparm, 2, 1, getAeteKey('2', pparm)); |
457 | aeteptr = _aete_property(aeteptr, pparm, 3, 1, getAeteKey('3', pparm)); |
470 | aeteptr = _aete_property(aeteptr, pparm, 3, 1, getAeteKey('3', pparm)); |
458 | aeteptr = _aete_property(aeteptr, pparm, 4, 2, getAeteKey('4', pparm)); |
471 | aeteptr = _aete_property(aeteptr, pparm, 4, 2, getAeteKey('4', pparm)); |
459 | aeteptr = _aete_property(aeteptr, pparm, 5, 2, getAeteKey('5', pparm)); |
472 | aeteptr = _aete_property(aeteptr, pparm, 5, 2, getAeteKey('5', pparm)); |
460 | aeteptr = _aete_property(aeteptr, pparm, 6, 3, getAeteKey('6', pparm)); |
473 | aeteptr = _aete_property(aeteptr, pparm, 6, 3, getAeteKey('6', pparm)); |
461 | aeteptr = _aete_property(aeteptr, pparm, 7, 3, getAeteKey('7', pparm)); |
474 | aeteptr = _aete_property(aeteptr, pparm, 7, 3, getAeteKey('7', pparm)); |
462 | } |
475 | } |
463 | } |
476 | } |
464 | 477 | ||
465 | /* non-filter plug-in class here */ |
478 | /* non-filter plug-in class here */ |
466 | AETE_WRITE_WORD(0); /* 0 classes */ |
479 | AETE_WRITE_WORD(0); /* 0 classes */ |
467 | {} |
480 | {} |
468 | AETE_WRITE_WORD(0); /* 0 comparison ops (not supported) */ |
481 | AETE_WRITE_WORD(0); /* 0 comparison ops (not supported) */ |
469 | {} |
482 | {} |
470 | AETE_WRITE_WORD(0); /* 0 enumerations */ |
483 | AETE_WRITE_WORD(0); /* 0 enumerations */ |
471 | {} |
484 | {} |
472 | } |
485 | } |
473 | AETE_WRITE_DWORD(0); /* padding (FIXME: do we need that? Adobe's Windows filters don't) */ |
486 | AETE_WRITE_DWORD(0); /* padding (FIXME: do we need that? Adobe's Windows filters don't) */ |
474 | 487 | ||
475 | return (unsigned char*)aeteptr - (unsigned char*)beginptr; // length of stuff written |
488 | return (unsigned char*)aeteptr - (unsigned char*)beginptr; // length of stuff written |
476 | } |
489 | } |
477 | 490 |