source: branches/branch-1-0/src/helpers/exeh.c@ 231

Last change on this file since 231 was 229, checked in by umoeller, 23 years ago

Sources as of 1.0.0.

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