Subversion Repositories filter_foundry

Rev

Rev 456 | Rev 492 | Go to most recent revision | 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.com.au
  4.     Copyright (C) 2018-2021 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. enum{
  37.         BUFSIZE = 4L<<10,
  38.         MAXLINE = 0x200,
  39. };
  40.  
  41. Boolean readparams_afs_pff(Handle h,char **reason){
  42.         Boolean res = false;
  43.         char linebuf[MAXLINE + 1] = { 0 };
  44.         char curexpr[MAXEXPR + 1] = { 0 };
  45.         char *p, *dataend, *q;
  46.         char c;
  47.         int linecnt, lineptr, exprcnt;
  48.  
  49.         if (!h) return false;
  50.  
  51.         p = PILOCKHANDLE(h,false);
  52.         dataend = p + PIGETHANDLESIZE(h);
  53.  
  54.         q = curexpr;
  55.         linecnt = exprcnt = lineptr = 0;
  56.  
  57.         //*reason = _strdup("File was too short.");
  58.         while(p < dataend){
  59.  
  60.                 c = *p++;
  61.  
  62.                 if(c==CR || c==LF){ /* detected end of line */
  63.  
  64.                         /* look ahead to see if we need to skip a line feed (DOS CRLF EOL convention) */
  65.                         if(p < dataend && c == CR && *p == LF)
  66.                                 ++p;
  67.  
  68.                         linebuf[lineptr] = 0; /* add terminating NUL to line buffer */
  69.  
  70.                         /* process complete line */
  71.                         if(linecnt==0){
  72.                                 if(strcmp(linebuf,"%RGB-1.0")){
  73.                                         // *reason = _strdup("This doesn't look like a Filter Factory file (first line is not \"%RGB-1.0\").");
  74.                                         break;
  75.                                 }
  76.                         }else if(linecnt<=8){
  77.                                 int v;
  78.                                 v = atoi(linebuf);
  79.                                 if (v < 0) v = 0;
  80.                                 else if (v > 255) v = 255;
  81.                                 slider[linecnt-1] = (uint8_t)v;
  82.                         }else{
  83.                                 if(lineptr){
  84.                                         /* it's not an empty line; append it to current expr string */
  85.                                         if( q+lineptr > curexpr+MAXEXPR ){
  86.                                                 *reason = _strdup("Found an expression longer than 1024 characters.");
  87.                                                 break;
  88.                                         }
  89.                                         q = cat(q,linebuf);
  90.                                 }else{
  91.                                         /* it's an empty line: we've completed the expr string */
  92.                                         if(expr[exprcnt])
  93.                                                 free(expr[exprcnt]);
  94.                                         *q = 0;
  95.                                         if(!(expr[exprcnt] = my_strdup(curexpr))){
  96.                                                 *reason = _strdup("Could not get memory for expression.");
  97.                                                 break;
  98.                                         }
  99.  
  100.                                         if(++exprcnt == 4){
  101.                                                 res = true;
  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)
  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."));
  130.                                         }
  131.                                 }//else if(alerts) alertuser((TCHAR*)TEXT("Warning:"),TEXT("truncated escape sequence ends input"));
  132.                         }
  133.  
  134.                         if(lineptr < MAXLINE)
  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. Boolean readfile_ffx(StandardFileReply* sfr, char** reason) {
  198.         Handle h;
  199.         Boolean res = false;
  200.         FILEREF refnum;
  201.         uint32_t len;
  202.         char* val;
  203.         int format_version = -1;
  204.         int i;
  205.  
  206.         UNREFERENCED_PARAMETER(reason);
  207.  
  208.         if (FSpOpenDF(&sfr->sfFile, fsRdPerm, &refnum) == noErr) {
  209.                 if ((h = readfileintohandle(refnum))) {
  210.                         char* q = (char*)PILOCKHANDLE(h, false);
  211.  
  212.                         len = *((uint32_t*)q);
  213.                         if (len == 6) {
  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.                                         simplewarning((TCHAR*)TEXT("Attention! You are loading a \"Filters Unlimited\" file. Please note that Filter Foundry only implements the basic Filter Factory functions. Therefore, most \"Filters Unlimited\" filters won't work with Filter Foundry."));
  221.  
  222.                                         val = _ffx_read_str(&q);
  223.                                         strcpy(gdata->parm.szTitle, val);
  224.                                         free(val);
  225.  
  226.                                         val = _ffx_read_str(&q);
  227.                                         strcpy(gdata->parm.szCategory, val);
  228.                                         free(val);
  229.  
  230.                                         val = _ffx_read_str(&q);
  231.                                         strcpy(gdata->parm.szAuthor, val);
  232.                                         free(val);
  233.  
  234.                                         val = _ffx_read_str(&q);
  235.                                         strcpy(gdata->parm.szCopyright, val);
  236.                                         free(val);
  237.  
  238.                                         // Channels I, R, G, B, A
  239.                                         for (i = 0; i < 4; i++) {
  240.                                                 val = _ffx_read_str(&q);
  241.                                                 if (i == 0) {
  242.                                                         char* val2 = _ffx_read_str(&q);
  243.                                                         if (strcmp(val, "0") != 0) {
  244.                                                                 // "Intro channel" existing
  245.                                                                 // C++ wrong warning: Using uninitialized memory "val2" (C6001)
  246.                                                                 #pragma warning(suppress : 6001)
  247.                                                                 char* combined = (char*)malloc(strlen(val) + strlen(",") + strlen(val2) + 1);
  248.                                                                 if (combined != NULL) {
  249.                                                                         sprintf(combined, "%s,%s", val, val2);
  250.                                                                         free(val);
  251.                                                                         free(val2);
  252.                                                                         val = combined;
  253.                                                                 }
  254.                                                         }
  255.                                                         else {
  256.                                                                 free(val);
  257.                                                                 val = val2;
  258.                                                         }
  259.                                                 }
  260.                                                 if (strlen(val) >= sizeof(gdata->parm.szFormula[i])) {
  261.                                                         if (i == 0) {
  262.                                                                 simplealert((TCHAR*)TEXT("Attention! The formula for channel I/R was too long (longer than 1023 characters) and was truncated."));
  263.                                                         }
  264.                                                         else if (i == 1) {
  265.                                                                 simplealert((TCHAR*)TEXT("Attention! The formula for channel G was too long (longer than 1023 characters) and was truncated."));
  266.                                                         }
  267.                                                         else if (i == 2) {
  268.                                                                 simplealert((TCHAR*)TEXT("Attention! The formula for channel B was too long (longer than 1023 characters) and was truncated."));
  269.                                                         }
  270.                                                         else if (i == 3) {
  271.                                                                 simplealert((TCHAR*)TEXT("Attention! The formula for channel A was too long (longer than 1023 characters) and was truncated."));
  272.                                                         }
  273.                                                         // C++ wrong warning: Buffer overflow (C6386)
  274.                                                         #pragma warning(suppress : 6386)
  275.                                                         val[sizeof(gdata->parm.szFormula[i]) - 1] = '\0';
  276.                                                 }
  277.                                                 if (expr[i]) free(expr[i]);
  278.                                                 expr[i] = my_strdup(val);
  279.                                                 strcpy(gdata->parm.szFormula[i], val);
  280.                                                 free(val);
  281.                                         }
  282.  
  283.                                         // Sliders
  284.                                         for (i = 0; i < 8; i++) {
  285.                                                 char* sliderName;
  286.                                                 int v;
  287.                                                 val = _ffx_read_str(&q);
  288.                                                 sliderName = val;
  289.                                                 if (format_version >= 12) {
  290.                                                         // Format FFX1.2 has prefixes {S} = Slider, {C} = Checkbox, none = Slider
  291.                                                         if ((sliderName[0] == '{') && (sliderName[1] == 'S') && (sliderName[2] == '}')) sliderName += 3;
  292.                                                         else if ((sliderName[0] == '{') && (sliderName[1] == 'C') && (sliderName[2] == '}')) sliderName += 3;
  293.                                                 }
  294.                                                 strcpy(gdata->parm.szCtl[i], sliderName);
  295.                                                 free(val);
  296.                                                 gdata->parm.ctl_used[i] = (bool32_t)*((byte*)q);
  297.                                                 q += sizeof(byte);
  298.                                                 gdata->parm.val[i] = *((uint32_t*)q);
  299.                                                 v = *((uint32_t*)q);
  300.                                                 if (v < 0) v = 0;
  301.                                                 else if (v > 255) v = 255;
  302.                                                 slider[i] = (uint8_t)v;
  303.                                                 q += sizeof(uint32_t);
  304.                                         }
  305.  
  306.                                         // Maps (are not part of the format!)
  307.                                         strcpy(gdata->parm.szMap[0], "Map 0:");
  308.                                         strcpy(gdata->parm.szMap[1], "Map 1:");
  309.                                         strcpy(gdata->parm.szMap[2], "Map 2:");
  310.                                         strcpy(gdata->parm.szMap[3], "Map 3:");
  311.  
  312.                                         res = true;
  313.                                 }
  314.                         }
  315.                         PIDISPOSEHANDLE(h);
  316.                 }
  317.                 FSClose(refnum);
  318.         }
  319.  
  320.         if (res) gdata->obfusc = false;
  321.         return res;
  322. }
  323.  
  324. Boolean readfile_8bf(StandardFileReply *sfr,char **reason){
  325.         unsigned char magic[2];
  326.         FILECOUNT count;
  327.         Handle h;
  328.         Boolean res = false;
  329.         FILEREF refnum;
  330.  
  331.         if(FSpOpenDF(&sfr->sfFile,fsRdPerm,&refnum) == noErr){
  332.                 // check DOS EXE magic number
  333.                 count = 2;
  334.                 if(FSRead(refnum,&count,magic) == noErr /*&& magic[0]=='M' && magic[1]=='Z'*/){
  335.                         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)
  336.                                 if( (h = readfileintohandle(refnum)) ){
  337.                                         long *q = (long*)PILOCKHANDLE(h,false);
  338.  
  339.                                         // look for signature at start of valid PARM resource
  340.                                         // This signature is observed in Filter Factory standalones.
  341.                                         for( count /= 4 ; count >= PARM_SIZE/4 ; --count, ++q )
  342.                                         {
  343.                                                 res = readPARM(&gdata->parm, (Ptr)q);
  344.                                                 if (res) break;
  345.                                         }
  346.  
  347.                                         PIDISPOSEHANDLE(h);
  348.                                 }
  349.                         }
  350.                 } // else no point in proceeding
  351.                 FSClose(refnum);
  352.         }else
  353.                 *reason = _strdup("Could not open file.");
  354.  
  355.         if (res) gdata->obfusc = false;
  356.         return res;
  357. }
  358.  
  359. Boolean readPARM(PARM_T* pparm, Ptr p){
  360.         Boolean towin, tomac, fromwin, frommac;
  361.         unsigned int signature = *((unsigned int*)p);
  362.         unsigned int standalone = *((unsigned int*)p+1);
  363.  
  364.         // Find out our OS ("reader") the OS of the plugin ("source")
  365.         #ifdef MAC_ENV
  366.         towin = false;
  367.         tomac = true;
  368.         fromwin = ((EndianS32_LtoN(signature) == PARM_SIZE) ||
  369.                 (EndianS32_LtoN(signature) == PARM_SIZE_PREMIERE) ||
  370.                 (EndianS32_LtoN(signature) == PARM_SIG_MAC)) && EndianS32_LtoN(standalone) == 1;
  371.         frommac = ((signature == PARM_SIZE) ||
  372.                 (signature == PARM_SIZE_PREMIERE) ||
  373.                 (signature == PARM_SIG_MAC)) && standalone == 1;
  374.         #else
  375.         towin = true;
  376.         tomac = false;
  377.         fromwin = ((signature == PARM_SIZE) ||
  378.                 (signature == PARM_SIZE_PREMIERE) ||
  379.                 (signature == PARM_SIG_MAC)) && standalone == 1;
  380.         frommac = ((EndianS32_LtoN(signature) == PARM_SIZE) ||
  381.                 (EndianS32_LtoN(signature) == PARM_SIZE_PREMIERE) ||
  382.                 (EndianS32_LtoN(signature) == PARM_SIG_MAC)) && EndianS32_LtoN(standalone) == 1;
  383.         #endif
  384.  
  385.         // Is it a valid signature?
  386.         if (!fromwin && !frommac) {
  387.                 // No valid signature found
  388.                 return false;
  389.         }
  390.  
  391.         // Does it come from Premiere or Photoshop?
  392.         // Initialize pparm
  393.         if ((signature == PARM_SIZE_PREMIERE) || (EndianS32_LtoN(signature) == PARM_SIZE_PREMIERE)) {
  394.                 // It comes from Premiere. Swap R and B channel and convert to a Photoshop PARM_T
  395.                 convert_premiere_to_photoshop(pparm, (PARM_T_PREMIERE*)p);
  396.         } else {
  397.                 // It is already Photoshop. Just copy to pparm.
  398.                 memcpy(pparm, p, sizeof(PARM_T));
  399.         }
  400.  
  401.         // Do we need to do string conversion?
  402.         if (frommac) {
  403.                 int i;
  404.  
  405.                 /* Mac PARM resource stores Pascal strings - convert to C strings, since this is what we work internally with (regardles of OS) */
  406.                 myp2cstr((unsigned char*)pparm->szCategory);
  407.                 myp2cstr((unsigned char*)pparm->szTitle);
  408.                 myp2cstr((unsigned char*)pparm->szCopyright);
  409.                 myp2cstr((unsigned char*)pparm->szAuthor);
  410.                 for (i = 0; i < 4; ++i)
  411.                         myp2cstr((unsigned char*)pparm->szMap[i]);
  412.                 for (i = 0; i < 8; ++i)
  413.                         myp2cstr((unsigned char*)pparm->szCtl[i]);
  414.         }
  415.  
  416.         // Case #1: Mac is reading Windows (Win16/32/64) plugin
  417.         if (fromwin && tomac) {
  418.                 size_t i;
  419.  
  420.                 // Convert copyright CRLF to CR (actually, just removing LF)
  421.                 char copyrightCRLF[256] = { 0 };
  422.                 char* pCopyright = &copyrightCRLF[0];
  423.                 for (i = 0; i < strlen(pparm->szCopyright); i++) {
  424.                         if (pparm->szCopyright[i] != LF) {
  425.                                 *pCopyright++ = pparm->szCopyright[i];
  426.                         }
  427.                 }
  428.                 *pCopyright++ = '\0';
  429.                 strcpy(pparm->szCopyright, copyrightCRLF);
  430.  
  431.                 // these are the only numeric fields we *have* to swap
  432.                 // all the rest are bool_t flags which (if we're careful) will work in either ordering
  433.                 for (i = 0; i < 8; ++i)
  434.                         pparm->val[i] = EndianS32_LtoN(pparm->val[i]);
  435.         }
  436.  
  437.         // Case #2: Mac is reading Mac (in case the normal resource extraction didn't work)
  438.         // Nothing to do
  439.  
  440.         // 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)
  441.         // Nothing to do
  442.  
  443.         // Case #4: Windows is reading an old FilterFactory Mac file
  444.         // Note: You must read the ".rsrc" resource fork, not the standalone binary!
  445.         if (frommac && towin) {
  446.                 size_t i;
  447.  
  448.                 // Convert CR in the copyright field to CRLF.
  449.                 char copyrightCRLF[256] = { 0 };
  450.                 char* pCopyright = &copyrightCRLF[0];
  451.                 for (i = 0; i < strlen(pparm->szCopyright); i++) {
  452.                         *pCopyright++ = pparm->szCopyright[i];
  453.                         if (pparm->szCopyright[i] == CR) {
  454.                                 *pCopyright++ = LF;
  455.                         }
  456.                 }
  457.                 *pCopyright++ = '\0';
  458.                 strcpy(pparm->szCopyright, copyrightCRLF);
  459.  
  460.                 // these are the only numeric fields we *have* to swap
  461.                 // all the rest are bool_t flags which (if we're careful) will work in either ordering
  462.                 for (i = 0; i < 8; ++i)
  463.                         pparm->val[i] = EndianS32_LtoN(pparm->val[i]);
  464.         }
  465.  
  466.         // Now set the values in pparm into the working variables expr[] and slider[], so that they are visible in the GUI
  467.         {
  468.                 int i;
  469.                 for (i = 0; i < 4; ++i) {
  470.                         if (expr[i]) free(expr[i]);
  471.                         expr[i] = my_strdup(pparm->szFormula[i]);
  472.                 }
  473.  
  474.                 for (i = 0; i < 8; ++i) {
  475.                         slider[i] = (uint8_t)pparm->val[i];
  476.                         /*
  477.                         if (slider[i] > 0xFF) {
  478.                                 // Wrong endianess (e.g. reading a Mac rsrc on Windows)
  479.                                 // Should not happen since we did the stuff above
  480.                                 slider[i] = (uint8_t)EndianS32_LtoN(slider[i]);
  481.                         }
  482.                         */
  483.                 }
  484.         }
  485.  
  486.         return true;
  487. }
  488.  
  489. Handle readfileintohandle(FILEREF r){
  490.         FILEPOS n;
  491.         Handle h;
  492.         Ptr p;
  493.  
  494.         if( GetEOF(r,&n) == noErr && (h = PINEWHANDLE(n)) ){
  495.                 p = PILOCKHANDLE(h,false);
  496.                 if(SetFPos(r,fsFromStart,0) == noErr && FSRead(r,(FILECOUNT*)&n,p) == noErr){
  497.                         PIUNLOCKHANDLE(h);
  498.                         return h;
  499.                 }
  500.                 PIDISPOSEHANDLE(h);
  501.         }
  502.         return NULL;
  503. }
  504.  
  505. Boolean _picoLineContainsKey(char* line, char** content, const char* searchkey/*=NULL*/) {
  506.         size_t i;
  507.         for (i = 0; i < strlen(line); i++) {
  508.                 if (line[i] == '?') break; // avoid that "a?b:c" is detected as key
  509.                 if (line[i] == ':') {
  510.                         // Note: We are ignoring whitespaces, i.e. " A :" != "A:" (TODO: should we change this?)
  511.                         if ((searchkey == NULL) || ((i == strlen(searchkey)) && (memcmp(line, searchkey, i) == 0))) {
  512.                                 i++; // jump over ':' char
  513.                                 //while ((line[i] == ' ') || (line[i] == TAB)) i++; // Trim value left
  514.                                 *content = line + i;
  515.                                 return true;
  516.                         }
  517.                 }
  518.         }
  519.         *content = line;
  520.         return false;
  521. }
  522.  
  523. void _ffdcomp_removebrackets(char* x, char* maxptr) {
  524.         char* closingBracketPos = NULL;
  525.         Boolean openingBracketFound = false;
  526.         if (x[0] == '[') {
  527.                 openingBracketFound = true;
  528.         }
  529.         x[0] = ':';
  530.         x++;
  531.         while (x < maxptr) {
  532.                 if ((!openingBracketFound) && (x[0] == '[')) {
  533.                         openingBracketFound = true;
  534.                         x[0] = ' ';
  535.                 }
  536.                 else if (openingBracketFound) {
  537.                         if (x[0] == ']') {
  538.                                 closingBracketPos = x;
  539.                         }
  540.                         else if ((x[0] == CR) || (x[0] == LF)) {
  541.                                 if (closingBracketPos) closingBracketPos[0] = ' '; // last closing pos before CR/LF
  542.                                 break;
  543.                         }
  544.                 }
  545.                 x++;
  546.         }
  547. }
  548.  
  549. // isFormula=false => outputFile is C string. TXT linebreaks become spaces.
  550. // isFormula=true  => outputFile is C string. TXT line breaks become CRLF line breaks
  551. Boolean _picoReadProperty(char* inputFile, int maxInput, const char* property, char* outputFile, size_t maxOutput, Boolean isFormula) {
  552.         int i;
  553.         char* outputwork;
  554.         char* sline;
  555.         char* svalue;
  556.         char* inputwork;
  557.         char* inputworkinitial;
  558.         outputwork = outputFile;
  559.         sline = NULL;
  560.         svalue = NULL;
  561.         // Check parameters
  562.         if (maxOutput == 0) return false;
  563.         if (inputFile == 0) return false;
  564.         // Let input memory be read-only, +1 for terminal zero
  565.         //char* inputwork = inputFile;
  566.         inputwork = (char*)malloc((size_t)maxInput + 1);
  567.         inputworkinitial = inputwork;
  568.         if (inputwork == 0) return false;
  569.         memcpy(inputwork, inputFile, maxInput);
  570.         inputwork[maxInput] = 0; // otherwise strstr() will crash
  571.  
  572.         // Transform "FFDecomp" TXT file into the similar "PluginCommander" TXT file
  573.         if (strstr(inputwork, "Filter Factory Plugin Information:")) {
  574.                 char* x;
  575.                 char* k1;
  576.                 char* k2;
  577.                 // Metadata:
  578.                 x = strstr(inputwork, "CATEGORY:");
  579.                 if (x) memcpy(x, "Category:", strlen("Category:"));
  580.                 x = strstr(inputwork, "TITLE:");
  581.                 if (x) memcpy(x, "Title:", strlen("Title:"));
  582.                 x = strstr(inputwork, "COPYRIGHT:");
  583.                 if (x) memcpy(x, "Copyright:", strlen("Copyright:"));
  584.                 x = strstr(inputwork, "AUTHOR:");
  585.                 if (x) memcpy(x, "Author:", strlen("Author:"));
  586.                 // Controls:
  587.                 for (i = 0; i < 8; i++) {
  588.                         k1 = (char*)malloc(strlen("Control X:") + 1);
  589.                         sprintf(k1, "Control %d:", i);
  590.                         x = strstr(inputwork, k1);
  591.                         if (x) {
  592.                                 k2 = (char*)malloc(strlen("ctl[X]:   ") + 1);
  593.                                 sprintf(k2, "ctl[%d]:   ", i);
  594.                                 memcpy(x, k2, strlen(k2));
  595.                                 x += strlen("ctl[X]");
  596.                                 _ffdcomp_removebrackets(x, inputwork + maxInput - 1);
  597.                                 free(k2);
  598.                         }
  599.                         free(k1);
  600.                 }
  601.                 // Maps:
  602.                 for (i = 0; i < 4; i++) {
  603.                         k1 = (char*)malloc(strlen("Map X:") + 1);
  604.                         sprintf(k1, "Map %d:", i);
  605.                         x = strstr(inputwork, k1);
  606.                         if (x) {
  607.                                 k2 = (char*)malloc(strlen("map[X]:") + 1);
  608.                                 sprintf(k2, "map[%d]:", i);
  609.                                 memcpy(x, k2, strlen(k2));
  610.                                 x += strlen("map[X]");
  611.                                 _ffdcomp_removebrackets(x, inputwork + maxInput - 1);
  612.                                 free(k2);
  613.                         }
  614.                         free(k1);
  615.                 }
  616.                 // Convert all '\r' to '\n' for the next step to be easier
  617.                 for (i = 0; i < maxInput; i++) {
  618.                         if (inputworkinitial[i] == CR) inputworkinitial[i] = LF;
  619.                 }
  620.                 x = strstr(inputwork, "\nR=\n");
  621.                 if (x) memcpy(x, "\nR:\n", strlen("\nR:\n"));
  622.                 x = strstr(inputwork, "\nG=\n");
  623.                 if (x) memcpy(x, "\nG:\n", strlen("\nG:\n"));
  624.                 x = strstr(inputwork, "\nB=\n");
  625.                 if (x) memcpy(x, "\nB:\n", strlen("\nB:\n"));
  626.                 x = strstr(inputwork, "\nA=\n");
  627.                 if (x) memcpy(x, "\nA:\n", strlen("\nA:\n"));
  628.         }
  629.         // Replace all \r and \n with \0, so that we can parse easier
  630.         for (i = 0; i < maxInput; i++) {
  631.                 if (inputworkinitial[i] == CR) inputworkinitial[i] = 0;
  632.                 else if (inputworkinitial[i] == LF) inputworkinitial[i] = 0;
  633.         }
  634.  
  635.         // Find line that contains out key
  636.         inputwork = inputworkinitial;
  637.         do {
  638.                 if (inputwork > inputworkinitial + maxInput) {
  639.                         // Key not found. Set output to empty string
  640.                         outputwork[0] = 0;
  641.                         free(inputworkinitial);
  642.                         return false;
  643.                 }
  644.                 sline = inputwork;
  645.                 inputwork += strlen(sline) + 1;
  646.                 if (inputwork - 1 > inputworkinitial + maxInput) {
  647.                         // Key not found. Set output to empty string
  648.                         // TODO: will that be ever called?
  649.                         outputwork[0] = 0;
  650.                         free(inputworkinitial);
  651.                         return false;
  652.                 }
  653.         } while (!_picoLineContainsKey(sline, &svalue, property));
  654.  
  655.         // Read line(s) until we find a line with another key, or the line end
  656.         do {
  657.                 while ((svalue[0] == ' ') || (svalue[0] == TAB)) svalue++; // Trim left
  658.                 while ((svalue[strlen(svalue) - 1] == ' ') || (svalue[strlen(svalue) - 1] == TAB)) svalue[strlen(svalue) - 1] = 0; // Trim right
  659.  
  660.                 if (strlen(svalue) > 0) {
  661.                         if (outputwork + strlen(svalue) + (isFormula ? 3/*CRLF+NUL*/ : 2/*space+NUL*/) > outputFile + maxOutput) {
  662.                                 size_t remaining = maxOutput - (outputwork - outputFile) - 1;
  663.                                 //printf("BUFFER FULL (remaining = %d)\n", remaining);
  664.                                 memcpy(outputwork, svalue, remaining);
  665.                                 outputwork += remaining;
  666.                                 outputwork[0] = 0;
  667.                                 free(inputworkinitial);
  668.                                 return true;
  669.                         }
  670.                         else {
  671.                                 memcpy(outputwork, svalue, strlen(svalue));
  672.                                 outputwork += strlen(svalue);
  673.                                 if (isFormula) {
  674.                                         // Formulas: TXT line break stays line break (important if you have comments!)
  675.                                         outputwork[0] = CR;
  676.                                         outputwork[1] = LF;
  677.                                         outputwork += 2;
  678.                                 }
  679.                                 else {
  680.                                         // Everything else: TXT line breaks becomes single whitespace
  681.                                         outputwork[0] = ' ';
  682.                                         outputwork++;
  683.                                 }
  684.                         }
  685.                 }
  686.                 outputwork[0] = 0;
  687.  
  688.                 // Process next line
  689.                 if (inputwork > inputworkinitial + maxInput) break;
  690.                 sline = inputwork;
  691.                 inputwork += strlen(sline) + 1;
  692.                 if (inputwork - 1 > inputworkinitial + maxInput) break; // TODO: will that be ever called?
  693.         } while (!_picoLineContainsKey(sline, &svalue, NULL));
  694.  
  695.         // Remove trailing whitespace
  696.         if (outputwork > outputFile) {
  697.                 outputwork -= 1;
  698.                 outputwork[0] = 0;
  699.         }
  700.         free(inputworkinitial);
  701.         return true;
  702. }
  703.  
  704. Boolean readfile_picotxt(StandardFileReply* sfr, char** reason) {
  705.         extern int ctls[], maps[];
  706.  
  707.         Handle h;
  708.         Boolean res = false;
  709.         FILEREF refnum;
  710.  
  711.         UNREFERENCED_PARAMETER(reason);
  712.  
  713.         if (!fileHasExtension(sfr, TEXT(".txt"))) return false;
  714.  
  715.         if (FSpOpenDF(&sfr->sfFile, fsRdPerm, &refnum) == noErr) {
  716.                 if ((h = readfileintohandle(refnum))) {
  717.                         FILECOUNT count = PIGETHANDLESIZE(h);
  718.                         char* q = PILOCKHANDLE(h, false);
  719.  
  720.                         char out[256];
  721.                         if (_picoReadProperty(q, count, "Title", out, sizeof(out), false)) {
  722.                                 int i;
  723.  
  724.                                 // Plugin infos
  725.                                 _picoReadProperty(q, count, "Title", gdata->parm.szTitle, sizeof(gdata->parm.szTitle), false);
  726.                                 _picoReadProperty(q, count, "Category", gdata->parm.szCategory, sizeof(gdata->parm.szCategory), false);
  727.                                 _picoReadProperty(q, count, "Author", gdata->parm.szAuthor, sizeof(gdata->parm.szAuthor), false);
  728.                                 _picoReadProperty(q, count, "Copyright", gdata->parm.szCopyright, sizeof(gdata->parm.szCopyright), false);
  729.                                 //_picoReadProperty(q, count, "Filename", gdata->parm.xxx, sizeof(gdata->parm.xxx), false);
  730.  
  731.                                 // Expressions
  732.                                 if (!_picoReadProperty(q, count, "R", gdata->parm.szFormula[0], sizeof(gdata->parm.szFormula[0]), true))
  733.                                         strcpy(gdata->parm.szFormula[0], "r");
  734.                                 if (!_picoReadProperty(q, count, "G", gdata->parm.szFormula[1], sizeof(gdata->parm.szFormula[1]), true))
  735.                                         strcpy(gdata->parm.szFormula[1], "g");
  736.                                 if (!_picoReadProperty(q, count, "B", gdata->parm.szFormula[2], sizeof(gdata->parm.szFormula[2]), true))
  737.                                         strcpy(gdata->parm.szFormula[2], "b");
  738.                                 if (!_picoReadProperty(q, count, "A", gdata->parm.szFormula[3], sizeof(gdata->parm.szFormula[3]), true))
  739.                                         strcpy(gdata->parm.szFormula[3], "a");
  740.                                 for (i = 0; i < 4; i++) {
  741.                                         if (expr[i]) free(expr[i]);
  742.                                         expr[i] = my_strdup(gdata->parm.szFormula[i]);
  743.                                 }
  744.  
  745.                                 // Slider names
  746.                                 for (i = 0; i < 8; i++) {
  747.                                         char keyname[7];
  748.                                         sprintf(keyname, "ctl[%d]", i);
  749.                                         _picoReadProperty(q, count, keyname, gdata->parm.szCtl[i], sizeof(gdata->parm.szCtl[i]), false);
  750.                                 }
  751.  
  752.                                 // Slider values
  753.                                 for (i = 0; i < 8; i++) {
  754.                                         char keyname[7], tmp[5];
  755.                                         int v;
  756.                                         sprintf(keyname, "val[%d]", i);
  757.                                         if (!_picoReadProperty(q, count, keyname, tmp, sizeof(tmp), false)) {
  758.                                                 sprintf(keyname, "def[%d]", i);
  759.                                                 if (!_picoReadProperty(q, count, keyname, tmp, sizeof(tmp), false)) {
  760.                                                         strcpy(tmp,"0");
  761.                                                 }
  762.                                         }
  763.                                         v = atoi(tmp);
  764.                                         if (v < 0) v = 0;
  765.                                         else if (v > 255) v = 255;
  766.                                         gdata->parm.val[i] = slider[i] = (uint8_t)v;
  767.                                 }
  768.  
  769.                                 // Map names
  770.                                 for (i = 0; i < 4; i++) {
  771.                                         char keyname[7];
  772.                                         sprintf(keyname, "map[%d]", i);
  773.                                         _picoReadProperty(q, count, keyname, gdata->parm.szMap[i], sizeof(gdata->parm.szMap[i]), false);
  774.                                 }
  775.  
  776.                                 //These will be set when the expressions are evaluated anyway. So this part is optional:
  777.                                 checksliders(4, ctls, maps);
  778.                                 for (i = 0; i < 8; i++) gdata->parm.ctl_used[i] = ctls[i];
  779.                                 for (i = 0; i < 4; i++) gdata->parm.map_used[i] = maps[i];
  780.  
  781.                                 res = true;
  782.                         }
  783.  
  784.                         PIUNLOCKHANDLE(h);
  785.                         PIDISPOSEHANDLE(h);
  786.                 }
  787.                 FSClose(refnum);
  788.         }
  789.  
  790.         return res;
  791. }
  792.  
  793. Boolean readfile_afs_pff(StandardFileReply *sfr,char **reason){
  794.         FILEREF r;
  795.         Handle h;
  796.         Boolean res = false;
  797.  
  798.         if(FSpOpenDF(&sfr->sfFile,fsRdPerm,&r) == noErr){
  799.                 if( (h = readfileintohandle(r)) ){
  800.                         if( (res = readparams_afs_pff(h,reason)) ) {
  801.                                 gdata->standalone = false; // so metadata fields will default, if user chooses Make...
  802.  
  803.                                 if (fileHasExtension(sfr, TEXT(".pff"))) {
  804.                                         // If it is a Premiere settings file, we need to swap the channels red and blue
  805.                                         // We just swap the pointers!
  806.                                         char* tmp;
  807.                                         tmp = expr[0];
  808.                                         expr[0] = expr[2];
  809.                                         expr[2] = tmp;
  810.                                 }
  811.                         }
  812.  
  813.                         PIDISPOSEHANDLE(h);
  814.                 }
  815.                 FSClose(r);
  816.         }else
  817.                 *reason = _strdup("Could not open the file.");
  818.  
  819.         return res;
  820. }
  821.