Subversion Repositories filter_foundry

Compare Revisions

No changes between revisions

Regard whitespace Rev 543 → Rev 544

/trunk/CHANGELOG.md
3,6 → 3,7
## 1.7.0.21 [Work-In-Progress]
- 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
- Implemented reading of FFL files (actually, they will be only extracted, so they can be read afterwards)
 
## 1.7.0.20 [21-Nov-2023]
- Implemented "GIMP UserFilter" (GUF) file format.
/trunk/README.md
64,9 → 64,10
|FFX |"Filters Unlimited" file. | | |Yes(2)|
|TXT |A text file created by "Plugin Commander" or "FFDecomp". | |Yes(3)|Yes |
|GUF |A filter file created by "GIMP UserFilter". | |Yes |Yes(2)|
|FFL |"Filter Factory Library" by "Plugin Commander". | | |Yes(4)|
|BIN or RSRC |Standalone filter created by Filter Factory/Foundry for Mac. | | |Yes |
 
Currently not supported are FFL (Filter Library) files.
Currently not supported are FFP (FilterMeister) files.
 
(1) Loading is only possible if the 8BF file was created by Filter Factory, or by Filter Foundry without protection.
 
74,7 → 75,9
 
(3) Title, Category, Author, Copyright, and Slider/Map names are left empty and must be added using a text editor.
 
(4) The FFL files only be extracted, so they can be read afterwards.
 
 
### Donation
 
If you use this program and like it, the original author Toby Thain asks to donate to his PayPal (5 USD suggested or what you think it is worth):
/trunk/TODO.md
30,8 → 30,6
Minor priority stuff or ideas
-----------------------------
 
* Import/Export FFL format (but which filter to select? We would need to show a select dialog...)
 
* Import/Export FFP format
 
* If controls are ambigous e.g. ctl(3+c), then you should be able to disable control in the "Make" dialog. Like in Filter Factory.
/trunk/ff.h
143,6 → 143,7
Boolean readfile_8bf(StandardFileReply *sfr, TCHAR**reason);
Handle readfileintohandle(FILEREF r);
Boolean readfile_afs_pff(StandardFileReply* sfr, TCHAR** reason);
Boolean readfile_ffl(StandardFileReply* sfr, TCHAR** reason);
Boolean readfile_ffx(StandardFileReply* sfr, TCHAR** reason);
Boolean readfile_picotxt_or_ffdecomp(StandardFileReply* sfr, TCHAR** reason);
Boolean readfile_guf(StandardFileReply* sfr, TCHAR** reason);
/trunk/language.h
264,6 → 264,14
#define MSG_INCOMPATIBLE_GUF_FILE_ENUS "Incompatible GIMP UserFilter File"
#define MSG_INCOMPATIBLE_GUF_FILE_DEDE "Inkompatible GIMP UserFilter Datei"
 
#define MSG_OPEN_FFL_ID 61
#define MSG_OPEN_FFL_ENUS "Filter Library"
#define MSG_OPEN_FFL_DEDE "Filter Bibliothek"
 
#define MSG_FFL_CONVERTED_ID 62
#define MSG_FFL_CONVERTED_ENUS "FFL file converted to TXT files. You can now open these TXT files."
#define MSG_FFL_CONVERTED_DEDE "FFL Datei wurde in TXT Dateien extrahiert. Sie können diese TXT Dateien nun öffnen."
 
void strcpy_advance_id(TCHAR** str, int msgid);
int FF_GetMsg(TCHAR* ret, int MsgId);
TCHAR* FF_GetMsg_Cpy(int MsgId);
/trunk/language_mac.r
98,6 → 98,8
MSG_ABOUT_CONTACT_AUTHOR_ENUS,
MSG_OPEN_GUF_ENUS,
MSG_SAVE_GUF_ENUS,
MSG_INCOMPATIBLE_GUF_FILE_ENUS
MSG_INCOMPATIBLE_GUF_FILE_ENUS,
MSG_OPEN_FFL_ENUS,
MSG_FFL_CONVERTED_ENUS
}
}
/trunk/language_win.rc
85,6 → 85,9
MSG_OPEN_GUF_ID, MSG_OPEN_GUF_ENUS
MSG_SAVE_GUF_ID, MSG_SAVE_GUF_ENUS
MSG_INCOMPATIBLE_GUF_FILE_ID, MSG_INCOMPATIBLE_GUF_FILE_ENUS
MSG_OPEN_FFL_ID, MSG_OPEN_FFL_ENUS
MSG_FFL_CONVERTED_ID, MSG_FFL_CONVERTED_ENUS
 
}
 
LANGUAGE LANG_GERMAN, SUBLANG_GERMAN
152,5 → 155,7
MSG_OPEN_GUF_ID, MSG_OPEN_GUF_DEDE
MSG_SAVE_GUF_ID, MSG_SAVE_GUF_DEDE
MSG_INCOMPATIBLE_GUF_FILE_ID, MSG_INCOMPATIBLE_GUF_FILE_DEDE
MSG_OPEN_FFL_ID, MSG_OPEN_FFL_DEDE
MSG_FFL_CONVERTED_ID, MSG_FFL_CONVERTED_DEDE
 
}
/trunk/load_mac.c
97,6 → 97,15
}
}
 
// Try to read the file as FFL file
if (*reason == NULL) {
if (readfile_ffl(sfr,reason)) {
gdata->parmloaded = false;
gdata->obfusc = false;
return true;
}
}
 
// then try "Filters Unlimited" file (FFX)
if (*reason == NULL) {
if (readfile_ffx(sfr,reason)) {
115,6 → 124,14
}
}
 
// 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;
}
}
 
// Try Mac plugin resource
if (*reason == NULL) {
if (readmacplugin(sfr,reason)) {
/trunk/load_win.c
103,6 → 103,15
}
}
 
// Try to read the file as FFL file
if (reasonstr == NULL) {
if (readfile_ffl(sfr, &reasonstr)) {
gdata->obfusc = false;
gdata->parmloaded = false;
return true;
}
}
 
// If that didn't work, try to load as Windows image file (Resource API for 8BF/PRM files)
if (reasonstr == NULL) {
if (hm = LoadLibraryEx(sfr->sfFile.szName, NULL, LOAD_LIBRARY_AS_DATAFILE)) {
121,7 → 130,7
}
}
 
// Is it a "Filters Unlimited" filter? (Only partially compatible with Filter Factory!!!)
// Is it a "Filters Unlimited" FFX filter? (Only partially compatible with Filter Factory!!!)
if (reasonstr == NULL) {
if (readfile_ffx(sfr, &reasonstr)) {
gdata->parmloaded = true;
129,7 → 138,7
}
}
 
// Is it a "Filters Unlimited" filter? (Only partially compatible with Filter Factory!!!)
// 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;
/trunk/read.c
38,8 → 38,8
 
Boolean readparams_afs_pff(Handle h, TCHAR**reason){
Boolean res = false;
char linebuf[MAXLINE + 1] = { 0 };
char curexpr[MAXEXPR + 1] = { 0 };
char linebuf[MAXLINE] = { 0 };
char curexpr[MAXEXPR] = { 0 };
char *p, *dataend, *q;
char c;
int linecnt, lineptr, exprcnt;
80,8 → 80,7
}else{
if(lineptr){
/* it's not an empty line; append it to current expr string */
if( q+lineptr > curexpr+MAXEXPR ){
// TODO: isn't the limit 1024?! (because we need to have the NUL too?)
if( (q-curexpr) + lineptr >= MAXEXPR) {
if (reason) *reason = FF_GetMsg_Cpy(MSG_EXPRESSION1024_FOUND_ID);
break;
}
116,7 → 115,7
case 'r':
#if WIN_ENV
c = CR;
if (lineptr < MAXLINE)
if (lineptr < MAXLINE-1)
linebuf[lineptr++] = c;
c = LF;
#else
130,7 → 129,7
}//else if(alerts) alertuser((TCHAR*)TEXT("Warning:"),TEXT("truncated escape sequence ends input")); // TODO (Not so important): TRANSLATE
}
 
if(lineptr < MAXLINE)
if(lineptr < MAXLINE-1)
linebuf[lineptr++] = c;
}
}
899,18 → 898,18
char out[256];
if (_gufReadProperty(q, count, "GUF", "Protocol", out, sizeof(out))) {
if (strcmp(out, "1") != 0) {
if (reason) *reason = FF_GetMsg_Cpy(MSG_INCOMPATIBLE_GUF_FILE_ID);
PIUNLOCKHANDLE(h);
PIDISPOSEHANDLE(h);
FSClose(refnum);
if (reason) *reason = FF_GetMsg_Cpy(MSG_INCOMPATIBLE_GUF_FILE_ID);
return false;
}
}
else {
if (reason) *reason = FF_GetMsg_Cpy(MSG_INCOMPATIBLE_GUF_FILE_ID);
PIUNLOCKHANDLE(h);
PIDISPOSEHANDLE(h);
FSClose(refnum);
if (reason) *reason = FF_GetMsg_Cpy(MSG_INCOMPATIBLE_GUF_FILE_ID);
return false;
}
if (_gufReadProperty(q, count, "Info", "Title", out, sizeof(out))) {
1016,3 → 1015,188
 
return res;
}
 
Boolean readfile_ffl(StandardFileReply* sfr, TCHAR** reason) {
FILEREF rTmp, refnum;
Handle h, hTmp;
Boolean res = false;
StandardFileReply sfrTmp;
OSErr e;
char* p, * start;
size_t est;
 
if (!fileHasExtension(sfr, TEXT(".ffl"))) return false;
 
if (FSpOpenDF(&sfr->sfFile, fsRdPerm, &refnum) == noErr) {
if ((h = readfileintohandle(refnum))) {
FILECOUNT count = (FILECOUNT)PIGETHANDLESIZE(h);
char* q = PILOCKHANDLE(h, false);
 
char* tmp_cur_filter_str[29];
 
int lineNumber = 0;
int countFilters = 0;
char* token = strtok(q, "\n");
while (token != NULL) {
size_t i;
char* token2 = my_strdup(token);
for (i = 0; i < strlen(token2); i++) {
if (token2[i] == '\r') token2[i] = '\0';
}
if (lineNumber == 0) {
if (strcmp(token2,"FFL1.0") != 0) {
PIUNLOCKHANDLE(h);
PIDISPOSEHANDLE(h);
FSClose(refnum);
//if (reason) *reason = TEXT("Invalid file signature"); // TODO: translate
return false;
}
}
else if (lineNumber == 1) {
countFilters = atoi(token2);
}
else
{
/*
* 0 filter_file1.8bf
* 1 Category 1 here
* 2 Filter Name 1 here
* 3 Autor 1 here
* 4 Copyright 1 here
* 5 Map1 name or empty line to disable map
* 6 Map2 name or empty line to disable map
* 7 Map3 name or empty line to disable map
* 8 Map4 name or empty line to disable map
* 9 Slider 1
* 10 Slider 2
* 11 Slider 3
* 12 Slider 4
* 13 Slider 5
* 14 Slider 6
* 15 Slider 7
* 16 Slider 8
* 17 100
* 18 110
* 19 120
* 20 130
* 21 140
* 22 150
* 23 160
* 24 170
* 25 r
* 26 g
* 27 b
* 28 a
*/
int filterLineNumber = (lineNumber - 2) % 29;
tmp_cur_filter_str[filterLineNumber] = token2;
if (filterLineNumber == 28) {
TCHAR* curFileNameOrig;
TCHAR* curFileName;
curFileNameOrig = curFileName = (TCHAR*)malloc(MAX_PATH*sizeof(TCHAR));
 
strcpy_advance(&curFileName, sfr->sfFile.szName);
curFileName -= 4; // remove ".ffl" extension
*curFileName = (TCHAR)0;
 
xstrcat(curFileNameOrig, TEXT("__"));
curFileName += strlen("__");
 
strcpy_advance_a(&curFileName, tmp_cur_filter_str[0]);
curFileName -= 4; // remove ".8bf" extension
*curFileName = (TCHAR)0;
 
#ifdef WIN_ENV
xstrcat(curFileNameOrig, TEXT(".txt"));
#endif
 
sfrTmp.sfGood = true;
sfrTmp.sfReplacing = true;
sfrTmp.sfType = TEXT_FILETYPE;
xstrcpy(sfrTmp.sfFile.szName, curFileNameOrig);
#ifdef WIN_ENV
sfrTmp.nFileExtension = (WORD)(xstrlen(curFileNameOrig) - strlen(".txt") + 1);
#endif
sfrTmp.sfScript = 0; // FIXME: is that ok?
 
est = 16000;
 
FSpDelete(&sfrTmp.sfFile);
if (FSpCreate(&sfrTmp.sfFile, SIG_SIMPLETEXT, TEXT_FILETYPE, sfr->sfScript) == noErr)
if (FSpOpenDF(&sfrTmp.sfFile, fsWrPerm, &rTmp) == noErr) {
if ((hTmp = PINEWHANDLE(1))) { // don't set initial size to 0, since some hosts (e.g. GIMP/PSPI) are incompatible with that.
PIUNLOCKHANDLE(hTmp); // should not be necessary
if (!(e = PISETHANDLESIZE(hTmp, (int32)(est))) && (p = start = PILOCKHANDLE(hTmp, false))) {
 
p += sprintf(p, "Category: %s\n", tmp_cur_filter_str[1]);
p += sprintf(p, "Title: %s\n", tmp_cur_filter_str[2]);
p += sprintf(p, "Copyright: %s\n", tmp_cur_filter_str[4]);
p += sprintf(p, "Author: %s\n", tmp_cur_filter_str[3]);
p += sprintf(p, "Filename: %s\n", tmp_cur_filter_str[0]);
p += sprintf(p, "\n");
p += sprintf(p, "R:\n");
p += sprintf(p, "%s\n", tmp_cur_filter_str[25]);
p += sprintf(p, "\n");
p += sprintf(p, "G:\n");
p += sprintf(p, "%s\n", tmp_cur_filter_str[26]);
p += sprintf(p, "\n");
p += sprintf(p, "B:\n");
p += sprintf(p, "%s\n", tmp_cur_filter_str[27]);
p += sprintf(p, "\n");
p += sprintf(p, "A:\n");
p += sprintf(p, "%s\n", tmp_cur_filter_str[28]);
p += sprintf(p, "\n");
 
// Maps
for (i = 0; i < 4; i++) {
char* tmp = tmp_cur_filter_str[5 + i];
if (strcmp(tmp, "") != 0) {
p += sprintf(p, "map[%d]: %s\n", i, tmp_cur_filter_str[5+i]);
}
}
p += sprintf(p, "\n");
 
// Controls
for (i = 0; i < 8; i++) {
char* tmp = tmp_cur_filter_str[9 + i];
if (strcmp(tmp, "") != 0) {
p += sprintf(p, "ctl[%d]: %s\n", i, tmp_cur_filter_str[9 + i]);
}
tmp = tmp_cur_filter_str[17 + i];
if (strcmp(tmp, "") != 0) {
p += sprintf(p, "val[%d]: %s\n", i, tmp_cur_filter_str[17 + i]);
}
}
p += sprintf(p, "\n");
 
PIUNLOCKHANDLE(hTmp);
e = PISETHANDLESIZE(hTmp, (int32)(p - start)); // could ignore this error, maybe
}
savehandleintofile(hTmp, rTmp);
PIDISPOSEHANDLE(hTmp);
}
FSClose(rTmp);
}
 
free(curFileNameOrig);
for (i = 0; i < 29; i++) {
free(tmp_cur_filter_str[i]); // free all "token2"
}
}
}
lineNumber++;
token = strtok(NULL, "\n");
}
 
PIUNLOCKHANDLE(h);
PIDISPOSEHANDLE(h);
}
FSClose(refnum);
}
 
// TODO: show a different message when no filters were processed for some reason...
// TODO: It's very confusing because this shows up as error message...
if (reason) *reason = FF_GetMsg_Cpy(MSG_FFL_CONVERTED_ID);
return false;
}
/trunk/testcases/import/example.ffl
0,0 → 1,89
FFL1.0
2
filter_file1.8bf
Category 1 here
Filter Name 1 here
Autor 1 here
Copyright 1 here
Map1 name or empty line to disable map
Map2 name or empty line to disable map
Map3 name or empty line to disable map
Map4 name or empty line to disable map
Slider 1
Slider 2
Slider 3
Slider 4
Slider 5
Slider 6
Slider 7
Slider 8
100
110
120
130
140
150
160
170
d
m
b
a
filter_file2.8bf
Category 2 here
Filter Name 2 here
Autor 2 here
Copyright 2 here
Map1 name or empty line to disable map
Map2 name or empty line to disable map
Map3 name or empty line to disable map
Map4 name or empty line to disable map
Slider 1
Slider 2
Slider 3
Slider 4
Slider 5
Slider 6
Slider 7
Slider 8
100
110
120
130
140
150
160
170
d
m
b
a
filter_file3.8bf
Category 3 here
Filter Name 3 here
Autor 3 here
Copyright 3 here
Map1 name or empty line to disable map
Map2 name or empty line to disable map
Map3 name or empty line to disable map
Map4 name or empty line to disable map
Slider 1
Slider 2
Slider 3
Slider 4
Slider 5
Slider 6
Slider 7
Slider 8
100
110
120
130
140
150
160
170
d
m
b
a
/trunk/testcases/import/not_implemented/ffl/example.ffl
File deleted
/trunk/testcases/import
Property changes:
Added: svn:ignore
+example__filter_file1.txt
+example__filter_file2.txt
+example__filter_file3.txt
/trunk/ui.c
75,7 → 75,7
 
/*
int i;
char s[MAXEXPR+1];
char s[MAXEXPR];
 
for(i = 0; i < 8; ++i)
slider[i] = (value_type)(GETSLIDERVALUE(dp,FIRSTCTLITEM+i));
83,7 → 83,7
if(!gdata->standalone)
for(i = 0; i < 4; ++i){
// stash expression strings
if(GETCTLTEXT(dp,FIRSTEXPRITEM+i,s,MAXEXPR)){
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);
95,7 → 95,7
}
 
struct node *updateexpr(DIALOGREF dp,int item){
char s[MAXEXPR+1];
char s[MAXEXPR];
int i;
 
i = item - FIRSTEXPRITEM;
103,7 → 103,7
freetree(tree[i]);
 
if(!gdata->standalone){
GETCTLTEXT(dp,item,s,MAXEXPR);
GETCTLTEXT(dp,item,s,MAXEXPR); // cchMax: NULL is included, so MAXEXPR is correct
 
if(expr[i])
free(expr[i]);
378,8 → 378,8
FF_GetMsg(title, MSG_LOAD_FILTER_SETTINGS_TITLE_ID);
 
strcpy_advance_id(&tmp1, MSG_ALL_SUPPORTED_FILES_ID);
strcpy_advance(&tmp1, (TCHAR*)TEXT(" (*.afs, *.8bf, *.pff, *.prm, *.bin, *.rsrc, *.txt, *.ffx, *.guf)")); tmp1++;
strcpy_advance(&tmp1, (TCHAR*)TEXT("*.afs;*.8bf;*.pff;*.prm;*.bin;*.rsrc;*.txt;*.ffx;*.guf")); tmp1++;
strcpy_advance(&tmp1, (TCHAR*)TEXT(" (*.afs, *.8bf, *.pff, *.prm, *.bin, *.rsrc, *.txt, *.ffx, *.ffl, *.guf)")); tmp1++;
strcpy_advance(&tmp1, (TCHAR*)TEXT("*.afs;*.8bf;*.pff;*.prm;*.bin;*.rsrc;*.txt;*.ffx;*.ffl;*.guf")); tmp1++;
 
strcpy_advance_id(&tmp1, MSG_OPEN_AFS_ID);
strcpy_advance(&tmp1, (TCHAR*)TEXT(" (*.afs)")); tmp1++;
409,6 → 409,10
strcpy_advance(&tmp1, (TCHAR*)TEXT(" (*.ffx)")); tmp1++;
strcpy_advance(&tmp1, (TCHAR*)TEXT("*.ffx")); tmp1++;
 
strcpy_advance_id(&tmp1, MSG_OPEN_FFL_ID);
strcpy_advance(&tmp1, (TCHAR*)TEXT(" (*.ffl)")); tmp1++;
strcpy_advance(&tmp1, (TCHAR*)TEXT("*.ffl")); tmp1++;
 
strcpy_advance_id(&tmp1, MSG_OPEN_GUF_ID);
strcpy_advance(&tmp1, (TCHAR*)TEXT(" (*.guf)")); tmp1++;
strcpy_advance(&tmp1, (TCHAR*)TEXT("*.guf")); tmp1++;