home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / xwphescr.zip / XWPH0208.ZIP / src / helpers / exeh.c < prev    next >
C/C++ Source or Header  |  2002-06-17  |  118KB  |  3,184 lines

  1.  
  2. /*
  3.  *@@sourcefile exeh.c:
  4.  *      contains code to load and parse executable headers
  5.  *      and resources. See exehOpen for details.
  6.  *
  7.  *      This file is new with V0.9.16 (2002-01-05) [umoeller]
  8.  *      and contains code formerly in dosh2.c.
  9.  *
  10.  *      Function prefixes:
  11.  *      --  exe*   executable helper functions.
  12.  *
  13.  *      Note: Version numbering in this file relates to XWorkplace version
  14.  *            numbering.
  15.  *
  16.  *@@header "helpers\exeh.h"
  17.  */
  18.  
  19. /*
  20.  *      This file Copyright (C) 2000-2002 Ulrich Möller,
  21.  *                                        Martin Lafaix.
  22.  *      This file is part of the "XWorkplace helpers" source package.
  23.  *      This is free software; you can redistribute it and/or modify
  24.  *      it under the terms of the GNU General Public License as published
  25.  *      by the Free Software Foundation, in version 2 as it comes in the
  26.  *      "COPYING" file of the XWorkplace main distribution.
  27.  *      This program is distributed in the hope that it will be useful,
  28.  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
  29.  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  30.  *      GNU General Public License for more details.
  31.  */
  32.  
  33. #define OS2EMX_PLAIN_CHAR
  34.     // this is needed for "os2emx.h"; if this is defined,
  35.     // emx will define PSZ as _signed_ char, otherwise
  36.     // as unsigned char
  37.  
  38. #define INCL_DOSMODULEMGR
  39. #define INCL_DOSPROCESS
  40. #define INCL_DOSEXCEPTIONS
  41. #define INCL_DOSSESMGR
  42. #define INCL_DOSQUEUES
  43. #define INCL_DOSMISC
  44. #define INCL_DOSDEVICES
  45. #define INCL_DOSDEVIOCTL
  46. #define INCL_DOSERRORS
  47. #include <os2.h>
  48.  
  49. // #include <stdlib.h>
  50. #include <string.h>
  51. #include <stdio.h>
  52. #include <setjmp.h>
  53.  
  54. #include "setup.h"                      // code generation and debugging options
  55.  
  56. #include "helpers\dosh.h"
  57. #include "helpers\ensure.h"
  58. #include "helpers\except.h"
  59. #include "helpers\exeh.h"
  60. #include "helpers\standards.h"
  61. #include "helpers\stringh.h"
  62.  
  63. #pragma hdrstop
  64.  
  65. /*
  66.  *@@category: Helpers\Control program helpers\Executable info
  67.  *      these functions can retrieve BLDLEVEL information,
  68.  *      imported modules information, exported functions information,
  69.  *      and resources information from any executable module. See
  70.  *      exehOpen.
  71.  */
  72.  
  73. /********************************************************************
  74.  *
  75.  *   Executable functions
  76.  *
  77.  ********************************************************************/
  78.  
  79. /*
  80.  *@@ exehOpen:
  81.  *      this opens the specified executable file
  82.  *      (which can be an .EXE, .COM, .DLL, or
  83.  *      driver file) for use with the other
  84.  *      exeh* functions.
  85.  *
  86.  *      Basically this does a plain DosOpen on the
  87.  *      executable file and reads in the various
  88.  *      executable headers manually. Since DosLoadModule
  89.  *      et al is never used, the OS/2 executable loader
  90.  *      is completely circumvented. (Side note:
  91.  *      DosLoadModule cannot be used on EXE files in
  92.  *      the first place.)
  93.  *
  94.  *      To be more precise, this uses doshOpen internally
  95.  *      and can thus profit from the caching that is
  96.  *      implemented there (V0.9.16).
  97.  *
  98.  *      If no error occurs, NO_ERROR is returned
  99.  *      and a pointer to a new EXECUTABLE structure
  100.  *      is stored in *ppExec. Consider this pointer a
  101.  *      handle and pass it to exehClose to clean up.
  102.  *
  103.  *      If NO_ERROR is returned, all the fields through
  104.  *      ulOS are set in EXECUTABLE. The psz* fields
  105.  *      which follow afterwards require an additional
  106.  *      call to exehQueryBldLevel.
  107.  *
  108.  *      NOTE: If NO_ERROR is returned, the executable
  109.  *      file is kept open by this function. It will
  110.  *      only be closed when you call exehClose.
  111.  *
  112.  *      If errors occur, this function returns the
  113.  *      following error codes:
  114.  *
  115.  *      -- ERROR_NOT_ENOUGH_MEMORY: malloc() failed.
  116.  *
  117.  *      -- ERROR_INVALID_EXE_SIGNATURE (191): header is
  118.  *              neither plain DOS, nor NE, nor LX, nor PE.
  119.  *              The given file probably isn't even an
  120.  *              executable.
  121.  *
  122.  *      -- ERROR_BAD_EXE_FORMAT (193): header was
  123.  *              recognized, but the header data was
  124.  *              not understood. Also this might be
  125.  *              returned for .COM files which are
  126.  *              not recognized.
  127.  *
  128.  *      -- ERROR_INVALID_PARAMETER: ppExec is NULL.
  129.  *
  130.  *      plus those of doshOpen and doshReadAt.
  131.  *
  132.  *      The following executable types are supported
  133.  *      (see EXECUTABLE for details):
  134.  *
  135.  *      --  Plain DOS 3.x executable without new header.
  136.  *
  137.  *      --  New Executable (NE), used by Win16 and
  138.  *          16-bit OS/2 and still many of today's drivers.
  139.  *
  140.  *      --  Linear Executable (LX), OS/2 2.x and above.
  141.  *
  142.  *      --  Portable Executable (PE), used by Win32.
  143.  *
  144.  *      --  For files with the .COM, .BAT, or .CMD
  145.  *          extensions, this will _not_ open the
  146.  *          executable file, but set flags in EXECUTABLE
  147.  *          only. Most importantly, EXECUTABLE.pDosExeHeader
  148.  *          will be NULL.
  149.  *
  150.  *      V0.9.12 adds support for NOSTUB executables,
  151.  *      which are new-style executables (NE or LX)
  152.  *      without a leading DOS header. JFS.IFS uses that
  153.  *      format, for example. The executable then starts
  154.  *      directly with the NE or LX header. I am not sure
  155.  *      whether PE supports such beasts  as well... if
  156.  *      so, it should be supported too.
  157.  *
  158.  *      Note that not all of the other exeh* functions
  159.  *      support all of the executable types. See the
  160.  *      respective function descriptions for remarks.
  161.  *
  162.  *      @@todo:
  163.  *
  164.  *          win95 \WINDOWS\extract.exe is NE with a non-standard format
  165.  *          win16 \WINDOWS\EXPAND.EXE
  166.  *          win16 \WINDOWS\MSD.EXE"
  167.  *
  168.  *@@added V0.9.0 [umoeller]
  169.  *@@changed V0.9.1 (2000-02-13) [umoeller]: fixed 32-bits flag
  170.  *@@changed V0.9.7 (2000-12-20) [lafaix]: fixed ulNewHeaderOfs
  171.  *@@changed V0.9.10 (2001-04-08) [lafaix]: added PE support
  172.  *@@changed V0.9.10 (2001-04-08) [umoeller]: now setting ppExec only if NO_ERROR is returned
  173.  *@@changed V0.9.12 (2001-05-03) [umoeller]: added support for NOSTUB newstyle executables
  174.  *@@changed V0.9.16 (2001-12-08) [umoeller]: now using OPEN_SHARE_DENYWRITE
  175.  *@@changed V0.9.16 (2001-12-08) [umoeller]: fLibrary was never set, works for LX, NE, and PE now
  176.  *@@changed V0.9.16 (2001-12-08) [umoeller]: speed optimizations, changed some return codes
  177.  *@@changed V0.9.16 (2002-01-04) [umoeller]: added fixes for COM, BAT, CMD extensions
  178.  */
  179.  
  180. APIRET exehOpen(const char* pcszExecutable,
  181.                 PEXECUTABLE* ppExec)
  182. {
  183.     APIRET      arc = NO_ERROR;
  184.  
  185.     PEXECUTABLE pExec = NULL;
  186.  
  187.     PXFILE      pFile = NULL;
  188.     ULONG       cbFile = 0;
  189.     PCSZ        pExt;
  190.     BOOL        fOpenFile = FALSE;
  191.     BOOL        fLoadNewHeader = FALSE;
  192.     ULONG       ulNewHeaderOfs = 0;       // V0.9.12 (2001-05-03) [umoeller]
  193.  
  194.     if (!ppExec)
  195.         return (ERROR_INVALID_PARAMETER);
  196.  
  197.     if (!(pExec = (PEXECUTABLE)malloc(sizeof(EXECUTABLE))))
  198.         return (ERROR_NOT_ENOUGH_MEMORY);
  199.  
  200.     memset(pExec, 0, sizeof(EXECUTABLE));
  201.  
  202.     // check some of the default extensions
  203.     // V0.9.16 (2002-01-04) [umoeller]
  204.     if (pExt = doshGetExtension(pcszExecutable))
  205.     {
  206.         if (!stricmp(pExt, "COM"))
  207.         {
  208.             // I am not willing to find out more about the
  209.             // .COM executable format, so for this one case,
  210.             // let OS/2 determine what we have here
  211.             // (otherwise we do _not_ use DosQueryAppType
  212.             // because it's quite an expensive call)
  213.             ULONG ulDosAppType = 0;
  214.             if (!(arc = DosQueryAppType((PSZ)pcszExecutable, &ulDosAppType)))
  215.             {
  216.                 if (ulDosAppType & FAPPTYP_DOS)           // 0x20
  217.                     pExec->ulOS = EXEOS_DOS3;
  218.                 else
  219.                 {
  220.                     ULONG fl = ulDosAppType & FAPPTYP_WINDOWAPI; // 0x03
  221.                     if (    (fl == FAPPTYP_WINDOWCOMPAT) // 0x02)
  222.                          || (fl == FAPPTYP_NOTWINDOWCOMPAT) // 0x01)
  223.                        )
  224.                         pExec->ulOS = EXEOS_OS2;
  225.                     else
  226.                         arc = ERROR_BAD_EXE_FORMAT;
  227.                 }
  228.  
  229.                 pExec->ulExeFormat = EXEFORMAT_COM;
  230.             }
  231.         }
  232.         else if (!stricmp(pExt, "BAT"))
  233.         {
  234.             pExec->ulOS = EXEOS_DOS3;
  235.             pExec->ulExeFormat = EXEFORMAT_TEXT_BATCH;
  236.         }
  237.         else if (!stricmp(pExt, "CMD"))
  238.         {
  239.             pExec->ulOS = EXEOS_OS2;
  240.             pExec->ulExeFormat = EXEFORMAT_TEXT_CMD;
  241.         }
  242.         else
  243.             fOpenFile = TRUE;
  244.     }
  245.  
  246.     if (    (fOpenFile)     // none of the above
  247.          && (!(arc = doshOpen((PSZ)pcszExecutable,
  248.                               XOPEN_READ_EXISTING,
  249.                               &cbFile,
  250.                               &pFile)))
  251.             // file opened successfully:
  252.        )
  253.     {
  254.         pExec->pFile = pFile;
  255.         pExec->cbDosExeHeader = sizeof(DOSEXEHEADER);
  256.  
  257.         // read old DOS EXE header
  258.         if (!(pExec->pDosExeHeader = (PDOSEXEHEADER)malloc(sizeof(DOSEXEHEADER))))
  259.             arc = ERROR_NOT_ENOUGH_MEMORY;
  260.         else if (!(arc = doshReadAt(pFile,
  261.                                     0,
  262.                                     &pExec->cbDosExeHeader,      // in/out
  263.                                     (PBYTE)pExec->pDosExeHeader,
  264.                                     DRFL_FAILIFLESS)))
  265.         {
  266.             // now check if we really have a DOS header
  267.             if (pExec->pDosExeHeader->usDosExeID != 0x5a4d)
  268.             {
  269.                 // arc = ERROR_INVALID_EXE_SIGNATURE;
  270.  
  271.                 // V0.9.12 (2001-05-03) [umoeller]
  272.                 // try loading new header directly; there are
  273.                 // drivers which were built with NOSTUB, and
  274.                 // the exe image starts out with the NE or LX
  275.                 // image directly (try JFS.IFS)
  276.                 fLoadNewHeader = TRUE;
  277.                         // ulNewHeaderOfs is 0 now
  278.  
  279.                 // remove the DOS header info, since we have none
  280.                 // V0.9.12 (2001-05-03) [umoeller]
  281.                 FREE(pExec->pDosExeHeader);
  282.                 pExec->cbDosExeHeader = 0;
  283.             }
  284.             else
  285.             {
  286.                 // we have a DOS header:
  287.                 if (pExec->pDosExeHeader->usRelocTableOfs < 0x40)
  288.                 {
  289.                     // neither LX nor PE nor NE:
  290.                     pExec->ulOS = EXEOS_DOS3;
  291.                     pExec->ulExeFormat = EXEFORMAT_OLDDOS;
  292.                 }
  293.                 else
  294.                 {
  295.                     // we have a new header offset:
  296.                     fLoadNewHeader = TRUE;
  297.                     ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
  298.                 }
  299.             }
  300.         }
  301.     }
  302.  
  303.     if (fLoadNewHeader)
  304.     {
  305.         // either LX or PE or NE:
  306.         // read in new header...
  307.         // ulNewHeaderOfs is now either 0 (if no DOS header
  308.         // was found) or pDosExeHeader->ulNewHeaderOfs
  309.         // V0.9.12 (2001-05-03) [umoeller]
  310.  
  311.         // read in the first two bytes to find out
  312.         // what extended header type we have; note,
  313.         // PE uses four bytes here
  314.         CHAR    achNewHeaderType[4] = "???";
  315.         ULONG   cbRead = 4;
  316.  
  317.         if (!(arc = doshReadAt(pFile,
  318.                                ulNewHeaderOfs,
  319.                                &cbRead,
  320.                                achNewHeaderType,
  321.                                DRFL_FAILIFLESS)))
  322.         {
  323.             PBYTE   pbCheckOS = NULL;
  324.  
  325.             if (!memcmp(achNewHeaderType, "NE", 2))
  326.             {
  327.                 // New Executable:
  328.                 pExec->ulExeFormat = EXEFORMAT_NE;
  329.                 cbRead = sizeof(NEHEADER);
  330.  
  331.                 // go read in the complete header then
  332.                 // (doshReadAt has this in the cache)
  333.                 if (!(pExec->pNEHeader = (PNEHEADER)malloc(cbRead)))
  334.                     arc = ERROR_NOT_ENOUGH_MEMORY;
  335.                 else if (!(arc = doshReadAt(pFile,
  336.                                             ulNewHeaderOfs,
  337.                                             &cbRead,
  338.                                             (PBYTE)pExec->pNEHeader,
  339.                                             0)))
  340.                 {
  341.                     if (cbRead < sizeof(NEHEADER))
  342.                         arc = ERROR_BAD_EXE_FORMAT;
  343.                     else
  344.                     {
  345.                         pExec->cbNEHeader = cbRead;
  346.                         pbCheckOS = &pExec->pNEHeader->bTargetOS;
  347.                         // set library flag V0.9.16 (2001-12-08) [umoeller]
  348.                         if (pExec->pNEHeader->usFlags & 0x8000)
  349.                             // library:
  350.                             pExec->fLibrary = TRUE;
  351.                     }
  352.                 }
  353.             }
  354.             else if (    (!memcmp(achNewHeaderType, "LX", 2))
  355.                       || (!memcmp(achNewHeaderType, "LE", 2))
  356.                                 // this is used by SMARTDRV.EXE
  357.                     )
  358.             {
  359.                 // OS/2 Linear Executable:
  360.                 pExec->ulExeFormat = EXEFORMAT_LX;
  361.                 cbRead = sizeof(LXHEADER);
  362.  
  363.                 // go read in the complete header then
  364.                 // (doshReadAt has this in the cache)
  365.                 if (!(pExec->pLXHeader = (PLXHEADER)malloc(cbRead)))
  366.                     arc = ERROR_NOT_ENOUGH_MEMORY;
  367.                 else if (!(arc = doshReadAt(pFile,
  368.                                             ulNewHeaderOfs,
  369.                                             &cbRead,
  370.                                             (PBYTE)pExec->pLXHeader,
  371.                                             0)))
  372.                 {
  373.                     if (cbRead < sizeof(LXHEADER))
  374.                         arc = ERROR_BAD_EXE_FORMAT;
  375.                     else
  376.                     {
  377.                         pExec->cbLXHeader = cbRead;
  378.                         pbCheckOS = (PBYTE)(&pExec->pLXHeader->usTargetOS);
  379.                         // set library flag V0.9.16 (2001-12-08) [umoeller]
  380.                         if (pExec->pLXHeader->ulFlags & 0x8000)
  381.                             // library:
  382.                             pExec->fLibrary = TRUE;
  383.                     }
  384.                 }
  385.             }
  386.             else if (!memcmp(achNewHeaderType, "PE\0\0", 4))
  387.             {
  388.                 pExec->ulExeFormat = EXEFORMAT_PE;
  389.  
  390.                 // PE has a standard header of 24 bytes
  391.                 // plus an extended header, so check
  392.                 // what we've got
  393.                 if (!(pExec->pPEHeader = (PPEHEADER)malloc(sizeof(PEHEADER))))
  394.                     arc = ERROR_NOT_ENOUGH_MEMORY;
  395.                 else
  396.                 {
  397.                     ULONG ulOfs = ulNewHeaderOfs + 4;
  398.                     PPEHEADER pPEHeader = pExec->pPEHeader;
  399.  
  400.                     // null the entire header
  401.                     memset(pExec->pPEHeader,
  402.                            0,
  403.                            sizeof(PEHEADER));
  404.  
  405.                     // copy sig
  406.                     pPEHeader->ulSignature = *((PULONG)&achNewHeaderType);
  407.  
  408.                     // read standard header
  409.                     cbRead = sizeof(IMAGE_FILE_HEADER);
  410.                     if (!(arc = doshReadAt(pFile,
  411.                                            ulOfs,
  412.                                            &cbRead,
  413.                                            (PBYTE)&pPEHeader->FileHeader,
  414.                                            0)))
  415.                     {
  416.                         if (cbRead < sizeof(IMAGE_FILE_HEADER))
  417.                             // only if we don't even have the
  418.                             // standard header, return an error
  419.                             arc = ERROR_BAD_EXE_FORMAT;
  420.                         else
  421.                         {
  422.                             pExec->f32Bits = TRUE;
  423.                             pExec->cbPEHeader = 4 + sizeof(PEHEADER);       // for now
  424.  
  425.                             if (pPEHeader->FileHeader.fsCharacteristics & IMAGE_FILE_DLL)
  426.                                 pExec->fLibrary = TRUE;
  427.  
  428.                             // try extended header
  429.                             ulOfs += sizeof(IMAGE_FILE_HEADER);
  430.                             if (    (cbRead = pPEHeader->FileHeader.usSizeOfOptionalHeader)
  431.                                  && (cbRead <= sizeof(IMAGE_OPTIONAL_HEADER))
  432.                                )
  433.                             {
  434.                                 if (!(arc = doshReadAt(pFile,
  435.                                                        ulOfs,
  436.                                                        &cbRead,
  437.                                                        (PBYTE)&pPEHeader->OptionalHeader,
  438.                                                        0)))
  439.                                 {
  440.                                     if (cbRead != sizeof(IMAGE_OPTIONAL_HEADER))
  441.                                         arc = ERROR_BAD_EXE_FORMAT;
  442.                                     else switch (pPEHeader->OptionalHeader.usSubsystem)
  443.                                     {
  444.                                         // case IMAGE_SUBSYSTEM_UNKNOWN:   // 0
  445.                                         // case IMAGE_SUBSYSTEM_NATIVE:    // 1
  446.                                         // case IMAGE_SUBSYSTEM_OS2_CUI:   // 5
  447.                                         // case IMAGE_SUBSYSTEM_POSIX_CUI: // 7
  448.                                                 // for these we shouldn't set win32
  449.  
  450.                                         case IMAGE_SUBSYSTEM_WINDOWS_GUI: // 2   // Windows GUI subsystem
  451.                                             pExec->ulOS = EXEOS_WIN32_GUI;
  452.                                         break;
  453.  
  454.                                         case IMAGE_SUBSYSTEM_WINDOWS_CUI: // 3   // Windows character subsystem
  455.                                             pExec->ulOS = EXEOS_WIN32_CLI;
  456.                                         break;
  457.                                     }
  458.  
  459.                                     pExec->cbPEHeader = sizeof(PEHEADER);
  460.                                 }
  461.                             }
  462.                         } // end else if (cbRead < sizeof(IMAGE_FILE_HEADER))
  463.                     } // end if (!(arc = doshReadAt(pFile,
  464.                 } // end else if (!(pExec->pPEHeader = (PPEHEADER)malloc(sizeof(PEHEADER))))
  465.             } // end else if (!memcmp(achNewHeaderType, "PE\0\0", 4))
  466.             else
  467.                 // strange type:
  468.                 arc = ERROR_INVALID_EXE_SIGNATURE;
  469.  
  470.             if ((!arc) && (pbCheckOS))
  471.             {
  472.                 // BYTE to check for operating system
  473.                 // (NE and LX):
  474.                 switch (*pbCheckOS)
  475.                 {
  476.                     case NEOS_OS2:
  477.                         pExec->ulOS = EXEOS_OS2;
  478.                         if (pExec->ulExeFormat == EXEFORMAT_LX)
  479.                             pExec->f32Bits = TRUE;
  480.                     break;
  481.  
  482.                     case NEOS_WIN16:
  483.                         pExec->ulOS = EXEOS_WIN16;
  484.                     break;
  485.  
  486.                     case NEOS_DOS4:
  487.                         pExec->ulOS = EXEOS_DOS4;
  488.                     break;
  489.  
  490.                     case NEOS_WIN386:
  491.                         pExec->ulOS = EXEOS_WIN386;
  492.                         pExec->f32Bits = TRUE;
  493.                     break;
  494.                 }
  495.             }
  496.         } // end if (!(arc = doshReadAt(hFile,
  497.     } // end if (fLoadNewHeader)
  498.  
  499.     if (arc != NO_ERROR)
  500.         // error: clean up
  501.         exehClose(&pExec);
  502.     else
  503.         *ppExec = pExec;
  504.  
  505.     return arc;
  506. }
  507.  
  508. /*
  509.  *@@ ParseBldLevel:
  510.  *      called from exehQueryBldLevel to parse
  511.  *      the BLDLEVEL string.
  512.  *
  513.  *      On entry, caller has copied the string into
  514.  *      pExec->pszDescription. The string is
  515.  *      null-terminated.
  516.  *
  517.  *      The BLDLEVEL string comes in two flavors.
  518.  *
  519.  *      --  The standard format is:
  520.  *
  521.  +              @#VENDOR:VERSION#@DESCRIPTION
  522.  *
  523.  *          DESCRIPTION can have leading spaces, but
  524.  *          need to have them.
  525.  *
  526.  *      --  However, there is an extended version
  527.  *          in that the DESCRIPTION field is split
  528.  *          up even more. The marker for this seems
  529.  *          to be that the description starts out
  530.  *          with "##1##".
  531.  *
  532.  +              ##1## DATETIME BUILDMACHINE:ASD:LANG:CTRY:REVISION:UNKNOWN:FIXPAK@@DESCRIPTION
  533.  *
  534.  *          The problem is that the DATETIME field comes
  535.  *          in several flavors. IBM uses things like
  536.  *
  537.  +              "Thu Nov 30 15:30:37 2000 BWBLD228"
  538.  *
  539.  *          while DANIS506.ADD has
  540.  *
  541.  +              "15.12.2000 18:22:57      Nachtigall"
  542.  *
  543.  *          Looks like the date/time string is standardized
  544.  *          to have 24 characters then.
  545.  *
  546.  *@@added V0.9.12 (2001-05-18) [umoeller]
  547.  *@@changed V0.9.12 (2001-05-19) [umoeller]: added extended BLDLEVEL support
  548.  */
  549.  
  550. static VOID ParseBldLevel(PEXECUTABLE pExec)
  551. {
  552.     PCSZ    pStartOfVendor,
  553.             pStartOfInfo,
  554.             pEndOfVendor;
  555.  
  556.     // @#VENDOR:VERSION#@ DESCRIPTION
  557.     // but skip the first byte, which has the string length
  558.     if (    (pStartOfVendor = strstr(pExec->pszDescription, "@#"))
  559.          && (pStartOfInfo = strstr(pStartOfVendor + 2, "#@"))
  560.          && (pEndOfVendor = strchr(pStartOfVendor + 2, ':'))
  561.        )
  562.     {
  563.         pExec->pszVendor = strhSubstr(pStartOfVendor + 2,
  564.                                       pEndOfVendor);
  565.         pExec->pszVersion = strhSubstr(pEndOfVendor + 1,
  566.                                        pStartOfInfo);
  567.         // skip "@#" in DESCRIPTION string
  568.         pStartOfInfo += 2;
  569.  
  570.         // now check if we have extended DESCRIPTION V0.9.12 (2001-05-19) [umoeller]
  571.         if (    (strlen(pStartOfInfo) > 6)
  572.              && (!memcmp(pStartOfInfo, "##1##", 5))
  573.            )
  574.         {
  575.             // yes: parse that beast
  576.             const char *p = pStartOfInfo + 5;
  577.  
  578.             // get build date/time
  579.             if (strlen(p) > 24)
  580.             {
  581.                 // date/time seems to be fixed 24 chars in length
  582.                 if (pExec->pszBuildDateTime = (PSZ)malloc(25))
  583.                 {
  584.                     memcpy(pExec->pszBuildDateTime,
  585.                            p,
  586.                            24);
  587.                     pExec->pszBuildDateTime[24] = '\0';
  588.  
  589.                     p += 24;
  590.  
  591.                     // now we're at the colon-separated
  592.                     // strings, first of which is the build machine;
  593.                     // skip leading spaces
  594.                     while (*p == ' ')
  595.                         p++;
  596.  
  597.                     if (*p)
  598.                     {
  599.                         char **papsz[] =
  600.                             {
  601.                                 &pExec->pszBuildMachine,
  602.                                 &pExec->pszASD,
  603.                                 &pExec->pszLanguage,
  604.                                 &pExec->pszCountry,
  605.                                 &pExec->pszRevision,
  606.                                 &pExec->pszUnknown,
  607.                                 &pExec->pszFixpak
  608.                             };
  609.                         ULONG ul;
  610.  
  611.                         for (ul = 0;
  612.                              ul < sizeof(papsz) / sizeof(papsz[0]);
  613.                              ul++)
  614.                         {
  615.                             BOOL fStop = FALSE;
  616.                             const char *pNextColon = strchr(p, ':'),
  617.                                        *pDoubleAt = strstr(p, "@@");
  618.                             if (!pNextColon)
  619.                             {
  620.                                 // last item:
  621.                                 if (pDoubleAt)
  622.                                     pNextColon = pDoubleAt;
  623.                                 else
  624.                                     pNextColon = p + strlen(p);
  625.  
  626.                                 fStop = TRUE;
  627.                             }
  628.  
  629.                             if (    (fStop)
  630.                                  || (    (pNextColon)
  631.                                       && (    (!pDoubleAt)
  632.                                            || (pNextColon < pDoubleAt)
  633.                                          )
  634.                                     )
  635.                                )
  636.                             {
  637.                                 if (pNextColon > p + 1)
  638.                                     *(papsz[ul]) = strhSubstr(p, pNextColon);
  639.                             }
  640.                             else
  641.                                 break;
  642.  
  643.                             if (fStop)
  644.                                 break;
  645.  
  646.                             p = pNextColon + 1;
  647.                         }
  648.                     }
  649.                 }
  650.             }
  651.  
  652.             pStartOfInfo = strstr(p,
  653.                                   "@@");
  654.             if (pStartOfInfo)
  655.                 pStartOfInfo += 2;
  656.         }
  657.  
  658.         // -- if we had no extended DESCRIPTION,
  659.         //    pStartOfInfo points to regular description now
  660.         // -- if we parse the extended DESCRIPTION above,
  661.         //    pStartOfInfo points to after @@ now
  662.         // -- if we had an error, pStartOfInfo is NULL
  663.         if (pStartOfInfo)
  664.         {
  665.             // add the regular DESCRIPTION then
  666.             // skip leading spaces in info string
  667.             while (*pStartOfInfo == ' ')
  668.                 pStartOfInfo++;
  669.             if (*pStartOfInfo)  // V0.9.9 (2001-04-04) [umoeller]
  670.                 // and copy until end of string
  671.                 pExec->pszInfo = strdup(pStartOfInfo);
  672.         }
  673.     }
  674. }
  675.  
  676. /*
  677.  *@@ exehQueryBldLevel:
  678.  *      this retrieves buildlevel information for an
  679.  *      LX or NE executable previously opened with
  680.  *      exehOpen.
  681.  *
  682.  *      BuildLevel information must be contained in the
  683.  *      DESCRIPTION field of an executable's module
  684.  *      definition (.DEF) file. In order to be readable
  685.  *      by BLDLEVEL.EXE (which ships with OS/2), this
  686.  *      string must have the following format:
  687.  *
  688.  +          Description '@#AUTHOR:VERSION#@ DESCRIPTION'
  689.  *
  690.  *      Example:
  691.  *
  692.  +          Description '@#Ulrich Möller:0.9.0#@ XWorkplace Sound Support Module'
  693.  *
  694.  *      The "Description" entry always ends up as the
  695.  *      very first entry in the non-resident name table
  696.  *      in LX and NE executables. So this is what we retrieve
  697.  *      here.
  698.  *
  699.  *      If the first entry in that table exists, NO_ERROR is
  700.  *      returned and at least the pszDescription field in
  701.  *      EXECUTABLE is set to that information.
  702.  *
  703.  *      If that string is in IBM BLDLEVEL format, the string
  704.  *      is automatically parsed, and the pszVendor, pszVersion,
  705.  *      and pszInfo fields are also set. In the above examples,
  706.  *      this would return the following information:
  707.  *
  708.  +          pszVendor = "Ulrich Möller"
  709.  +          pszVersion = "0.9.0"
  710.  +          pszInfo = "XWorkplace Sound Support Module"
  711.  *
  712.  *      See ParseBldLevel for extended formats.
  713.  *
  714.  *      If that string is not in BLDLEVEL format, only
  715.  *      pszDescription will be set. The other fields remain
  716.  *      NULL. Still, NO_ERROR is returned.
  717.  *
  718.  *      This returns the following errors:
  719.  *
  720.  *      -- ERROR_INVALID_PARAMETER: pExec is NULL.
  721.  *
  722.  *      -- ERROR_INVALID_EXE_SIGNATURE (191): pExec is not in
  723.  *                  LX or NE format.
  724.  *
  725.  *      -- ERROR_INVALID_DATA (13): non-resident name table not found,
  726.  *                  or table is empty.
  727.  *
  728.  *      -- ERROR_NOT_ENOUGH_MEMORY: malloc() failed.
  729.  *
  730.  *      plus the error codes of doshReadAt.
  731.  *
  732.  *@@added V0.9.0 [umoeller]
  733.  *@@changed V0.9.0 (99-10-22) [umoeller]: NE format now supported
  734.  *@@changed V0.9.1 (99-12-06): fixed memory leak
  735.  *@@changed V0.9.9 (2001-04-04) [umoeller]: added more error checking
  736.  *@@changed V0.9.12 (2001-05-18) [umoeller]: extracted ParseBldLevel
  737.  *@@changed V0.9.16 (2002-01-05) [umoeller]: optimizations
  738.  */
  739.  
  740. APIRET exehQueryBldLevel(PEXECUTABLE pExec)
  741. {
  742.     APIRET      arc = NO_ERROR;
  743.  
  744.     if (!pExec)
  745.         arc = ERROR_INVALID_PARAMETER;
  746.     else
  747.     {
  748.         PXFILE      pFile = pExec->pFile;
  749.  
  750.         ULONG       ulNRNTOfs = 0;
  751.  
  752.         if (pExec->ulExeFormat == EXEFORMAT_LX)
  753.         {
  754.             // OK, LX format:
  755.             // check if we have a non-resident name table
  756.             if (pExec->pLXHeader == NULL)
  757.                 arc = ERROR_INVALID_DATA;
  758.             else if (pExec->pLXHeader->ulNonResdNameTblOfs == 0)
  759.                 arc = ERROR_INVALID_DATA;
  760.             else
  761.                 ulNRNTOfs = pExec->pLXHeader->ulNonResdNameTblOfs;
  762.         }
  763.         else if (pExec->ulExeFormat == EXEFORMAT_NE)
  764.         {
  765.             // OK, NE format:
  766.             // check if we have a non-resident name table
  767.             if (pExec->pNEHeader == NULL)
  768.                 arc = ERROR_INVALID_DATA;
  769.             else if (pExec->pNEHeader->ulNonResdTblOfs == 0)
  770.                 arc = ERROR_INVALID_DATA;
  771.             else
  772.                 ulNRNTOfs = pExec->pNEHeader->ulNonResdTblOfs;
  773.         }
  774.         else
  775.             // neither LX nor NE: stop
  776.             arc = ERROR_INVALID_EXE_SIGNATURE;
  777.  
  778.         if (    (!arc)
  779.              && (ulNRNTOfs)
  780.            )
  781.         {
  782.             ULONG       cb = 2000;
  783.  
  784.             PSZ         pszNameTable;
  785.  
  786.             if (!(pszNameTable = (PSZ)malloc(2001)))
  787.                 arc = ERROR_NOT_ENOUGH_MEMORY;
  788.             else
  789.             {
  790.                 // V0.9.16 (2002-01-05) [umoeller]: rewrote the following
  791.  
  792.                 // read from offset of non-resident name table
  793.                 if (!(arc = doshReadAt(pFile,           // file is still open
  794.                                        ulNRNTOfs,       // ofs determined above
  795.                                        &cb,             // 2000
  796.                                        pszNameTable,
  797.                                        0)))
  798.                 {
  799.                     // the string is in Pascal format, so the
  800.                     // first byte has the length
  801.                     BYTE bLen;
  802.                     if (!(bLen = *pszNameTable))
  803.                         // length byte is null:
  804.                         arc = ERROR_INVALID_DATA;
  805.                     else
  806.                     {
  807.                         // now copy the string
  808.                         if (!(pExec->pszDescription = (PSZ)malloc(bLen + 1)))
  809.                             arc = ERROR_NOT_ENOUGH_MEMORY;
  810.                         else
  811.                         {
  812.                             memcpy(pExec->pszDescription,
  813.                                    pszNameTable + 1,    // skip length byte
  814.                                    bLen);               // length byte
  815.                             // terminate string
  816.                             pExec->pszDescription[bLen] = 0;
  817.  
  818.                             ParseBldLevel(pExec);
  819.                         }
  820.                     }
  821.                 }
  822.  
  823.                 free(pszNameTable);
  824.             }
  825.         }
  826.     } // end if (!pExec)
  827.  
  828.     return arc;
  829. }
  830.  
  831. /*
  832.  *@@ exehQueryImportedModules:
  833.  *      returns an array of FSYSMODULE structure describing all
  834.  *      imported modules.
  835.  *
  836.  *      *pcModules receives the # of items in the array (not the
  837.  *      array size!).  Use doshFreeImportedModules to clean up.
  838.  *
  839.  *      This returns a standard OS/2 error code, which might be
  840.  *      any of the codes returned by DosSetFilePtr and DosRead.
  841.  *      In addition, this may return:
  842.  *
  843.  *      --  ERROR_NOT_ENOUGH_MEMORY
  844.  *
  845.  *      --  ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
  846.  *          than LX or NE, which is not understood by this function.
  847.  *
  848.  *      Even if NO_ERROR is returned, the array pointer might still
  849.  *      be NULL if the module contains no such data.
  850.  *
  851.  *@@added V0.9.9 (2001-03-11) [lafaix]
  852.  *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
  853.  *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
  854.  *@@changed V0.9.10 (2001-04-10) [lafaix]: added Win16 and Win386 support
  855.  *@@changed V0.9.10 (2001-04-13) [lafaix]: removed 127 characters limit
  856.  *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
  857.  */
  858.  
  859. APIRET exehQueryImportedModules(PEXECUTABLE pExec,
  860.                                 PFSYSMODULE *ppaModules,    // out: modules array
  861.                                 PULONG pcModules)           // out: array item count
  862. {
  863.     if (    (pExec)
  864.          && (    (pExec->ulOS == EXEOS_OS2)
  865.               || (pExec->ulOS == EXEOS_WIN16)
  866.               || (pExec->ulOS == EXEOS_WIN386)
  867.             )
  868.        )
  869.     {
  870.         ENSURE_BEGIN;
  871.         ULONG       cModules = 0;
  872.         PFSYSMODULE paModules = NULL;
  873.         int i;
  874.         HFILE hfExe = pExec->pFile->hf;
  875.  
  876.         ULONG ulNewHeaderOfs = 0;       // V0.9.12 (2001-05-03) [umoeller]
  877.  
  878.         if (pExec->pDosExeHeader)
  879.             // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
  880.             ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
  881.  
  882.         if (pExec->ulExeFormat == EXEFORMAT_LX)
  883.         {
  884.             // 32-bit OS/2 executable:
  885.             cModules = pExec->pLXHeader->ulImportModTblCnt;
  886.  
  887.             if (cModules)
  888.             {
  889.                 ULONG   cb = sizeof(FSYSMODULE) * cModules; // V0.9.9 (2001-04-03) [umoeller]
  890.                 ULONG   ulDummy;
  891.  
  892.                 paModules = (PFSYSMODULE)malloc(cb);
  893.                 if (!paModules)
  894.                     ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY); // V0.9.9 (2001-04-03) [umoeller]
  895.  
  896.                 memset(paModules, 0, cb);   // V0.9.9 (2001-04-03) [umoeller]
  897.  
  898.                 ENSURE_SAFE(DosSetFilePtr(hfExe,
  899.                                           pExec->pLXHeader->ulImportModTblOfs
  900.                                             + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
  901.                                           FILE_BEGIN,
  902.                                           &ulDummy));
  903.  
  904.                 for (i = 0; i < cModules; i++)
  905.                 {
  906.                     BYTE bLen = 0;
  907.  
  908.                     // reading the length of the module name
  909.                     ENSURE_SAFE(DosRead(hfExe, &bLen, 1, &ulDummy));
  910.  
  911.                     // reading the module name
  912.                     ENSURE_SAFE(DosRead(hfExe,
  913.                                         paModules[i].achModuleName,
  914.                                         bLen,
  915.                                         &ulDummy));
  916.  
  917.                     // module names are not null terminated, so we must
  918.                     // do it now
  919.                     paModules[i].achModuleName[bLen] = 0;
  920.                 } // end for
  921.             }
  922.         } // end LX
  923.         else if (pExec->ulExeFormat == EXEFORMAT_NE)
  924.         {
  925.             // 16-bit executable:
  926.             cModules = pExec->pNEHeader->usModuleTblEntries;
  927.  
  928.             if (cModules)
  929.             {
  930.                 ULONG cb = sizeof(FSYSMODULE) * cModules;
  931.  
  932.                 paModules = (PFSYSMODULE)malloc(cb);
  933.                 if (!paModules)
  934.                     ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);  // V0.9.9 (2001-04-03) [umoeller]
  935.  
  936.                 memset(paModules, 0, cb);   // V0.9.9 (2001-04-03) [umoeller]
  937.  
  938.                 for (i = 0; i < cModules; i ++)
  939.                 {
  940.                     BYTE bLen;
  941.                     USHORT usOfs;
  942.                     ULONG ulDummy;
  943.  
  944.                     // the module reference table contains offsets
  945.                     // relative to the import table; we hence read
  946.                     // the offset in the module reference table, and
  947.                     // then we read the name in the import table
  948.  
  949.                     ENSURE_SAFE(DosSetFilePtr(hfExe,
  950.                                               pExec->pNEHeader->usModRefTblOfs
  951.                                                 + ulNewHeaderOfs // V0.9.12 (2001-05-03) [umoeller]
  952.                                                 + sizeof(usOfs) * i,
  953.                                               FILE_BEGIN,
  954.                                               &ulDummy));
  955.  
  956.                     ENSURE_SAFE(DosRead(hfExe, &usOfs, 2, &ulDummy));
  957.  
  958.                     ENSURE_SAFE(DosSetFilePtr(hfExe,
  959.                                               pExec->pNEHeader->usImportTblOfs
  960.                                                 + ulNewHeaderOfs // V0.9.12 (2001-05-03) [umoeller]
  961.                                                 + usOfs,
  962.                                               FILE_BEGIN,
  963.                                               &ulDummy));
  964.  
  965.                     ENSURE_SAFE(DosRead(hfExe, &bLen, 1, &ulDummy));
  966.  
  967.                     ENSURE_SAFE(DosRead(hfExe,
  968.                                         paModules[i].achModuleName,
  969.                                         bLen,
  970.                                         &ulDummy));
  971.  
  972.                     paModules[i].achModuleName[bLen] = 0;
  973.                 } // end for
  974.             }
  975.         } // end NE
  976.         else
  977.             ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
  978.  
  979.         // no error: output data
  980.         *ppaModules = paModules;
  981.         *pcModules = cModules;
  982.  
  983.         ENSURE_FINALLY;
  984.             // if we had an error above, clean up
  985.             free(paModules);
  986.         ENSURE_END;
  987.     }
  988.     else
  989.         ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
  990.  
  991.     ENSURE_OK;
  992. }
  993.  
  994. /*
  995.  *@@ exehFreeImportedModules:
  996.  *      frees resources allocated by exehQueryImportedModules.
  997.  *
  998.  *@@added V0.9.9 (2001-03-11)
  999.  */
  1000.  
  1001. APIRET exehFreeImportedModules(PFSYSMODULE paModules)
  1002. {
  1003.     free(paModules);
  1004.     return NO_ERROR;
  1005. }
  1006.  
  1007. /*
  1008.  *@@ ScanLXEntryTable:
  1009.  *      returns the number of exported entries in the entry table.
  1010.  *
  1011.  *      If paFunctions is not NULL, then successive entries are
  1012.  *      filled with the found type and ordinal values.
  1013.  *
  1014.  *@@added V0.9.9 (2001-03-30) [lafaix]
  1015.  *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
  1016.  *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
  1017.  *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
  1018.  */
  1019.  
  1020. static APIRET ScanLXEntryTable(PEXECUTABLE pExec,
  1021.                                PFSYSFUNCTION paFunctions,
  1022.                                PULONG pcEntries)        // out: entry table entry count; ptr can be NULL
  1023. {
  1024.     ULONG  ulDummy;
  1025.     USHORT usOrdinal = 1,
  1026.            usCurrent = 0;
  1027.     int    i;
  1028.  
  1029.     ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
  1030.     HFILE hfExe = pExec->pFile->hf;
  1031.  
  1032.     if (pExec->pDosExeHeader)
  1033.         // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
  1034.         ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
  1035.  
  1036.     ENSURE(DosSetFilePtr(hfExe,
  1037.                          pExec->pLXHeader->ulEntryTblOfs
  1038.                            + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
  1039.                          FILE_BEGIN,
  1040.                          &ulDummy));
  1041.  
  1042.     while (TRUE)
  1043.     {
  1044.         BYTE   bCnt,
  1045.                bType,
  1046.                bFlag;
  1047.  
  1048.         ENSURE(DosRead(hfExe, &bCnt, 1, &ulDummy));
  1049.  
  1050.         if (bCnt == 0)
  1051.             // end of the entry table
  1052.             break;
  1053.  
  1054.         ENSURE(DosRead(hfExe, &bType, 1, &ulDummy));
  1055.  
  1056.         switch (bType & 0x7F)
  1057.         {
  1058.             /*
  1059.              * unused entries
  1060.              *
  1061.              */
  1062.  
  1063.             case 0:
  1064.                 usOrdinal += bCnt;
  1065.             break;
  1066.  
  1067.             /*
  1068.              * 16-bit entries
  1069.              *
  1070.              * the bundle type is followed by the object number
  1071.              * and by bCnt bFlag+usOffset entries
  1072.              *
  1073.              */
  1074.  
  1075.             case 1:
  1076.                 ENSURE(DosSetFilePtr(hfExe,
  1077.                                      sizeof(USHORT),
  1078.                                      FILE_CURRENT,
  1079.                                      &ulDummy));
  1080.  
  1081.                 for (i = 0; i < bCnt; i ++)
  1082.                 {
  1083.                     ENSURE(DosRead(hfExe, &bFlag, 1, &ulDummy));
  1084.  
  1085.                     if (bFlag & 0x01)
  1086.                     {
  1087.                         if (paFunctions)
  1088.                         {
  1089.                             paFunctions[usCurrent].ulOrdinal = usOrdinal;
  1090.                             paFunctions[usCurrent].ulType = 1;
  1091.                             paFunctions[usCurrent].achFunctionName[0] = 0;
  1092.                         }
  1093.                         usCurrent++;
  1094.                     }
  1095.  
  1096.                     usOrdinal++;
  1097.  
  1098.                     ENSURE(DosSetFilePtr(hfExe,
  1099.                                          sizeof(USHORT),
  1100.                                          FILE_CURRENT,
  1101.                                          &ulDummy));
  1102.  
  1103.                 } // end for
  1104.             break;
  1105.  
  1106.             /*
  1107.              * 286 call gate entries
  1108.              *
  1109.              * the bundle type is followed by the object number
  1110.              * and by bCnt bFlag+usOffset+usCallGate entries
  1111.              *
  1112.              */
  1113.  
  1114.             case 2:
  1115.                 ENSURE(DosSetFilePtr(hfExe,
  1116.                                      sizeof(USHORT),
  1117.                                      FILE_CURRENT,
  1118.                                      &ulDummy));
  1119.  
  1120.                 for (i = 0; i < bCnt; i ++)
  1121.                 {
  1122.                     ENSURE(DosRead(hfExe, &bFlag, 1, &ulDummy));
  1123.  
  1124.                     if (bFlag & 0x01)
  1125.                     {
  1126.                         if (paFunctions)
  1127.                         {
  1128.                             paFunctions[usCurrent].ulOrdinal = usOrdinal;
  1129.                             paFunctions[usCurrent].ulType = 2;
  1130.                             paFunctions[usCurrent].achFunctionName[0] = 0;
  1131.                         }
  1132.                         usCurrent++;
  1133.                     }
  1134.  
  1135.                     usOrdinal++;
  1136.  
  1137.                     ENSURE(DosSetFilePtr(hfExe,
  1138.                                          sizeof(USHORT) + sizeof(USHORT),
  1139.                                          FILE_CURRENT,
  1140.                                          &ulDummy));
  1141.  
  1142.                 } // end for
  1143.             break;
  1144.  
  1145.             /*
  1146.              * 32-bit entries
  1147.              *
  1148.              * the bundle type is followed by the object number
  1149.              * and by bCnt bFlag+ulOffset entries
  1150.              *
  1151.              */
  1152.  
  1153.             case 3:
  1154.                 ENSURE(DosSetFilePtr(hfExe,
  1155.                                      sizeof(USHORT),
  1156.                                      FILE_CURRENT,
  1157.                                      &ulDummy));
  1158.  
  1159.                 for (i = 0; i < bCnt; i ++)
  1160.                 {
  1161.                     ENSURE(DosRead(hfExe, &bFlag, 1, &ulDummy));
  1162.  
  1163.                     if (bFlag & 0x01)
  1164.                     {
  1165.                         if (paFunctions)
  1166.                         {
  1167.                             paFunctions[usCurrent].ulOrdinal = usOrdinal;
  1168.                             paFunctions[usCurrent].ulType = 3;
  1169.                             paFunctions[usCurrent].achFunctionName[0] = 0;
  1170.                         }
  1171.                         usCurrent++;
  1172.                     }
  1173.  
  1174.                     usOrdinal++;
  1175.  
  1176.                     ENSURE(DosSetFilePtr(hfExe,
  1177.                                          sizeof(ULONG),
  1178.                                          FILE_CURRENT,
  1179.                                          &ulDummy));
  1180.                 } // end for
  1181.             break;
  1182.  
  1183.             /*
  1184.              * forwarder entries
  1185.              *
  1186.              * the bundle type is followed by a reserved word
  1187.              * and by bCnt bFlag+usModOrd+ulOffsOrdNum entries
  1188.              *
  1189.              */
  1190.  
  1191.             case 4:
  1192.                 ENSURE(DosSetFilePtr(hfExe,
  1193.                                      sizeof(USHORT),
  1194.                                      FILE_CURRENT,
  1195.                                      &ulDummy));
  1196.  
  1197.                 for (i = 0; i < bCnt; i ++)
  1198.                 {
  1199.                     ENSURE(DosSetFilePtr(hfExe,
  1200.                                          sizeof(BYTE) + sizeof(USHORT) + sizeof(ULONG),
  1201.                                          FILE_CURRENT,
  1202.                                          &ulDummy));
  1203.  
  1204.                     if (paFunctions)
  1205.                     {
  1206.                         paFunctions[usCurrent].ulOrdinal = usOrdinal;
  1207.                         paFunctions[usCurrent].ulType = 4;
  1208.                         paFunctions[usCurrent].achFunctionName[0] = 0;
  1209.                     }
  1210.                     usCurrent++;
  1211.  
  1212.                     usOrdinal++;
  1213.                 } // end for
  1214.             break;
  1215.  
  1216.             /*
  1217.              * unknown bundle type
  1218.              *
  1219.              * we don't know how to handle this bundle, so we must
  1220.              * stop parsing the entry table here (as we don't know the
  1221.              * bundle size); if paFunctions is not null, we fill it with
  1222.              * informative data
  1223.              */
  1224.  
  1225.             default:
  1226.                 if (paFunctions)
  1227.                 {
  1228.                     paFunctions[usCurrent].ulOrdinal = usOrdinal;
  1229.                     paFunctions[usCurrent].ulType = bType;
  1230.                     sprintf(paFunctions[usCurrent].achFunctionName,
  1231.                             "Unknown bundle type encountered (%d).  Aborting entry table scan.",
  1232.                             bType);
  1233.  
  1234.                     usCurrent++;
  1235.                 }
  1236.                 ENSURE_FAIL(ERROR_INVALID_LIST_FORMAT);
  1237.                     // whatever
  1238.                     // V0.9.9 (2001-04-03) [umoeller]
  1239.         } // end switch (bType & 0x7F)
  1240.     } // end while (TRUE)
  1241.  
  1242.     if (pcEntries)
  1243.        *pcEntries = usCurrent;
  1244.  
  1245.     ENSURE_OK;
  1246. }
  1247.  
  1248. /*
  1249.  *@@ ScanNEEntryTable:
  1250.  *      returns the number of exported entries in the entry table.
  1251.  *
  1252.  *      if paFunctions is not NULL, then successive entries are
  1253.  *      filled with the found type and ordinal values.
  1254.  *
  1255.  *@@added V0.9.9 (2001-03-30) [lafaix]
  1256.  *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
  1257.  *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
  1258.  *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
  1259.  */
  1260.  
  1261. static APIRET ScanNEEntryTable(PEXECUTABLE pExec,
  1262.                                PFSYSFUNCTION paFunctions,
  1263.                                PULONG pcEntries)        // out: entry table entry count; ptr can be NULL
  1264. {
  1265.     ULONG  ulDummy;
  1266.     USHORT usOrdinal = 1,
  1267.            usCurrent = 0;
  1268.     int    i;
  1269.  
  1270.     ULONG ulNewHeaderOfs = 0;
  1271.     HFILE hfExe = pExec->pFile->hf;
  1272.  
  1273.     if (pExec->pDosExeHeader)
  1274.         // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
  1275.         ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
  1276.  
  1277.     ENSURE(DosSetFilePtr(hfExe,
  1278.                          pExec->pNEHeader->usEntryTblOfs
  1279.                            + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
  1280.                          FILE_BEGIN,
  1281.                          &ulDummy));
  1282.  
  1283.     while (TRUE)
  1284.     {
  1285.         BYTE bCnt,
  1286.              bType,
  1287.              bFlag;
  1288.  
  1289.         ENSURE(DosRead(hfExe, &bCnt, 1, &ulDummy));
  1290.  
  1291.         if (bCnt == 0)
  1292.             // end of the entry table
  1293.             break;
  1294.  
  1295.         ENSURE(DosRead(hfExe, &bType, 1, &ulDummy));
  1296.  
  1297.         if (bType)
  1298.         {
  1299.             for (i = 0; i < bCnt; i++)
  1300.             {
  1301.                 ENSURE(DosRead(hfExe,
  1302.                                &bFlag,
  1303.                                1,
  1304.                                &ulDummy));
  1305.  
  1306.                 if (bFlag & 0x01)
  1307.                 {
  1308.                     if (paFunctions)
  1309.                     {
  1310.                         paFunctions[usCurrent].ulOrdinal = usOrdinal;
  1311.                         paFunctions[usCurrent].ulType = 1; // 16-bit entry
  1312.                         paFunctions[usCurrent].achFunctionName[0] = 0;
  1313.                     }
  1314.                     usCurrent++;
  1315.                 }
  1316.  
  1317.                 usOrdinal++;
  1318.  
  1319.                 if (bType == 0xFF)
  1320.                 {
  1321.                     // moveable segment
  1322.                     ENSURE(DosSetFilePtr(hfExe,
  1323.                                          5,
  1324.                                          FILE_CURRENT,
  1325.                                          &ulDummy));
  1326.                 }
  1327.                 else
  1328.                 {
  1329.                     // fixed segment or constant (0xFE)
  1330.                     ENSURE(DosSetFilePtr(hfExe,
  1331.                                          2,
  1332.                                          FILE_CURRENT,
  1333.                                          &ulDummy));
  1334.                 }
  1335.  
  1336.             } // end for
  1337.         }
  1338.         else
  1339.             usOrdinal += bCnt;
  1340.     } // end while (TRUE)
  1341.  
  1342.     if (pcEntries)
  1343.         *pcEntries = usCurrent;
  1344.  
  1345.     ENSURE_OK;
  1346. }
  1347.  
  1348. /*
  1349.  *@@ Compare:
  1350.  *      binary search helper
  1351.  *
  1352.  *@@added V0.9.9 (2001-04-01) [lafaix]
  1353.  *@@changed V0.9.9 (2001-04-07) [umoeller]: added _Optlink, or this won't compile as C++
  1354.  */
  1355.  
  1356. static int _Optlink Compare(const void *key,
  1357.                             const void *element)
  1358. {
  1359.     USHORT        usOrdinal = *((PUSHORT) key);
  1360.     PFSYSFUNCTION pFunction = (PFSYSFUNCTION)element;
  1361.  
  1362.     if (usOrdinal > pFunction->ulOrdinal)
  1363.         return (1);
  1364.     else if (usOrdinal < pFunction->ulOrdinal)
  1365.         return (-1);
  1366.     else
  1367.         return (0);
  1368. }
  1369.  
  1370. /*
  1371.  *@@ ScanNameTable:
  1372.  *      scans a resident or non-resident name table, and fills the
  1373.  *      appropriate paFunctions entries when it encounters exported
  1374.  *      entries names.
  1375.  *
  1376.  *      This functions works for both NE and LX executables.
  1377.  *
  1378.  *@@added V0.9.9 (2001-03-30) [lafaix]
  1379.  *@@changed V0.9.9 (2001-04-02) [lafaix]: the first entry is special
  1380.  *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
  1381.  *@@changed V0.9.9 (2001-04-05) [lafaix]: removed the 127 char limit
  1382.  *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
  1383.  */
  1384.  
  1385. static APIRET ScanNameTable(PEXECUTABLE pExec,
  1386.                             ULONG cFunctions,
  1387.                             PFSYSFUNCTION paFunctions)
  1388. {
  1389.     ULONG   ulDummy;
  1390.  
  1391.     USHORT        usOrdinal;
  1392.     PFSYSFUNCTION pFunction;
  1393.     HFILE hfExe = pExec->pFile->hf;
  1394.  
  1395.     while (TRUE)
  1396.     {
  1397.         BYTE   bLen;
  1398.         CHAR   achName[256];
  1399.         // int    i;
  1400.  
  1401.         ENSURE(DosRead(hfExe, &bLen, 1, &ulDummy));
  1402.  
  1403.         if (bLen == 0)
  1404.             // end of the name table
  1405.             break;
  1406.  
  1407.         ENSURE(DosRead(hfExe, &achName, bLen, &ulDummy));
  1408.         achName[bLen] = 0;
  1409.  
  1410.         ENSURE(DosRead(hfExe, &usOrdinal, sizeof(USHORT), &ulDummy));
  1411.  
  1412.         if ((pFunction = (PFSYSFUNCTION)bsearch(&usOrdinal,
  1413.                                                 paFunctions,
  1414.                                                 cFunctions,
  1415.                                                 sizeof(FSYSFUNCTION),
  1416.                                                 Compare)))
  1417.         {
  1418.             memcpy(pFunction->achFunctionName,
  1419.                    achName,
  1420.                    bLen+1);
  1421.         }
  1422.     }
  1423.  
  1424.     ENSURE_OK;
  1425. }
  1426.  
  1427. /*
  1428.  *@@ exehQueryExportedFunctions:
  1429.  *      returns an array of FSYSFUNCTION structure describing all
  1430.  *      exported functions.
  1431.  *
  1432.  *      *pcFunctions receives the # of items in the array (not the
  1433.  *      array size!).  Use doshFreeExportedFunctions to clean up.
  1434.  *
  1435.  *      Note that the returned array only contains entry for exported
  1436.  *      functions.  Empty export entries are _not_ included.
  1437.  *
  1438.  *      This returns a standard OS/2 error code, which might be
  1439.  *      any of the codes returned by DosSetFilePtr and DosRead.
  1440.  *      In addition, this may return:
  1441.  *
  1442.  *      --  ERROR_NOT_ENOUGH_MEMORY
  1443.  *
  1444.  *      --  ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
  1445.  *          than LX or NE, which is not understood by this function.
  1446.  *
  1447.  *      --  If ERROR_INVALID_LIST_FORMAT is returned, the format of an
  1448.  *          export entry wasn't understood here.
  1449.  *
  1450.  *      Even if NO_ERROR is returned, the array pointer might still
  1451.  *      be NULL if the module contains no such data.
  1452.  *
  1453.  *@@added V0.9.9 (2001-03-11) [lafaix]
  1454.  *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
  1455.  *@@changed V0.9.9 (2001-04-05) [lafaix]: rewritten error checking code
  1456.  *@@changed V0.9.10 (2001-04-10) [lafaix]: added Win16 and Win386 support
  1457.  *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
  1458.  */
  1459.  
  1460. APIRET exehQueryExportedFunctions(PEXECUTABLE pExec,
  1461.                                   PFSYSFUNCTION *ppaFunctions,  // out: functions array
  1462.                                   PULONG pcFunctions)           // out: array item count
  1463. {
  1464.     if (    (pExec)
  1465.          && (    (pExec->ulOS == EXEOS_OS2)
  1466.               || (pExec->ulOS == EXEOS_WIN16)
  1467.               || (pExec->ulOS == EXEOS_WIN386)
  1468.             )
  1469.        )
  1470.     {
  1471.         ENSURE_BEGIN;
  1472.         ULONG         cFunctions = 0;
  1473.         PFSYSFUNCTION paFunctions = NULL;
  1474.  
  1475.         ULONG ulDummy;
  1476.  
  1477.         HFILE hfExe = pExec->pFile->hf;
  1478.         ULONG ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
  1479.  
  1480.         if (pExec->pDosExeHeader)
  1481.             // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
  1482.             ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
  1483.  
  1484.         if (pExec->ulExeFormat == EXEFORMAT_LX)
  1485.         {
  1486.             // It's a 32bit OS/2 executable
  1487.  
  1488.             // the number of exported entry points is not stored
  1489.             // in the executable header; we have to count them in
  1490.             // the entry table
  1491.  
  1492.             ENSURE(ScanLXEntryTable(pExec, NULL, &cFunctions));
  1493.  
  1494.             // we now have the number of exported entries; let us
  1495.             // build them
  1496.  
  1497.             if (cFunctions)
  1498.             {
  1499.                 ULONG cb = sizeof(FSYSFUNCTION) * cFunctions;
  1500.  
  1501.                 paFunctions = (PFSYSFUNCTION)malloc(cb);
  1502.                 if (!paFunctions)
  1503.                     ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
  1504.  
  1505.                 // we rescan the entry table (the cost is not as bad
  1506.                 // as it may seem, due to disk caching)
  1507.  
  1508.                 ENSURE_SAFE(ScanLXEntryTable(pExec, paFunctions, NULL));
  1509.  
  1510.                 // we now scan the resident name table entries
  1511.  
  1512.                 ENSURE_SAFE(DosSetFilePtr(hfExe,
  1513.                                           pExec->pLXHeader->ulResdNameTblOfs
  1514.                                             + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
  1515.                                           FILE_BEGIN,
  1516.                                           &ulDummy));
  1517.  
  1518.                 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
  1519.  
  1520.                 // we now scan the non-resident name table entries,
  1521.                 // whose offset is _from the begining of the file_
  1522.  
  1523.                 ENSURE_SAFE(DosSetFilePtr(hfExe,
  1524.                                           pExec->pLXHeader->ulNonResdNameTblOfs,
  1525.                                           FILE_BEGIN,
  1526.                                           &ulDummy));
  1527.  
  1528.                 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
  1529.             } // end if (cFunctions)
  1530.         }
  1531.         else if (pExec->ulExeFormat == EXEFORMAT_NE)
  1532.         {
  1533.             // it's a "new" segmented 16bit executable
  1534.  
  1535.             // here too the number of exported entry points
  1536.             // is not stored in the executable header; we
  1537.             // have to count them in the entry table
  1538.  
  1539.             ENSURE(ScanNEEntryTable(pExec, NULL, &cFunctions));
  1540.  
  1541.             // we now have the number of exported entries; let us
  1542.             // build them
  1543.  
  1544.             if (cFunctions)
  1545.             {
  1546.                 // USHORT usOrdinal = 1;
  1547.                        // usCurrent = 0;
  1548.  
  1549.                 paFunctions = (PFSYSFUNCTION)malloc(sizeof(FSYSFUNCTION) * cFunctions);
  1550.                 if (!paFunctions)
  1551.                     ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
  1552.  
  1553.                 // we rescan the entry table (the cost is not as bad
  1554.                 // as it may seem, due to disk caching)
  1555.  
  1556.                 ENSURE_SAFE(ScanNEEntryTable(pExec, paFunctions, NULL));
  1557.  
  1558.                 // we now scan the resident name table entries
  1559.  
  1560.                 ENSURE_SAFE(DosSetFilePtr(hfExe,
  1561.                                           pExec->pNEHeader->usResdNameTblOfs
  1562.                                             + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
  1563.                                           FILE_BEGIN,
  1564.                                           &ulDummy));
  1565.  
  1566.                 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
  1567.  
  1568.                 // we now scan the non-resident name table entries,
  1569.                 // whose offset is _from the begining of the file_
  1570.  
  1571.                 ENSURE_SAFE(DosSetFilePtr(hfExe,
  1572.                                           pExec->pNEHeader->ulNonResdTblOfs,
  1573.                                           FILE_BEGIN,
  1574.                                           &ulDummy));
  1575.  
  1576.                 ENSURE_SAFE(ScanNameTable(pExec, cFunctions, paFunctions));
  1577.             }
  1578.         }
  1579.         else
  1580.             ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
  1581.  
  1582.         // no error: output data
  1583.         *ppaFunctions = paFunctions;
  1584.         *pcFunctions = cFunctions;
  1585.  
  1586.         ENSURE_FINALLY;
  1587.             // if we had an error above, clean up
  1588.             free(paFunctions);
  1589.         ENSURE_END;
  1590.     }
  1591.     else
  1592.         ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
  1593.  
  1594.     ENSURE_OK;
  1595. }
  1596.  
  1597. /*
  1598.  *@@ exehFreeExportedFunctions:
  1599.  *      frees resources allocated by exehQueryExportedFunctions.
  1600.  *
  1601.  *@@added V0.9.9 (2001-03-11)
  1602.  */
  1603.  
  1604. APIRET exehFreeExportedFunctions(PFSYSFUNCTION paFunctions)
  1605. {
  1606.     free(paFunctions);
  1607.  
  1608.     return NO_ERROR;
  1609. }
  1610.  
  1611. /*
  1612.  *@@ exehQueryResources:
  1613.  *      returns an array of FSYSRESOURCE structures describing all
  1614.  *      available resources in the module.
  1615.  *
  1616.  *      *pcResources receives the no. of items in the array
  1617.  *      (not the array size!). Use exehFreeResources to clean up.
  1618.  *
  1619.  *      This returns a standard OS/2 error code, which might be
  1620.  *      any of the codes returned by DosSetFilePtr and DosRead.
  1621.  *      In addition, this may return:
  1622.  *
  1623.  *      --  ERROR_NOT_ENOUGH_MEMORY
  1624.  *
  1625.  *      --  ERROR_INVALID_EXE_SIGNATURE: exe is in a format other
  1626.  *          than LX or NE, which is not understood by this function.
  1627.  *
  1628.  *      Even if NO_ERROR is returned, the array pointer might still
  1629.  *      be NULL if the module contains no such data.
  1630.  *
  1631.  *@@added V0.9.7 (2000-12-18) [lafaix]
  1632.  *@@changed V0.9.9 (2001-04-03) [umoeller]: added tons of error checking, changed prototype to return APIRET
  1633.  *@@changed V0.9.10 (2001-04-10) [lafaix]: added Win16 and Win386 support
  1634.  *@@changed V0.9.12 (2001-05-03) [umoeller]: adjusted for new NOSTUB support
  1635.  */
  1636.  
  1637. APIRET exehQueryResources(PEXECUTABLE pExec,     // in: executable from exehOpen
  1638.                           PFSYSRESOURCE *ppaResources,   // out: res's array
  1639.                           PULONG pcResources)    // out: array item count
  1640. {
  1641.     if (    (pExec)
  1642.          && (    (pExec->ulOS == EXEOS_OS2)
  1643.               || (pExec->ulOS == EXEOS_WIN16)
  1644.               || (pExec->ulOS == EXEOS_WIN386)
  1645.             )
  1646.        )
  1647.     {
  1648.         ENSURE_BEGIN;
  1649.         ULONG           cResources = 0;
  1650.         PFSYSRESOURCE   paResources = NULL;
  1651.  
  1652.         HFILE hfExe = pExec->pFile->hf;
  1653.         ULONG           ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
  1654.  
  1655.         if (pExec->pDosExeHeader)
  1656.             // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
  1657.             ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
  1658.  
  1659.         if (pExec->ulExeFormat == EXEFORMAT_LX)
  1660.         {
  1661.             // 32-bit OS/2 executable:
  1662.             PLXHEADER pLXHeader = pExec->pLXHeader;
  1663.             if (cResources = pLXHeader->ulResTblCnt)
  1664.             {
  1665.                 #pragma pack(1)     // V0.9.9 (2001-04-02) [umoeller]
  1666.                 struct rsrc32               // Resource Table Entry
  1667.                 {
  1668.                     unsigned short  type;   // Resource type
  1669.                     unsigned short  name;   // Resource name
  1670.                     unsigned long   cb;     // Resource size
  1671.                     unsigned short  obj;    // Object number
  1672.                     unsigned long   offset; // Offset within object
  1673.                 } rs;
  1674.  
  1675.                 struct o32_obj                    // Flat .EXE object table entry
  1676.                 {
  1677.                     unsigned long   o32_size;     // Object virtual size
  1678.                     unsigned long   o32_base;     // Object base virtual address
  1679.                     unsigned long   o32_flags;    // Attribute flags
  1680.                     unsigned long   o32_pagemap;  // Object page map index
  1681.                     unsigned long   o32_mapsize;  // Number of entries in object page map
  1682.                     unsigned long   o32_reserved; // Reserved
  1683.                 } ot;
  1684.                 #pragma pack() // V0.9.9 (2001-04-03) [umoeller]
  1685.  
  1686.                 ULONG cb = sizeof(FSYSRESOURCE) * cResources;
  1687.                 ULONG ulDummy;
  1688.                 int i;
  1689.                 ULONG ulCurOfs;
  1690.  
  1691.                 paResources = (PFSYSRESOURCE)malloc(cb);
  1692.                 if (!paResources)
  1693.                     ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
  1694.  
  1695.                 memset(paResources, 0, cb); // V0.9.9 (2001-04-03) [umoeller]
  1696.  
  1697.                 ENSURE_SAFE(DosSetFilePtr(hfExe,
  1698.                                           pLXHeader->ulResTblOfs
  1699.                                             + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
  1700.                                           FILE_BEGIN,
  1701.                                           &ulDummy));
  1702.  
  1703.                 for (i = 0; i < cResources; i++)
  1704.                 {
  1705.                     ENSURE_SAFE(DosRead(hfExe, &rs, 14, &ulDummy));
  1706.  
  1707.                     paResources[i].ulID = rs.name;
  1708.                     paResources[i].ulType = rs.type;
  1709.                     paResources[i].ulSize = rs.cb;
  1710.                     paResources[i].ulFlag = rs.obj; // Temp storage for Object
  1711.                                                     // number.  Will be filled
  1712.                                                     // with resource flag
  1713.                                                     // later.
  1714.                 }
  1715.  
  1716.                 for (i = 0; i < cResources; i++)
  1717.                 {
  1718.                     ULONG ulOfsThis =   pLXHeader->ulObjTblOfs
  1719.                                       + ulNewHeaderOfs // V0.9.12 (2001-05-03) [umoeller]
  1720.                                       + (   sizeof(ot)
  1721.                                           * (paResources[i].ulFlag - 1));
  1722.  
  1723.                     ENSURE_SAFE(DosSetFilePtr(hfExe,
  1724.                                               ulOfsThis,
  1725.                                               FILE_BEGIN,
  1726.                                               &ulDummy));
  1727.  
  1728.                     ENSURE_SAFE(DosRead(hfExe, &ot, sizeof(ot), &ulDummy));
  1729.  
  1730.                     paResources[i].ulFlag  = ((ot.o32_flags & OBJWRITE)
  1731.                                                     ? 0
  1732.                                                     : RNPURE);
  1733.                     paResources[i].ulFlag |= ((ot.o32_flags & OBJDISCARD)
  1734.                                                     ? 4096
  1735.                                                     : 0);
  1736.                     paResources[i].ulFlag |= ((ot.o32_flags & OBJSHARED)
  1737.                                                     ? RNMOVE
  1738.                                                     : 0);
  1739.                     paResources[i].ulFlag |= ((ot.o32_flags & OBJPRELOAD)
  1740.                                                     ? RNPRELOAD
  1741.                                                     : 0);
  1742.                 } // end for
  1743.             } // end if (cResources)
  1744.         } // end if (pExec->ulExeFormat == EXEFORMAT_LX)
  1745.         else if (pExec->ulExeFormat == EXEFORMAT_NE)
  1746.         {
  1747.             PNEHEADER pNEHeader = pExec->pNEHeader;
  1748.  
  1749.             if (pExec->ulOS == EXEOS_OS2)
  1750.             {
  1751.                 // 16-bit OS/2 executable:
  1752.                 cResources = pNEHeader->usResSegmCount;
  1753.  
  1754.                 if (cResources)
  1755.                 {
  1756.                     #pragma pack(1)     // V0.9.9 (2001-04-02) [umoeller]
  1757.                     struct {unsigned short type; unsigned short name;} rti;
  1758.                     struct new_seg                          // New .EXE segment table entry
  1759.                     {
  1760.                         unsigned short      ns_sector;      // File sector of start of segment
  1761.                         unsigned short      ns_cbseg;       // Number of bytes in file
  1762.                         unsigned short      ns_flags;       // Attribute flags
  1763.                         unsigned short      ns_minalloc;    // Minimum allocation in bytes
  1764.                     } ns;
  1765.                     #pragma pack()
  1766.  
  1767.                     ULONG cb = sizeof(FSYSRESOURCE) * cResources;
  1768.                     ULONG ulDummy;
  1769.                     int i;
  1770.  
  1771.                     paResources = (PFSYSRESOURCE)malloc(cb);
  1772.                     if (!paResources)
  1773.                         ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
  1774.  
  1775.                     memset(paResources, 0, cb);     // V0.9.9 (2001-04-03) [umoeller]
  1776.  
  1777.                     // we first read the resources IDs and types
  1778.  
  1779.                     ENSURE_SAFE(DosSetFilePtr(hfExe,
  1780.                                               pNEHeader->usResTblOfs
  1781.                                                 + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
  1782.                                               FILE_BEGIN,
  1783.                                               &ulDummy));
  1784.  
  1785.                     for (i = 0; i < cResources; i++)
  1786.                     {
  1787.                         ENSURE_SAFE(DosRead(hfExe, &rti, sizeof(rti), &ulDummy));
  1788.  
  1789.                         paResources[i].ulID = rti.name;
  1790.                         paResources[i].ulType = rti.type;
  1791.                     }
  1792.  
  1793.                     // we then read their sizes and flags
  1794.  
  1795.                     for (i = 0; i < cResources; i++)
  1796.                     {
  1797.                         ENSURE_SAFE(DosSetFilePtr(hfExe,
  1798.                                                   ulNewHeaderOfs // V0.9.12 (2001-05-03) [umoeller]
  1799.                                                     + pNEHeader->usSegTblOfs
  1800.                                                     + (sizeof(ns)
  1801.                                                     * (  pNEHeader->usSegTblEntries
  1802.                                                        - pNEHeader->usResSegmCount
  1803.                                                        + i)),
  1804.                                                     FILE_BEGIN,
  1805.                                                     &ulDummy));
  1806.  
  1807.                         ENSURE_SAFE(DosRead(hfExe, &ns, sizeof(ns), &ulDummy));
  1808.  
  1809.                         paResources[i].ulSize = ns.ns_cbseg;
  1810.  
  1811.                         paResources[i].ulFlag  = (ns.ns_flags & OBJPRELOAD) ? RNPRELOAD : 0;
  1812.                         paResources[i].ulFlag |= (ns.ns_flags & OBJSHARED) ? RNPURE : 0;
  1813.                         paResources[i].ulFlag |= (ns.ns_flags & OBJDISCARD) ? RNMOVE : 0;
  1814.                         paResources[i].ulFlag |= (ns.ns_flags & OBJDISCARD) ? 4096 : 0;
  1815.                     }
  1816.                 } // end if (cResources)
  1817.             }
  1818.             else
  1819.             {
  1820.                 // 16-bit Windows executable
  1821.                 USHORT usAlignShift;
  1822.                 ULONG  ulDummy;
  1823.  
  1824.                 ENSURE(DosSetFilePtr(hfExe,
  1825.                                      pNEHeader->usResTblOfs
  1826.                                        + ulNewHeaderOfs, // V0.9.12 (2001-05-03) [umoeller]
  1827.                                      FILE_BEGIN,
  1828.                                      &ulDummy));
  1829.  
  1830.                 ENSURE(DosRead(hfExe,
  1831.                                &usAlignShift,
  1832.                                sizeof(usAlignShift),
  1833.                                &ulDummy));
  1834.  
  1835.                 while (TRUE)
  1836.                 {
  1837.                     USHORT usTypeID;
  1838.                     USHORT usCount;
  1839.  
  1840.                     ENSURE(DosRead(hfExe,
  1841.                                    &usTypeID,
  1842.                                    sizeof(usTypeID),
  1843.                                    &ulDummy));
  1844.  
  1845.                     if (usTypeID == 0)
  1846.                         break;
  1847.  
  1848.                     ENSURE(DosRead(hfExe,
  1849.                                    &usCount,
  1850.                                    sizeof(usCount),
  1851.                                    &ulDummy));
  1852.  
  1853.                     ENSURE(DosSetFilePtr(hfExe,
  1854.                                          sizeof(ULONG),
  1855.                                          FILE_CURRENT,
  1856.                                          &ulDummy));
  1857.  
  1858.                     cResources += usCount;
  1859.  
  1860.                     // first pass, skip NAMEINFO table
  1861.                     ENSURE(DosSetFilePtr(hfExe,
  1862.                                          usCount*6*sizeof(USHORT),
  1863.                                          FILE_CURRENT,
  1864.                                          &ulDummy));
  1865.                 }
  1866.  
  1867.                 if (cResources)
  1868.                 {
  1869.                     USHORT usCurrent = 0;
  1870.                     ULONG cb = sizeof(FSYSRESOURCE) * cResources;
  1871.  
  1872.                     paResources = (PFSYSRESOURCE)malloc(cb);
  1873.                     if (!paResources)
  1874.                         ENSURE_FAIL(ERROR_NOT_ENOUGH_MEMORY);
  1875.  
  1876.                     memset(paResources, 0, cb);
  1877.  
  1878.                     ENSURE_SAFE(DosSetFilePtr(hfExe,
  1879.                                               pNEHeader->usResTblOfs
  1880.                                                 + ulNewHeaderOfs,
  1881.                                               FILE_BEGIN,
  1882.                                               &ulDummy));
  1883.  
  1884.                     ENSURE_SAFE(DosRead(hfExe,
  1885.                                         &usAlignShift,
  1886.                                         sizeof(usAlignShift),
  1887.                                         &ulDummy));
  1888.  
  1889.                     while (TRUE)
  1890.                     {
  1891.                         USHORT usTypeID;
  1892.                         USHORT usCount;
  1893.                         int i;
  1894.  
  1895.                         ENSURE_SAFE(DosRead(hfExe,
  1896.                                             &usTypeID,
  1897.                                             sizeof(usTypeID),
  1898.                                             &ulDummy));
  1899.  
  1900.                         if (usTypeID == 0)
  1901.                             break;
  1902.  
  1903.                         ENSURE_SAFE(DosRead(hfExe,
  1904.                                             &usCount,
  1905.                                             sizeof(usCount),
  1906.                                             &ulDummy));
  1907.  
  1908.                         ENSURE_SAFE(DosSetFilePtr(hfExe,
  1909.                                                   sizeof(ULONG),
  1910.                                                   FILE_CURRENT,
  1911.                                                   &ulDummy));
  1912.  
  1913.                         // second pass, read NAMEINFO table
  1914.                         for (i = 0; i < usCount; i++)
  1915.                         {
  1916.                             USHORT usLength,
  1917.                                    usFlags,
  1918.                                    usID;
  1919.  
  1920.                             ENSURE_SAFE(DosSetFilePtr(hfExe,
  1921.                                                       sizeof(USHORT),
  1922.                                                       FILE_CURRENT,
  1923.                                                       &ulDummy));
  1924.  
  1925.                             ENSURE_SAFE(DosRead(hfExe,
  1926.                                                 &usLength,
  1927.                                                 sizeof(USHORT),
  1928.                                                 &ulDummy));
  1929.                             ENSURE_SAFE(DosRead(hfExe,
  1930.                                                 &usFlags,
  1931.                                                 sizeof(USHORT),
  1932.                                                 &ulDummy));
  1933.                             ENSURE_SAFE(DosRead(hfExe,
  1934.                                                 &usID,
  1935.                                                 sizeof(USHORT),
  1936.                                                 &ulDummy));
  1937.  
  1938.                             ENSURE_SAFE(DosSetFilePtr(hfExe,
  1939.                                                       2*sizeof(USHORT),
  1940.                                                       FILE_CURRENT,
  1941.                                                       &ulDummy));
  1942.  
  1943.                             // !!! strings ids and types not handled yet
  1944.                             // !!! 15th bit is used to denotes strings
  1945.                             // !!! offsets [lafaix]
  1946.                             paResources[usCurrent].ulType = usTypeID ^ 0x8000;
  1947.                             paResources[usCurrent].ulID = usID ^ 0x8000;
  1948.                             paResources[usCurrent].ulSize = usLength << usAlignShift;
  1949.                             paResources[usCurrent].ulFlag = usFlags & 0x70;
  1950.  
  1951.                             usCurrent++;
  1952.                         }
  1953.                     }
  1954.                 }
  1955.             }
  1956.         } // end else if (pExec->ulExeFormat == EXEFORMAT_NE)
  1957.         else
  1958.             ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
  1959.  
  1960.         *ppaResources = paResources;
  1961.         *pcResources = cResources;
  1962.  
  1963.         ENSURE_FINALLY;
  1964.             // if we had an error above, clean up
  1965.             free(paResources);
  1966.         ENSURE_END;
  1967.     }
  1968.     else
  1969.         ENSURE_FAIL(ERROR_INVALID_EXE_SIGNATURE); // V0.9.9 (2001-04-03) [umoeller]
  1970.  
  1971.     ENSURE_OK;
  1972. }
  1973.  
  1974. /*
  1975.  *@@ exehFreeResources:
  1976.  *      frees resources allocated by exehQueryResources.
  1977.  *
  1978.  *@@added V0.9.7 (2000-12-18) [lafaix]
  1979.  */
  1980.  
  1981. APIRET exehFreeResources(PFSYSRESOURCE paResources)
  1982. {
  1983.     free(paResources);
  1984.     return NO_ERROR;
  1985. }
  1986.  
  1987. /*
  1988.  *@@ exehLoadLXMaps:
  1989.  *      loads the three main LX maps into the given
  1990.  *      EXECUTABLE structure.
  1991.  *
  1992.  *      This loads:
  1993.  *
  1994.  *      1)  the LX resource table;
  1995.  *
  1996.  *      2)  the LX object table;
  1997.  *
  1998.  *      3)  the LX object _page_ table (object map).
  1999.  *
  2000.  *      Note that this is not automatically called
  2001.  *      by exehOpen to save time, since the LX
  2002.  *      maps are not needed for all the other exe
  2003.  *      functions. However, this does get called
  2004.  *      from exehLoadLXResource if needed.
  2005.  *
  2006.  *      This returns:
  2007.  *
  2008.  *      --  NO_ERROR: all three LX maps were loaded,
  2009.  *          and pExec->fLXMapsLoaded was set to TRUE.
  2010.  *
  2011.  *      --  ERROR_INVALID_PARAMETER
  2012.  *
  2013.  *      --  ERROR_INVALID_EXE_SIGNATURE: pExec does
  2014.  *          not specify an LX executable.
  2015.  *
  2016.  *      --  ERROR_NO_DATA: at least one of the structs
  2017.  *          does not exist.
  2018.  *
  2019.  *      --  ERROR_NOT_ENOUGH_MEMORY
  2020.  *
  2021.  *      plus the error codes of doshReadAt.
  2022.  *
  2023.  *      Call exehFreeLXMaps to clean up explicitly, but
  2024.  *      that func automatically gets called by exehClose.
  2025.  *
  2026.  *@@added V0.9.16 (2001-12-08) [umoeller]
  2027.  */
  2028.  
  2029. APIRET exehLoadLXMaps(PEXECUTABLE pExec)
  2030. {
  2031.     APIRET arc;
  2032.  
  2033.     PLXHEADER pLXHeader;
  2034.  
  2035.     if (!pExec)
  2036.         arc = ERROR_INVALID_PARAMETER;
  2037.     else if (pExec->fLXMapsLoaded)
  2038.         // already loaded:
  2039.         arc = NO_ERROR;
  2040.     else if (    (pExec->ulExeFormat != EXEFORMAT_LX)
  2041.               || (!(pLXHeader = pExec->pLXHeader))
  2042.             )
  2043.         arc = ERROR_INVALID_EXE_SIGNATURE;
  2044.     else
  2045.     {
  2046.         PXFILE pFile = pExec->pFile;
  2047.         ULONG ulNewHeaderOfs = 0;
  2048.         ULONG cb;
  2049.  
  2050.         if (pExec->pDosExeHeader)
  2051.             // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
  2052.             ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
  2053.  
  2054.         // resource table
  2055.         if (    (!(arc = doshAllocArray(pLXHeader->ulResTblCnt,
  2056.                                         sizeof(RESOURCETABLEENTRY),
  2057.                                         (PBYTE*)&pExec->pRsTbl,
  2058.                                         &cb)))
  2059.              && (!(arc = doshReadAt(pFile,
  2060.                                     pLXHeader->ulResTblOfs
  2061.                                       + ulNewHeaderOfs,
  2062.                                     &cb,
  2063.                                     (PBYTE)pExec->pRsTbl,
  2064.                                     DRFL_FAILIFLESS)))
  2065.             )
  2066.         {
  2067.             // object table
  2068.             if (    (!(arc = doshAllocArray(pLXHeader->ulObjCount,
  2069.                                             sizeof(OBJECTTABLEENTRY),
  2070.                                             (PBYTE*)&pExec->pObjTbl,
  2071.                                             &cb)))
  2072.                  && (!(arc = doshReadAt(pFile,
  2073.                                         pLXHeader->ulObjTblOfs
  2074.                                           + ulNewHeaderOfs,
  2075.                                         &cb,
  2076.                                         (PBYTE)pExec->pObjTbl,
  2077.                                         DRFL_FAILIFLESS)))
  2078.                )
  2079.             {
  2080.                 // object page table
  2081.                 if (    (!(arc = doshAllocArray(pLXHeader->ulPageCount,
  2082.                                                 sizeof(OBJECTPAGETABLEENTRY),
  2083.                                                 (PBYTE*)&pExec->pObjPageTbl,
  2084.                                                 &cb)))
  2085.                      && (!(arc = doshReadAt(pFile,
  2086.                                             pLXHeader->ulObjPageTblOfs
  2087.                                               + ulNewHeaderOfs,
  2088.                                             &cb,
  2089.                                             (PBYTE)pExec->pObjPageTbl,
  2090.                                             DRFL_FAILIFLESS)))
  2091.                    )
  2092.                 {
  2093.                 }
  2094.             }
  2095.         }
  2096.  
  2097.         if (!arc)
  2098.             pExec->fLXMapsLoaded = TRUE;
  2099.         else
  2100.             exehFreeLXMaps(pExec);
  2101.     }
  2102.  
  2103.     return arc;
  2104. }
  2105.  
  2106. /*
  2107.  *@@ exehFreeLXMaps:
  2108.  *      frees data allocated by exehLoadLXMaps.
  2109.  *      Gets called automatically by exehClose.
  2110.  *
  2111.  *@@added V0.9.16 (2001-12-08) [umoeller]
  2112.  */
  2113.  
  2114. VOID exehFreeLXMaps(PEXECUTABLE pExec)
  2115. {
  2116.     FREE(pExec->pRsTbl);
  2117.     FREE(pExec->pObjTbl);
  2118.     FREE(pExec->pObjPageTbl);
  2119.     pExec->fLXMapsLoaded = FALSE;
  2120. }
  2121.  
  2122. // page flags (OBJECTPAGETABLEENTRY.o32_pageflags)
  2123. #define VALID           0x0000                // Valid Physical Page in .EXE
  2124. #define ITERDATA        0x0001                // Iterated Data Page
  2125. #define INVALID         0x0002                // Invalid Page
  2126. #define ZEROED          0x0003                // Zero Filled Page
  2127. #define RANGE           0x0004                // Range of pages
  2128. #define ITERDATA2       0x0005                // Iterated Data Page Type II
  2129.  
  2130. /*
  2131.  *@@ ExpandIterdata1:
  2132.  *      expands a page compressed with the old exepack
  2133.  *      method introduced with OS/2 2.0 (plain /EXEPACK).
  2134.  *
  2135.  *      Returns either ERROR_BAD_FORMAT or NO_ERROR.
  2136.  *
  2137.  *      (C) Knut Stange Osmundsen. Used with permission.
  2138.  *
  2139.  *@@added V0.9.16 (2001-12-08) [umoeller]
  2140.  */
  2141.  
  2142. static APIRET ExpandIterdata1(char *pabTarget,         // out: page data (pagesize as in lx spec)
  2143.                               int cbTarget,            // in: sizeof *pabTarget (pagesize as in lx spec)
  2144.                               const char *pabSource,   // in: compressed source data in EXEPACK:1 format
  2145.                               int cbSource)            // in: sizeof *pabSource
  2146. {
  2147.     PLXITER             pIter = (PLXITER)pabSource;
  2148.     // store the pointer for boundary checking
  2149.     char                *pabTargetOriginal = pabTarget;
  2150.  
  2151.     // validate size of data
  2152.     if (cbSource >= cbTarget - 2)
  2153.         return ERROR_BAD_FORMAT;
  2154.  
  2155.     // expand the page
  2156.     while (    (pIter->LX_nIter)
  2157.             && (cbSource > 0)
  2158.           )
  2159.     {
  2160.         // check if we're out of bound
  2161.         ULONG nIter = pIter->LX_nIter,
  2162.               nBytes = pIter->LX_nBytes;
  2163.  
  2164.         if (    (pabTarget - pabTargetOriginal + nIter * nBytes > cbTarget)
  2165.              || (cbSource <= 0)
  2166.            )
  2167.             return ERROR_BAD_FORMAT;
  2168.  
  2169.         if (nBytes == 1)
  2170.         {
  2171.             // one databyte
  2172.             memset(pabTarget, pIter->LX_Iterdata, nIter);
  2173.             pabTarget += nIter;
  2174.             cbSource -= 4 + 1;
  2175.             pIter++;
  2176.         }
  2177.         else
  2178.         {
  2179.             int i;
  2180.             for (i = nIter;
  2181.                  i > 0;
  2182.                  i--, pabTarget += nBytes)
  2183.                 memcpy(pabTarget, &pIter->LX_Iterdata, nBytes);
  2184.             cbSource -= 4 + nBytes;
  2185.             pIter   = (PLXITER)((char*)pIter + 4 + nBytes);
  2186.         }
  2187.     }
  2188.  
  2189.     // zero remaining part of the page
  2190.     if (pabTarget - pabTargetOriginal < cbTarget)
  2191.         memset(pabTarget, 0, cbTarget - (pabTarget - pabTargetOriginal));
  2192.  
  2193.     return NO_ERROR;
  2194. }
  2195.  
  2196. /*
  2197.  *@@ memcpyw:
  2198.  *      a special memcpy for expandPage2 which performs a
  2199.  *      word based copy. The difference between this, memmove
  2200.  *      and memcpy is that we'll allways read words.
  2201.  *
  2202.  *      (C) Knut Stange Osmundsen. Used with permission.
  2203.  *
  2204.  *@@added V0.9.16 (2001-12-08) [umoeller]
  2205.  */
  2206.  
  2207. static void memcpyw(char *pch1, const char *pch2, size_t cch)
  2208. {
  2209.     /*
  2210.      * Use memcpy if possible.
  2211.      */
  2212.     if ((pch2 > pch1 ? pch2 - pch1 : pch1 - pch2) >= 4)
  2213.     {
  2214.         memcpy(pch1, pch2, cch);        /* BUGBUG! ASSUMES that memcpy move NO more than 4 bytes at the time! */
  2215.         return;
  2216.     }
  2217.  
  2218.     /*
  2219.      * Difference is less than 3 bytes.
  2220.      */
  2221.     if (cch & 1)
  2222.         *pch1++ = *pch2++;
  2223.  
  2224.     for (cch >>= 1;
  2225.          cch > 0;
  2226.          cch--, pch1 += 2, pch2 += 2)
  2227.         *(PUSHORT)pch1 = *(PUSHORT)pch2;
  2228. }
  2229.  
  2230. /*
  2231.  *@@ memcpyb:
  2232.  *      a special memcpy for expandPage2 which performs a memmove
  2233.  *      operation. The difference between this and memmove is that
  2234.  *      this one works.
  2235.  *
  2236.  *      (C) Knut Stange Osmundsen. Used with permission.
  2237.  *
  2238.  *@@added V0.9.16 (2001-12-08) [umoeller]
  2239.  */
  2240.  
  2241. static void memcpyb(char *pch1, const char *pch2, size_t cch)
  2242. {
  2243.     /*
  2244.      * Use memcpy if possible.
  2245.      */
  2246.     if ((pch2 > pch1 ? pch2 - pch1 : pch1 - pch2) >= 4)
  2247.     {
  2248.         memcpy(pch1, pch2, cch);
  2249.         return;
  2250.     }
  2251.  
  2252.     /*
  2253.      * Difference is less than 3 bytes.
  2254.      */
  2255.     while(cch--)
  2256.         *pch1++ = *pch2++;
  2257. }
  2258.  
  2259. /*
  2260.  *@@ ExpandIterdata2:
  2261.  *      expands a page compressed with the new exepack
  2262.  *      method introduced with OS/2 Warp 3.0 (/EXEPACK:2).
  2263.  *
  2264.  *      Returns either ERROR_BAD_FORMAT or NO_ERROR.
  2265.  *
  2266.  *      (C) Knut Stange Osmundsen. Used with permission.
  2267.  *
  2268.  *      Note that we call special (slow) memcpy versions
  2269.  *      here because the standard memcpy will fail on
  2270.  *      certain bit combinations here for some unknown
  2271.  *      reason.
  2272.  *
  2273.  *@@added V0.9.16 (2001-12-08) [umoeller]
  2274.  */
  2275.  
  2276. static APIRET ExpandIterdata2(char *pachPage,              // out: page data (pagesize as in lx spec)
  2277.                               int cchPage,                 // in: sizeof *pachPage (pagesize as in lx spec)
  2278.                               const char *pachSrcPage,     // in: compressed source data in EXEPACK:1 format
  2279.                               int cchSrcPage)              // in: size of source buf
  2280. {
  2281.     char    *pachDestPage = pachPage; /* Store the pointer for boundrary checking. */
  2282.  
  2283.     while (cchSrcPage > 0)
  2284.     {
  2285.         /*
  2286.          * Bit 0 and 1 is the encoding type.
  2287.          */
  2288.  
  2289.         char cSrc = *pachSrcPage;
  2290.  
  2291.         switch (cSrc & 0x03)
  2292.         {
  2293.             /*
  2294.              *
  2295.              *  0  1  2  3  4  5  6  7
  2296.              *  type  |              |
  2297.              *        ----------------
  2298.              *             cch        <cch bytes of data>
  2299.              *
  2300.              * Bits 2-7 is, if not zero, the length of an uncompressed run
  2301.              *   starting at the following byte.
  2302.              *
  2303.              *  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
  2304.              *  type  |              |  |                    | |                     |
  2305.              *        ----------------  ---------------------- -----------------------
  2306.              *             zero                 cch                char to multiply
  2307.              *
  2308.              * If the bits are zero, the following two bytes describes a
  2309.              *   1 byte interation run. First byte is count, second is the byte to copy.
  2310.              *   A count of zero is means end of data, and we simply stops. In that case
  2311.              *   the rest of the data should be zero.
  2312.              */
  2313.  
  2314.             case 0:
  2315.             {
  2316.                 if (cSrc)
  2317.                 {
  2318.                     int cch = cSrc >> 2;
  2319.                     if (    (cchPage >= cch)
  2320.                          && (cchSrcPage >= cch + 1)
  2321.                        )
  2322.                     {
  2323.                         memcpy(pachPage, pachSrcPage + 1, cch);
  2324.                         pachPage += cch, cchPage -= cch;
  2325.                         pachSrcPage += cch + 1, cchSrcPage -= cch + 1;
  2326.                         break; // switch (cSrc & 0x03)
  2327.                     }
  2328.                     return ERROR_BAD_FORMAT;
  2329.                 }
  2330.  
  2331.                 if (cchSrcPage >= 2)
  2332.                 {
  2333.                     int cch;
  2334.                     if (cch = pachSrcPage[1])
  2335.                     {
  2336.                         if (    (cchSrcPage >= 3)
  2337.                              && (cchPage >= cch)
  2338.                            )
  2339.                         {
  2340.                             memset(pachPage, pachSrcPage[2], cch);
  2341.                             pachPage += cch, cchPage -= cch;
  2342.                             pachSrcPage += 3, cchSrcPage -= 3;
  2343.                             break; // switch (cSrc & 0x03)
  2344.                         }
  2345.                     }
  2346.                     else
  2347.                         goto endloop;
  2348.                 }
  2349.  
  2350.                 return ERROR_BAD_FORMAT;
  2351.             }
  2352.  
  2353.  
  2354.             /*
  2355.              *  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
  2356.              *  type  |  |  |     |  |                       |
  2357.              *        ----  -------  -------------------------
  2358.              *        cch1  cch2 - 3          offset            <cch1 bytes of data>
  2359.              *
  2360.              *  Two bytes layed out as described above, followed by cch1 bytes of data to be copied.
  2361.              *  The cch2(+3) and offset describes an amount of data to be copied from the expanded
  2362.              *    data relative to the current position. The data copied as you would expect it to be.
  2363.              */
  2364.  
  2365.             case 1:
  2366.             {
  2367.                 if (cchSrcPage >= 2)
  2368.                 {
  2369.                     int off = *(PUSHORT)pachSrcPage >> 7;
  2370.                     int cch1 = cSrc >> 2 & 3;
  2371.                     int cch2 = (cSrc >> 4 & 7) + 3;
  2372.                     pachSrcPage += 2, cchSrcPage -= 2;
  2373.                     if (    (cchSrcPage >= cch1)
  2374.                          && (cchPage >= cch1 + cch2)
  2375.                          && (pachPage + cch1 - off >= pachDestPage)
  2376.                        )
  2377.                     {
  2378.                         memcpy(pachPage, pachSrcPage, cch1);
  2379.                         pachPage += cch1, cchPage -= cch1;
  2380.                         pachSrcPage += cch1, cchSrcPage -= cch1;
  2381.                         memcpyb(pachPage, pachPage - off, cch2); //memmove doesn't do a good job here for some stupid reason.
  2382.                         pachPage += cch2, cchPage -= cch2;
  2383.                         break; // switch (cSrc & 0x03)
  2384.                     }
  2385.                 }
  2386.                 return ERROR_BAD_FORMAT;
  2387.             }
  2388.  
  2389.             /*
  2390.              *  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
  2391.              *  type  |  |  |                                |
  2392.              *        ----  ----------------------------------
  2393.              *       cch-3              offset
  2394.              *
  2395.              *  Two bytes layed out as described above.
  2396.              *  The cch(+3) and offset describes an amount of data to be copied from the expanded
  2397.              *  data relative to the current position.
  2398.              *
  2399.              *  If offset == 1 the data is not copied as expected, but in the memcpyw manner.
  2400.              */
  2401.  
  2402.             case 2:
  2403.             {
  2404.                 if (cchSrcPage >= 2)
  2405.                 {
  2406.                     int off = *(PUSHORT)pachSrcPage >> 4;
  2407.                     int cch = (cSrc >> 2 & 3) + 3;
  2408.                     pachSrcPage += 2, cchSrcPage -= 2;
  2409.                     if (    (cchPage >= cch)
  2410.                          && (pachPage - off >= pachDestPage)
  2411.                        )
  2412.                     {
  2413.                         memcpyw(pachPage, pachPage - off, cch);
  2414.                         pachPage += cch, cchPage -= cch;
  2415.                         break; // switch (cSrc & 0x03)
  2416.                     }
  2417.                 }
  2418.                 return ERROR_BAD_FORMAT;
  2419.             }
  2420.  
  2421.  
  2422.             /*
  2423.              *  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
  2424.              *  type  |        |  |              |  |                                |
  2425.              *        ----------  ----------------  ----------------------------------
  2426.              *           cch1           cch2                     offset                <cch1 bytes of data>
  2427.              *
  2428.              *  Three bytes layed out as described above, followed by cch1 bytes of data to be copied.
  2429.              *  The cch2 and offset describes an amount of data to be copied from the expanded
  2430.              *  data relative to the current position.
  2431.              *
  2432.              *  If offset == 1 the data is not copied as expected, but in the memcpyw manner.
  2433.              */
  2434.  
  2435.             case 3:
  2436.             {
  2437.                 if (cchSrcPage >= 3)
  2438.                 {
  2439.                     int cch1 = cSrc >> 2 & 0x000f;
  2440.                     int cch2 = *(PUSHORT)pachSrcPage >> 6 & 0x003f;
  2441.                     int off  = *(PUSHORT)(pachSrcPage + 1) >> 4;
  2442.                     pachSrcPage += 3, cchSrcPage -= 3;
  2443.                     if (    (cchSrcPage >= cch1)
  2444.                          && (cchPage >= cch1 + cch2)
  2445.                          && (pachPage - off + cch1 >= pachDestPage)
  2446.                        )
  2447.                     {
  2448.                         memcpy(pachPage, pachSrcPage, cch1);
  2449.                         pachPage += cch1, cchPage -= cch1;
  2450.                         pachSrcPage += cch1, cchSrcPage -= cch1;
  2451.                         memcpyw(pachPage, pachPage - off, cch2);
  2452.                         pachPage += cch2, cchPage -= cch2;
  2453.                         break; // switch (cSrc & 0x03)
  2454.                     }
  2455.                 }
  2456.                 return ERROR_BAD_FORMAT;
  2457.             }
  2458.         } // end switch (cSrc & 0x03)
  2459.     }
  2460.  
  2461. endloop:;
  2462.  
  2463.     /*
  2464.      * Zero the rest of the page.
  2465.      */
  2466.     if (cchPage > 0)
  2467.         memset(pachPage, 0, cchPage);
  2468.  
  2469.     return 0;
  2470. }
  2471.  
  2472. /*
  2473.  *@@ GetOfsFromPageTableIndex:
  2474.  *      returns the offset from the executable start
  2475.  *      for the given page table index. This checks
  2476.  *      the given index and returns ERROR_INVALID_SEGMENT_NUMBER
  2477.  *      if it is invalid (for safety).
  2478.  *
  2479.  *      Otherwise this returns NO_ERROR and sets
  2480.  *      *pulPageOfs, *pulFlags, and *pulSize to
  2481.  *      the respective fields from the page table
  2482.  *      entry automatically.
  2483.  *
  2484.  *      Note that the caller must manually add the
  2485.  *      page data offset for resources (or whatever
  2486.  *      other offset from the LX header is applicable).
  2487.  *
  2488.  *@@added V0.9.16 (2001-12-08) [umoeller]
  2489.  */
  2490.  
  2491. static APIRET GetOfsFromPageTableIndex(PEXECUTABLE pExec,   // in: executable from exehOpen
  2492.                                        ULONG ulObjPageTblIndexThis,  // in: object page table index to look for
  2493.                                        PULONG pulFlags,        // out: page flags
  2494.                                        PULONG pulSize,         // out: page size
  2495.                                        PULONG pulPageOfs)      // out: page ofs (add pLXHeader->ulDataPagesOfs to this)
  2496. {
  2497.     OBJECTPAGETABLEENTRY *pObjPageTblEntry;
  2498.  
  2499.     // watch out for out of range, or we'll trap
  2500.     if (ulObjPageTblIndexThis - 1 >= pExec->pLXHeader->ulPageCount)
  2501.     {
  2502.         // _Pmpf(("ulObjPageTblIndexThis %d is too large", ulObjPageTblIndexThis));
  2503.         return ERROR_INVALID_SEGMENT_NUMBER;     // 180
  2504.     }
  2505.  
  2506.     pObjPageTblEntry = &pExec->pObjPageTbl[ulObjPageTblIndexThis - 1];
  2507.  
  2508.     // page offset: shift left by what was specified in LX header
  2509.     *pulPageOfs     =    pObjPageTblEntry->o32_pagedataoffset
  2510.                       << pExec->pLXHeader->ulPageLeftShift;
  2511.     *pulFlags       = pObjPageTblEntry->o32_pageflags;
  2512.     *pulSize        = pObjPageTblEntry->o32_pagesize;
  2513.  
  2514.     return NO_ERROR;
  2515. }
  2516.  
  2517. /*
  2518.  *@@ exehReadLXPage:
  2519.  *      loads and possibly unpacks one LX page.
  2520.  *
  2521.  *      In order to reduce memory allocations, the
  2522.  *      caller is responsible for allocating a temp
  2523.  *      buffer, which must be passed in with
  2524.  *      pabCompressed.
  2525.  *
  2526.  *      Returns:
  2527.  *
  2528.  *      --  NO_ERROR: pbData was filled with data,
  2529.  *          which is pLXHeader->ulPageSize in size.
  2530.  *
  2531.  *      --  ERROR_INVALID_SEGMENT_NUMBER: segment
  2532.  *          number is out of range.
  2533.  *
  2534.  *      --  ERROR_BAD_FORMAT: compressed page data
  2535.  *          is screwed somehow, or page size is
  2536.  *          too large.
  2537.  *
  2538.  *      plus the error codes of doshReadAt.
  2539.  *
  2540.  *@@added V0.9.16 (2002-01-05) [umoeller]
  2541.  */
  2542.  
  2543. APIRET exehReadLXPage(PEXECUTABLE pExec,        // in: executable from exehOpen
  2544.                       ULONG ulObjPageTblIndex,  // in: page table index to read
  2545.                       ULONG ulExeOffset,        // in: for resources, pLXHeader->ulDataPagesOfs
  2546.                       PBYTE pabCompressed,      // in: ptr to temp buffer which must be
  2547.                                                 // pLXHeader->ulPageSize + 4 bytes in size
  2548.                       PBYTE pbData)             // out: ptr to buffer which receives actual
  2549.                                                 // uncompressed page data (pLXHeader->ulPageSize)
  2550. {
  2551.     APIRET  arc;
  2552.     ULONG   ulFlags,
  2553.             ulSize,
  2554.             ulOffset;
  2555.  
  2556.     if (!(arc = GetOfsFromPageTableIndex(pExec,
  2557.                                          ulObjPageTblIndex,
  2558.                                          &ulFlags,
  2559.                                          &ulSize,
  2560.                                          &ulOffset)))
  2561.     {
  2562.         ULONG ulPageSize = pExec->pLXHeader->ulPageSize;
  2563.  
  2564.         ulOffset += ulExeOffset;
  2565.  
  2566.         /* _Pmpf(("  reading pgtbl %d, ofs %d, type %s",
  2567.                 ulObjPageTblIndex,
  2568.                 ulOffset,
  2569.                 (ulFlags == 0x0001) ? "ITERDATA"
  2570.                 : (ulFlags == 0x0005) ? "ITERDATA2"
  2571.                 : "uncompressed")); */
  2572.  
  2573.         if (ulSize > ulPageSize)
  2574.             arc = ERROR_BAD_FORMAT;
  2575.         // go read the page data (might be compressed)
  2576.         else if (!(arc = doshReadAt(pExec->pFile,
  2577.                                     ulOffset,
  2578.                                     &ulSize,
  2579.                                     pabCompressed,
  2580.                                     0)))
  2581.         {
  2582.             // _Pmpf(("   %d bytes read", ulSize));
  2583.  
  2584.             // terminate buffer for decompress
  2585.             *(PULONG)(pabCompressed + ulSize) = 0;
  2586.  
  2587.             switch (ulFlags)
  2588.             {
  2589.                 case ITERDATA:
  2590.                     // OS/2 2.x:
  2591.                     arc = ExpandIterdata1(pbData,
  2592.                                           ulPageSize,
  2593.                                           pabCompressed,
  2594.                                           ulSize);            // this page's size
  2595.                 break;
  2596.  
  2597.                 case ITERDATA2:
  2598.                     // Warp 3:
  2599.                     arc = ExpandIterdata2(pbData,
  2600.                                           ulPageSize,
  2601.                                           pabCompressed,
  2602.                                           ulSize);            // this page's size
  2603.                 break;
  2604.  
  2605.                 case VALID:
  2606.                     // uncompressed
  2607.                     memcpy(pbData,
  2608.                            pabCompressed,
  2609.                            ulPageSize);
  2610.                 break;
  2611.             }
  2612.         }
  2613.     }
  2614.  
  2615.     return arc;
  2616. }
  2617.  
  2618. /*
  2619.  *@@ exehLoadLXResource:
  2620.  *      attempts to load the data of the resource
  2621.  *      with the specified type and id from an LX
  2622.  *      executable.
  2623.  *
  2624.  *      If (idResource == 0), the first resource of
  2625.  *      the specified type is loaded. Otherwise we
  2626.  *      try to find the resource of the specified
  2627.  *      type _and_ ID.
  2628.  *
  2629.  *      If NO_ERROR is returned, *ppbResData receives
  2630.  *      a new buffer with the raw resource data, and
  2631.  *      *pcbResData receives the size of that buffer.
  2632.  *      The caller must then free() that buffer.
  2633.  *
  2634.  *      This code will properly unpack compressed
  2635.  *      pages in the executable so the returned
  2636.  *      data is always unpacked and can be used
  2637.  *      directly.
  2638.  *
  2639.  *      Otherwise this returns:
  2640.  *
  2641.  *      --  ERROR_INVALID_EXE_SIGNATURE: pExec is not
  2642.  *          LX format.
  2643.  *
  2644.  *      --  ERROR_NO_DATA: resource not found.
  2645.  *
  2646.  *      --  ERROR_NOT_ENOUGH_MEMORY
  2647.  *
  2648.  *      plus the error codes from exehReadLXPage.
  2649.  *
  2650.  *@@added V0.9.16 (2001-12-08) [umoeller]
  2651.  *@@changed V0.9.16 (2002-01-05) [umoeller]: largely rewritten to handle non-first icons properly
  2652.  */
  2653.  
  2654. APIRET exehLoadLXResource(PEXECUTABLE pExec,     // in: executable from exehOpen
  2655.                           ULONG ulType,          // in: RT_* type (e.g. RT_POINTER)
  2656.                           ULONG idResource,      // in: resource ID or 0 for first
  2657.                           PBYTE *ppbResData,     // out: resource data (to be free()'d)
  2658.                           PULONG pcbResData)     // out: size of resource data (ptr can be NULL)
  2659. {
  2660.     APIRET          arc = NO_ERROR;
  2661.     ULONG           cResources = 0;
  2662.  
  2663.     ULONG           ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
  2664.  
  2665.     PLXHEADER       pLXHeader;
  2666.  
  2667.     *ppbResData = 0;
  2668.  
  2669.     /* _Pmpf((__FUNCTION__ " %s: ulType = %d, idResource %d",
  2670.             pExec->pFile->pszFilename,
  2671.             ulType, idResource)); */
  2672.  
  2673.     if (!(pLXHeader = pExec->pLXHeader))
  2674.         return (ERROR_INVALID_EXE_SIGNATURE);
  2675.  
  2676.     if (pExec->pDosExeHeader)
  2677.         // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
  2678.         ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
  2679.  
  2680.     if (!(cResources = pLXHeader->ulResTblCnt))
  2681.         // no resources at all:
  2682.         return (ERROR_NO_DATA);
  2683.  
  2684.     if (!pExec->fLXMapsLoaded)
  2685.         arc = exehLoadLXMaps(pExec);
  2686.  
  2687.     if (!arc)
  2688.     {
  2689.         // alright, we're in:
  2690.  
  2691.         // run thru the resources
  2692.         PXFILE  pFile = pExec->pFile;
  2693.         BOOL fPtrFound = FALSE;
  2694.  
  2695.         ULONG i;
  2696.         for (i = 0;
  2697.              i < cResources;
  2698.              i++)
  2699.         {
  2700.             // ptr to resource table entry
  2701.             RESOURCETABLEENTRY *pRsEntry = &pExec->pRsTbl[i];
  2702.  
  2703.             // check resource type and ID
  2704.             if (    (pRsEntry->type == ulType)
  2705.                  && (    (idResource == 0)
  2706.                       || (idResource == pRsEntry->name)
  2707.                     )
  2708.                )
  2709.             {
  2710.                 // hooray, found it: that was the easy part...
  2711.                 // finding the actual resource data isn't that
  2712.                 // trivial:
  2713.  
  2714.                 // first find the object that the resource
  2715.                 // resides in, but check the bounds
  2716.                 if (pRsEntry->obj - 1 >= pLXHeader->ulObjCount)
  2717.                 {
  2718.                     // _Pmpf(("pRsEntry->obj %d is too large", pRsEntry->obj));
  2719.                     arc = ERROR_INVALID_SEGMENT_NUMBER;     // 180
  2720.                 }
  2721.                 else
  2722.                 {
  2723.                     // get the object table entry from the index
  2724.                     OBJECTTABLEENTRY *pObjTblEntry = &pExec->pObjTbl[pRsEntry->obj - 1];
  2725.  
  2726.                     // get the object page table index for the
  2727.                     // first resource entry in this object; to
  2728.                     // this index we will need to add something
  2729.                     // which depends on pRsEntry->offset
  2730.                     ULONG   ulObjPageTblIndex = pObjTblEntry->o32_pagemap;
  2731.  
  2732.                     ULONG   ulPageSize = pLXHeader->ulPageSize;
  2733.  
  2734.                     // if this resource has specified an
  2735.                     // offset into the object, this offset
  2736.                     // specifies the offset in the uncompressed
  2737.                     // data of the whole object:
  2738.                     // for example:
  2739.                     //      res 0       ofs 0          cb 9808
  2740.                     //      res 1       ofs 9808       cb 3344
  2741.                     //      res 2       ofs 13152      cb ...
  2742.                     // and so on.
  2743.                     // So what we need to do is read in the page
  2744.                     // where the resource offset points into (which
  2745.                     // might be somewhere in the middle):
  2746.                     ULONG   ulFirstPage =   pRsEntry->offset
  2747.                                           / ulPageSize;
  2748.  
  2749.                     // get the offset of the resource data into
  2750.                     // that first page:
  2751.                     ULONG   ulResOffsetInFirstPage = pRsEntry->offset % ulPageSize;
  2752.  
  2753.                     ULONG   ulLastPage =    (pRsEntry->offset + pRsEntry->cb - 1)
  2754.                                           / ulPageSize;
  2755.  
  2756.                     // and we need as many pages as the resource occupies:
  2757.                     ULONG   cPages = ulLastPage - ulFirstPage + 1;
  2758.  
  2759.                     ULONG   cbAlloc = 0;
  2760.  
  2761.                     // now allocate temporary buffers
  2762.                     PBYTE   pabCompressed = NULL,
  2763.                             pabUncompressed = NULL;
  2764.  
  2765.                     // 4096 bytes for each page that is read in
  2766.                     // plus 4 extra bytes to terminate for decompression
  2767.                     if (!(pabCompressed = (PBYTE)malloc(ulPageSize + 4)))
  2768.                         arc = ERROR_NOT_ENOUGH_MEMORY;
  2769.                     // 4096 * cPages for the data that is composed from that
  2770.                     else if (!(arc = doshAllocArray(cPages,
  2771.                                                     ulPageSize,
  2772.                                                     &pabUncompressed,
  2773.                                                     &cbAlloc)))
  2774.                     {
  2775.                         // current pointer into pabUncompressed
  2776.                         PBYTE   pbCurrent = pabUncompressed;
  2777.  
  2778.                         ULONG   ul,
  2779.                                 ulPageThis;
  2780.  
  2781.                         /* _Pmpf(("  found RT_POINTER %d, size %d, resofs %d",
  2782.                                 pRsEntry->name,
  2783.                                 pRsEntry->cb,
  2784.                                 pRsEntry->offset));
  2785.                         _Pmpf(("  ulFirstPage %d, ulResOffsetInFirstPage %d, cPages %d",
  2786.                                 ulFirstPage, ulResOffsetInFirstPage, cPages)); */
  2787.  
  2788.                         ulPageThis = ulObjPageTblIndex + ulFirstPage;
  2789.  
  2790.                         // now go for each page:
  2791.                         for (ul = 0;
  2792.                              (ul < cPages) && (!arc);
  2793.                              ul++, ulPageThis++)
  2794.                         {
  2795.                             if (!(arc = exehReadLXPage(pExec,
  2796.                                                        ulPageThis,
  2797.                                                        pLXHeader->ulDataPagesOfs,
  2798.                                                        pabCompressed,
  2799.                                                        pbCurrent)))
  2800.                             {
  2801.                                 // got the data:
  2802.                                 // advance target buffer pointer for
  2803.                                 // next page
  2804.                                 pbCurrent += ulPageSize;
  2805.  
  2806.                                 // make sure we don't write too far away
  2807.                                 if (pbCurrent > pabUncompressed + cbAlloc)
  2808.                                     arc = ERROR_BAD_FORMAT;
  2809.                             }
  2810.                         } // end for
  2811.  
  2812.                         // ok, now we got all the pages that do contain
  2813.                         // data for this resource:
  2814.  
  2815.                         if (!arc)
  2816.                         {
  2817.                             // allocate a new buffer for caller
  2818.                             if (!(*ppbResData = (PBYTE)malloc(pRsEntry->cb)))
  2819.                                 arc = ERROR_NOT_ENOUGH_MEMORY;
  2820.                             else
  2821.                             {
  2822.                                 // copy into that buffer from the offset
  2823.                                 // into the first page and the data from
  2824.                                 // the subsequent pages too
  2825.                                 memcpy(*ppbResData,
  2826.                                        pabUncompressed + ulResOffsetInFirstPage,
  2827.                                        pRsEntry->cb);
  2828.  
  2829.                                 if (pcbResData)
  2830.                                     *pcbResData = pRsEntry->cb;
  2831.                             }
  2832.  
  2833.                             fPtrFound = TRUE;
  2834.                         }
  2835.  
  2836.                         FREE(pabUncompressed);
  2837.                         FREE(pabCompressed);
  2838.                     }
  2839.                 }
  2840.             }
  2841.  
  2842.             if (fPtrFound || arc)
  2843.                 break;
  2844.  
  2845.         } // end for
  2846.  
  2847.         if ((!fPtrFound) && (!arc))
  2848.             arc = ERROR_NO_DATA;
  2849.     }
  2850.  
  2851.     // _Pmpf((__FUNCTION__ ": returning %d", arc));
  2852.  
  2853.     return arc;
  2854. }
  2855.  
  2856. /*
  2857.  *@@ exehLoadOS2NEMaps:
  2858.  *      loads the the two main OS/2 NE maps into the
  2859.  *      given EXECUTABLE structure.
  2860.  *
  2861.  *      This loads:
  2862.  *
  2863.  *      1)  the OS/2 NE resource table;
  2864.  *
  2865.  *      2)  the OS/2 NE segment table.
  2866.  *
  2867.  *      Note that this is not automatically called
  2868.  *      by exehOpen to save time, since the NE
  2869.  *      maps are not needed for all the other exe
  2870.  *      functions. However, this does get called
  2871.  *      from exehLoadOS2NEResource if needed.
  2872.  *
  2873.  *      This returns:
  2874.  *
  2875.  *      --  NO_ERROR: both maps were loaded,
  2876.  *          and pExec->fOS2NEMapsLoaded was set to TRUE.
  2877.  *
  2878.  *      --  ERROR_INVALID_PARAMETER
  2879.  *
  2880.  *      --  ERROR_INVALID_EXE_SIGNATURE: pExec does
  2881.  *          not specify an NE executable, or the OS
  2882.  *          flag is != OS/2. This func does not work
  2883.  *          for Win16 executables.
  2884.  *
  2885.  *      --  ERROR_NO_DATA: at least one of the structs
  2886.  *          does not exist.
  2887.  *
  2888.  *      --  ERROR_NOT_ENOUGH_MEMORY
  2889.  *
  2890.  *      plus the error codes of doshReadAt.
  2891.  *
  2892.  *      Call exehFreeNEMaps to clean up explicitly, but
  2893.  *      that func automatically gets called by exehClose.
  2894.  *
  2895.  *@@added V0.9.16 (2001-12-08) [umoeller]
  2896.  */
  2897.  
  2898. APIRET exehLoadOS2NEMaps(PEXECUTABLE pExec)
  2899. {
  2900.     APIRET arc;
  2901.  
  2902.     PNEHEADER pNEHeader;
  2903.  
  2904.     if (!pExec)
  2905.         arc = ERROR_INVALID_PARAMETER;
  2906.     else if (pExec->fOS2NEMapsLoaded)
  2907.         // already loaded:
  2908.         arc = NO_ERROR;
  2909.     else if (    (pExec->ulExeFormat != EXEFORMAT_NE)
  2910.               || (pExec->ulOS != EXEOS_OS2)
  2911.               || (!(pNEHeader = pExec->pNEHeader))
  2912.             )
  2913.         arc = ERROR_INVALID_EXE_SIGNATURE;
  2914.     else
  2915.     {
  2916.         PXFILE pFile = pExec->pFile;
  2917.         ULONG ulNewHeaderOfs = 0;
  2918.         ULONG cb;
  2919.  
  2920.         if (pExec->pDosExeHeader)
  2921.             // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
  2922.             ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
  2923.  
  2924.         // resource table
  2925.         if (    (!(arc = doshAllocArray(pNEHeader->usResSegmCount,
  2926.                                         sizeof(OS2NERESTBLENTRY),
  2927.                                         (PBYTE*)&pExec->paOS2NEResTblEntry,
  2928.                                         &cb)))
  2929.              && (!(arc = doshReadAt(pFile,
  2930.                                     pNEHeader->usResTblOfs
  2931.                                       + ulNewHeaderOfs,
  2932.                                     &cb,
  2933.                                     (PBYTE)pExec->paOS2NEResTblEntry,
  2934.                                     DRFL_FAILIFLESS)))
  2935.             )
  2936.         {
  2937.             // resource segments
  2938.             if (    (!(arc = doshAllocArray(pNEHeader->usResSegmCount,
  2939.                                             sizeof(OS2NESEGMENT),
  2940.                                             (PBYTE*)&pExec->paOS2NESegments,
  2941.                                             &cb)))
  2942.                  && (!(arc = doshReadAt(pFile,
  2943.                                         pNEHeader->usResTblOfs
  2944.                                           + ulNewHeaderOfs
  2945.                                           - cb, // pNEHeader->usResSegmCount * sizeof(struct new_seg)
  2946.                                         &cb,
  2947.                                         (PBYTE)pExec->paOS2NESegments,
  2948.                                         DRFL_FAILIFLESS)))
  2949.                 )
  2950.             {
  2951.             }
  2952.         }
  2953.  
  2954.         if (!arc)
  2955.             pExec->fOS2NEMapsLoaded = TRUE;
  2956.         else
  2957.             exehFreeNEMaps(pExec);
  2958.     }
  2959.  
  2960.     return arc;
  2961. }
  2962.  
  2963. /*
  2964.  *@@ exehFreeNEMaps:
  2965.  *      frees data allocated by exehLoadOS2NEMaps.
  2966.  *      Gets called automatically by exehClose.
  2967.  *
  2968.  *@@added V0.9.16 (2001-12-08) [umoeller]
  2969.  */
  2970.  
  2971. VOID exehFreeNEMaps(PEXECUTABLE pExec)
  2972. {
  2973.     FREE(pExec->paOS2NEResTblEntry);
  2974.     FREE(pExec->paOS2NESegments);
  2975.     pExec->fOS2NEMapsLoaded = FALSE;
  2976. }
  2977.  
  2978. /*
  2979.  *@@ exehLoadOS2NEResource:
  2980.  *      attempts to load the data of the resource
  2981.  *      with the specified type and id from an OS/2
  2982.  *      NE executable.
  2983.  *
  2984.  *      Note that NE executables with resources in
  2985.  *      OS/2 format are very, very rare. The
  2986.  *      only OS/2 NE executables with resources I
  2987.  *      could find at this point were in an old 1.3
  2988.  *      Toolkit, but with them, this code works.
  2989.  *
  2990.  *      If (idResource == 0), the first resource of
  2991.  *      the specified type is loaded. Otherwise we
  2992.  *      try to find the resource of the specified
  2993.  *      type _and_ ID.
  2994.  *
  2995.  *      If NO_ERROR is returned, *ppbResData receives
  2996.  *      a new buffer with the raw resource data, and
  2997.  *      *pcbResData receives the size of that buffer.
  2998.  *      The caller must then free() that buffer.
  2999.  *
  3000.  *      Since NE doesn't support packing, the data is
  3001.  *      unpacked always and can be used directly.
  3002.  *
  3003.  *      Otherwise this returns:
  3004.  *
  3005.  *      --  ERROR_INVALID_EXE_SIGNATURE: pExec is not
  3006.  *          NE or not OS/2. This func does not work
  3007.  *          for Win16 executables.
  3008.  *
  3009.  *      --  ERROR_NO_DATA: resource not found.
  3010.  *
  3011.  *      --  ERROR_BAD_FORMAT: cannot handle resource format.
  3012.  *
  3013.  *@@added V0.9.16 (2001-12-08) [umoeller]
  3014.  */
  3015.  
  3016. APIRET exehLoadOS2NEResource(PEXECUTABLE pExec,     // in: executable from exehOpen
  3017.                              ULONG ulType,          // in: RT_* type (e.g. RT_POINTER)
  3018.                              ULONG idResource,      // in: resource ID or 0 for first
  3019.                              PBYTE *ppbResData,     // out: resource data (to be free()'d)
  3020.                              PULONG pcbResData)     // out: size of resource  data (ptr can be NULL)
  3021. {
  3022.     APIRET          arc = NO_ERROR;
  3023.     ULONG           cResources = 0;
  3024.  
  3025.     ULONG           ulNewHeaderOfs = 0; // V0.9.12 (2001-05-03) [umoeller]
  3026.  
  3027.     PNEHEADER       pNEHeader;
  3028.  
  3029.     if (!(pNEHeader = pExec->pNEHeader))
  3030.         return (ERROR_INVALID_EXE_SIGNATURE);
  3031.  
  3032.     if (pExec->pDosExeHeader)
  3033.         // executable has DOS stub: V0.9.12 (2001-05-03) [umoeller]
  3034.         ulNewHeaderOfs = pExec->pDosExeHeader->ulNewHeaderOfs;
  3035.  
  3036.     // _Pmpf((__FUNCTION__ ": entering, checking %d resources", pNEHeader->usResSegmCount));
  3037.  
  3038.     if (!(cResources = pNEHeader->usResSegmCount))
  3039.         // no resources at all:
  3040.         return (ERROR_NO_DATA);
  3041.  
  3042.     if (!pExec->fOS2NEMapsLoaded)
  3043.         arc = exehLoadOS2NEMaps(pExec);
  3044.  
  3045.     if (!arc)
  3046.     {
  3047.         // alright, we're in:
  3048.         PXFILE  pFile = pExec->pFile;
  3049.  
  3050.         // run thru the resources
  3051.         BOOL fPtrFound = FALSE;
  3052.  
  3053.         ULONG i;
  3054.         POS2NERESTBLENTRY pResTblEntryThis = pExec->paOS2NEResTblEntry;
  3055.         POS2NESEGMENT pSegThis = pExec->paOS2NESegments;
  3056.         for (i = 0;
  3057.              i < cResources;
  3058.              i++, pResTblEntryThis++, pSegThis++)
  3059.         {
  3060.             // check resource type and ID
  3061.             if (    (pResTblEntryThis->usType == ulType)
  3062.                  && (    (idResource == 0)
  3063.                       || (idResource == pResTblEntryThis->usID)
  3064.                     )
  3065.                )
  3066.             {
  3067.                 // hooray, we found the resource...
  3068.  
  3069.                 // look up the corresponding segment
  3070.  
  3071.                 ULONG ulOffset = (    (ULONG)pSegThis->ns_sector
  3072.                                    << pNEHeader->usLogicalSectShift
  3073.                                  );
  3074.  
  3075.                 ULONG cb = pSegThis->ns_cbseg;        // resource size
  3076.                 PBYTE pb;
  3077.                 if (!(*ppbResData = (PBYTE)malloc(cb)))
  3078.                     arc = ERROR_NOT_ENOUGH_MEMORY;
  3079.                 else
  3080.                 {
  3081.                     if (!(arc = doshReadAt(pFile,
  3082.                                            ulOffset,
  3083.                                            &cb,
  3084.                                            *ppbResData,
  3085.                                            DRFL_FAILIFLESS)))
  3086.                     {
  3087.                         if (pcbResData)
  3088.                             *pcbResData = cb;
  3089.                         fPtrFound = TRUE;
  3090.                     }
  3091.                     else
  3092.                         // error reading:
  3093.                         free(*ppbResData);
  3094.                 }
  3095.             }
  3096.  
  3097.             if (fPtrFound || arc)
  3098.                 break;
  3099.  
  3100.         } // end for
  3101.  
  3102.         if ((!fPtrFound) && (!arc))
  3103.             arc = ERROR_NO_DATA;
  3104.     }
  3105.     // else
  3106.         // _Pmpf(("exehLoadOS2NEMaps returned %d"));
  3107.  
  3108.     return arc;
  3109. }
  3110.  
  3111. /*
  3112.  *@@ exehClose:
  3113.  *      this closes an executable opened with exehOpen.
  3114.  *      Always call this function if NO_ERROR was returned by
  3115.  *      exehOpen.
  3116.  *
  3117.  *      This automaticall calls exehFreeLXMaps and
  3118.  *      exehFreeNEMaps.
  3119.  *
  3120.  *@@added V0.9.0 [umoeller]
  3121.  *@@changed V0.9.16 (2001-12-08) [umoeller]: fixed memory leaks
  3122.  *@@changed V0.9.16 (2001-12-08) [umoeller]: changed prototype to null the pExec ptr
  3123.  */
  3124.  
  3125. APIRET exehClose(PEXECUTABLE *ppExec)
  3126. {
  3127.     APIRET arc = NO_ERROR;
  3128.     PEXECUTABLE pExec;
  3129.     if (    (ppExec)
  3130.          && (pExec = *ppExec)
  3131.        )
  3132.     {
  3133.         char **papsz[] =
  3134.             {
  3135.                 (char**)&pExec->pDosExeHeader,
  3136.                 (char**)&pExec->pNEHeader,
  3137.                 (char**)&pExec->pLXHeader,
  3138.                 (char**)&pExec->pPEHeader,
  3139.  
  3140.                 &pExec->pszDescription,
  3141.                 &pExec->pszVendor,
  3142.                 &pExec->pszVersion,
  3143.                 &pExec->pszInfo,
  3144.  
  3145.                 &pExec->pszBuildDateTime,
  3146.                 &pExec->pszBuildMachine,
  3147.                 &pExec->pszASD,
  3148.                 &pExec->pszLanguage,
  3149.                 &pExec->pszCountry,
  3150.                 &pExec->pszRevision,
  3151.                 &pExec->pszUnknown,
  3152.                 &pExec->pszFixpak
  3153.             };
  3154.         ULONG ul;
  3155.  
  3156.         exehFreeLXMaps(pExec);
  3157.         exehFreeNEMaps(pExec);
  3158.  
  3159.         // fixed the memory leaks with the missing fields,
  3160.         // turned this into a loop
  3161.         for (ul = 0;
  3162.              ul < sizeof(papsz) / sizeof(papsz[0]);
  3163.              ul++)
  3164.         {
  3165.             PSZ pThis;
  3166.             if (pThis = *papsz[ul])
  3167.             {
  3168.                 free(pThis);
  3169.                 *papsz[ul] = NULL;
  3170.             }
  3171.         }
  3172.  
  3173.         doshClose(&pExec->pFile);
  3174.  
  3175.         free(pExec);
  3176.         *ppExec = NULL;
  3177.     }
  3178.     else
  3179.         arc = ERROR_INVALID_PARAMETER;
  3180.  
  3181.     return arc;
  3182. }
  3183.  
  3184.