Subversion Repositories filter_foundry

Rev

Rev 556 | Blame | Compare with Previous | Last modification | View Log | RSS feed

  1. /*
  2.     This file is part of "Filter Foundry", a filter plugin for Adobe Photoshop
  3.     Copyright (C) 2003-2009 Toby Thain, toby@telegraphics.net
  4.     Copyright (C) 2018-2023 Daniel Marschall, ViaThinkSoft
  5.  
  6.     This program is free software; you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License as published by
  8.     the Free Software Foundation; either version 2 of the License, or
  9.     (at your option) any later version.
  10.  
  11.     This program is distributed in the hope that it will be useful,
  12.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.     GNU General Public License for more details.
  15.  
  16.     You should have received a copy of the GNU General Public License
  17.     along with this program; if not, write to the Free Software
  18.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19. */
  20.  
  21. #include "ff.h"
  22.  
  23. #include "file_compat.h"
  24.  
  25. #ifdef MAC_ENV
  26. #include <Endian.h>
  27. #else
  28. int EndianS32_LtoN(int num) {
  29.         return ((num>>24)&0xff) +      // move byte 3 to byte 0
  30.                ((num<<8)&0xff0000) +   // move byte 1 to byte 2
  31.                ((num>>8)&0xff00) +     // move byte 2 to byte 1
  32.                ((num<<24)&0xff000000); // byte 0 to byte 3
  33. }
  34. #endif
  35.  
  36. #define BUFSIZE 4L<<10
  37. #define MAXLINE 0x200
  38.  
  39. FFLoadingResult readparams_afs_pff(Handle h, Boolean premiereOrder){
  40.         FFLoadingResult res = MSG_LOADFILE_UNKNOWN_FORMAT_ID;
  41.         char linebuf[MAXLINE] = { 0 };
  42.         char curexpr[MAXEXPR] = { 0 };
  43.         char *p, *dataend, *q;
  44.         char c;
  45.         int linecnt, lineptr, exprcnt;
  46.  
  47.         //if (!h) return nilHandleErr;
  48.  
  49.         p = PILOCKHANDLE(h,false);
  50.         dataend = p + PIGETHANDLESIZE(h);
  51.  
  52.         q = curexpr;
  53.         linecnt = exprcnt = lineptr = 0;
  54.  
  55.         while(p < dataend){
  56.  
  57.                 c = *p++;
  58.  
  59.                 if(c==CR || c==LF){ /* detected end of line */
  60.  
  61.                         /* look ahead to see if we need to skip a line feed (DOS CRLF EOL convention) */
  62.                         if(p < dataend && c == CR && *p == LF)
  63.                                 ++p;
  64.  
  65.                         linebuf[lineptr] = '\0'; /* add terminating NUL to line buffer */
  66.  
  67.                         /* process complete line */
  68.                         if(linecnt==0){
  69.                                 if(strcmp(linebuf,"%RGB-1.0") != 0){
  70.                                         res = MSG_INVALID_FILE_SIGNATURE_ID;
  71.                                         break;
  72.                                 }
  73.                         }else if(linecnt<=8){
  74.                                 int v;
  75.                                 v = atoi(linebuf);
  76.                                 if (v < 0) v = 0;
  77.                                 else if (v > 255) v = 255;
  78.                                 gdata->parm.val[linecnt-1] = (uint8_t)v;
  79.                         }else{
  80.                                 if(lineptr){
  81.                                         /* it's not an empty line; append it to current expr string */
  82.                                         if( (q-curexpr) + lineptr >= MAXEXPR) {
  83.                                                 res = MSG_EXPRESSION1024_FOUND_ID;
  84.                                                 break;
  85.                                         }
  86.                                         q = cat(q,linebuf);
  87.                                 }else{
  88.                                         /* it's an empty line: we've completed the expr string */
  89.                                         *q = '\0';
  90.                                         if (premiereOrder) {
  91.                                                 // Premiere has the order BGRA, while Photoshop (and our internal order) is RGBA
  92.                                                 if (exprcnt == 0) strcpy(gdata->parm.szFormula[2], curexpr);
  93.                                                 else if (exprcnt == 2) strcpy(gdata->parm.szFormula[0], curexpr);
  94.                                                 else strcpy(gdata->parm.szFormula[exprcnt], curexpr);
  95.                                         }
  96.                                         else {
  97.                                                 strcpy(gdata->parm.szFormula[exprcnt], curexpr);
  98.                                         }
  99.  
  100.                                         if(++exprcnt == 4){
  101.                                                 res = LOADING_OK;
  102.                                                 break; /* got everything we want */
  103.                                         }
  104.  
  105.                                         q = curexpr; /* empty current expr, ready for next one */
  106.                                 }
  107.                         }
  108.  
  109.                         ++linecnt;
  110.                         lineptr = 0;
  111.                 }else{
  112.                         /* store character */
  113.                         if(c=='\\'){ /* escape sequence */
  114.                                 if(p < dataend){
  115.                                         c = *p++;
  116.                                         switch(c){
  117.                                         case 'r':
  118.                                                 #if WIN_ENV
  119.                                                 c = CR;
  120.                                                 if (lineptr < MAXLINE-1)
  121.                                                         linebuf[lineptr++] = c;
  122.                                                 c = LF;
  123.                                                 #else
  124.                                                 c = CR;
  125.                                                 #endif
  126.                                                 break;
  127.                                         case '\\': break;
  128.                                         //default:
  129.                                         //      if(alerts) alertuser((TCHAR*)TEXT("Warning:"),TEXT("Unknown escape sequence in input.")); // TODO (Not so important): TRANSLATE
  130.                                         }
  131.                                 }//else if(alerts) alertuser((TCHAR*)TEXT("Warning:"),TEXT("truncated escape sequence ends input")); // TODO (Not so important): TRANSLATE
  132.                         }
  133.  
  134.                         if(lineptr < MAXLINE-1)
  135.                                 linebuf[lineptr++] = c;
  136.                 }
  137.         }
  138.  
  139.         PIUNLOCKHANDLE(h);
  140.  
  141.         return res;
  142. }
  143.  
  144. void convert_premiere_to_photoshop(PARM_T* photoshop, PARM_T_PREMIERE* premiere) {
  145.         int i;
  146.  
  147.         photoshop->cbSize = sizeof(PARM_T);
  148.         photoshop->standalone = premiere->standalone;
  149.         for (i=0;i<8;++i)
  150.                 photoshop->val[i] = premiere->val[i];
  151.         photoshop->popDialog = premiere->popDialog;
  152.         photoshop->unknown1 = premiere->unknown1;
  153.         photoshop->unknown2 = premiere->unknown2;
  154.         photoshop->unknown3 = premiere->unknown3;
  155.         for (i=0;i<4;++i)
  156.                 photoshop->map_used[i] = premiere->map_used[i];
  157.         for (i=0;i<8;++i)
  158.                 photoshop->ctl_used[i] = premiere->ctl_used[i];
  159.         sprintf(photoshop->szCategory, "Filter Factory"); // Premiere plugins do not have a category attribute
  160.         photoshop->iProtected = 0; // Premiere plugins do not have a protect flag
  161.         memcpy((void*)photoshop->szTitle, (void*)premiere->szTitle, sizeof(photoshop->szTitle));
  162.         memcpy((void*)photoshop->szCopyright, (void*)premiere->szCopyright, sizeof(photoshop->szCopyright));
  163.         memcpy((void*)photoshop->szAuthor, (void*)premiere->szAuthor, sizeof(photoshop->szAuthor));
  164.         for (i=0;i<4;++i)
  165.                 memcpy((void*)photoshop->szMap[i], (void*)premiere->szMap[i], sizeof(photoshop->szMap[i]));
  166.         for (i=0;i<8;++i)
  167.                 memcpy((void*)photoshop->szCtl[i], (void*)premiere->szCtl[i], sizeof(photoshop->szCtl[i]));
  168.  
  169.         if (premiere->singleExpression) {
  170.                 memcpy((void*)photoshop->szFormula[0], (void*)premiere->szFormula[3], sizeof(photoshop->szFormula[3]));
  171.                 memcpy((void*)photoshop->szFormula[1], (void*)premiere->szFormula[3], sizeof(photoshop->szFormula[3]));
  172.                 memcpy((void*)photoshop->szFormula[2], (void*)premiere->szFormula[3], sizeof(photoshop->szFormula[3]));
  173.                 memcpy((void*)photoshop->szFormula[3], (void*)premiere->szFormula[3], sizeof(photoshop->szFormula[3]));
  174.         } else {
  175.                 memcpy((void*)photoshop->szFormula[0], (void*)premiere->szFormula[2], sizeof(photoshop->szFormula[2]));
  176.                 memcpy((void*)photoshop->szFormula[1], (void*)premiere->szFormula[1], sizeof(photoshop->szFormula[1]));
  177.                 memcpy((void*)photoshop->szFormula[2], (void*)premiere->szFormula[0], sizeof(photoshop->szFormula[0]));
  178.                 memcpy((void*)photoshop->szFormula[3], (void*)premiere->szFormula[3], sizeof(photoshop->szFormula[3]));
  179.         }
  180. }
  181.  
  182. char* _ffx_read_str(char** q) {
  183.         uint32_t len;
  184.         char* val;
  185.  
  186.         len = *((uint32_t*)*q);
  187.         *q += sizeof(uint32_t);
  188.         val = (char*)malloc((size_t)len + 1);
  189.         if (val != NULL) {
  190.                 memcpy(val, (char*)*q, len);
  191.                 val[len] = '\0';
  192.         }
  193.         *q += len;
  194.         return val;
  195. }
  196.  
  197. FFLoadingResult readfile_ffx(StandardFileReply* sfr) {
  198.         Handle h;
  199.         FFLoadingResult res = MSG_LOADFILE_UNKNOWN_FORMAT_ID;
  200.         FILEREF refnum;
  201.         uint32_t len;
  202.         char* val;
  203.         int format_version = -1;
  204.         int i;
  205.  
  206.         if (FSpOpenDF(&sfr->sfFile, fsRdPerm, &refnum) == noErr) {
  207.                 if ((h = readfileintohandle(refnum))) {
  208.                         char* q = (char*)PILOCKHANDLE(h, false);
  209.  
  210.                         len = *((uint32_t*)q);
  211.                         if (len != 6) {
  212.                                 res = MSG_INVALID_FILE_SIGNATURE_ID;
  213.                         } else {
  214.                                 val = _ffx_read_str(&q);
  215.                                 if (strcmp(val, "FFX1.0") == 0) format_version = 10;
  216.                                 else if (strcmp(val, "FFX1.1") == 0) format_version = 11;
  217.                                 else if (strcmp(val, "FFX1.2") == 0) format_version = 12;
  218.                                 free(val);
  219.                                 if (format_version < 0) {
  220.                                         res = MSG_INVALID_FILE_SIGNATURE_ID;
  221.                                 } else {
  222.                                         simplewarning_id(MSG_FILTERS_UNLIMITED_WARNING_ID);
  223.  
  224.                                         val = _ffx_read_str(&q);
  225.                                         if (strlen(val) >= sizeof(gdata->parm.szTitle)) {
  226.                                                 val[sizeof(gdata->parm.szTitle) - 1] = '\0';
  227.                                         }
  228.                                         strcpy(gdata->parm.szTitle, val);
  229.                                         free(val);
  230.  
  231.                                         val = _ffx_read_str(&q);
  232.                                         if (strlen(val) >= sizeof(gdata->parm.szCategory)) {
  233.                                                 val[sizeof(gdata->parm.szCategory) - 1] = '\0';
  234.                                         }
  235.                                         strcpy(gdata->parm.szCategory, val);
  236.                                         free(val);
  237.  
  238.                                         val = _ffx_read_str(&q);
  239.                                         if (strlen(val) >= sizeof(gdata->parm.szAuthor)) {
  240.                                                 val[sizeof(gdata->parm.szAuthor) - 1] = '\0';
  241.                                         }
  242.                                         strcpy(gdata->parm.szAuthor, val);
  243.                                         free(val);
  244.  
  245.                                         val = _ffx_read_str(&q);
  246.                                         if (strlen(val) >= sizeof(gdata->parm.szCopyright)) {
  247.                                                 val[sizeof(gdata->parm.szCopyright) - 1] = '\0';
  248.                                         }
  249.                                         strcpy(gdata->parm.szCopyright, val);
  250.                                         free(val);
  251.  
  252.                                         // Channels I, R, G, B, A
  253.                                         for (i = 0; i < 4; i++) {
  254.                                                 val = _ffx_read_str(&q);
  255.                                                 if (i == 0) {
  256.                                                         char* val2 = _ffx_read_str(&q);
  257.                                                         if (strcmp(val, "0") != 0) {
  258.                                                                 // "Intro channel" existing
  259.                                                                 // C++ wrong warning: Using uninitialized memory "val2" (C6001)
  260.                                                                 #pragma warning(suppress : 6001)
  261.                                                                 char* combined = (char*)malloc(strlen(val) + strlen(",") + strlen(val2) + 1/*NUL byte*/);
  262.                                                                 if (combined != NULL) {
  263.                                                                         sprintf(combined, "%s,%s", val, val2);
  264.                                                                         free(val);
  265.                                                                         free(val2);
  266.                                                                         val = combined;
  267.                                                                 }
  268.                                                         }
  269.                                                         else {
  270.                                                                 free(val);
  271.                                                                 val = val2;
  272.                                                         }
  273.                                                 }
  274.                                                 if (strlen(val) >= sizeof(gdata->parm.szFormula[i])) {
  275.                                                         if (i == 0) {
  276.                                                                 simplealert_id(MSG_FORMULA_IR_1023_TRUNCATED_ID);
  277.                                                         }
  278.                                                         else if (i == 1) {
  279.                                                                 simplealert_id(MSG_FORMULA_G_1023_TRUNCATED_ID);
  280.                                                         }
  281.                                                         else if (i == 2) {
  282.                                                                 simplealert_id(MSG_FORMULA_B_1023_TRUNCATED_ID);
  283.                                                         }
  284.                                                         else if (i == 3) {
  285.                                                                 simplealert_id(MSG_FORMULA_A_1023_TRUNCATED_ID);
  286.                                                         }
  287.                                                         // C++ wrong warning: Buffer overflow (C6386)
  288.                                                         #pragma warning(suppress : 6386)
  289.                                                         val[sizeof(gdata->parm.szFormula[i]) - 1] = '\0';
  290.                                                 }
  291.                                                 strcpy(gdata->parm.szFormula[i], val);
  292.                                                 free(val);
  293.                                         }
  294.  
  295.                                         // Sliders
  296.                                         for (i = 0; i < 8; i++) {
  297.                                                 char* sliderName;
  298.                                                 int v;
  299.                                                 val = _ffx_read_str(&q);
  300.                                                 sliderName = val;
  301.                                                 if (format_version >= 12) {
  302.                                                         // Format FFX1.2 has prefixes {S} = Slider, {C} = Checkbox, none = Slider
  303.                                                         if ((sliderName[0] == '{') && (sliderName[1] == 'S') && (sliderName[2] == '}')) sliderName += 3;
  304.                                                         else if ((sliderName[0] == '{') && (sliderName[1] == 'C') && (sliderName[2] == '}')) sliderName += 3;
  305.                                                 }
  306.                                                 if (strlen(val) >= sizeof(gdata->parm.szCtl[i])) {
  307.                                                         val[sizeof(gdata->parm.szCtl[i]) - 1] = '\0';
  308.                                                 }
  309.                                                 strcpy(gdata->parm.szCtl[i], sliderName);
  310.                                                 free(val);
  311.                                                 gdata->parm.ctl_used[i] = (bool32_t)*((byte*)q);
  312.                                                 q += sizeof(byte);
  313.                                                 gdata->parm.val[i] = *((uint32_t*)q);
  314.                                                 v = *((uint32_t*)q);
  315.                                                 if (v < 0) v = 0;
  316.                                                 else if (v > 255) v = 255;
  317.                                                 gdata->parm.val[i] = (uint8_t)v;
  318.                                                 q += sizeof(uint32_t);
  319.                                         }
  320.  
  321.                                         // Maps (are not part of the format!)
  322.                                         strcpy(gdata->parm.szMap[0], "Map 0:");
  323.                                         strcpy(gdata->parm.szMap[1], "Map 1:");
  324.                                         strcpy(gdata->parm.szMap[2], "Map 2:");
  325.                                         strcpy(gdata->parm.szMap[3], "Map 3:");
  326.  
  327.                                         res = LOADING_OK;
  328.                                 }
  329.                         }
  330.                         PIDISPOSEHANDLE(h);
  331.                 }
  332.                 FSClose(refnum);
  333.         }
  334.  
  335.         if (res == LOADING_OK) gdata->obfusc = false;
  336.         return res;
  337. }
  338.  
  339. FFLoadingResult readfile_8bf(StandardFileReply *sfr){
  340.         unsigned char magic[2];
  341.         FILECOUNT count;
  342.         Handle h;
  343.         FFLoadingResult res = MSG_LOADFILE_UNKNOWN_FORMAT_ID;
  344.         FILEREF refnum;
  345.  
  346.         if(FSpOpenDF(&sfr->sfFile,fsRdPerm,&refnum) == noErr){
  347.                 // check DOS EXE magic number
  348.                 count = 2;
  349.                 if(FSRead(refnum,&count,magic) == noErr /*&& magic[0]=='M' && magic[1]=='Z'*/){
  350.                         if(GetEOF(refnum,(FILEPOS*)&count) == noErr && count < 4096L<<10){ // sanity check file size < 4MiB (note that "Debug" builds can have approx 700 KiB while "Release" builds have approx 300 KiB)
  351.                                 if( (h = readfileintohandle(refnum)) ){
  352.                                         long *q = (long*)PILOCKHANDLE(h,false);
  353.  
  354.                                         // look for signature at start of valid PARM resource
  355.                                         // This signature is observed in Filter Factory standalones.
  356.                                         for( count /= 4 ; count >= PARM_SIZE/4 ; --count, ++q )
  357.                                         {
  358.                                                 res = readPARM(&gdata->parm, (Ptr)q);
  359.                                                 if (res == LOADING_OK) break;
  360.                                         }
  361.  
  362.                                         PIDISPOSEHANDLE(h);
  363.                                 }
  364.                         }
  365.                 } // else no point in proceeding
  366.                 FSClose(refnum);
  367.         }else
  368.                 res = MSG_CANNOT_OPEN_FILE_ID;
  369.  
  370.         if (res == LOADING_OK) gdata->obfusc = false;
  371.         return res;
  372. }
  373.  
  374. FFLoadingResult readPARM(PARM_T* pparm, Ptr p){
  375.         Boolean towin, tomac, fromwin, frommac;
  376.         unsigned int signature = *((unsigned int*)p);
  377.         unsigned int standalone = *((unsigned int*)p+1);
  378.  
  379.         // Find out our OS ("reader") the OS of the plugin ("source")
  380.         #ifdef MAC_ENV
  381.         towin = false;
  382.         tomac = true;
  383.         fromwin = ((EndianS32_LtoN(signature) == PARM_SIZE) ||
  384.                 (EndianS32_LtoN(signature) == PARM_SIZE_PREMIERE) ||
  385.                 (EndianS32_LtoN(signature) == PARM_SIG_MAC)) && EndianS32_LtoN(standalone) == 1;
  386.         frommac = ((signature == PARM_SIZE) ||
  387.                 (signature == PARM_SIZE_PREMIERE) ||
  388.                 (signature == PARM_SIG_MAC)) && standalone == 1;
  389.         #else
  390.         towin = true;
  391.         tomac = false;
  392.         fromwin = ((signature == PARM_SIZE) ||
  393.                 (signature == PARM_SIZE_PREMIERE) ||
  394.                 (signature == PARM_SIG_MAC)) && standalone == 1;
  395.         frommac = ((EndianS32_LtoN(signature) == PARM_SIZE) ||
  396.                 (EndianS32_LtoN(signature) == PARM_SIZE_PREMIERE) ||
  397.                 (EndianS32_LtoN(signature) == PARM_SIG_MAC)) && EndianS32_LtoN(standalone) == 1;
  398.         #endif
  399.  
  400.         // Is it a valid signature?
  401.         if (!fromwin && !frommac) {
  402.                 // No valid signature found
  403.                 return MSG_INVALID_FILE_SIGNATURE_ID;
  404.         }
  405.  
  406.         // Does it come from Premiere or Photoshop?
  407.         // Initialize pparm
  408.         if ((signature == PARM_SIZE_PREMIERE) || (EndianS32_LtoN(signature) == PARM_SIZE_PREMIERE)) {
  409.                 // It comes from Premiere. Swap R and B channel and convert to a Photoshop PARM_T
  410.                 convert_premiere_to_photoshop(pparm, (PARM_T_PREMIERE*)p);
  411.         } else {
  412.                 // It is already Photoshop. Just copy to pparm.
  413.                 memcpy(pparm, p, sizeof(PARM_T));
  414.         }
  415.  
  416.         // Do we need to do string conversion?
  417.         if (frommac) {
  418.                 int i;
  419.  
  420.                 /* Mac PARM resource stores Pascal strings - convert to C strings, since this is what we work internally with (regardles of OS) */
  421.                 myp2cstr((unsigned char*)pparm->szCategory);
  422.                 myp2cstr((unsigned char*)pparm->szTitle);
  423.                 myp2cstr((unsigned char*)pparm->szCopyright);
  424.                 myp2cstr((unsigned char*)pparm->szAuthor);
  425.                 for (i = 0; i < 4; ++i)
  426.                         myp2cstr((unsigned char*)pparm->szMap[i]);
  427.                 for (i = 0; i < 8; ++i)
  428.                         myp2cstr((unsigned char*)pparm->szCtl[i]);
  429.         }
  430.  
  431.         // Case #1: Mac is reading Windows (Win16/32/64) plugin
  432.         if (fromwin && tomac) {
  433.                 size_t i;
  434.  
  435.                 // Convert copyright CRLF to CR (actually, just removing LF)
  436.                 char copyrightCRLF[256] = { 0 };
  437.                 char* pCopyright = &copyrightCRLF[0];
  438.                 for (i = 0; i < strlen(pparm->szCopyright); i++) {
  439.                         if (pparm->szCopyright[i] != LF) {
  440.                                 *pCopyright++ = pparm->szCopyright[i];
  441.                         }
  442.                 }
  443.                 *pCopyright++ = '\0';
  444.                 strcpy(pparm->szCopyright, copyrightCRLF);
  445.  
  446.                 // these are the only numeric fields we *have* to swap
  447.                 // all the rest are bool_t flags which (if we're careful) will work in either ordering
  448.                 for (i = 0; i < 8; ++i)
  449.                         pparm->val[i] = EndianS32_LtoN(pparm->val[i]);
  450.         }
  451.  
  452.         // Case #2: Mac is reading Mac (in case the normal resource extraction didn't work)
  453.         // Nothing to do
  454.  
  455.         // Case #3: Windows is reading a Windows plugin (if Resource API failed, e.g. Win64 tries to open Win16 NE file or Win32 tries to open Win64 file)
  456.         // Nothing to do
  457.  
  458.         // Case #4: Windows is reading an old FilterFactory Mac file
  459.         // Note: You must read the ".rsrc" resource fork, not the standalone binary!
  460.         if (frommac && towin) {
  461.                 size_t i;
  462.  
  463.                 // Convert CR in the copyright field to CRLF.
  464.                 char copyrightCRLF[256] = { 0 };
  465.                 char* pCopyright = &copyrightCRLF[0];
  466.                 for (i = 0; i < strlen(pparm->szCopyright); i++) {
  467.                         *pCopyright++ = pparm->szCopyright[i];
  468.                         if (pparm->szCopyright[i] == CR) {
  469.                                 *pCopyright++ = LF;
  470.                         }
  471.                 }
  472.                 *pCopyright++ = '\0';
  473.                 strcpy(pparm->szCopyright, copyrightCRLF);
  474.  
  475.                 // these are the only numeric fields we *have* to swap
  476.                 // all the rest are bool_t flags which (if we're careful) will work in either ordering
  477.                 for (i = 0; i < 8; ++i)
  478.                         pparm->val[i] = EndianS32_LtoN(pparm->val[i]);
  479.         }
  480.  
  481.         return 0;
  482. }
  483.  
  484. Handle readfileintohandle(FILEREF r){
  485.         FILEPOS n;
  486.         Handle h;
  487.         Ptr p;
  488.  
  489.         if( GetEOF(r,&n) == noErr && (h = PINEWHANDLE(n)) ){
  490.                 p = PILOCKHANDLE(h,false);
  491.                 if(SetFPos(r,fsFromStart,0) == noErr && FSRead(r,(FILECOUNT*)&n,p) == noErr){
  492.                         PIUNLOCKHANDLE(h);
  493.                         return h;
  494.                 }
  495.                 PIDISPOSEHANDLE(h);
  496.         }
  497.         return NULL;
  498. }
  499.  
  500. Boolean _picoLineContainsKey(char* line, char** content, const char* searchkey/*=NULL*/) {
  501.         size_t i;
  502.         for (i = 0; i < strlen(line); i++) {
  503.                 if (line[i] == '?') break; // avoid that "a?b:c" is detected as key
  504.                 if (line[i] == ':') {
  505.                         // Note: We are ignoring whitespaces, i.e. " A :" != "A:" (TODO: should we change this?)
  506.                         if ((searchkey == NULL) || ((i == strlen(searchkey)) && (memcmp(line, searchkey, i) == 0))) {
  507.                                 i++; // jump over ':' char
  508.                                 //while ((line[i] == ' ') || (line[i] == TAB)) i++; // Trim value left
  509.                                 *content = line + i;
  510.                                 return true;
  511.                         }
  512.                 }
  513.         }
  514.         *content = line;
  515.         return false;
  516. }
  517.  
  518. void _ffdcomp_removebrackets(char* x, char* maxptr) {
  519.         char* closingBracketPos = NULL;
  520.         Boolean openingBracketFound = false;
  521.         if (x[0] == '[') {
  522.                 openingBracketFound = true;
  523.         }
  524.         x[0] = ':';
  525.         x++;
  526.         while (x < maxptr) {
  527.                 if ((!openingBracketFound) && (x[0] == '[')) {
  528.                         openingBracketFound = true;
  529.                         x[0] = ' ';
  530.                 }
  531.                 else if (openingBracketFound) {
  532.                         if (x[0] == ']') {
  533.                                 closingBracketPos = x;
  534.                         }
  535.                         else if ((x[0] == CR) || (x[0] == LF)) {
  536.                                 if (closingBracketPos) closingBracketPos[0] = ' '; // last closing pos before CR/LF
  537.                                 break;
  538.                         }
  539.                 }
  540.                 x++;
  541.         }
  542. }
  543.  
  544. // isFormula=false => outputFile is C string. TXT linebreaks become spaces.
  545. // isFormula=true  => outputFile is C string. TXT line breaks become CRLF line breaks
  546. Boolean _picoReadProperty(char* inputFile, size_t maxInput, const char* property, char* outputFile, size_t maxOutput, Boolean isFormula) {
  547.         size_t i;
  548.         char* outputwork;
  549.         char* sline;
  550.         char* svalue;
  551.         char* inputwork;
  552.         char* inputworkinitial;
  553.         outputwork = outputFile;
  554.         sline = NULL;
  555.         svalue = NULL;
  556.         // Check parameters
  557.         if (maxOutput == 0) return false;
  558.         if (inputFile == NULL) return false;
  559.         // Let input memory be read-only, +1 for terminal zero
  560.         //char* inputwork = inputFile;
  561.         inputwork = (char*)malloc((size_t)maxInput + 1/*NUL byte*/);
  562.         inputworkinitial = inputwork;
  563.         if (inputwork == NULL) return false;
  564.         memcpy(inputwork, inputFile, maxInput);
  565.         inputwork[maxInput] = '\0'; // otherwise strstr() will crash
  566.  
  567.         // Transform "FFDecomp" TXT file into the similar "PluginCommander" TXT file
  568.         if (strstr(inputwork, "Filter Factory Plugin Information:")) {
  569.                 char* x;
  570.                 char* k1;
  571.                 char* k2;
  572.                 // Metadata:
  573.                 x = strstr(inputwork, "CATEGORY:");
  574.                 if (x) memcpy(x, "Category:", strlen("Category:"));
  575.                 x = strstr(inputwork, "TITLE:");
  576.                 if (x) memcpy(x, "Title:", strlen("Title:"));
  577.                 x = strstr(inputwork, "COPYRIGHT:");
  578.                 if (x) memcpy(x, "Copyright:", strlen("Copyright:"));
  579.                 x = strstr(inputwork, "AUTHOR:");
  580.                 if (x) memcpy(x, "Author:", strlen("Author:"));
  581.                 // Controls:
  582.                 for (i = 0; i < 8; i++) {
  583.                         k1 = (char*)malloc(strlen("Control X:") + 1/*NUL byte*/);
  584.                         sprintf(k1, "Control %d:", (int)i);
  585.                         x = strstr(inputwork, k1);
  586.                         if (x) {
  587.                                 k2 = (char*)malloc(strlen("ctl[X]:   ") + 1/*NUL byte*/);
  588.                                 sprintf(k2, "ctl[%d]:   ", (int)i);
  589.                                 memcpy(x, k2, strlen(k2));
  590.                                 x += strlen("ctl[X]");
  591.                                 _ffdcomp_removebrackets(x, inputwork + maxInput - 1);
  592.                                 free(k2);
  593.                         }
  594.                         free(k1);
  595.                 }
  596.                 // Maps:
  597.                 for (i = 0; i < 4; i++) {
  598.                         k1 = (char*)malloc(strlen("Map X:") + 1/*NUL byte*/);
  599.                         sprintf(k1, "Map %d:", (int)i);
  600.                         x = strstr(inputwork, k1);
  601.                         if (x) {
  602.                                 k2 = (char*)malloc(strlen("map[X]:") + 1/*NUL byte*/);
  603.                                 sprintf(k2, "map[%d]:", (int)i);
  604.                                 memcpy(x, k2, strlen(k2));
  605.                                 x += strlen("map[X]");
  606.                                 _ffdcomp_removebrackets(x, inputwork + maxInput - 1);
  607.                                 free(k2);
  608.                         }
  609.                         free(k1);
  610.                 }
  611.                 // Convert all '\r' to '\n' for the next step to be easier
  612.                 for (i = 0; i < maxInput; i++) {
  613.                         if (inputworkinitial[i] == CR) inputworkinitial[i] = LF;
  614.                 }
  615.                 x = strstr(inputwork, "\nR=\n");
  616.                 if (x) memcpy(x, "\nR:\n", strlen("\nR:\n"));
  617.                 x = strstr(inputwork, "\nG=\n");
  618.                 if (x) memcpy(x, "\nG:\n", strlen("\nG:\n"));
  619.                 x = strstr(inputwork, "\nB=\n");
  620.                 if (x) memcpy(x, "\nB:\n", strlen("\nB:\n"));
  621.                 x = strstr(inputwork, "\nA=\n");
  622.                 if (x) memcpy(x, "\nA:\n", strlen("\nA:\n"));
  623.         }
  624.         // Replace all \r and \n with \0, so that we can parse easier
  625.         for (i = 0; i < maxInput; i++) {
  626.                 if (inputworkinitial[i] == CR) inputworkinitial[i] = '\0';
  627.                 else if (inputworkinitial[i] == LF) inputworkinitial[i] = '\0';
  628.         }
  629.  
  630.         // Find line that contains out key
  631.         inputwork = inputworkinitial;
  632.         do {
  633.                 if (inputwork > inputworkinitial + maxInput) {
  634.                         // Key not found. Set output to empty string
  635.                         outputwork[0] = '\0';
  636.                         free(inputworkinitial);
  637.                         return false;
  638.                 }
  639.                 sline = inputwork;
  640.                 inputwork += strlen(sline) + 1;
  641.                 if (inputwork - 1 > inputworkinitial + maxInput) {
  642.                         // Key not found. Set output to empty string
  643.                         // TODO: will that be ever called?
  644.                         outputwork[0] = '\0';
  645.                         free(inputworkinitial);
  646.                         return false;
  647.                 }
  648.         } while (!_picoLineContainsKey(sline, &svalue, property));
  649.  
  650.         // Read line(s) until we find a line with another key, or the line end
  651.         do {
  652.                 while ((svalue[0] == ' ') || (svalue[0] == TAB)) svalue++; // Trim left
  653.                 while ((svalue[strlen(svalue) - 1] == ' ') || (svalue[strlen(svalue) - 1] == TAB)) svalue[strlen(svalue) - 1] = '\0'; // Trim right
  654.  
  655.                 if (strlen(svalue) > 0) {
  656.                         if (outputwork + strlen(svalue) + (isFormula ? 3/*CRLF+NUL*/ : 2/*space+NUL*/) > outputFile + maxOutput) {
  657.                                 size_t remaining = maxOutput - (outputwork - outputFile) - 1;
  658.                                 //printf("BUFFER FULL (remaining = %d)\n", remaining);
  659.                                 memcpy(outputwork, svalue, remaining);
  660.                                 outputwork += remaining;
  661.                                 outputwork[0] = '\0';
  662.                                 free(inputworkinitial);
  663.                                 return true;
  664.                         }
  665.                         else {
  666.                                 memcpy(outputwork, svalue, strlen(svalue));
  667.                                 outputwork += strlen(svalue);
  668.                                 if (isFormula) {
  669.                                         // Formulas: TXT line break stays line break (important if you have comments!)
  670.                                         outputwork[0] = CR;
  671.                                         outputwork[1] = LF;
  672.                                         outputwork += 2;
  673.                                 }
  674.                                 else {
  675.                                         // Everything else: TXT line breaks becomes single whitespace
  676.                                         outputwork[0] = ' ';
  677.                                         outputwork++;
  678.                                 }
  679.                         }
  680.                 }
  681.                 outputwork[0] = '\0';
  682.  
  683.                 // Process next line
  684.                 if (inputwork > inputworkinitial + maxInput) break;
  685.                 sline = inputwork;
  686.                 inputwork += strlen(sline) + 1;
  687.                 if (inputwork - 1 > inputworkinitial + maxInput) break; // TODO: will that be ever called?
  688.         } while (!_picoLineContainsKey(sline, &svalue, NULL));
  689.  
  690.         // Remove trailing whitespace
  691.         if (outputwork > outputFile) {
  692.                 outputwork -= 1;
  693.                 outputwork[0] = '\0';
  694.         }
  695.         free(inputworkinitial);
  696.         return true;
  697. }
  698.  
  699. FFLoadingResult readfile_picotxt_or_ffdecomp(StandardFileReply* sfr) {
  700.         Handle h;
  701.         FFLoadingResult res = MSG_LOADFILE_UNKNOWN_FORMAT_ID;
  702.         FILEREF refnum;
  703.  
  704.         if (FSpOpenDF(&sfr->sfFile, fsRdPerm, &refnum) == noErr) {
  705.                 if ((h = readfileintohandle(refnum))) {
  706.                         FILECOUNT count = (FILECOUNT)PIGETHANDLESIZE(h);
  707.                         char* q = PILOCKHANDLE(h, false);
  708.  
  709.                         char dummy[256];
  710.                         if (_picoReadProperty(q, count, "Title", dummy, sizeof(dummy), false)) {
  711.                                 int i;
  712.  
  713.                                 // Plugin infos
  714.                                 _picoReadProperty(q, count, "Title", gdata->parm.szTitle, sizeof(gdata->parm.szTitle), false);
  715.                                 _picoReadProperty(q, count, "Category", gdata->parm.szCategory, sizeof(gdata->parm.szCategory), false);
  716.                                 _picoReadProperty(q, count, "Author", gdata->parm.szAuthor, sizeof(gdata->parm.szAuthor), false);
  717.                                 _picoReadProperty(q, count, "Copyright", gdata->parm.szCopyright, sizeof(gdata->parm.szCopyright), false);
  718.                                 //_picoReadProperty(q, count, "Filename", gdata->parm.xxx, sizeof(gdata->parm.xxx), false);
  719.  
  720.                                 // Expressions
  721.                                 if (!_picoReadProperty(q, count, "R", gdata->parm.szFormula[0], sizeof(gdata->parm.szFormula[0]), true))
  722.                                         strcpy(gdata->parm.szFormula[0], "r");
  723.                                 if (!_picoReadProperty(q, count, "G", gdata->parm.szFormula[1], sizeof(gdata->parm.szFormula[1]), true))
  724.                                         strcpy(gdata->parm.szFormula[1], "g");
  725.                                 if (!_picoReadProperty(q, count, "B", gdata->parm.szFormula[2], sizeof(gdata->parm.szFormula[2]), true))
  726.                                         strcpy(gdata->parm.szFormula[2], "b");
  727.                                 if (!_picoReadProperty(q, count, "A", gdata->parm.szFormula[3], sizeof(gdata->parm.szFormula[3]), true))
  728.                                         strcpy(gdata->parm.szFormula[3], "a");
  729.  
  730.                                 for (i = 0; i < 8; i++) {
  731.                                         if (gdata->parm.ctl_used[i]) {
  732.                                                 int v;
  733.                                                 char keyname[6/*strlen("ctl[X]")*/ + 1/*strlen("\0")*/], tmp[5];
  734.  
  735.                                                 // Slider names
  736.                                                 sprintf(keyname, "ctl[%d]", i);
  737.                                                 _picoReadProperty(q, count, keyname, gdata->parm.szCtl[i], sizeof(gdata->parm.szCtl[i]), false);
  738.  
  739.                                                 // Slider values
  740.                                                 sprintf(keyname, "val[%d]", i);
  741.                                                 if (!_picoReadProperty(q, count, keyname, tmp, sizeof(tmp), false)) {
  742.                                                         sprintf(keyname, "def[%d]", i);
  743.                                                         if (!_picoReadProperty(q, count, keyname, tmp, sizeof(tmp), false)) {
  744.                                                                 strcpy(tmp, "0");
  745.                                                         }
  746.                                                 }
  747.                                                 v = atoi(tmp);
  748.                                                 if (v < 0) v = 0;
  749.                                                 else if (v > 255) v = 255;
  750.                                                 gdata->parm.val[i] = gdata->parm.val[i] = (uint8_t)v;
  751.                                         }
  752.                                 }
  753.  
  754.                                 // Map names
  755.                                 for (i = 0; i < 4; i++) {
  756.                                         if (gdata->parm.map_used[i]) {
  757.                                                 char keyname[6/*strlen("map[X]")*/ + 1/*strlen("\0")*/];
  758.                                                 sprintf(keyname, "map[%d]", i);
  759.                                                 _picoReadProperty(q, count, keyname, gdata->parm.szMap[i], sizeof(gdata->parm.szMap[i]), false);
  760.                                         }
  761.                                 }
  762.  
  763.                                 res = LOADING_OK;
  764.                         }
  765.  
  766.                         PIUNLOCKHANDLE(h);
  767.                         PIDISPOSEHANDLE(h);
  768.                 }
  769.                 FSClose(refnum);
  770.         }
  771.  
  772.         return res;
  773. }
  774.  
  775. Boolean _gufReadProperty(char* fileContents, size_t argMaxInputLength, const char* section, const char* keyname, char* argOutput, size_t argMaxOutLength) {
  776.         size_t iTmp;
  777.         char* tmpFileContents, * tmpSection, * tmpStart, * tmpStart2, * tmpEnd, * tmpKeyname, * tmpStart3, * tmpStart4, * inputwork;
  778.  
  779.         // Check parameters
  780.         if (argMaxOutLength == 0) return false;
  781.         if (fileContents == NULL) return false;
  782.  
  783.         // Handle argMaxInputLength
  784.         //char* inputwork = fileContents;
  785.         inputwork = (char*)malloc((size_t)argMaxInputLength + 1/*NUL byte*/);
  786.         if (inputwork == NULL) return false;
  787.         memcpy(inputwork, fileContents, argMaxInputLength);
  788.         inputwork[argMaxInputLength] = '\0';
  789.  
  790.         // Prepare the input file contents to make it easier parse-able
  791.         iTmp = strlen(inputwork) + strlen("\n\n[");
  792.         tmpFileContents = (char*)malloc(iTmp + 1/*NUL byte*/);
  793.         if (tmpFileContents == NULL) return false;
  794.         sprintf(tmpFileContents, "\n%s\n[", inputwork);
  795.         for (iTmp = 0; iTmp < strlen(tmpFileContents); iTmp++) {
  796.                 if (tmpFileContents[iTmp] == CR) tmpFileContents[iTmp] = LF;
  797.         }
  798.  
  799.         // Find the section begin
  800.         iTmp = strlen(section) + strlen("\n[]\n");
  801.         tmpSection = (char*)malloc(iTmp + 1/*NUL byte*/);
  802.         if (tmpSection == NULL) return false;
  803.         sprintf(tmpSection, "\n[%s]\n", section);
  804.         tmpStart = strstr(tmpFileContents, tmpSection);
  805.         if (tmpStart == NULL) return false;
  806.         tmpStart += iTmp;
  807.  
  808.         // Find the end of the section and set a NULL terminator to it
  809.         iTmp = strlen(tmpStart) + strlen("\n");
  810.         tmpStart2 = (char*)malloc(iTmp + 1/*NUL byte*/);
  811.         if (tmpStart2 == NULL) return false;
  812.         sprintf(tmpStart2, "\n%s", tmpStart);
  813.         tmpEnd = strstr(tmpStart2, "\n[");
  814.         if (tmpEnd == NULL) return false;
  815.         tmpEnd[0] = '\0';
  816.  
  817.         // Find the start of the value
  818.         iTmp = strlen(keyname) + strlen("\n=");
  819.         tmpKeyname = (char*)malloc(iTmp + 1/*NUL byte*/);
  820.         if (tmpKeyname == NULL) return false;
  821.         sprintf(tmpKeyname, "\n%s=", keyname);
  822.         tmpStart3 = strstr(tmpStart2, tmpKeyname);
  823.         if (tmpStart3 == NULL) return false;
  824.         tmpStart3 += strlen("\n");
  825.         tmpStart4 = strstr(tmpStart3, "=");
  826.         if (tmpStart4 == NULL) return false;
  827.         tmpStart4 += strlen("=");
  828.  
  829.         // Find the end of the value
  830.         tmpEnd = strstr(tmpStart4, "\n");
  831.         if (tmpEnd == NULL) return false;
  832.         tmpEnd[0] = '\0';
  833.  
  834.         // Copy to output
  835.         if (strlen(tmpStart4) < argMaxOutLength + 1) argMaxOutLength = strlen(tmpStart4) + 1;
  836.         memcpy(argOutput, tmpStart4, argMaxOutLength);
  837.         argOutput[argMaxOutLength - 1] = '\0';
  838.  
  839.         // Free all temporary stuff
  840.         //for (iTmp = 0; iTmp < strlen(tmpFileContents); iTmp++) tmpFileContents[iTmp] = '\0';
  841.         free(tmpFileContents);
  842.         //for (iTmp = 0; iTmp < strlen(tmpSection); iTmp++) tmpSection[iTmp] = '\0';
  843.         free(tmpSection);
  844.         //for (iTmp = 0; iTmp < strlen(tmpKeyname); iTmp++) tmpKeyname[iTmp] = '\0';
  845.         free(tmpKeyname);
  846.         //for (iTmp = 0; iTmp < strlen(tmpStart2); iTmp++) tmpStart2[iTmp] = '\0';
  847.         free(tmpStart2);
  848.  
  849.         // Return success
  850.         return true;
  851. }
  852.  
  853. FFLoadingResult readfile_guf(StandardFileReply* sfr) {
  854.         Handle h;
  855.         FFLoadingResult res = MSG_LOADFILE_UNKNOWN_FORMAT_ID;
  856.         FILEREF refnum;
  857.  
  858.         // TODO: Decode UTF-8 to ANSI (or "?" for unknown characters)
  859.  
  860.         if (FSpOpenDF(&sfr->sfFile, fsRdPerm, &refnum) == noErr) {
  861.                 if ((h = readfileintohandle(refnum))) {
  862.                         FILECOUNT count = (FILECOUNT)PIGETHANDLESIZE(h);
  863.                         char* q = PILOCKHANDLE(h, false);
  864.                         char protocol[256];
  865.  
  866.                         if (!_gufReadProperty(q, count, "GUF", "Protocol", protocol, sizeof(protocol))) {
  867.                                 res = MSG_INVALID_FILE_SIGNATURE_ID;
  868.                         }
  869.                         else if (strcmp(protocol, "1") != 0) {
  870.                                 res = MSG_INCOMPATIBLE_GUF_FILE_ID;
  871.                         }
  872.                         else {
  873.                                 int i;
  874.                                 char tmp[256];
  875.                                 char* tmp2;
  876.  
  877.                                 // Plugin infos
  878.                                 _gufReadProperty(q, count, "Info", "Title", gdata->parm.szTitle, sizeof(gdata->parm.szTitle));
  879.                                 _gufReadProperty(q, count, "Info", "Category", tmp, sizeof(tmp));
  880.                                 tmp2 = strrchr(tmp, '/');
  881.                                 if (tmp2 == NULL) {
  882.                                         strcpy(gdata->parm.szCategory, tmp);
  883.                                 } else {
  884.                                         strcpy(gdata->parm.szCategory, tmp2+1);
  885.                                 }
  886.                                 _gufReadProperty(q, count, "Info", "Author", gdata->parm.szAuthor, sizeof(gdata->parm.szAuthor));
  887.                                 _gufReadProperty(q, count, "Info", "Copyright", gdata->parm.szCopyright, sizeof(gdata->parm.szCopyright));
  888.                                 //_gufReadProperty(q, count, "Filter Factory", "8bf", gdata->parm.xxx, sizeof(gdata->parm.xxx));
  889.  
  890.                                 // Expressions
  891.                                 if (!_gufReadProperty(q, count, "Code", "R", gdata->parm.szFormula[0], sizeof(gdata->parm.szFormula[0])))
  892.                                         strcpy(gdata->parm.szFormula[0], "r");
  893.                                 if (!_gufReadProperty(q, count, "Code", "G", gdata->parm.szFormula[1], sizeof(gdata->parm.szFormula[1])))
  894.                                         strcpy(gdata->parm.szFormula[1], "g");
  895.                                 if (!_gufReadProperty(q, count, "Code", "B", gdata->parm.szFormula[2], sizeof(gdata->parm.szFormula[2])))
  896.                                         strcpy(gdata->parm.szFormula[2], "b");
  897.                                 if (!_gufReadProperty(q, count, "Code", "A", gdata->parm.szFormula[3], sizeof(gdata->parm.szFormula[3])))
  898.                                         strcpy(gdata->parm.szFormula[3], "a");
  899.  
  900.                                 for (i = 0; i < 8; i++) {
  901.                                         if (gdata->parm.ctl_used[i]) {
  902.                                                 int v;
  903.                                                 char keyname[9/*strlen("Control X")*/ + 1/*strlen("\0")*/], tmp[5];
  904.                                                 sprintf(keyname, "Control %d", i);
  905.  
  906.                                                 // Slider names
  907.                                                 _gufReadProperty(q, count, keyname, "Label", gdata->parm.szCtl[i], sizeof(gdata->parm.szCtl[i]));
  908.  
  909.                                                 // Slider values
  910.                                                 if (!_gufReadProperty(q, count, keyname, "Preset", tmp, sizeof(tmp))) {
  911.                                                         strcpy(tmp, "0");
  912.                                                 }
  913.                                                 v = atoi(tmp);
  914.                                                 if (v < 0) v = 0;
  915.                                                 else if (v > 255) v = 255;
  916.                                                 gdata->parm.val[i] = gdata->parm.val[i] = (uint8_t)v;
  917.                                         }
  918.                                 }
  919.  
  920.                                 // Map names
  921.                                 for (i = 0; i < 4; i++) {
  922.                                         if (gdata->parm.map_used[i]) {
  923.                                                 char keyname[5/*strlen("Map X")*/ + 1/*strlen("\0")*/];
  924.                                                 sprintf(keyname, "Map %d", i);
  925.                                                 _gufReadProperty(q, count, keyname, "Label", gdata->parm.szMap[i], sizeof(gdata->parm.szMap[i]));
  926.                                         }
  927.                                 }
  928.  
  929.                                 res = LOADING_OK;
  930.                         }
  931.  
  932.                         PIUNLOCKHANDLE(h);
  933.                         PIDISPOSEHANDLE(h);
  934.                 }
  935.                 FSClose(refnum);
  936.         }
  937.  
  938.         return res;
  939. }
  940.  
  941. FFLoadingResult readfile_afs_pff(StandardFileReply *sfr){
  942.         FILEREF r;
  943.         Handle h;
  944.         FFLoadingResult res = MSG_LOADFILE_UNKNOWN_FORMAT_ID;
  945.  
  946.         if(FSpOpenDF(&sfr->sfFile,fsRdPerm,&r) == noErr){
  947.                 if( (h = readfileintohandle(r)) ){
  948.                         // Note in re fileHasExtension(): Mac does not have file extensions like Windows,
  949.                         // so, the detection if the channel order is BGRA (Premiere *.pff) or RGBA (Photoshop *.afs)
  950.                         // is not possible, except if the file has an extension (e.g. when it was copied from a Windows system)
  951.                         res = readparams_afs_pff(h, fileHasExtension(sfr, TEXT(".pff")));
  952.                         PIDISPOSEHANDLE(h);
  953.                 }
  954.                 FSClose(r);
  955.         }
  956.         else
  957.                 res = MSG_CANNOT_OPEN_FILE_ID;
  958.  
  959.         return res;
  960. }
  961.  
  962. FFLoadingResult readfile_ffl(StandardFileReply* sfr) {
  963.         FILEREF rTmp, refnum;
  964.         Handle h, hTmp;
  965.         FFLoadingResult res = LOADING_OK;
  966.         StandardFileReply sfrTmp;
  967.         OSErr e;
  968.         char* p, * start;
  969.         size_t est;
  970.         int foundFilters = 0;
  971.  
  972.         if (FSpOpenDF(&sfr->sfFile, fsRdPerm, &refnum) == noErr) {
  973.                 if ((h = readfileintohandle(refnum))) {
  974.                         FILECOUNT count = (FILECOUNT)PIGETHANDLESIZE(h);
  975.                         char* q = PILOCKHANDLE(h, false);
  976.  
  977.                         char* q2, * tmp_cur_filter_str[29], *token;
  978.                         int lineNumber = 0;
  979.  
  980.                         if (count < 6) {
  981.                                 res = MSG_INVALID_FILE_SIGNATURE_ID;
  982.                         }
  983.                         else {
  984.                                 char testsig[7];
  985.                                 memcpy(testsig, q, 6); // only read 6 chars. Avoid reading the whole file yet
  986.                                 testsig[6] = '\0';
  987.                                 if (strcmp(testsig, "FFL1.0") != 0) {
  988.                                         res = MSG_INVALID_FILE_SIGNATURE_ID;
  989.                                 }
  990.                                 else {
  991.                                         // This is required to make strtok work, because q is not zero-terminated...
  992.                                         q2 = (char*)malloc(count + 1/*NUL byte*/);
  993.                                         if (q2 == NULL) {
  994.                                                 res = MSG_OUT_OF_MEMORY_ID;
  995.                                         }
  996.                                         else {
  997.                                                 memcpy(q2, q, count);
  998.                                                 q2[count] = '\0';
  999.  
  1000.                                                 token = strtok(q2, "\n");
  1001.                                                 while (token != NULL) {
  1002.                                                         size_t i;
  1003.                                                         char* token2 = my_strdup(token);
  1004.                                                         for (i = 0; i < strlen(token2); i++) {
  1005.                                                                 if (token2[i] == CR) token2[i] = '\0';
  1006.                                                         }
  1007.                                                         if (lineNumber == 0) {
  1008.                                                                 // We already checked it above (in order to avoid an unnecessary memory copy)
  1009.                                                                 /*
  1010.                                                                 if (strcmp(token2, "FFL1.0") != 0) {
  1011.                                                                         res = MSG_INVALID_FILE_SIGNATURE_ID;
  1012.                                                                         break;
  1013.                                                                 }
  1014.                                                                 */
  1015.                                                         }
  1016.                                                         else if (lineNumber == 1) {
  1017.                                                                 // We don't check that. We just stop at every 28th line of each filter
  1018.                                                                 /*
  1019.                                                                 countFilters = atoi(token2);
  1020.                                                                 */
  1021.                                                         }
  1022.                                                         else
  1023.                                                         {
  1024.                                                                 /*
  1025.                                                                 * 0 filter_file1.8bf
  1026.                                                                 * 1 Category 1 here
  1027.                                                                 * 2 Filter Name 1 here
  1028.                                                                 * 3 Autor 1 here
  1029.                                                                 * 4 Copyright 1 here
  1030.                                                                 * 5 Map1 name or empty line to disable map
  1031.                                                                 * 6 Map2 name or empty line to disable map
  1032.                                                                 * 7 Map3 name or empty line to disable map
  1033.                                                                 * 8 Map4 name or empty line to disable map
  1034.                                                                 * 9 Slider 1
  1035.                                                                 * 10 Slider 2
  1036.                                                                 * 11 Slider 3
  1037.                                                                 * 12 Slider 4
  1038.                                                                 * 13 Slider 5
  1039.                                                                 * 14 Slider 6
  1040.                                                                 * 15 Slider 7
  1041.                                                                 * 16 Slider 8
  1042.                                                                 * 17 100
  1043.                                                                 * 18 110
  1044.                                                                 * 19 120
  1045.                                                                 * 20 130
  1046.                                                                 * 21 140
  1047.                                                                 * 22 150
  1048.                                                                 * 23 160
  1049.                                                                 * 24 170
  1050.                                                                 * 25 r
  1051.                                                                 * 26 g
  1052.                                                                 * 27 b
  1053.                                                                 * 28 a
  1054.                                                                 */
  1055.                                                                 int filterLineNumber = (lineNumber - 2) % 29;
  1056.                                                                 tmp_cur_filter_str[filterLineNumber] = token2;
  1057.                                                                 if (filterLineNumber == 28) {
  1058.                                                                         TCHAR* curFileNameOrig;
  1059.                                                                         TCHAR* curFileName;
  1060.                                                                         char n[5];
  1061.  
  1062.                                                                         foundFilters++;
  1063.  
  1064.                                                                         curFileNameOrig = curFileName = (TCHAR*)malloc(MAX_PATH * sizeof(TCHAR));
  1065.  
  1066.                                                                         strcpy_advance(&curFileName, sfr->sfFile.szName);
  1067.                                                                         curFileName -= 4; // remove ".ffl" extension
  1068.                                                                         *curFileName = (TCHAR)0;
  1069.  
  1070.                                                                         xstrcat(curFileNameOrig, TEXT("__"));
  1071.                                                                         curFileName += strlen("__");
  1072.                                                
  1073.                                                                         sprintf(n, "%04d", foundFilters);
  1074.                                                                         strcpy_advance_a(&curFileName, n);
  1075.  
  1076.                                                                         xstrcat(curFileNameOrig, TEXT("_"));
  1077.                                                                         curFileName += strlen("_");
  1078.  
  1079.                                                                         strcpy_advance_a(&curFileName, tmp_cur_filter_str[0]);
  1080.                                                                         curFileName -= 4; // remove ".8bf" extension
  1081.                                                                         *curFileName = (TCHAR)0;
  1082.  
  1083. #ifdef WIN_ENV
  1084.                                                                         xstrcat(curFileNameOrig, TEXT(".txt"));
  1085. #endif
  1086.  
  1087.                                                                         sfrTmp.sfGood = true;
  1088.                                                                         sfrTmp.sfReplacing = true;
  1089.                                                                         sfrTmp.sfType = TEXT_FILETYPE;
  1090.                                                                         xstrcpy(sfrTmp.sfFile.szName, curFileNameOrig);
  1091. #ifdef WIN_ENV
  1092.                                                                         sfrTmp.nFileExtension = (WORD)(xstrlen(curFileNameOrig) - strlen(".txt") + 1);
  1093. #endif
  1094.                                                                         sfrTmp.sfScript = smSystemScript;
  1095.  
  1096.                                                                         est = 16000;
  1097.  
  1098.                                                                         FSpDelete(&sfrTmp.sfFile);
  1099.                                                                         if (FSpCreate(&sfrTmp.sfFile, SIG_SIMPLETEXT, TEXT_FILETYPE, sfr->sfScript) == noErr) {
  1100.                                                                                 if (FSpOpenDF(&sfrTmp.sfFile, fsWrPerm, &rTmp) == noErr) {
  1101.                                                                                         if ((hTmp = PINEWHANDLE(1))) { // don't set initial size to 0, since some hosts (e.g. GIMP/PSPI) are incompatible with that.
  1102.                                                                                                 PIUNLOCKHANDLE(hTmp); // should not be necessary
  1103.                                                                                                 if (!(e = PISETHANDLESIZE(hTmp, (int32)(est))) && (p = start = PILOCKHANDLE(hTmp, false))) {
  1104.  
  1105.                                                                                                         p += sprintf(p, "Category: %s\n", tmp_cur_filter_str[1]);
  1106.                                                                                                         p += sprintf(p, "Title: %s\n", tmp_cur_filter_str[2]);
  1107.                                                                                                         p += sprintf(p, "Copyright: %s\n", tmp_cur_filter_str[4]);
  1108.                                                                                                         p += sprintf(p, "Author: %s\n", tmp_cur_filter_str[3]);
  1109.                                                                                                         p += sprintf(p, "Filename: %s\n", tmp_cur_filter_str[0]);
  1110.                                                                                                         p += sprintf(p, "\n");
  1111.                                                                                                         p += sprintf(p, "R:\n");
  1112.                                                                                                         p += sprintf(p, "%s\n", tmp_cur_filter_str[25]);
  1113.                                                                                                         p += sprintf(p, "\n");
  1114.                                                                                                         p += sprintf(p, "G:\n");
  1115.                                                                                                         p += sprintf(p, "%s\n", tmp_cur_filter_str[26]);
  1116.                                                                                                         p += sprintf(p, "\n");
  1117.                                                                                                         p += sprintf(p, "B:\n");
  1118.                                                                                                         p += sprintf(p, "%s\n", tmp_cur_filter_str[27]);
  1119.                                                                                                         p += sprintf(p, "\n");
  1120.                                                                                                         p += sprintf(p, "A:\n");
  1121.                                                                                                         p += sprintf(p, "%s\n", tmp_cur_filter_str[28]);
  1122.                                                                                                         p += sprintf(p, "\n");
  1123.  
  1124.                                                                                                         // Maps
  1125.                                                                                                         for (i = 0; i < 4; i++) {
  1126.                                                                                                                 char* tmp = tmp_cur_filter_str[5 + i];
  1127.                                                                                                                 if (strcmp(tmp, "") != 0) {
  1128.                                                                                                                         p += sprintf(p, "map[%d]: %s\n", (int)i, tmp_cur_filter_str[5 + i]);
  1129.                                                                                                                 }
  1130.                                                                                                         }
  1131.                                                                                                         p += sprintf(p, "\n");
  1132.  
  1133.                                                                                                         // Controls
  1134.                                                                                                         for (i = 0; i < 8; i++) {
  1135.                                                                                                                 char* tmp = tmp_cur_filter_str[9 + i];
  1136.                                                                                                                 if (strcmp(tmp, "") != 0) {
  1137.                                                                                                                         p += sprintf(p, "ctl[%d]: %s\n", (int)i, tmp_cur_filter_str[9 + i]);
  1138.                                                                                                                 }
  1139.                                                                                                                 tmp = tmp_cur_filter_str[17 + i];
  1140.                                                                                                                 if (strcmp(tmp, "") != 0) {
  1141.                                                                                                                         p += sprintf(p, "val[%d]: %s\n", (int)i, tmp_cur_filter_str[17 + i]);
  1142.                                                                                                                 }
  1143.                                                                                                         }
  1144.                                                                                                         p += sprintf(p, "\n");
  1145.  
  1146.                                                                                                         PIUNLOCKHANDLE(hTmp);
  1147.                                                                                                         e = PISETHANDLESIZE(hTmp, (int32)(p - start)); // could ignore this error, maybe
  1148.                                                                                                 }
  1149.                                                                                                 savehandleintofile(hTmp, rTmp);
  1150.                                                                                                 PIDISPOSEHANDLE(hTmp);
  1151.                                                                                         }
  1152.                                                                                         else res = MSG_OUT_OF_MEMORY_ID;
  1153.                                                                                         FSClose(rTmp);
  1154.                                                                                 }
  1155.                                                                                 else res = MSG_CANNOT_OPEN_FILE_ID;
  1156.                                                                         }
  1157.                                                                         else res = MSG_CANNOT_CREATE_FILE_ID;
  1158.  
  1159.                                                                         free(curFileNameOrig);
  1160.                                                                         for (i = 0; i < 29; i++) {
  1161.                                                                                 free(tmp_cur_filter_str[i]); // free all "token2"
  1162.                                                                         }
  1163.                                                                 }
  1164.                                                         }
  1165.                                                         lineNumber++;
  1166.                                                         token = strtok(NULL, "\n");
  1167.                                                 }
  1168.                                         }
  1169.  
  1170.                                         free(q2);
  1171.                                 }
  1172.                         }
  1173.  
  1174.                         PIUNLOCKHANDLE(h);
  1175.                         PIDISPOSEHANDLE(h);
  1176.                 }
  1177.                 FSClose(refnum);
  1178.         }
  1179.  
  1180.         if (res == LOADING_OK) {
  1181.                 res = foundFilters == 0 ? MSG_FFL_NO_FILTERS_DETECTED_ID : MSG_FFL_CONVERTED_ID;
  1182.         }
  1183.         return res;
  1184. }