Subversion Repositories filter_foundry

Compare Revisions

Regard whitespace Rev 549 → Rev 550

/trunk/CHANGELOG.md
1,5 → 1,9
# Changelog
 
## 1.7.0.22 [Work-In-Progress]
- Simplified source code (expressions, slider names, etc. are now loaded directly in the PARM resource).
- As side effect: The slider positions will be preserved when a filter is made
 
## 1.7.0.21 [23-Nov-2023]
- Read FFX file: Fixed buffer overflow when some strings (Title,Category,Author,Copyright,SliderNames) are too long
- Read GUF file: Only the last part of the Category will be imported
/trunk/ff.h
1,7 → 1,7
/*
This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.net
Copyright (C) 2018-2022 Daniel Marschall, ViaThinkSoft
Copyright (C) 2018-2023 Daniel Marschall, ViaThinkSoft
 
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
81,15 → 81,13
// The "gdata" structure contains all values which MUST be kept between filter invocations.
// All other working-data (which automatically gets calculated etc.) are NOT part of this structure.
// To increase performance, the lookup tables *tantab and *costab have been included here, so that
// they only need to be calculated one.
// they only need to be calculated once.
// size: 0x2098 (8344 Bytes) for 32-bit
// size: 0x20AC (8364 Bytes) for 64-bit
typedef struct globals_t_ {
PARM_T parm;
Boolean standalone;
Boolean parmloaded; // this means that the filter is loaded, but without PARM (title, author, etc.)
Boolean obfusc;
// (padding of 1 byte here)
// (padding of 3 bytes here)
OSType lastKnownBufferVersion;
OSType lastKnownHandleVersion;
double* tantab;
109,9 → 107,7
extern struct node *tree[4];
extern TCHAR *err[4];
extern int errpos[4],errstart[4];//,nplanes;
extern uint8_t slider[8];
extern value_type cell[NUM_CELLS];
extern char *expr[4];
 
extern int tokpos,tokstart,varused[];
extern TCHAR *errstr;
119,16 → 115,11
//#define DEBUG
 
typedef struct InternalState_ {
PARM_T bak_parm;
Boolean bak_obfusc;
Boolean bak_standalone;
Boolean bak_parmloaded;
char* bak_expr[4];
uint8_t bak_slider[8];
PARM_T bak_parm;
} InternalState;
 
// from main.c
unsigned long get_parm_hash(PARM_T *parm);
void DoPrepare(FilterRecordPtr epb);
void DoStart(FilterRecordPtr epb);
OSErr DoContinue(FilterRecordPtr epb);
136,6 → 127,9
void RequestNext(FilterRecordPtr epb);
InternalState saveInternalState(void);
void restoreInternalState(InternalState state);
unsigned long parm_hash(PARM_T* parm);
void parm_reset(Boolean resetMetadata, Boolean resetSliderValues, Boolean resetSliderNames, Boolean resetFormulas);
void parm_cleanup();
 
// from read.c
Boolean readparams_afs_pff(Handle h, TCHAR**reason);
151,7 → 145,7
 
// from save.c
OSErr saveparams_afs_pff(Handle h);
OSErr saveparams_picotxt(Handle h, Boolean useparm);
OSErr saveparams_picotxt(Handle h);
OSErr savehandleintofile(Handle h,FILEREF r);
Boolean savefile_afs_pff_picotxt_guf(StandardFileReply *sfr);
 
/trunk/funcs.c
1,7 → 1,7
/*
This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.net
Copyright (C) 2018-2022 Daniel Marschall, ViaThinkSoft
Copyright (C) 2018-2023 Daniel Marschall, ViaThinkSoft
 
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
253,7 → 253,6
 
// -------------------------------------------------------------------------------------------
 
extern uint8_t slider[];
extern value_type cell[], var[];
extern unsigned char* image_ptr;
 
487,7 → 486,7
#ifdef PARSERTEST
return 0;
#else
return i >= 0 && i <= 7 ? slider[i] : 0;
return i >= 0 && i <= 7 ? gdata->parm.val[i] : 0;
#endif
}
 
500,7 → 499,7
return 0;
#else
if (i < 0 || i > 7) return 0;
return ((long)slider[i] * (b - a)) / 255 + a;
return ((long)(gdata->parm.val[i]) * (b - a)) / 255 + a;
#endif
}
 
536,8 → 535,8
// This is how Filter Factory for Windows implements it:
if (i < 0) return 0;
if (i > 3) return 0;
H = slider[i2]; // ctl(2i)
L = slider[i2 + 1]; // ctl(2i+1)
H = gdata->parm.val[i2]; // ctl(2i)
L = gdata->parm.val[i2 + 1]; // ctl(2i+1)
if (n < 0) n = 0;
if (n > 255) n = 255; // Note: MacFF probably does "return 255" if n>255 (see testcases/map1_factory_win.png)
 
/trunk/load_mac.c
1,7 → 1,7
/*
This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.net
Copyright (C) 2018-2021 Daniel Marschall, ViaThinkSoft
Copyright (C) 2018-2023 Daniel Marschall, ViaThinkSoft
 
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
91,7 → 91,7
// first try to read text parameters (AFS, TXT, PFF)
if (*reason == NULL) {
if (readfile_afs_pff(sfr,reason)) {
gdata->parmloaded = false;
parm_reset(true, false, true, false);
gdata->obfusc = false;
return true;
}
100,7 → 100,7
// Try to read the file as FFL file
if (*reason == NULL) {
if (readfile_ffl(sfr,reason)) {
gdata->parmloaded = false;
parm_reset(true, true, true, true);
gdata->obfusc = false;
return true;
}
109,7 → 109,6
// then try "Filters Unlimited" file (FFX)
if (*reason == NULL) {
if (readfile_ffx(sfr,reason)) {
gdata->parmloaded = true;
gdata->obfusc = false;
return true;
}
118,7 → 117,6
// then try "PluginCommander TXT" file (TXT)
if (*reason == NULL) {
if (readfile_picotxt(sfr,reason)) {
gdata->parmloaded = true;
gdata->obfusc = false;
return true;
}
127,7 → 125,6
// Is it a "GIMP UserFilter (GUF)" file? (Only partially compatible with Filter Factory!!!)
if (*reason == NULL) {
if (readfile_guf(sfr,reason)) {
gdata->parmloaded = true;
return true;
}
}
136,9 → 133,9
if (*reason == NULL) {
if (readmacplugin(sfr,reason)) {
if (gdata->parm.iProtected) {
parm_reset(true, true, true, true);
*reason = "The filter is protected.";
} else {
gdata->parmloaded = true;
return true;
}
}
149,9 → 146,9
if (*reason == NULL) {
if (readfile_8bf(sfr,reason)) {
if (gdata->parm.iProtected) {
parm_reset(true, true, true, true);
*reason = "The filter is protected.";
} else {
gdata->parmloaded = true;
return true;
}
}
/trunk/load_win.c
1,7 → 1,7
/*
This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.net
Copyright (C) 2018-2022 Daniel Marschall, ViaThinkSoft
Copyright (C) 2018-2023 Daniel Marschall, ViaThinkSoft
 
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
98,7 → 98,7
if (reasonstr == NULL) {
if (readfile_afs_pff(sfr, &reasonstr)) {
gdata->obfusc = false;
gdata->parmloaded = false;
parm_reset(true, false, true, false);
return true;
}
}
107,7 → 107,7
if (reasonstr == NULL) {
if (readfile_ffl(sfr, &reasonstr)) {
gdata->obfusc = false;
gdata->parmloaded = false;
parm_reset(true, true, true, true);
return true;
}
}
116,12 → 116,12
if (reasonstr == NULL) {
if (hm = LoadLibraryEx(sfr->sfFile.szName, NULL, LOAD_LIBRARY_AS_DATAFILE)) {
if (readPARMresource(hm, &reasonstr)) {
gdata->parm.standalone = false; // just because the loaded file is standalone, does not mean that WE are standalone
if (gdata->parm.iProtected) {
parm_reset(true, true, true, true);
reasonstr = FF_GetMsg_Cpy(MSG_FILTER_PROTECTED_ID);
//gdata->parmloaded = false;
}
else {
gdata->parmloaded = true;
FreeLibrary(hm);
return true;
}
133,7 → 133,6
// Is it a "Filters Unlimited" FFX filter? (Only partially compatible with Filter Factory!!!)
if (reasonstr == NULL) {
if (readfile_ffx(sfr, &reasonstr)) {
gdata->parmloaded = true;
return true;
}
}
141,7 → 140,6
// Is it a "Filters Unlimited" TXT filter? (Only partially compatible with Filter Factory!!!)
if (reasonstr == NULL) {
if (readfile_picotxt_or_ffdecomp(sfr, &reasonstr)) {
gdata->parmloaded = true;
return true;
}
}
149,7 → 147,6
// Is it a "GIMP UserFilter (GUF)" file? (Only partially compatible with Filter Factory!!!)
if (reasonstr == NULL) {
if (readfile_guf(sfr, &reasonstr)) {
gdata->parmloaded = true;
return true;
}
}
160,10 → 157,10
if (readfile_8bf(sfr, &reasonstr)) {
if (gdata->parm.iProtected) {
// This is for purely protected filters before the time when obfuscation and protection was merged
parm_reset(true, true, true, true);
reasonstr = FF_GetMsg_Cpy(MSG_FILTER_PROTECTED_ID);
}
else {
gdata->parmloaded = true;
return true;
}
}
/trunk/main.c
1,7 → 1,7
/*
This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.net
Copyright (C) 2018-2022 Daniel Marschall, ViaThinkSoft
Copyright (C) 2018-2023 Daniel Marschall, ViaThinkSoft
 
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
42,8 → 42,6
struct node *tree[4];
TCHAR *err[4];
int errpos[4],errstart[4],nplanes,cnvused,chunksize,toprow;
uint8_t slider[8]; // this is the "working data". We cannot always use gdata->parm, because parm will not be loaded if a AFS file is read
char* expr[4]; // this is the "working data". We cannot always use gdata->parm, because parm will not be loaded if a AFS file is read
value_type cell[NUM_CELLS];
 
// this is the only memory area that keeps preserved by Photoshop:
68,7 → 66,7
DLLEXPORT MACPASCAL
void ENTRYPOINT(short selector,FilterRecordPtr pb,intptr_t *data,short *result);
 
unsigned long get_parm_hash(PARM_T *parm) {
unsigned long parm_hash(PARM_T *parm) {
unsigned long hash;
int i;
 
103,7 → 101,7
if (xstrlen(out) > 0) xstrcat(out, TEXT("/"));
#endif
 
hash = (isStandalone) ? get_parm_hash(parm) : 0;
hash = (isStandalone) ? parm_hash(parm) : 0;
 
// sprintf(outfilename, "%sFilterFoundry%d.afs", atempdir, hash);
xstrcat(out, TEXT("FilterFoundry"));
314,7 → 312,6
 
if (selector != filterSelectorAbout && !*data) {
// The filter was never called before. We allocate (zeroed) memory now.
// Note: gdata->standalone and gdata->parmloaded will be set later
CreateDataPointer(data);
if (!*data) {
*result = memFullErr;
329,13 → 326,16
switch (selector){
case filterSelectorAbout:
if (!gdata) {
Boolean parmReadOk;
// This is a temporary gdata structure just for the About dialog!
// Not to be confused with the "real" gdata during the filter invocation (containing more data).
gdata = (globals_t*)malloc(sizeof(globals_t));
if (!gdata) break;
gdata->hWndMainDlg = (HWND)((PlatformData*)((AboutRecordPtr)pb)->platformData)->hwnd; // so that simplealert() works
gdata->standalone = gdata->parmloaded = readPARMresource((HMODULE)hDllInstance, NULL);
if (gdata->parmloaded && (gdata->parm.cbSize != PARM_SIZE) && (gdata->parm.cbSize != PARM_SIZE_PREMIERE) && (gdata->parm.cbSize != PARM_SIG_MAC)) {
parmReadOk = readPARMresource((HMODULE)hDllInstance, NULL);
if (!parmReadOk) gdata->parm.standalone = false;
if (parmReadOk && (gdata->parm.cbSize != PARM_SIZE) && (gdata->parm.cbSize != PARM_SIZE_PREMIERE) && (gdata->parm.cbSize != PARM_SIG_MAC)) {
parm_reset(true, true, true, true);
if (gdata->obfusc) {
simplealert_id(MSG_INCOMPATIBLE_OBFUSCATION_ID);
}
381,7 → 381,7
wantdialog |= checkandinitparams(pb->parameters);
 
/* wantdialog = false means that we never got a Parameters call, so we're not supposed to ask user */
if (wantdialog && (!gdata->standalone || gdata->parm.popDialog)) {
if (wantdialog && (!gdata->parm.standalone || gdata->parm.popDialog)) {
if (maindialog(pb)) {
if (!host_preserves_parameters()) {
/* Workaround for GIMP/PSPI, to avoid that formulas vanish when you re-open the main window.
393,14 → 393,13
// Workaround: Save settings in "FilterFoundryXX.afs" if the host does not preserve pb->parameters
TCHAR outfilename[MAX_PATH + 1];
StandardFileReply sfr;
char* bakexpr[4];
int i;
InternalState tmpState;
 
sfr.sfGood = true;
sfr.sfReplacing = true;
sfr.sfType = PS_FILTER_FILETYPE;
 
get_temp_afs(&outfilename[0], gdata->standalone, &gdata->parm);
get_temp_afs(&outfilename[0], gdata->parm.standalone, &gdata->parm);
 
xstrcpy(sfr.sfFile.szName, outfilename);
#ifdef WIN_ENV
410,26 → 409,18
 
// We only want the parameters (ctl,map) in the temporary .afs file
// It is important to remove the expressions, otherwise they would be
// revealed in the temporary files.
for (i = 0; i < 4; i++) {
bakexpr[i] = expr[i]; // moved out of the if-definition to make the compiler happy
// revealed in the temporary files (That might be bad for obfuscated filters).
if (gdata->parm.standalone) {
tmpState = saveInternalState();
parm_reset(false, false, false, true);
}
if (gdata->standalone) {
expr[0] = _strdup("r");
expr[1] = _strdup("g");
expr[2] = _strdup("b");
expr[3] = _strdup("a");
}
 
savefile_afs_pff_picotxt_guf(&sfr);
 
if (gdata->standalone) {
for (i = 0; i < 4; i++) {
if (expr[i]) free(expr[i]);
expr[i] = bakexpr[i];
if (gdata->parm.standalone) {
restoreInternalState(tmpState);
}
}
}
else {
/* update stored parameters from new user settings */
if (pb->parameters)
471,27 → 462,114
ExitCodeResource();
}
 
void parm_reset(Boolean resetMetadata, Boolean resetSliderValues, Boolean resetSliderNames, Boolean resetFormulas) {
gdata->parm.cbSize = PARM_SIZE;
if (resetMetadata) {
strcpy(gdata->parm.szCategory, "Filter Foundry");
strcpy(gdata->parm.szTitle, "Untitled");
strcpy(gdata->parm.szCopyright, ""); //"Filter Foundry Copyright (C) 2003-2009 Toby Thain, 2018-" RELEASE_YEAR " Daniel Marschall"
strcpy(gdata->parm.szAuthor, "Anonymous");
}
if (resetSliderValues) {
int i;
for (i = 0; i < 8; ++i) {
gdata->parm.val[i] = (uint8_t)(i * 10 + 100);
}
}
if (resetSliderNames) {
int i;
for (i = 0; i < 8; ++i) {
strcpy(gdata->parm.szCtl[i], "ctl(X)");
gdata->parm.szCtl[i][4] = '0' + i;
}
for (i = 0; i < 4; ++i) {
strcpy(gdata->parm.szMap[i], "Map X");
gdata->parm.szMap[i][4] = '0' + i;
}
}
if (resetFormulas) {
if (gpb->imageMode == plugInModeRGBColor) {
strcpy(gdata->parm.szFormula[0], "r");
strcpy(gdata->parm.szFormula[1], "g");
strcpy(gdata->parm.szFormula[2], "b");
strcpy(gdata->parm.szFormula[3], "a");
}
else {
strcpy(gdata->parm.szFormula[0], "c");
strcpy(gdata->parm.szFormula[1], "c");
strcpy(gdata->parm.szFormula[2], "c");
strcpy(gdata->parm.szFormula[3], "c");
}
}
}
 
void parm_cleanup() {
// Cleanup "PARM" resource by removing stuff after the null terminators, to avoid that parts of confidential formulas are leaked
int i;
 
{
char tmp[256];
 
strcpy(tmp, gdata->parm.szCategory);
memset(gdata->parm.szCategory, 0, sizeof(gdata->parm.szCategory));
strcpy(gdata->parm.szCategory, tmp);
 
strcpy(tmp, gdata->parm.szTitle);
memset(gdata->parm.szTitle, 0, sizeof(gdata->parm.szTitle));
strcpy(gdata->parm.szTitle, tmp);
 
strcpy(tmp, gdata->parm.szCopyright);
memset(gdata->parm.szCopyright, 0, sizeof(gdata->parm.szCopyright));
strcpy(gdata->parm.szCopyright, tmp);
 
strcpy(tmp, gdata->parm.szAuthor);
memset(gdata->parm.szAuthor, 0, sizeof(gdata->parm.szAuthor));
strcpy(gdata->parm.szAuthor, tmp);
}
 
for (i = 0; i < 4; i++) {
char tmp[256];
strcpy(tmp, gdata->parm.szMap[i]);
memset(gdata->parm.szMap[i], 0, sizeof(gdata->parm.szMap[i]));
strcpy(gdata->parm.szMap[i], tmp);
}
 
for (i = 0; i < 8; i++) {
char tmp[256];
strcpy(tmp, gdata->parm.szCtl[i]);
memset(gdata->parm.szCtl[i], 0, sizeof(gdata->parm.szCtl[i]));
strcpy(gdata->parm.szCtl[i], tmp);
}
 
for (i = 0; i < 4; i++) {
char tmp[1024];
strcpy(tmp, gdata->parm.szFormula[i]);
memset(gdata->parm.szFormula[i], 0, sizeof(gdata->parm.szFormula[i]));
strcpy(gdata->parm.szFormula[i], tmp);
}
}
 
int checkandinitparams(Handle params){
int i;
Boolean bUninitializedParams;
Boolean showdialog;
InternalState tmpState;
 
if (!host_preserves_parameters()) {
// Workaround: Load settings in "FilterFoundryXX.afs" if host does not preserve pb->parameters
TCHAR outfilename[MAX_PATH + 1];
Boolean isStandalone;
Boolean parmReadOk;
StandardFileReply sfr;
char* bakexpr[4];
char bakexpr[4][MAXEXPR];
 
sfr.sfGood = true;
sfr.sfReplacing = true;
sfr.sfType = PS_FILTER_FILETYPE;
 
// We need to set gdata->standalone after loadfile(), but we must call readPARMresource() before loadfile()
// Reason: readPARMresource() reads parameters from the DLL while loadfile() reads parameters from the AFS file
// But loadfile() will reset gdata->standalone ...
isStandalone = readPARMresource((HMODULE)hDllInstance, NULL);
if (isStandalone && (gdata->parm.cbSize != PARM_SIZE) && (gdata->parm.cbSize != PARM_SIZE_PREMIERE) && (gdata->parm.cbSize != PARM_SIG_MAC)) {
parmReadOk = readPARMresource((HMODULE)hDllInstance, NULL);
if (!parmReadOk) gdata->parm.standalone = false;
if (parmReadOk && (gdata->parm.cbSize != PARM_SIZE) && (gdata->parm.cbSize != PARM_SIZE_PREMIERE) && (gdata->parm.cbSize != PARM_SIG_MAC)) {
parm_reset(true, true, true, true);
if (gdata->obfusc) {
simplealert_id(MSG_INCOMPATIBLE_OBFUSCATION_ID);
}
498,11 → 576,10
else {
simplealert_id(MSG_INVALID_PARAMETER_DATA_ID);
}
gdata->parmloaded = false;
return false;
}
 
get_temp_afs(&outfilename[0], isStandalone, &gdata->parm);
get_temp_afs(&outfilename[0], parmReadOk, &gdata->parm);
 
xstrcpy(sfr.sfFile.szName, outfilename);
#ifdef WIN_ENV
510,22 → 587,22
#endif
sfr.sfScript = smSystemScript;
 
// We only want the parameters (ctl,map) in the temporary .afs file
if (isStandalone) {
for (i = 0; i < 4; i++) {
bakexpr[i] = my_strdup(expr[i]);
if (parmReadOk) {
tmpState = saveInternalState();
}
}
 
if (loadfile(&sfr, NULL)) {
gdata->standalone = gdata->parmloaded = isStandalone;
 
if (isStandalone) {
if (parmReadOk) {
// In the standalone filter, we only want the parameters (ctl,map) in the temporary .afs file, not the formulas
// We do not need to care about the metadata, because the AFS does not touch the metadata anyway
for (i = 0; i < 4; i++) {
if (expr[i]) free(expr[i]);
expr[i] = bakexpr[i];
strcpy(bakexpr[i], gdata->parm.szFormula[i]);
}
restoreInternalState(tmpState);
for (i = 0; i < 4; i++) {
strcpy(gdata->parm.szFormula[i],bakexpr[i]);
}
}
 
return true;
}
535,9 → 612,13
/* either the parameter handle was uninitialised,
or the parameter data couldn't be read; set default values */
 
Boolean parmReadOk;
 
// see if saved parameters exist
gdata->standalone = gdata->parmloaded = readPARMresource((HMODULE)hDllInstance, NULL);
if (gdata->parmloaded && (gdata->parm.cbSize != PARM_SIZE) && (gdata->parm.cbSize != PARM_SIZE_PREMIERE) && (gdata->parm.cbSize != PARM_SIG_MAC)) {
parmReadOk = readPARMresource((HMODULE)hDllInstance, NULL);
if (!parmReadOk) gdata->parm.standalone = false;
if (parmReadOk && (gdata->parm.cbSize != PARM_SIZE) && (gdata->parm.cbSize != PARM_SIZE_PREMIERE) && (gdata->parm.cbSize != PARM_SIG_MAC)) {
parm_reset(true, true, true, true);
if (gdata->obfusc) {
simplealert_id(MSG_INCOMPATIBLE_OBFUSCATION_ID);
}
544,30 → 625,14
else {
simplealert_id(MSG_INVALID_PARAMETER_DATA_ID);
}
gdata->parmloaded = false;
return false;
}
 
if(!gdata->standalone){
if(!gdata->parm.standalone){
// no saved settings (not standalone)
for(i = 0; i < 8; ++i)
slider[i] = (uint8_t)(i*10+100);
for(i = 0; i < 4; ++i)
if(expr[i])
free(expr[i]);
if(gpb->imageMode == plugInModeRGBColor){
expr[0] = _strdup("r");
expr[1] = _strdup("g");
expr[2] = _strdup("b");
expr[3] = _strdup("a");
}else{
expr[0] = _strdup("c");
expr[1] = _strdup("c");
expr[2] = _strdup("c");
expr[3] = _strdup("c");
parm_reset(true, true, true, true);
}
}
}
 
// let scripting system change parameters, if we're scripted;
// user may want to force display of dialog during scripting playback
639,7 → 704,6
int i;
 
for(i = 4; i--;){
expr[i] = NULL;
tree[i] = NULL;
err[i] = NULL;
}
838,35 → 902,18
 
for(i = 4; i--;){
freetree(tree[i]);
if(expr[i]) free(expr[i]);
}
}
 
InternalState saveInternalState(void) {
InternalState ret;
int i;
 
ret.bak_obfusc = gdata->obfusc;
ret.bak_standalone = gdata->standalone;
ret.bak_parmloaded = gdata->parmloaded;
memcpy(&ret.bak_parm, &gdata->parm, sizeof(PARM_T));
for (i = 0; i < 4; i++) ret.bak_expr[i] = my_strdup(expr[i]);
for (i = 0; i < 8; i++) ret.bak_slider[i] = slider[i];
 
return ret;
}
 
void restoreInternalState(InternalState state) {
int i;
gdata->obfusc = state.bak_obfusc;
gdata->standalone = state.bak_standalone;
gdata->parmloaded = state.bak_parmloaded;
memcpy(&gdata->parm, &state.bak_parm, sizeof(PARM_T));
for (i = 0; i < 4; i++) {
if (expr[i]) free(expr[i]);
expr[i] = state.bak_expr[i];
}
for (i = 0; i < 8; i++) {
slider[i] = state.bak_slider[i];
}
}
/trunk/preview.c
1,7 → 1,7
/*
This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.net
Copyright (C) 2018-2022 Daniel Marschall, ViaThinkSoft
Copyright (C) 2018-2023 Daniel Marschall, ViaThinkSoft
 
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
480,10 → 480,10
// TODO? When a formula has an error, the preview should not be changeable
// (This code does not work because you can still try to zoom)
/*
if (!gdata->standalone) {
if (!gdata->parm.standalone) {
int i;
for (i = 0; i < 4; i++)
if (!parseexpr(expr[i])) return;
if (!parseexpr(gdata->parm.szFormula[i])) return;
}
*/
 
/trunk/process.c
1,7 → 1,7
/*
This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.net
Copyright (C) 2018-2022 Daniel Marschall, ViaThinkSoft
Copyright (C) 2018-2023 Daniel Marschall, ViaThinkSoft
 
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
80,8 → 80,8
varused[i] = 0;
needall = srcrad = cnvused = state_changing_funcs_used = 0;
for(i = 0; i < nplanes; ++i){
//char s[100];sprintf(s,"expr[%d]=%#x",i,expr[i]);dbg(s);
if( tree[i] || (tree[i] = parseexpr(expr[i])) )
//char s[100];sprintf(s,"gdata->parm.szFormula[%d]=%#x",i,gdata->parm.szFormula[i]);dbg(s);
if( tree[i] || (tree[i] = parseexpr(gdata->parm.szFormula[i])) )
// if src() and rad() is used => needall=1, since we need arbitary access to all pixels
checkvars(tree[i],varused,&cnvused,&srcrad,&state_changing_funcs_used);
else
/trunk/read.c
76,7 → 76,7
v = atoi(linebuf);
if (v < 0) v = 0;
else if (v > 255) v = 255;
slider[linecnt-1] = (uint8_t)v;
gdata->parm.val[linecnt-1] = (uint8_t)v;
}else{
if(lineptr){
/* it's not an empty line; append it to current expr string */
87,13 → 87,8
q = cat(q,linebuf);
}else{
/* it's an empty line: we've completed the expr string */
if(expr[exprcnt])
free(expr[exprcnt]);
*q = '\0';
if(!(expr[exprcnt] = my_strdup(curexpr))){
if (reason) *reason = FF_GetMsg_Cpy(MSG_EXPRESSION_OOM_ID);
break;
}
strcpy(gdata->parm.szFormula[exprcnt], curexpr);
 
if(++exprcnt == 4){
res = true;
284,8 → 279,6
#pragma warning(suppress : 6386)
val[sizeof(gdata->parm.szFormula[i]) - 1] = '\0';
}
if (expr[i]) free(expr[i]);
expr[i] = my_strdup(val);
strcpy(gdata->parm.szFormula[i], val);
free(val);
}
312,7 → 305,7
v = *((uint32_t*)q);
if (v < 0) v = 0;
else if (v > 255) v = 255;
slider[i] = (uint8_t)v;
gdata->parm.val[i] = (uint8_t)v;
q += sizeof(uint32_t);
}
 
476,26 → 469,6
pparm->val[i] = EndianS32_LtoN(pparm->val[i]);
}
 
// Now set the values in pparm into the working variables expr[] and slider[], so that they are visible in the GUI
{
int i;
for (i = 0; i < 4; ++i) {
if (expr[i]) free(expr[i]);
expr[i] = my_strdup(pparm->szFormula[i]);
}
 
for (i = 0; i < 8; ++i) {
slider[i] = (uint8_t)pparm->val[i];
/*
if (slider[i] > 0xFF) {
// Wrong endianess (e.g. reading a Mac rsrc on Windows)
// Should not happen since we did the stuff above
slider[i] = (uint8_t)EndianS32_LtoN(slider[i]);
}
*/
}
}
 
return true;
}
 
715,8 → 688,6
}
 
Boolean readfile_picotxt_or_ffdecomp(StandardFileReply* sfr, TCHAR** reason) {
extern int ctls[], maps[];
 
Handle h;
Boolean res = false;
FILEREF refnum;
750,12 → 721,9
strcpy(gdata->parm.szFormula[2], "b");
if (!_picoReadProperty(q, count, "A", gdata->parm.szFormula[3], sizeof(gdata->parm.szFormula[3]), true))
strcpy(gdata->parm.szFormula[3], "a");
for (i = 0; i < 4; i++) {
if (expr[i]) free(expr[i]);
expr[i] = my_strdup(gdata->parm.szFormula[i]);
}
 
for (i = 0; i < 8; i++) {
if (gdata->parm.ctl_used[i]) {
int v;
char keyname[6/*strlen("ctl[X]")*/ + 1/*strlen("\0")*/], tmp[5];
 
774,21 → 742,19
v = atoi(tmp);
if (v < 0) v = 0;
else if (v > 255) v = 255;
gdata->parm.val[i] = slider[i] = (uint8_t)v;
gdata->parm.val[i] = gdata->parm.val[i] = (uint8_t)v;
}
}
 
// Map names
for (i = 0; i < 4; i++) {
if (gdata->parm.map_used[i]) {
char keyname[6/*strlen("map[X]")*/ + 1/*strlen("\0")*/];
sprintf(keyname, "map[%d]", i);
_picoReadProperty(q, count, keyname, gdata->parm.szMap[i], sizeof(gdata->parm.szMap[i]), false);
}
}
 
//These will be set when the expressions are evaluated anyway. So this part is optional:
checksliders(4, ctls, maps);
for (i = 0; i < 8; i++) gdata->parm.ctl_used[i] = ctls[i];
for (i = 0; i < 4; i++) gdata->parm.map_used[i] = maps[i];
 
res = true;
}
 
880,8 → 846,6
}
 
Boolean readfile_guf(StandardFileReply* sfr, TCHAR** reason) {
extern int ctls[], maps[];
 
Handle h;
Boolean res = false;
FILEREF refnum;
939,12 → 903,9
strcpy(gdata->parm.szFormula[2], "b");
if (!_gufReadProperty(q, count, "Code", "A", gdata->parm.szFormula[3], sizeof(gdata->parm.szFormula[3])))
strcpy(gdata->parm.szFormula[3], "a");
for (i = 0; i < 4; i++) {
if (expr[i]) free(expr[i]);
expr[i] = my_strdup(gdata->parm.szFormula[i]);
}
 
for (i = 0; i < 8; i++) {
if (gdata->parm.ctl_used[i]) {
int v;
char keyname[9/*strlen("Control X")*/ + 1/*strlen("\0")*/], tmp[5];
sprintf(keyname, "Control %d", i);
959,21 → 920,19
v = atoi(tmp);
if (v < 0) v = 0;
else if (v > 255) v = 255;
gdata->parm.val[i] = slider[i] = (uint8_t)v;
gdata->parm.val[i] = gdata->parm.val[i] = (uint8_t)v;
}
}
 
// Map names
for (i = 0; i < 4; i++) {
if (gdata->parm.map_used[i]) {
char keyname[5/*strlen("Map X")*/ + 1/*strlen("\0")*/];
sprintf(keyname, "Map %d", i);
_gufReadProperty(q, count, keyname, "Label", gdata->parm.szMap[i], sizeof(gdata->parm.szMap[i]));
}
}
 
//These will be set when the expressions are evaluated anyway. So this part is optional:
checksliders(4, ctls, maps);
for (i = 0; i < 8; i++) gdata->parm.ctl_used[i] = ctls[i];
for (i = 0; i < 4; i++) gdata->parm.map_used[i] = maps[i];
 
res = true;
}
 
994,15 → 953,13
if(FSpOpenDF(&sfr->sfFile,fsRdPerm,&r) == noErr){
if( (h = readfileintohandle(r)) ){
if( (res = readparams_afs_pff(h,reason)) ) {
gdata->standalone = false; // so metadata fields will default, if user chooses Make...
 
if (fileHasExtension(sfr, TEXT(".pff"))) {
// If it is a Premiere settings file, we need to swap the channels red and blue
// We just swap the pointers!
char* tmp;
tmp = expr[0];
expr[0] = expr[2];
expr[2] = tmp;
char tmp[MAXEXPR];
strcpy(tmp, gdata->parm.szFormula[0]);
strcpy(gdata->parm.szFormula[0], gdata->parm.szFormula[2]);
strcpy(gdata->parm.szFormula[2], tmp);
}
}
 
/trunk/save.c
58,7 → 58,7
 
if (!h) return nilHandleErr;
 
est = strlen(expr[0]) + strlen(expr[1]) + strlen(expr[2]) + strlen(expr[3]);
est = strlen(gdata->parm.szFormula[0]) + strlen(gdata->parm.szFormula[1]) + strlen(gdata->parm.szFormula[2]) + strlen(gdata->parm.szFormula[3]);
// do not be tempted to combine into one expression: 'est' is referenced below
est += strlen(afs_sig) + est/CHOPLINES + 4 + 8*6 + 64 /*slop*/ ;
 
69,11 → 69,11
 
/* then slider values, one per line */
for( i=0 ; i<8 ; ++i )
p += sprintf(p, "%d\r", slider[i]);
p += sprintf(p, "%d\r", gdata->parm.val[i]);
 
/* expressions, broken into lines no longer than CHOPLINES characters */
for( i=0 ; i<4 ; ++i ){
if ((r = expr[i])) {
if ((r = gdata->parm.szFormula[i])) {
chunk = 0; // to avoid that compiler complains
for (n = strlen(r); n; n -= chunk) {
chunk = n > (int)CHOPLINES ? (int)CHOPLINES : n;
110,9 → 110,7
return e;
}
 
OSErr saveparams_picotxt(Handle h, Boolean useparm) {
extern int ctls[], maps[];
 
OSErr saveparams_picotxt(Handle h) {
char * p, *start;
int i;
OSErr e;
120,30 → 118,29
 
if (!h) return nilHandleErr;
 
est = strlen(expr[0]) + strlen(expr[1]) + strlen(expr[2]) + strlen(expr[3]);
est = strlen(gdata->parm.szFormula[0]) + strlen(gdata->parm.szFormula[1]) + strlen(gdata->parm.szFormula[2]) + strlen(gdata->parm.szFormula[3]);
// do not be tempted to combine into one expression: 'est' is referenced below
est += 16000;
 
PIUNLOCKHANDLE(h); // should not be necessary
if (!(e = PISETHANDLESIZE(h, (int32)(est))) && (p = start = PILOCKHANDLE(h, false))) {
checksliders(4, ctls, maps);
checksliders(4);
 
// Metadata
p += sprintf(p, "Category: %s\r\n", useparm ? gdata->parm.szCategory : "...");
p += sprintf(p, "Title: %s\r\n", useparm ? gdata->parm.szTitle : "...");
p += sprintf(p, "Copyright: %s\r\n", useparm ? gdata->parm.szCopyright : "...");
p += sprintf(p, "Author: %s\r\n", useparm ? gdata->parm.szAuthor : "...");
p += sprintf(p, "Filename: %s\r\n", useparm ? "Untitled.8bf" : "Untitled.8bf"); // TODO: get .txt filename and change .txt to .8bf
p += sprintf(p, "Category: %s\r\n", gdata->parm.szCategory);
p += sprintf(p, "Title: %s\r\n", gdata->parm.szTitle);
p += sprintf(p, "Copyright: %s\r\n", gdata->parm.szCopyright);
p += sprintf(p, "Author: %s\r\n", gdata->parm.szAuthor);
p += sprintf(p, "Filename: %s\r\n", "Untitled.8bf"); // TODO: get .txt filename and change .txt to .8bf
p += sprintf(p, "\r\n");
p += sprintf(p, "R: %s\r\n", useparm ? gdata->parm.szFormula[0] : expr[0]);
p += sprintf(p, "R: %s\r\n", gdata->parm.szFormula[0]);
p += sprintf(p, "\r\n");
p += sprintf(p, "G: %s\r\n", useparm ? gdata->parm.szFormula[1] : expr[1]);
p += sprintf(p, "G: %s\r\n", gdata->parm.szFormula[1]);
p += sprintf(p, "\r\n");
p += sprintf(p, "B: %s\r\n", useparm ? gdata->parm.szFormula[2] : expr[2]);
p += sprintf(p, "B: %s\r\n", gdata->parm.szFormula[2]);
p += sprintf(p, "\r\n");
p += sprintf(p, "A: %s\r\n", useparm ? gdata->parm.szFormula[3] : expr[3]);
p += sprintf(p, "A: %s\r\n", gdata->parm.szFormula[3]);
p += sprintf(p, "\r\n");
if (useparm) {
for (i = 0; i < 8; i++) {
if (gdata->parm.ctl_used[i]) {
p += sprintf(p, "ctl[%d]: %s\r\n", i, gdata->parm.szCtl[i]);
168,33 → 165,6
}
}
*/
}
else {
for (i = 0; i < 8; i++) {
if (ctls[i]) {
p += sprintf(p, "ctl[%d]: %s\r\n", i, "...");
}
}
for (i = 0; i < 4; i++) {
if (maps[i]) {
p += sprintf(p, "map[%d]: %s\r\n", i, "...");
}
}
p += sprintf(p, "\r\n");
for (i = 0; i < 8; i++) {
if (ctls[i]) {
p += sprintf(p, "val[%d]: %d\r\n", i, slider[i]);
}
}
/*
p += sprintf(p, "\r\n");
for (i = 0; i < 8; i++) {
if (ctls[i]) {
p += sprintf(p, "def[%d]: %s\r\n", i, "...");
}
}
*/
}
 
PIUNLOCKHANDLE(h);
e = PISETHANDLESIZE(h, (int32)(p - start)); // could ignore this error, maybe
203,9 → 173,7
return e;
}
 
OSErr saveparams_guf(Handle h, Boolean useparm) {
extern int ctls[], maps[];
 
OSErr saveparams_guf(Handle h) {
char* p, * start;
int i;
OSErr e;
213,7 → 181,7
 
if (!h) return nilHandleErr;
 
est = strlen(expr[0]) + strlen(expr[1]) + strlen(expr[2]) + strlen(expr[3]);
est = strlen(gdata->parm.szFormula[0]) + strlen(gdata->parm.szFormula[1]) + strlen(gdata->parm.szFormula[2]) + strlen(gdata->parm.szFormula[3]);
// do not be tempted to combine into one expression: 'est' is referenced below
est += 16000;
 
225,7 → 193,7
time_t iBuildDate = time(0);
strftime(strBuildDate, 11, "%Y-%m-%d", localtime(&iBuildDate));
 
checksliders(4, ctls, maps);
checksliders(4);
 
// Metadata
p += sprintf(p, "# Created with Filter Foundry %s\r\n", VERSION_STR);
234,10 → 202,10
p += sprintf(p, "Protocol=1\r\n");
p += sprintf(p, "\r\n");
p += sprintf(p, "[Info]\r\n");
p += sprintf(p, "Category=<Image>/Filter Factory/%s\r\n", useparm ? gdata->parm.szCategory : "...");
p += sprintf(p, "Title=%s\r\n", useparm ? gdata->parm.szTitle : "...");
p += sprintf(p, "Copyright=%s\r\n", useparm ? gdata->parm.szCopyright : "...");
p += sprintf(p, "Author=%s\r\n", useparm ? gdata->parm.szAuthor : "...");
p += sprintf(p, "Category=<Image>/Filter Factory/%s\r\n", gdata->parm.szCategory);
p += sprintf(p, "Title=%s\r\n", gdata->parm.szTitle);
p += sprintf(p, "Copyright=%s\r\n", gdata->parm.szCopyright);
p += sprintf(p, "Author=%s\r\n", gdata->parm.szAuthor);
p += sprintf(p, "\r\n");
p += sprintf(p, "[Version]\r\n");
p += sprintf(p, "Major=1\r\n");
245,16 → 213,15
p += sprintf(p, "Micro=0\r\n");
p += sprintf(p, "\r\n");
p += sprintf(p, "[Filter Factory]\r\n");
p += sprintf(p, "8bf=%s\r\n", useparm ? "Untitled.8bf" : "Untitled.8bf"); // TODO: get .guf filename and change .guf to .8bf
p += sprintf(p, "8bf=%s\r\n", "Untitled.8bf"); // TODO: get .guf filename and change .guf to .8bf
p += sprintf(p, "\r\n");
p += sprintf(p, "[Gimp]\r\n");
p += sprintf(p, "Registered=false\r\n");
p += sprintf(p, "Description=%s\r\n", useparm ? gdata->parm.szTitle : "...");
p += sprintf(p, "Description=%s\r\n", gdata->parm.szTitle);
p += sprintf(p, "EdgeMode=2\r\n");
p += sprintf(p, "Date=%s\r\n", strBuildDate);
p += sprintf(p, "\r\n");
 
if (useparm) {
for (i = 0; i < 8; i++) {
p += sprintf(p, "[Control %d]\r\n", i);
p += sprintf(p, "Enabled=%s\r\n", gdata->parm.ctl_used[i] ? "true" : "false");
269,29 → 236,12
p += sprintf(p, "Label=%s\r\n", gdata->parm.szMap[i]);
p += sprintf(p, "\r\n");
}
}
else {
for (i = 0; i < 8; i++) {
p += sprintf(p, "[Control %d]\r\n", i);
p += sprintf(p, "Enabled=%s\r\n", ctls[i] ? "true" : "false");
p += sprintf(p, "Label=%s\r\n", "...");
p += sprintf(p, "Preset=%d\r\n", slider[i]);
p += sprintf(p, "Step=1\r\n");
p += sprintf(p, "\r\n");
}
for (i = 0; i < 4; i++) {
p += sprintf(p, "[Map %d]\r\n", i);
p += sprintf(p, "Enabled=%s\r\n", maps[i] ? "true" : "false");
p += sprintf(p, "Label=%s\r\n", "...");
p += sprintf(p, "\r\n");
}
}
p += sprintf(p, "[Code]\r\n");
p += sprintf(p, "R=%s\r\n", useparm ? gdata->parm.szFormula[0] : expr[0]);
p += sprintf(p, "G=%s\r\n", useparm ? gdata->parm.szFormula[1] : expr[1]);
p += sprintf(p, "B=%s\r\n", useparm ? gdata->parm.szFormula[2] : expr[2]);
p += sprintf(p, "A=%s\r\n", useparm ? gdata->parm.szFormula[3] : expr[3]);
p += sprintf(p, "R=%s\r\n", gdata->parm.szFormula[0]);
p += sprintf(p, "G=%s\r\n", gdata->parm.szFormula[1]);
p += sprintf(p, "B=%s\r\n", gdata->parm.szFormula[2]);
p += sprintf(p, "A=%s\r\n", gdata->parm.szFormula[3]);
 
PIUNLOCKHANDLE(h);
e = PISETHANDLESIZE(h, (int32)(p - start)); // could ignore this error, maybe
326,7 → 276,7
if (fileHasExtension(sfr, TEXT(".txt"))) {
// PluginCommander .txt
if ((h = PINEWHANDLE(1))) { // don't set initial size to 0, since some hosts (e.g. GIMP/PSPI) are incompatible with that.
res = !(saveparams_picotxt(h, false) || savehandleintofile(h, r));
res = !(saveparams_picotxt(h) || savehandleintofile(h, r));
PIDISPOSEHANDLE(h);
}
}
334,7 → 284,7
if (fileHasExtension(sfr, TEXT(".guf"))) {
// GIMP UserFilter file
if ((h = PINEWHANDLE(1))) { // don't set initial size to 0, since some hosts (e.g. GIMP/PSPI) are incompatible with that.
res = !(saveparams_guf(h, false) || savehandleintofile(h, r));
res = !(saveparams_guf(h) || savehandleintofile(h, r));
PIDISPOSEHANDLE(h);
}
}
343,10 → 293,10
if (fileHasExtension(sfr, TEXT(".pff"))) {
// If it is a Premiere settings file, we need to swap the channels red and blue
// We just swap the pointers!
char* tmp;
tmp = expr[0];
expr[0] = expr[2];
expr[2] = tmp;
char tmp[MAXEXPR];
strcpy(tmp, gdata->parm.szFormula[0]);
strcpy(gdata->parm.szFormula[0], gdata->parm.szFormula[2]);
strcpy(gdata->parm.szFormula[2], tmp);
}
 
if ((h = PINEWHANDLE(1))) { // don't set initial size to 0, since some hosts (e.g. GIMP/PSPI) are incompatible with that.
356,10 → 306,10
 
if (fileHasExtension(sfr, TEXT(".pff"))) {
// Swap back so that the other program stuff will work normally again
char* tmp;
tmp = expr[0];
expr[0] = expr[2];
expr[2] = tmp;
char tmp[MAXEXPR];
strcpy(tmp, gdata->parm.szFormula[0]);
strcpy(gdata->parm.szFormula[0], gdata->parm.szFormula[2]);
strcpy(gdata->parm.szFormula[2], tmp);
}
}
 
/trunk/scripting.c
1,7 → 1,7
/*
This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.net
Copyright (C) 2018-2022 Daniel Marschall, ViaThinkSoft
Copyright (C) 2018-2023 Daniel Marschall, ViaThinkSoft
 
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
123,30 → 123,34
token = OpenReader(array);
if (token) {
while (PIGetKey(token, &key, &type, &flags)) {
if (key == getAeteKey('R', gdata->standalone ? &gdata->parm : NULL)) {
if (expr[0]) free(expr[0]);
expr[0] = get_cstring(token);
if (key == getAeteKey('R', gdata->parm.standalone ? &gdata->parm : NULL)) {
char *tmp = get_cstring(token);
strcpy(gdata->parm.szFormula[0], tmp);
free(tmp);
}
else if (key == getAeteKey('G', gdata->standalone ? &gdata->parm : NULL)) {
if (expr[1]) free(expr[1]);
expr[1] = get_cstring(token);
else if (key == getAeteKey('G', gdata->parm.standalone ? &gdata->parm : NULL)) {
char* tmp = get_cstring(token);
strcpy(gdata->parm.szFormula[1], tmp);
free(tmp);
}
else if (key == getAeteKey('B', gdata->standalone ? &gdata->parm : NULL)) {
if (expr[2]) free(expr[2]);
expr[2] = get_cstring(token);
else if (key == getAeteKey('B', gdata->parm.standalone ? &gdata->parm : NULL)) {
char* tmp = get_cstring(token);
strcpy(gdata->parm.szFormula[2], tmp);
free(tmp);
}
else if (key == getAeteKey('A', gdata->standalone ? &gdata->parm : NULL)) {
if (expr[3]) free(expr[3]);
expr[3] = get_cstring(token);
else if (key == getAeteKey('A', gdata->parm.standalone ? &gdata->parm : NULL)) {
char* tmp = get_cstring(token);
strcpy(gdata->parm.szFormula[3], tmp);
free(tmp);
}
else {
int i;
for (i = 0; i <= 7; ++i) {
if (key == getAeteKey((char)('0'+i), gdata->standalone ? &gdata->parm : NULL)) {
if (key == getAeteKey((char)('0'+i), gdata->parm.standalone ? &gdata->parm : NULL)) {
PIGetInt(token, &v);
if (v < 0) v = 0;
else if (v > 255) v = 255;
slider[i] = (uint8_t)v;
gdata->parm.val[i] = (uint8_t)v;
}
}
}
169,7 → 173,7
{
PIWriteDescriptor token;
OSErr gotErr = noErr;
extern int ctls[], maps[], nplanes;
extern int nplanes;
int i, allctls;
 
if (DescriptorAvailable(NULL)) { /* recording. Do our thing. */
179,18 → 183,18
// 2. Call various Put routines such as PutIntegerProc, PutFloatProc, etc., to add key/value pairs to writeToken. The keys and value types must correspond to those in your terminology resource.
 
// write keys here
if (!gdata->standalone) {
if (nplanes > 0) put_cstring(token, getAeteKey('R', gdata->standalone ? &gdata->parm : NULL), expr[0]);
if (nplanes > 1) put_cstring(token, getAeteKey('G', gdata->standalone ? &gdata->parm : NULL), expr[1]);
if (nplanes > 2) put_cstring(token, getAeteKey('B', gdata->standalone ? &gdata->parm : NULL), expr[2]);
if (nplanes > 3) put_cstring(token, getAeteKey('A', gdata->standalone ? &gdata->parm : NULL), expr[3]);
if (!gdata->parm.standalone) {
if (nplanes > 0) put_cstring(token, getAeteKey('R', gdata->parm.standalone ? &gdata->parm : NULL), gdata->parm.szFormula[0]);
if (nplanes > 1) put_cstring(token, getAeteKey('G', gdata->parm.standalone ? &gdata->parm : NULL), gdata->parm.szFormula[1]);
if (nplanes > 2) put_cstring(token, getAeteKey('B', gdata->parm.standalone ? &gdata->parm : NULL), gdata->parm.szFormula[2]);
if (nplanes > 3) put_cstring(token, getAeteKey('A', gdata->parm.standalone ? &gdata->parm : NULL), gdata->parm.szFormula[3]);
}
 
/* only write values for the sliders that are actually used! */
allctls = checksliders(4, ctls, maps);
for (i = 0; i <= 7; ++i) {
if (allctls || ctls[i]) {
PIPutInt(token, getAeteKey((char)('0'+i), gdata->standalone ? &gdata->parm : NULL), slider[i]);
allctls = checksliders(4);
for (i = 0; i < 8; ++i) {
if (allctls || gdata->parm.ctl_used[i]) {
PIPutInt(token, getAeteKey((char)('0'+i), gdata->parm.standalone ? &gdata->parm : NULL), gdata->parm.val[i]);
}
}
 
/trunk/ui.c
1,7 → 1,7
/*
This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.net
Copyright (C) 2018-2022 Daniel Marschall, ViaThinkSoft
Copyright (C) 2018-2023 Daniel Marschall, ViaThinkSoft
 
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
47,18 → 47,18
doupdates = false;
 
for(i = 0; i < 8; ++i){
SETSLIDERVALUE(dp,FIRSTCTLITEM+i,slider[i]);
SETCTLTEXTINT(dp,FIRSTCTLTEXTITEM+i,slider[i],false);
SETSLIDERVALUE(dp,FIRSTCTLITEM+i, gdata->parm.val[i]);
SETCTLTEXTINT(dp,FIRSTCTLTEXTITEM+i, gdata->parm.val[i],false);
}
 
for(i = 0; i < 4; ++i){
if(!gdata->standalone)
SETCTLTEXT(dp,FIRSTEXPRITEM+i,expr[i] ? expr[i] : "");
if(!gdata->parm.standalone)
SETCTLTEXT(dp,FIRSTEXPRITEM+i, gdata->parm.szFormula[i]);
if(i < nplanes)
updateexpr(dp,FIRSTEXPRITEM+i);
}
 
if(!gdata->standalone)
if(!gdata->parm.standalone)
SELECTCTLTEXT(dp,FIRSTEXPRITEM,0,-1);
 
doupdates = true;
70,29 → 70,7
 
UNREFERENCED_PARAMETER(dp);
 
// DM 28 Nov 2021: Removed this function. It makes no sense! The internal state is in the memory
// and the dialog is only the view!
 
/*
int i;
char s[MAXEXPR];
 
for(i = 0; i < 8; ++i)
slider[i] = (value_type)(GETSLIDERVALUE(dp,FIRSTCTLITEM+i));
 
if(!gdata->standalone)
for(i = 0; i < 4; ++i){
// stash expression strings
if(GETCTLTEXT(dp,FIRSTEXPRITEM+i,s,MAXEXPR)){ // cchMax: NULL is included, so MAXEXPR is correct
if(expr[i])
free(expr[i]);
expr[i] = _strdup(s);
}
if(!expr[i])
expr[i] = _strdup("c");
}
*/
}
 
struct node *updateexpr(DIALOGREF dp,int item){
char s[MAXEXPR];
102,17 → 80,14
 
freetree(tree[i]);
 
if(!gdata->standalone){
if(!gdata->parm.standalone){
GETCTLTEXT(dp,item,s,MAXEXPR); // cchMax: NULL is included, so MAXEXPR is correct
 
if(expr[i])
free(expr[i]);
expr[i] = _strdup(s);
strcpy(gdata->parm.szFormula[i], s);
}
 
tree[i] = parseexpr(expr[i]);
tree[i] = parseexpr(gdata->parm.szFormula[i]);
 
if(!gdata->standalone){
if(!gdata->parm.standalone){
if(tree[i])
HideDialogItem(dp,FIRSTICONITEM+i);
else{
172,18 → 147,18
return result;
}
 
int checksliders(int exprs,int ctlflags[],int mapflags[]){
int checksliders(int exprs){
int i, result;
 
result = 0;
 
for(i = 4; i--;)
mapflags[i] = 0;
gdata->parm.map_used[i] = 0;
for(i = 8; i--;)
ctlflags[i] = 0;
gdata->parm.ctl_used[i] = 0;
 
for(i = 0; i < exprs; i++)
result |= _checksl(tree[i],ctlflags,mapflags);
result |= _checksl(tree[i], gdata->parm.ctl_used, gdata->parm.map_used);
 
return result;
}
193,7 → 168,7
if (v < 0) v = 0;
else if (v > 255) v = 255;
i -= FIRSTCTLITEM;
slider[i] = (uint8_t)v;
gdata->parm.val[i] = (uint8_t)v;
SETCTLTEXTINT(dp,i+FIRSTCTLTEXTITEM,v,false);
}
 
203,16 → 178,16
else if (v > 255) v = 255;
i -= FIRSTCTLTEXTITEM;
SETSLIDERVALUE(dp,i+FIRSTCTLITEM,v);
slider[i] = (uint8_t)v;
gdata->parm.val[i] = (uint8_t)v;
}
 
void maindlgupdate(DIALOGREF dp){
int i,unknown,ctls[8],maps[4];
int i,unknown;
 
unknown = checksliders(nplanes,ctls,maps);
unknown = checksliders(nplanes);
 
for(i = 0; i < 8; i++)
if(unknown || ctls[i]){
if(unknown || gdata->parm.ctl_used[i]){
ENABLEDLGITEM(dp,FIRSTCTLITEM+i); // TODO: slider is still shown as disabled
REPAINTCTL(dp, FIRSTCTLITEM+i); // required for PLUGIN.DLL sliders
ENABLEDLGITEM(dp,FIRSTCTLLABELITEM+i);
228,7 → 203,7
if(!tree[i]){
/* uh oh, couldn't parse one of the saved expressions...this is fatal */
DISABLEDLGITEM(dp,IDOK);
if(gdata->standalone){
if(gdata->parm.standalone){
// TODO: But before this happens, we get filterBadParameters in filterSelectorStart, since setup() failed
// so, do we need this message here at all?
simplealert_id(MSG_SAVED_EXPR_ERR_ID);
245,7 → 220,7
recalc_preview(gpb,dp);
 
ENABLEDLGITEM(dp,IDOK);
if(!gdata->standalone){
if(!gdata->parm.standalone){
ENABLEDLGITEM(dp,SAVEITEM);
ENABLEDLGITEM(dp,MAKEITEM);
ENABLEDLGITEM(dp,HELPITEM);
264,7 → 239,7
};
 
/* hide unused expression items */
if(gdata->standalone){
if(gdata->parm.standalone){
strcpy_win_replace_ampersand(&s[0], &gdata->parm.szAuthor[0]);
SETCTLTEXT(dp,PARAMAUTHORITEM,s);
strcpy_win_replace_ampersand(&s[0], &gdata->parm.szCopyright[0]);
421,7 → 396,7
strcpy_advance(&tmp1, (TCHAR*)TEXT(" (*.*)")); tmp1++;
strcpy_advance(&tmp1, (TCHAR*)TEXT("*.*")); tmp1++;
 
loadDlgRet = !gdata->standalone && choosefiletypes(
loadDlgRet = !gdata->parm.standalone && choosefiletypes(
#ifdef MAC_ENV
"\pChoose filter settings", // "\p" means "Pascal string" // TODO (Not important yet): TRANSLATE
&sfr, &reply, types, 2,
493,7 → 468,7
strcpy_advance(&tmp1, (TCHAR*)TEXT(" (*.*)")); tmp1++;
strcpy_advance(&tmp1, (TCHAR*)TEXT("*.*")); tmp1++;
 
saveDlgRet = !gdata->standalone && putfile(
saveDlgRet = !gdata->parm.standalone && putfile(
#ifdef MAC_ENV
"\pSave filter settings", // "\p" means "Pascal string" // TODO (Not important yet): TRANSLATE
"\0",
550,7 → 525,7
break;
}
case MAKEITEM:
if (gdata->standalone) return true; // should not happen since the button should be grayed out
if (gdata->parm.standalone) return true; // should not happen since the button should be grayed out
 
builddialog(gpb);
 
/trunk/ui.h
1,7 → 1,7
/*
This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.net
Copyright (C) 2018-2022 Daniel Marschall, ViaThinkSoft
Copyright (C) 2018-2023 Daniel Marschall, ViaThinkSoft
 
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
116,7 → 116,7
Boolean maindlgitem(DIALOGREF dp,int item);
void maindlgupdate(DIALOGREF dp);
void slidermoved(DIALOGREF dp,int item);
int checksliders(int exprs,int ctlflags[],int mapflags[]);
int checksliders(int exprs);
 
void builddlginit(DIALOGREF dp);
Boolean builddlgitem(DIALOGREF dp,int item);
/trunk/ui_build.c
1,7 → 1,7
/*
This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.net
Copyright (C) 2018-2022 Daniel Marschall, ViaThinkSoft
Copyright (C) 2018-2023 Daniel Marschall, ViaThinkSoft
 
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
23,7 → 23,6
#include "ff.h"
#include "compat_string.h"
 
int ctls[8],maps[4];
int checksliders_result;
 
/* one-time initialisation of dialog box */
30,9 → 29,7
 
void builddlginit(DIALOGREF dp){
int i;
char s[0x100];
 
if(gdata->parmloaded){
SETCTLTEXT(dp,CATEGORYITEM, gdata->parm.szCategory);
SETCTLTEXT(dp,TITLEITEM, gdata->parm.szTitle);
SETCTLTEXT(dp,COPYRIGHTITEM,gdata->parm.szCopyright);
43,29 → 40,11
for(i=0;i<8;++i){
SETCTLTEXT(dp,FIRSTCTLNAMEITEM+i,gdata->parm.szCtl[i]);
}
}else{
/* strictly speaking this is not needed on the Mac,
we can set initial values statically in the rez description */
SETCTLTEXT(dp,CATEGORYITEM, "Filter Foundry");
SETCTLTEXT(dp,TITLEITEM, "Untitled");
SETCTLTEXT(dp,COPYRIGHTITEM,""); //"Filter Foundry Copyright (C) 2003-2009 Toby Thain, 2018-" RELEASE_YEAR " Daniel Marschall"
SETCTLTEXT(dp,AUTHORITEM, "Anonymous");
strcpy(s,"Map X");
for(i = 0; i < 4; ++i){
s[4] = (char)('0' + i);
SETCTLTEXT(dp,FIRSTMAPNAMEITEM+i,s);
}
strcpy(s,"ctl(X)");
for(i = 0; i < 8; ++i){
s[4] = (char)('0' + i);
SETCTLTEXT(dp,FIRSTCTLNAMEITEM+i,s);
}
}
 
checksliders_result = checksliders(4,ctls,maps);
checksliders_result = checksliders(4);
for(i = 4; i--;){
DISABLEDLGITEM(dp,FIRSTMAPCHECKITEM+i);
if(maps[i] || (checksliders_result & CHECKSLIDERS_MAP_AMBIGUOUS))
if(gdata->parm.map_used[i] || (checksliders_result & CHECKSLIDERS_MAP_AMBIGUOUS))
CHECKDLGBUTTON(dp,FIRSTMAPCHECKITEM+i,true);
else
HideDialogItem(dp,FIRSTMAPNAMEITEM+i);
72,10 → 51,10
}
for(i = 8; i--;){
DISABLEDLGITEM(dp,FIRSTCTLCHECKITEM+i);
if((ctls[i] || (checksliders_result & CHECKSLIDERS_CTL_AMBIGUOUS)) &&
if((gdata->parm.ctl_used[i] || (checksliders_result & CHECKSLIDERS_CTL_AMBIGUOUS)) &&
// When map() is activated, we don't need ctl labels,
// since the standalone filter will only show map labels
!maps[i/2] &&
!gdata->parm.map_used[i/2] &&
(!(checksliders_result & CHECKSLIDERS_MAP_AMBIGUOUS))
)
CHECKDLGBUTTON(dp,FIRSTCTLCHECKITEM+i,true);
132,6 → 111,7
Boolean unicode;
#endif
Boolean extCharset;
InternalState tmpState;
 
switch(item){
#ifdef MAC_ENV
167,7 → 147,7
 
// The AETE structure does only define single byte charsets
for (i = 0; i < 8; ++i) {
if (ctls[i] || (checksliders_result & CHECKSLIDERS_CTL_AMBIGUOUS)) {
if (gdata->parm.ctl_used[i] || (checksliders_result & CHECKSLIDERS_CTL_AMBIGUOUS)) {
#ifdef UNICODE
unicode |= containsUnicodeInput(dp, FIRSTCTLNAMEITEM + i);
#endif
175,7 → 155,7
}
}
for (i = 0; i < 4; ++i) {
if (maps[i] || (checksliders_result & CHECKSLIDERS_MAP_AMBIGUOUS)) {
if (gdata->parm.map_used[i] || (checksliders_result & CHECKSLIDERS_MAP_AMBIGUOUS)) {
#ifdef UNICODE
unicode |= containsUnicodeInput(dp, FIRSTMAPNAMEITEM + i);
#endif
196,46 → 176,27
}
 
// Now begin
memset(&gdata->parm,0,sizeof(PARM_T));
GetDlgItemTextA(dp,CATEGORYITEM,gdata->parm.szCategory,MAXFIELD-4/*ProtectFlag*/);
GetDlgItemTextA(dp,TITLEITEM,gdata->parm.szTitle,MAXFIELD);
GetDlgItemTextA(dp,COPYRIGHTITEM,gdata->parm.szCopyright,MAXFIELD);
GetDlgItemTextA(dp,AUTHORITEM,gdata->parm.szAuthor,MAXFIELD);
gdata->parm.cbSize = PARM_SIZE;
gdata->parm.standalone = 1; //0=original FF, 1=standalone filter
needui = 0;
// Sliders
for(i = 0; i < 8; ++i){
gdata->parm.val[i] = slider[i];
gdata->parm.ctl_used[i] = ctls[i] || (checksliders_result & CHECKSLIDERS_CTL_AMBIGUOUS);
gdata->parm.ctl_used[i] = gdata->parm.ctl_used[i] || (checksliders_result & CHECKSLIDERS_CTL_AMBIGUOUS);
needui |= gdata->parm.ctl_used[i];
GetDlgItemTextA(dp,FIRSTCTLNAMEITEM+i, gdata->parm.szCtl[i],MAXFIELD);
}
// Maps
for (i = 0; i < 4; ++i) {
gdata->parm.map_used[i] = maps[i] || (checksliders_result & CHECKSLIDERS_MAP_AMBIGUOUS);
gdata->parm.map_used[i] = gdata->parm.map_used[i] || (checksliders_result & CHECKSLIDERS_MAP_AMBIGUOUS);
needui |= gdata->parm.map_used[i];
GetDlgItemTextA(dp, FIRSTMAPNAMEITEM + i, gdata->parm.szMap[i], MAXFIELD);
}
// Expressions
for (i = 0; i < 4; ++i) {
if (strlen(expr[i]) >= sizeof(gdata->parm.szFormula[i])) {
if (i == 0) {
simplealert_id(MSG_FORMULA_R_1023_TRUNCATED_ID);
}
else if (i == 1) {
simplealert_id(MSG_FORMULA_G_1023_TRUNCATED_ID);
}
else if (i == 2) {
simplealert_id(MSG_FORMULA_B_1023_TRUNCATED_ID);
}
else if (i == 3) {
simplealert_id(MSG_FORMULA_A_1023_TRUNCATED_ID);
}
expr[i][sizeof(gdata->parm.szFormula[i]) - 1] = '\0';
}
strcpy(gdata->parm.szFormula[i], expr[i]);
}
 
tmpState = saveInternalState(); // the standalone flag and obfuscation must not be preserved, otherwise we cannot continue editing the filter
 
gdata->parm.standalone = 1;
gdata->parm.popDialog = needui; //true if need to pop a parameter dialog
gdata->parm.unknown1 = gdata->parm.unknown2 = gdata->parm.unknown3 = 0;
gdata->parm.iProtected = ISDLGBUTTONCHECKED(dp,PROTECTITEM); // == 1 means protected
290,13 → 251,13
free(title);
 
if (makeDlgRet) {
parm_cleanup();
make_standalone(&sfr);
}
else {
return true; // keep going. Let the user correct their input
}
}
 
restoreInternalState(tmpState);
 
return false; // end dialog
#ifdef MAC_ENV
case cancel:
/trunk/ui_mac.c
1,7 → 1,7
/*
This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.net
Copyright (C) 2018-2022 Daniel Marschall, ViaThinkSoft
Copyright (C) 2018-2023 Daniel Marschall, ViaThinkSoft
 
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
43,7 → 43,7
void DoAbout(AboutRecordPtr prec){
ModalFilterUPP filterproc_UPP = NewModalFilterUPP(aboutfilter);
 
if(gdata && gdata->standalone){
if(gdata && gdata->parm.standalone){
ParamText(myc2pstr(_strdup(gdata->parm.szTitle)), myc2pstr(_strdup(gdata->parm.szAuthor)), myc2pstr(_strdup(gdata->parm.szCopyright)), NULL);
Alert(ID_ABOUTSTANDALONEDLG,filterproc_UPP);
}else
197,9 → 197,9
 
action_UPP = NewControlActionUPP(slideraction);
 
dp = thedialog = GetNewDialog(gdata->standalone ? ID_PARAMDLG : ID_MAINDLG,nil,(WindowPtr)-1);
dp = thedialog = GetNewDialog(gdata->parm.standalone ? ID_PARAMDLG : ID_MAINDLG,nil,(WindowPtr)-1);
 
if(gdata->standalone)
if(gdata->parm.standalone)
SetWTitle(GetDialogWindow(dp), myc2pstr(_strdup(gdata->parm.szTitle)));
 
GetDialogItem(dp,PREVIEWITEM,&itemType,&itemHdl,&preview_rect);
211,7 → 211,7
SetDialogCancelItem(dp,cancel);
SetDialogTracksCursor(dp,true);
 
if(!gdata->standalone)
if(!gdata->parm.standalone)
for(i = 0; i < 4; ++i)
GetDialogItemAsControl(dp,FIRSTEXPRITEM+i,&exprctls[i]);
 
/trunk/ui_win.c
1,7 → 1,7
/*
This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.net
Copyright (C) 2018-2022 Daniel Marschall, ViaThinkSoft
Copyright (C) 2018-2023 Daniel Marschall, ViaThinkSoft
 
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
47,7 → 47,7
memset(filters, 0, 4096);
tmp1 = filters;
 
if (gdata && gdata->standalone) {
if (gdata && gdata->parm.standalone) {
// strcpy(gdata->parm.szTitle, "TestTitle");
// strcpy(gdata->parm.szAuthor, "TestAuthor");
// strcpy(gdata->parm.szCopyright, "TestCopyright");
90,7 → 90,7
Boolean simplealert(TCHAR* s){
HWND hwnd;
TCHAR title[256] = { 0 };
if (gdata && gdata->standalone) {
if (gdata && gdata->parm.standalone) {
#ifdef UNICODE
mbstowcs(&title[0], (const char*)gdata->parm.szTitle, 256);
#else
107,7 → 107,7
Boolean simplewarning(TCHAR* s) {
HWND hwnd;
TCHAR title[256] = { 0 };
if (gdata && gdata->standalone) {
if (gdata && gdata->parm.standalone) {
#ifdef UNICODE
mbstowcs(&title[0], (const char*)gdata->parm.szTitle, 256);
#else
123,7 → 123,7
Boolean showmessage(TCHAR *s) {
HWND hwnd;
TCHAR title[256] = { 0 };
if (gdata && gdata->standalone) {
if (gdata && gdata->parm.standalone) {
#ifdef UNICODE
mbstowcs(&title[0], (const char*)gdata->parm.szTitle, 256);
#else
195,8 → 195,8
if (hWnd == GetDlgItem(gdata->hWndMainDlg, FIRSTCTLTEXTITEM + sliderNum)) {
if ((uMsg == WM_KEYDOWN) && (wParam == VK_UP) && doupdates)
{
uint8_t sliderVal = slider[sliderNum] < 255 ? slider[sliderNum] + 1 : slider[sliderNum];
slider[sliderNum] = sliderVal;
uint8_t sliderVal = gdata->parm.val[sliderNum] < 255 ? gdata->parm.val[sliderNum] + 1 : gdata->parm.val[sliderNum];
gdata->parm.val[sliderNum] = sliderVal;
 
SETCTLTEXTINT(gdata->hWndMainDlg, FIRSTCTLTEXTITEM + sliderNum, sliderVal, false);
REPAINTCTL(gdata->hWndMainDlg, FIRSTCTLTEXTITEM + sliderNum);
207,8 → 207,8
}
if ((uMsg == WM_KEYDOWN) && (wParam == VK_DOWN) && doupdates)
{
uint8_t sliderVal = slider[sliderNum] > 0 ? slider[sliderNum] - 1 : slider[sliderNum];
slider[sliderNum] = sliderVal;
uint8_t sliderVal = gdata->parm.val[sliderNum] > 0 ? gdata->parm.val[sliderNum] - 1 : gdata->parm.val[sliderNum];
gdata->parm.val[sliderNum] = sliderVal;
 
SETCTLTEXTINT(gdata->hWndMainDlg, FIRSTCTLTEXTITEM + sliderNum, sliderVal, false);
REPAINTCTL(gdata->hWndMainDlg, FIRSTCTLTEXTITEM + sliderNum);
244,7 → 244,7
int sliderVal = (lParam & 0xFFFF);
if (sliderVal < 0) sliderVal = 0;
else if (sliderVal > 255) sliderVal = 255;
slider[sliderNum] = (uint8_t)sliderVal;
gdata->parm.val[sliderNum] = (uint8_t)sliderVal;
 
SETCTLTEXTINT(hDlg, FIRSTCTLTEXTITEM + sliderNum, sliderVal, false);
REPAINTCTL(hDlg, FIRSTCTLTEXTITEM + sliderNum);
267,7 → 267,7
case WM_INITDIALOG:
gdata->hWndMainDlg = hDlg;
 
if(gdata->standalone){
if(gdata->parm.standalone){
SetWindowTextA(hDlg,gdata->parm.szTitle); // window title bar
}
centre_window(hDlg);
442,7 → 442,7
p = (PlatformData*)pb->platformData;
 
// Note: "Invalid Cursor Handle" is the error when an unrecognized control class is detected
res = DialogBoxParam(hDllInstance,MAKEINTRESOURCE(gdata->standalone ? ID_PARAMDLG : ID_MAINDLG),
res = DialogBoxParam(hDllInstance,MAKEINTRESOURCE(gdata->parm.standalone ? ID_PARAMDLG : ID_MAINDLG),
(HWND)p->hwnd,maindlgproc,0);
if (res == 0) {
simplealert((TCHAR*)TEXT("DialogBoxParam invalid parent window handle")); // TODO (Not so important): TRANSLATE