home *** CD-ROM | disk | FTP | other *** search
/ Chip 2004 June / CHIP_CD_2004-06.iso / software / miranda_hit / files / mirinstsetup.exe / Miranda Installer 0.0.1.2 / Install.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2004-04-20  |  17.1 KB  |  586 lines

  1. /*
  2.     Miranda Installer - Installs nightlies and Miranda addons.
  3.     Copyright (C) 2002-2003 Goblineye Entertainment
  4.  
  5.     Authors: Saar (Tornado) and Kai (kai_b)
  6.  
  7.     This program is free software; you can redistribute it and/or modify
  8.     it under the terms of the GNU General Public License as published by
  9.     the Free Software Foundation; either version 2 of the License, or
  10.     (at your option) any later version.
  11.  
  12.     This program is distributed in the hope that it will be useful,
  13.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  14.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15.     GNU General Public License for more details.
  16.  
  17.     You should have received a copy of the GNU General Public License
  18.     along with this program; if not, write to the Free Software
  19.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  20. */
  21.  
  22. #include "Install.h"
  23. #pragma hdrstop
  24.  
  25. // gave problems in headers. this is nicer.
  26. const char* INSTALLATION_TYPES_ARR[10] = { "Plugin", "Icon", "Sound", "LangPack", "Tool", "Source", "Doc", "Skin", "Nightly", NULL };
  27. const char* INSTALLATION_EXTS_ARR[10] = { "mir", "mii", "mis", "mil", "mit", "mio", "mic", "mik", "min", NULL };
  28.  
  29.  
  30. void XMLHandler_GetAddonBasicInfo(byte bMsg,char *lpszData,unsigned long ulLen,LPARAM lParam)
  31. {
  32.     static byte s_bDepth; // depth of script. not really used the way you think it's used :)
  33.     static char s_szElement[32]; // long enough
  34.     static bool s_fOptPackage; // is current package optional?
  35.     static GENERAL_FILELIST *s_pFileList; // local filelist, per package. we don't assume crap, a package might not have any files (scripter's fault, but we should still watch out)
  36.  
  37.     switch(bMsg)
  38.     {
  39.         case XMLPARSER_NOTIFY_STARTELEMENT:
  40.         {
  41.             if ((s_bDepth == 2) || (s_bDepth == 5)) // inside info or packageinfo
  42.             {
  43.                 // remember the element type
  44.                 if (ulLen < 32) // just the right size
  45.                 {
  46.                     strncpy(s_szElement,lpszData,ulLen);
  47.                     s_szElement[ulLen] = '\0';
  48.  
  49.                     if (lstrcmpi(s_szElement,"optional") == 0) // optional empty tag
  50.                     {
  51.                         s_fOptPackage = true;
  52.                     }
  53.                 }
  54.                 else
  55.                 {
  56.                     s_szElement[0] = '\0'; // no element, can't be (our tags are shorter)
  57.                 }
  58.             }
  59.             else if (_strnicmp(lpszData,"installscript",ulLen) == 0) // going into script
  60.             {
  61.                 if (s_bDepth == 0)
  62.                 {
  63.                     s_bDepth++;
  64.                 }
  65.             }
  66.             else if (_strnicmp(lpszData,"info",ulLen) == 0) // info realm
  67.             {
  68.                 if (s_bDepth == 1)
  69.                 {
  70.                     s_bDepth++;
  71.                 }
  72.             }
  73.             else if (_strnicmp(lpszData,"packageinfo",ulLen) == 0) // packageinfo realm
  74.             {
  75.                 s_fOptPackage = false;
  76.                 s_pFileList = NULL;
  77.  
  78.                 if (s_bDepth == 1)
  79.                 {
  80.                     s_bDepth = 5; // skip to 5 (made it up :))
  81.                 }
  82.             }
  83.         } break;
  84.         case XMLPARSER_NOTIFY_ENDELEMENT:
  85.         {
  86.             if (_strnicmp(lpszData,"info",ulLen) == 0) // done with info
  87.             {
  88.                 if (s_bDepth == 2)
  89.                 {
  90.                     s_bDepth--;
  91.                 }
  92.             }
  93.             else if (_strnicmp(lpszData,"installscript",ulLen) == 0) // done with installscript
  94.             {
  95.                 if (s_bDepth == 1)
  96.                 {
  97.                     s_bDepth--;
  98.                 }
  99.             }
  100.             else if (_strnicmp(lpszData,"packageinfo",ulLen) == 0) // done with packageinfo
  101.             {
  102.                 if (s_bDepth == 5)
  103.                 {
  104.                     ADDON_BASICINFO *basicInfo = (ADDON_BASICINFO*)lParam;
  105.                     if (!s_fOptPackage) // no optional tag found
  106.                     {
  107.                         basicInfo->wMainPackages++; // another main package
  108.                     }
  109.                     basicInfo->wTotalPackages++; // another total package
  110.  
  111.                     // examine s_pFileList
  112.                     if (s_pFileList != NULL) // got files
  113.                     {
  114.                         if (basicInfo->pFileList == NULL)
  115.                         {
  116.                             basicInfo->pFileList = s_pFileList;
  117.                         }
  118.                         else // add at end of master list
  119.                         {
  120.                             GENERAL_FILELIST *pLast = basicInfo->pFileList;
  121.                             while (pLast->next != NULL) { pLast = pLast->next; }
  122.                             pLast->next = s_pFileList;
  123.                         }
  124.                         s_pFileList = NULL; // disallocation isn't done by us
  125.                     }
  126.                     else
  127.                     {
  128.                         basicInfo->fGotEmptyPackage = true; // usually a deal-breaker (package with no files)
  129.                     }
  130.  
  131.                     s_bDepth = 1;
  132.                 }
  133.             }
  134.         } break;
  135.         case XMLPARSER_NOTIFY_DATA:
  136.         {
  137.             if (ulLen > 0) // do we have info in there?
  138.             {
  139.                 ADDON_BASICINFO *basicInfo = (ADDON_BASICINFO*)lParam;
  140.  
  141.                 if (s_bDepth == 2) // info
  142.                 {
  143.                     if (lstrcmpi(s_szElement,"name") == 0) // name, let's get it
  144.                     {
  145.                         basicInfo->lpszName = new char[ulLen + 1];
  146.  
  147.                         lstrcpyn(basicInfo->lpszName,lpszData,ulLen+1);
  148.                     }
  149.                     else if (lstrcmpi(s_szElement,"author") == 0) // author, let's get it
  150.                     {
  151.                         basicInfo->lpszAuthor = new char[ulLen + 1];
  152.  
  153.                         lstrcpyn(basicInfo->lpszAuthor,lpszData,ulLen+1);
  154.                     }
  155.                     else if (lstrcmpi(s_szElement,"version") == 0) // version, let's get it
  156.                     {
  157.                         basicInfo->lpszVersion = new char[ulLen + 1];
  158.  
  159.                         lstrcpyn(basicInfo->lpszVersion,lpszData,ulLen+1);
  160.                     }
  161.                     else if (lstrcmpi(s_szElement,"type") == 0) // type, let's get it
  162.                     {
  163.                         basicInfo->lpszType = new char[ulLen + 1];
  164.  
  165.                         lstrcpyn(basicInfo->lpszType,lpszData,ulLen+1);
  166.                     }
  167.                 }
  168.                 else if (s_bDepth == 5) // packageinfo
  169.                 {
  170.                     if (lstrcmpi(s_szElement,"file") == 0) // file, let's get it
  171.                     {
  172.                         GENERAL_FILELIST *node = new GENERAL_FILELIST;
  173.                         node->lpFilename = new char[ulLen + 1];
  174.                         lstrcpyn(node->lpFilename,lpszData,ulLen+1);
  175.                         Switch_Slashes(node->lpFilename,true); // switch slashes (to slashes)
  176.  
  177.                         // always add at beginning
  178.                         node->next = s_pFileList;
  179.                         s_pFileList = node;
  180.                     }
  181.                 }
  182.             }
  183.         } break;
  184.     }
  185. }
  186.  
  187.  
  188.  
  189. // on success, returns an allocated structure with basic information about
  190. // the given addon
  191. // on failure, returns NULL
  192. // errors are *NOT* shown here!
  193. static ADDON_BASICINFO *Install_GetBasicInfo(const char *lpFilePath)
  194. {
  195.     // upon allocation, the structure MUST be memset to 0, to prevent any problems later
  196.     unzFile hFile = unzOpen(lpFilePath);
  197.  
  198.     if (hFile != NULL)
  199.     {
  200.         ADDON_BASICINFO *basicInfo = new ADDON_BASICINFO; // allocate it
  201.         ZeroMemory(basicInfo,sizeof(ADDON_BASICINFO));
  202.  
  203.         if (unzLocateFile(hFile,INSTALLSCRIPT_FILENAME,2) == UNZ_OK) // try to find the file (insensitive)
  204.         {
  205.             // we got a script!
  206.             unz_file_info fileInfo;
  207.             unzGetCurrentFileInfo(hFile,&fileInfo,NULL,0,NULL,0,NULL,0); // we need its size
  208.  
  209.             if (fileInfo.uncompressed_size > 0) // valid
  210.             {
  211.                 basicInfo->fScripted = true; // script is good.
  212.  
  213.                 if (unzOpenCurrentFile(hFile) == UNZ_OK)
  214.                 {
  215.                     char *lpScriptBuffer = new char[fileInfo.uncompressed_size + 1];
  216.  
  217.                     // the script can't be that big
  218.                     // that's the basic assumption here
  219.                     int iResult = unzReadCurrentFile(hFile,lpScriptBuffer,fileInfo.uncompressed_size);
  220.  
  221.                     if (iResult < 0) // error
  222.                     {
  223.                         // no script, some error
  224.                         delete [] lpScriptBuffer;
  225.                         delete basicInfo;
  226.                         unzCloseCurrentFile(hFile);
  227.                         unzClose(hFile);
  228.                         return(NULL);
  229.                     }
  230.  
  231.                     if (unzCloseCurrentFile(hFile) == UNZ_CRCERROR)
  232.                     {
  233.                         // no script, CRC error
  234.                         delete [] lpScriptBuffer;
  235.                         delete basicInfo;
  236.                         unzClose(hFile);
  237.                         return(NULL);
  238.                     }
  239.  
  240.                     lpScriptBuffer[fileInfo.uncompressed_size] = '\0'; // close it
  241.  
  242.                     XMLParser_FeedDocument(XMLHandler_GetAddonBasicInfo,lpScriptBuffer,(LPARAM)basicInfo);
  243.  
  244.                     delete [] lpScriptBuffer;
  245.                 }
  246.             }
  247.         }
  248.  
  249.         unzClose(hFile);
  250.  
  251.         if (!basicInfo->fScripted) // no script
  252.         {
  253.             basicInfo->wMainPackages = basicInfo->wTotalPackages = 1; // one package
  254.         }
  255.  
  256.         if (basicInfo->lpszName == NULL)
  257.         {
  258.             char *lpFileName = strrchr(lpFilePath,'\\');
  259.  
  260.             if (lpFileName != NULL)
  261.             {
  262.                 lpFileName++; // advance
  263.                 basicInfo->lpszName = new char[lstrlen(lpFileName) + 1]; // len until the end
  264.                 lstrcpy(basicInfo->lpszName,lpFileName);
  265.             }
  266.         }
  267.  
  268.         // now the type has to be handled too
  269.         // let's check if we have a type from before, if we do, we still might have to check the extension
  270.         if (basicInfo->lpszType != NULL) // previous type!
  271.         {
  272.             for (int index=0;INSTALLATION_TYPES_ARR[index] != NULL;index++)
  273.             {
  274.                 if (lstrcmpi(INSTALLATION_TYPES_ARR[index],basicInfo->lpszType) == 0) // try to match the scripted type with one of our own
  275.                 {
  276.                     // we got a match, we're also going to delete what we got, and use our type instead
  277.                     // ours will ALWAYS look nice :)
  278.                     delete [] basicInfo->lpszType;
  279.                     basicInfo->lpszType = new char[lstrlen(INSTALLATION_TYPES_ARR[index]) + 1];
  280.                     lstrcpy(basicInfo->lpszType,INSTALLATION_TYPES_ARR[index]); // copy it
  281.                     break;
  282.                 }
  283.             }
  284.  
  285.             if (INSTALLATION_TYPES_ARR[index] == NULL) // argg? not in the list... kill it
  286.             {
  287.                 delete [] basicInfo->lpszType;
  288.                 basicInfo->lpszType = NULL;
  289.             }
  290.         }
  291.  
  292.         if (basicInfo->lpszType == NULL) // could've been non-NULL before, and it is now
  293.         {
  294.             char *lpExtension = strrchr(lpFilePath,'.');
  295.  
  296.             if (lpExtension != NULL)
  297.             {
  298.                 lpExtension++; // advance
  299.  
  300.                 for (int index=0;INSTALLATION_EXTS_ARR[index] != NULL;index++)
  301.                 {
  302.                     if (lstrcmpi(INSTALLATION_EXTS_ARR[index],lpExtension) == 0) // try to match the extension
  303.                     {
  304.                         // weee
  305.                         basicInfo->lpszType = new char[lstrlen(INSTALLATION_TYPES_ARR[index]) + 1];
  306.                         lstrcpy(basicInfo->lpszType,INSTALLATION_TYPES_ARR[index]); // corresponding type from type array
  307.                         break;
  308.                     }
  309.                 }
  310.             }
  311.         }
  312.  
  313.         return(basicInfo);
  314.     }
  315.     return(NULL);
  316. }
  317.  
  318.  
  319.  
  320. // frees and deallocates a basic info structure
  321. static void Free_BasicInfo(ADDON_BASICINFO **pBasicInfo)
  322. {
  323.     if ((pBasicInfo == NULL) || (*pBasicInfo == NULL))
  324.     {
  325.         return;
  326.     }
  327.  
  328.     if ((*pBasicInfo)->lpszName != NULL)
  329.     {
  330.         delete [] (*pBasicInfo)->lpszName;
  331.     }
  332.  
  333.     if ((*pBasicInfo)->lpszAuthor != NULL)
  334.     {
  335.         delete [] (*pBasicInfo)->lpszAuthor;
  336.     }
  337.  
  338.     if ((*pBasicInfo)->lpszVersion != NULL)
  339.     {
  340.         delete [] (*pBasicInfo)->lpszVersion;
  341.     }
  342.  
  343.     if ((*pBasicInfo)->lpszType != NULL)
  344.     {
  345.         delete [] (*pBasicInfo)->lpszType;
  346.     }
  347.  
  348.     Cleanup_FileList(&((*pBasicInfo)->pFileList)); // release filelist
  349.     
  350.     delete *pBasicInfo;
  351.     *pBasicInfo = NULL;
  352. }
  353.  
  354.  
  355.  
  356. static void InstallList_AddToList(HWND hListView,ADDON_BASICINFO *pBasicInfo,const char *lpFilePath)
  357. {
  358.     LVITEM lvItem;
  359.     // allocate to lParam
  360.     ADDON_LISTINFO *pListInfo = new ADDON_LISTINFO;
  361.     ZeroMemory(pListInfo,sizeof(ADDON_LISTINFO));
  362.  
  363.     for (byte index=0;INSTALLATION_TYPES_ARR[index] != NULL;index++)
  364.     {
  365.         if (lstrcmpi(INSTALLATION_TYPES_ARR[index],pBasicInfo->lpszType) == 0) // match the type
  366.         {
  367.             pListInfo->bType = index; // const array is synced with types
  368.             break;
  369.         }
  370.     }
  371.  
  372.     // now for file path
  373.     pListInfo->lpFilePath = new char[lstrlen(lpFilePath) + 1]; // wee
  374.     lstrcpy(pListInfo->lpFilePath,lpFilePath); // make a copy
  375.  
  376.     lvItem.iSubItem = 0;
  377.     lvItem.mask = LVIF_PARAM | LVIF_TEXT;
  378.     lvItem.iItem = ListView_GetItemCount(hListView); // this doesn't matter since we're sorting later
  379.     lvItem.pszText = pBasicInfo->lpszName;
  380.     lvItem.lParam = (LPARAM)pListInfo;
  381.     lvItem.iItem = ListView_InsertItem(hListView,&lvItem); // lvItem.iItem won't have to be touched anymore
  382.     
  383.     lvItem.mask = LVIF_TEXT;
  384.     lvItem.iSubItem = 1;
  385.     if (pBasicInfo->lpszAuthor == NULL) // might not be filled
  386.     {
  387.         lvItem.pszText = Translate("N/A");
  388.     }
  389.     else
  390.     {
  391.         lvItem.pszText = pBasicInfo->lpszAuthor;
  392.     }
  393.     ListView_SetItem(hListView,&lvItem);
  394.  
  395.     lvItem.iSubItem = 2;
  396.     if (pBasicInfo->lpszVersion == NULL) // might not be filled
  397.     {
  398.         lvItem.pszText = Translate("N/A");
  399.     }
  400.     else
  401.     {
  402.         lvItem.pszText = pBasicInfo->lpszVersion;
  403.     }
  404.     ListView_SetItem(hListView,&lvItem);
  405.  
  406.     lvItem.iSubItem = 3;
  407.     lvItem.pszText = Translate(pBasicInfo->lpszType);
  408.     ListView_SetItem(hListView,&lvItem);
  409.  
  410.     lvItem.iSubItem = 4;
  411.     char szTemp[32];
  412.     sprintf(szTemp,"%d/%d",pBasicInfo->wMainPackages,pBasicInfo->wTotalPackages);
  413.     lvItem.pszText = szTemp;
  414.     ListView_SetItem(hListView,&lvItem);
  415.  
  416.     lvItem.iSubItem = 5;
  417.     lvItem.pszText = Translate("Waiting...");
  418.     ListView_SetItem(hListView,&lvItem);
  419.  
  420.     SendMessage(GetParent(hListView),WM_USER+112,true,0); // let parent know we have a change in num of items
  421. }
  422.  
  423.  
  424.  
  425. void InstallList_AddFile(HWND hwndDlg,const char *lpFilePath,bool fQuiet)
  426. {
  427.     // before we even look at the file
  428.     // we'll make sure it's not in the list already
  429.     HWND hListView = GetDlgItem(hwndDlg,IDC_INSTALLATIONS);
  430.     LVITEM lvItem;
  431.     lvItem.mask = LVIF_PARAM;
  432.     lvItem.iSubItem = 0;
  433.     for (int index=0,iCount=ListView_GetItemCount(hListView);index < iCount;index++)
  434.     {
  435.         lvItem.iItem = index;
  436.         ListView_GetItem(hListView,&lvItem);
  437.  
  438.         if (lstrcmpi(((ADDON_LISTINFO*)lvItem.lParam)->lpFilePath,lpFilePath) == 0) // same!
  439.         {
  440.             return; // not going to install it
  441.         }
  442.     }
  443.  
  444.     ADDON_BASICINFO *pBasicInfo = Install_GetBasicInfo(lpFilePath);
  445.  
  446.     if (pBasicInfo != NULL)
  447.     {
  448.         // right, now we check for deal breakers
  449.         // 1.
  450.         if ((pBasicInfo->lpszType == NULL) || (pBasicInfo->lpszName == NULL)) // unknown type
  451.         {
  452.             if (!fQuiet) // only notice if this isn't quiet mode
  453.             {
  454.                 MessageBox(hwndDlg,Translate("Type of package is unknown, file cannot be installed! (Does the file has a script? If not, does it have the right extension?)"),Translate("Miranda Installer Error"),MB_ICONWARNING);
  455.             }
  456.             Free_BasicInfo(&pBasicInfo);
  457.             return;
  458.         }
  459.         // 2.
  460.         if ((pBasicInfo->fScripted) && (pBasicInfo->wMainPackages == 0)) // no main packages are defined!
  461.         {
  462.             if (!fQuiet)
  463.             {
  464.                 MessageBox(hwndDlg,Translate("No Main packages were found in file, cannot be installed! (Probably a bad script)"),Translate("Miranda Installer Error"),MB_ICONWARNING);
  465.             }
  466.             Free_BasicInfo(&pBasicInfo);
  467.             return;
  468.         }
  469.         // 3.
  470.         if (pBasicInfo->fGotEmptyPackage) // empty package!? (we *KNOW* that we have a script, since this is inited to 0 when we start)
  471.         {
  472.             if (!fQuiet)
  473.             {
  474.                 MessageBox(hwndDlg,Translate("One of the packages in the zip contains no files, cannot be installed! (Probably a bad script)"),Translate("Miranda Installer Error"),MB_ICONWARNING);
  475.             }
  476.             Free_BasicInfo(&pBasicInfo);
  477.             return;
  478.         }
  479.         // 4. (go through file list and find invalid and dupes, argg)
  480.         if ((pBasicInfo->fScripted) && (pBasicInfo->pFileList != NULL))
  481.         {
  482.             GENERAL_FILELIST *pFileList = pBasicInfo->pFileList;
  483.             unzFile hFile = unzOpen(lpFilePath);
  484.             byte bError = 0; // 1 - invalid file, 2 - dupe (pFileList->lpFilename will point in either case - for formatting purposes)
  485.             if (hFile != NULL)
  486.             {
  487.                 while (pFileList != NULL)
  488.                 {
  489.                     if (unzLocateFile(hFile,pFileList->lpFilename,2) != UNZ_OK) // argg
  490.                     {
  491.                         bError = 1; // invalid
  492.                         break;
  493.                     }
  494.  
  495.                     // ok now we have to go through the file list and see if this file exists somewhere
  496.                     GENERAL_FILELIST *pCompFileList = pBasicInfo->pFileList;
  497.                     while (pCompFileList != NULL)
  498.                     {
  499.                         if ((pCompFileList != pFileList) && (lstrcmp(pFileList->lpFilename,pCompFileList->lpFilename) == 0)) // argg
  500.                         {
  501.                             bError = 2;
  502.                             break;
  503.                         }
  504.                         pCompFileList = pCompFileList->next;
  505.                     }
  506.  
  507.                     if (bError == 2)
  508.                     {
  509.                         break;
  510.                     }
  511.  
  512.                     pFileList = pFileList->next;
  513.                 }
  514.                 unzClose(hFile);
  515.             }
  516.  
  517.             if (bError) // some kind of error
  518.             {
  519.                 if (!fQuiet)
  520.                 {
  521.                     char szMsg[512];
  522.  
  523.                     Switch_Slashes(pFileList->lpFilename,false); // switch it again, to make it displayable
  524.                     if (bError == 1) // invalid
  525.                     {
  526.                         wsprintf(szMsg,Translate("The file \"%s\" used in the script was not found in the zip file, can't install file"),pFileList->lpFilename);
  527.                     }
  528.                     else // dupe
  529.                     {
  530.                         wsprintf(szMsg,Translate("The file \"%s\" is used more than once in the script, can't install file"),pFileList->lpFilename);
  531.                     }
  532.                     MessageBox(hwndDlg,szMsg,Translate("Miranda Installer Error"),MB_ICONWARNING);
  533.                 }
  534.                 Free_BasicInfo(&pBasicInfo);
  535.                 return;
  536.             }
  537.         }
  538.         // ok now let's add this one to the list
  539.         InstallList_AddToList(hListView,pBasicInfo,lpFilePath);
  540.  
  541.         Free_BasicInfo(&pBasicInfo);
  542.     }
  543.     else // something wrong
  544.     {
  545. //        MessageBox(hwndDlg,Translate("File type is invalid!"),Translate("Miranda Installer Error"),MB_ICONWARNING);
  546.     }
  547. }
  548.  
  549.  
  550.  
  551. void InstallList_AddDirectory(HWND hwndDlg,const char *lpDirectoryPath)
  552. {
  553.     char szSearchDir[MAX_PATH + 1];
  554.     HWND hListView = GetDlgItem(hwndDlg,IDC_INSTALLATIONS);
  555.  
  556.     // go over all extensions
  557.     lstrcpy(szSearchDir,lpDirectoryPath);
  558.     lstrcat(szSearchDir,"\\*.zip");
  559.     int index = 0; // needed, otherwise we'd miss the NULL
  560.     do // this way we can stick one more type (.zip) that's not "officially" included (zip files can also be used, if they have installation scripts)
  561.     {
  562.         // find files
  563.         WIN32_FIND_DATA findData;
  564.         HANDLE hFile = FindFirstFile(szSearchDir,&findData);
  565.         if (hFile != INVALID_HANDLE_VALUE)
  566.         {
  567.             do
  568.             {
  569.                 // find info and add to list here
  570.                 lstrcpy(szSearchDir,lpDirectoryPath);
  571. //                lstrcat(szSearchDir,"\\"); // causes trouble
  572.                 // no need for it either
  573.                 lstrcat(szSearchDir,findData.cFileName);
  574.                 InstallList_AddFile(hwndDlg,szSearchDir,true); // install the file
  575.             } while (FindNextFile(hFile,&findData));
  576.         }
  577.  
  578.         // go to next file type
  579.         if (INSTALLATION_EXTS_ARR[index] != NULL)
  580.         {
  581.             lstrcpy(szSearchDir,lpDirectoryPath);
  582.             lstrcat(szSearchDir,"\\*.");
  583.             lstrcat(szSearchDir,INSTALLATION_EXTS_ARR[index]); // add extension
  584.         }
  585.     } while (INSTALLATION_EXTS_ARR[index++] != NULL);
  586. }