source: trunk/src/helpers/exeh.c@ 154

Last change on this file since 154 was 142, checked in by umoeller, 24 years ago

misc. updates

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