635 lines
20 KiB
C
635 lines
20 KiB
C
/*static char *SCCSID = "%W% %E%";*/
|
|
/*
|
|
* Copyright Microsoft Corporation 1983-1987
|
|
*
|
|
* This Module contains Proprietary Information of Microsoft
|
|
* Corporation and should be treated as Confidential.
|
|
*/
|
|
|
|
/* Exepack */
|
|
|
|
/****************************************************************
|
|
* *
|
|
* PACK.C *
|
|
* *
|
|
****************************************************************/
|
|
|
|
#include <minlit.h> /* Types, constants */
|
|
#include <bndtrn.h> /* More types and constants */
|
|
#include <bndrel.h> /* More types and constants */
|
|
#include <lnkio.h> /* Linker I/O definitions */
|
|
#include <lnkmsg.h> /* Error messages */
|
|
#include <extern.h> /* External declarations */
|
|
|
|
#if FEXEPACK AND ODOS3EXE /* Whole file is conditional */
|
|
typedef struct _RUNTYPE
|
|
{
|
|
WORD wSignature;
|
|
WORD cbLastp;
|
|
WORD cpnRes;
|
|
WORD irleMax;
|
|
WORD cparDirectory;
|
|
WORD cparMinAlloc;
|
|
WORD cparMaxAlloc;
|
|
WORD saStack;
|
|
WORD raStackInit;
|
|
WORD wchksum;
|
|
WORD raStart;
|
|
WORD saStart;
|
|
WORD rbrgrle;
|
|
WORD iovMax;
|
|
WORD doslev;
|
|
}
|
|
RUNTYPE;
|
|
|
|
/* States of automaton */
|
|
#define STARTSTATE 0
|
|
#define FINDREPEAT 1
|
|
#define FINDENDRPT 2
|
|
#define EMITRECORD 3
|
|
|
|
/*
|
|
* LOCAL FUNCTION PROTOTYPES
|
|
*/
|
|
|
|
|
|
LOCAL void NEAR EmitRecords(void);
|
|
LOCAL unsigned char NEAR GetFromVM(void);
|
|
LOCAL unsigned short NEAR ScanWhileSame(void);
|
|
LOCAL unsigned short NEAR ScanWhileDifferent(void);
|
|
LOCAL WORD NEAR AfterScanning(unsigned short l);
|
|
LOCAL void NEAR OutEnum(void);
|
|
LOCAL void NEAR OutIter(SATYPE sa, WORD length);
|
|
|
|
|
|
/*
|
|
* DATA DEFINED IN UNPACK MODULE: unpack.asm
|
|
*/
|
|
|
|
#if NOT defined( _WIN32 )
|
|
extern char * FAR cdecl UnpackModule; /* Unpacker/Relocator module */
|
|
extern char FAR cdecl SegStart; // Start of unpacker
|
|
extern WORD FAR cdecl cbUnpack; /* Length of UnpackModule */
|
|
extern WORD FAR cdecl ipsave; /* Original IP */
|
|
extern WORD FAR cdecl cssave; /* Original CS */
|
|
extern WORD FAR cdecl spsave; /* Original SP */
|
|
extern WORD FAR cdecl sssave; /* Original SS */
|
|
extern WORD FAR cdecl cparExp; /* # para. in expanded image */
|
|
extern WORD FAR cdecl raStartUnpack; /* Offset of code start in unpacker */
|
|
extern WORD FAR cdecl raMoveUnpack; /* Offset of self-moving in unpacker */
|
|
extern WORD FAR cdecl raBadStack; /* Bottom of bad stack range */
|
|
extern WORD FAR cdecl szBadStack; /* Bad stack range */
|
|
#else // _WIN32
|
|
|
|
//
|
|
// For the portable NTGroup version of this linker, we can't use unpack32.asm
|
|
// directly because we need to run on RISC platforms. But we still need this
|
|
// code, which is real-mode x86 code tacked onto the DOS binary which unpacks
|
|
// the packed EXE and then calls the real entrypoint. So it's defined as a
|
|
// byte array, and the interesting offsets are hard-coded here. I came across
|
|
// these values and the code debugging link3216.exe built as the languages group
|
|
// did.
|
|
//
|
|
|
|
#define SegStart unpack
|
|
#define cbUnpack (*(WORD *) &unpack[6])
|
|
#define ipsave (*(WORD *) &unpack[0])
|
|
#define cssave (*(WORD *) &unpack[2])
|
|
#define spsave (*(WORD *) &unpack[8])
|
|
#define sssave (*(WORD *) &unpack[0xa])
|
|
#define cparExp (*(WORD *) &unpack[0xc])
|
|
#define raStartUnpack 0x10
|
|
#define raMoveUnpack 0x33
|
|
#define raBadStack 0
|
|
#define szBadStack 0x35
|
|
|
|
unsigned char unpack[] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x01,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x42,
|
|
0x8b, 0xe8, 0x8c, 0xc0, 0x05, 0x10, 0x00, 0x0e,
|
|
0x1f, 0xa3, 0x04, 0x00, 0x03, 0x06, 0x0c, 0x00,
|
|
0x8e, 0xc0, 0x8b, 0x0e, 0x06, 0x00, 0x8b, 0xf9,
|
|
0x4f, 0x8b, 0xf7, 0xfd, 0xf3, 0xa4, 0x50, 0xb8,
|
|
0x34, 0x00, 0x50, 0xcb, 0x8c, 0xc3, 0x8c, 0xd8,
|
|
0x48, 0x8e, 0xd8, 0x8e, 0xc0, 0xbf, 0x0f, 0x00,
|
|
0xb9, 0x10, 0x00, 0xb0, 0xff, 0xf3, 0xae, 0x47,
|
|
0x8b, 0xf7, 0x8b, 0xc3, 0x48, 0x8e, 0xc0, 0xbf,
|
|
0x0f, 0x00, 0xb1, 0x04, 0x8b, 0xc6, 0xf7, 0xd0,
|
|
0xd3, 0xe8, 0x8c, 0xda, 0x2b, 0xd0, 0x73, 0x04,
|
|
0x8c, 0xd8, 0x2b, 0xd2, 0xd3, 0xe0, 0x03, 0xf0,
|
|
0x8e, 0xda, 0x8b, 0xc7, 0xf7, 0xd0, 0xd3, 0xe8,
|
|
0x8c, 0xc2, 0x2b, 0xd0, 0x73, 0x04, 0x8c, 0xc0,
|
|
0x2b, 0xd2, 0xd3, 0xe0, 0x03, 0xf8, 0x8e, 0xc2,
|
|
|
|
0xac, 0x8a, 0xd0, 0x4e, 0xad, 0x8b, 0xc8, 0x46,
|
|
0x8a, 0xc2, 0x24, 0xfe, 0x3c, 0xb0, 0x75, 0x05,
|
|
0xac, 0xf3, 0xaa, 0xeb, 0x06, 0x3c, 0xb2, 0x75,
|
|
0x6d, 0xf3, 0xa4, 0x8a, 0xc2, 0xa8, 0x01, 0x74,
|
|
0xb1, 0xbe, 0x32, 0x01, 0x0e, 0x1f, 0x8b, 0x1e,
|
|
0x04, 0x00, 0xfc, 0x33, 0xd2, 0xad, 0x8b, 0xc8,
|
|
0xe3, 0x13, 0x8b, 0xc2, 0x03, 0xc3, 0x8e, 0xc0,
|
|
0xad, 0x8b, 0xf8, 0x83, 0xff, 0xff, 0x74, 0x11,
|
|
0x26, 0x01, 0x1d, 0xe2, 0xf3, 0x81, 0xfa, 0x00,
|
|
0xf0, 0x74, 0x16, 0x81, 0xc2, 0x00, 0x10, 0xeb,
|
|
0xdc, 0x8c, 0xc0, 0x40, 0x8e, 0xc0, 0x83, 0xef,
|
|
0x10, 0x26, 0x01, 0x1d, 0x48, 0x8e, 0xc0, 0xeb,
|
|
0xe2, 0x8b, 0xc3, 0x8b, 0x3e, 0x08, 0x00, 0x8b,
|
|
0x36, 0x0a, 0x00, 0x03, 0xf0, 0x01, 0x06, 0x02,
|
|
0x00, 0x2d, 0x10, 0x00, 0x8e, 0xd8, 0x8e, 0xc0,
|
|
0xbb, 0x00, 0x00, 0xfa, 0x8e, 0xd6, 0x8b, 0xe7,
|
|
|
|
0xfb, 0x8b, 0xc5, 0x2e, 0xff, 0x2f, 0xb4, 0x40,
|
|
0xbb, 0x02, 0x00, 0xb9, 0x16, 0x00, 0x8c, 0xca,
|
|
0x8e, 0xda, 0xba, 0x1c, 0x01, 0xcd, 0x21, 0xb8,
|
|
0xff, 0x4c, 0xcd, 0x21, 0x50, 0x61, 0x63, 0x6b,
|
|
0x65, 0x64, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20,
|
|
0x69, 0x73, 0x20, 0x63, 0x6f, 0x72, 0x72, 0x75,
|
|
0x70, 0x74
|
|
};
|
|
|
|
#endif // _WIN32
|
|
|
|
LOCAL WORD lastc; /* last character */
|
|
LOCAL WORD c; /* current or next character */
|
|
LOCAL WORD State = STARTSTATE;
|
|
/* current state */
|
|
LOCAL FTYPE fEnumOK; /* OK to emit enumerated records */
|
|
LOCAL WORD cbRepeat; /* length of repeated stream */
|
|
LOCAL WORD cbEnum; /* length of enumerated stream */
|
|
|
|
#define EHLEN 3 /* 2 for length + 1 for type */
|
|
#define MAXRPT 0xfff0 /* Maximum length to compress */
|
|
#define MAXENM (0xfff0-(EHLEN+1))
|
|
/* Maximum length of enum. stream */
|
|
#define MINEXP (2*EHLEN+2) /* Minimum length of repeated stream,
|
|
* after the first repeate record */
|
|
#define toAdr20(seg, off) (((long)seg << 4) + off)
|
|
|
|
LOCAL WORD minRpt = (18 * EHLEN) + 1;
|
|
/* Minimum for rpt rec begins larger */
|
|
|
|
/* Type values for expansion record headers */
|
|
|
|
#define RPTREC 0xb0 /* Repeat record */
|
|
#define ENMREC 0xb2 /* Enumerated record */
|
|
|
|
|
|
/*
|
|
* OutPack - Run a buffer through the compactor. Return value is
|
|
* undefined.
|
|
*/
|
|
void OutPack (pb, cb)
|
|
REGISTER BYTE *pb; /* Pointer to buffer */
|
|
unsigned cb; /* Number of bytes to compress */
|
|
{
|
|
REGISTER BYTE *endp; /* Pointer to end of buffer */
|
|
|
|
endp = &pb[cb];
|
|
|
|
while (pb < endp)
|
|
switch (State)
|
|
{
|
|
case STARTSTATE:
|
|
lastc = *pb++;
|
|
State = FINDREPEAT;
|
|
break;
|
|
|
|
case FINDREPEAT:
|
|
if (cbEnum >= MAXENM)
|
|
{
|
|
EmitRecords();
|
|
State = FINDREPEAT;
|
|
break;
|
|
}
|
|
c = *pb++;
|
|
if (c == lastc)
|
|
{
|
|
cbRepeat = 2;
|
|
State = FINDENDRPT;
|
|
break;
|
|
}
|
|
/* At this point c != lastc */
|
|
fputc(lastc, bsRunfile);
|
|
cbEnum++;
|
|
lastc = c;
|
|
break;
|
|
|
|
case FINDENDRPT:
|
|
c = *pb++;
|
|
if (c == lastc && cbRepeat < MAXRPT)
|
|
{
|
|
cbRepeat++;
|
|
break;
|
|
}
|
|
if (cbRepeat < minRpt)
|
|
{
|
|
/*
|
|
* Not long enough. Enum record swallows
|
|
* repeated chars.
|
|
*/
|
|
while (cbEnum <= MAXENM && cbRepeat > 0)
|
|
{
|
|
fputc(lastc, bsRunfile);
|
|
cbEnum++;
|
|
cbRepeat--;
|
|
}
|
|
if (cbRepeat > 0)
|
|
EmitRecords();
|
|
} else
|
|
EmitRecords();
|
|
lastc = c; /* Prepare for next stream */
|
|
State = FINDREPEAT;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* EmitRecords - Emits 1 or 2 expansion records. Return value is
|
|
* undefined.
|
|
*/
|
|
|
|
LOCAL void NEAR EmitRecords ()
|
|
{
|
|
/* We have 1 or 2 records to output */
|
|
if (cbEnum > 0)
|
|
{
|
|
#if MDEBUG AND FDEBUG
|
|
if (fDebug) fprintf(stdout, "E%8x\n", cbEnum);
|
|
#endif
|
|
if (fEnumOK)
|
|
{
|
|
/* Output an enumerated record header */
|
|
OutWord(cbEnum);
|
|
fputc(ENMREC, bsRunfile);
|
|
}
|
|
cbEnum = 0;
|
|
}
|
|
if (cbRepeat >= minRpt)
|
|
{
|
|
#if MDEBUG AND FDEBUG
|
|
if (fDebug) fprintf(stdout, "R%8x\n", cbRepeat);
|
|
#endif
|
|
/* Output a repeat record */
|
|
fputc(lastc, bsRunfile);
|
|
OutWord(cbRepeat);
|
|
if (!fEnumOK)
|
|
{
|
|
/* 1st record header generated */
|
|
fputc(RPTREC|1, bsRunfile);
|
|
fEnumOK = 1;
|
|
} else
|
|
fputc(RPTREC, bsRunfile);
|
|
cbRepeat = 0;
|
|
minRpt = MINEXP; /* 1st is out, so reset minRpt */
|
|
} else if (cbRepeat > 0)
|
|
{
|
|
cbEnum = cbRepeat;
|
|
while (cbRepeat-- > 0)
|
|
fputc(lastc, bsRunfile);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* EndPack - End the packing procedure: add the relocator module.
|
|
*/
|
|
void EndPack (prun)
|
|
RUNTYPE *prun; /* Pointer to runfile header */
|
|
{
|
|
long fpos; /* File position */
|
|
WORD cparPacked; /* # paras in packed image */
|
|
WORD cparUnpack; /* Size of Unpack module (paras) */
|
|
int i;
|
|
int crle; /* Count of relocs for a frame */
|
|
long cTmp; /* Temporary count */
|
|
long us; /* User's stack in minalloc */
|
|
long los; /* Low end of forbidden stack */
|
|
|
|
|
|
fseek(bsRunfile, 0L, 2); /* Go to end of file */
|
|
|
|
cTmp = (((((long)prun->cpnRes-1)<<5) - prun->cparDirectory) << 4) +
|
|
(prun->cbLastp ? prun->cbLastp : 512);
|
|
/* Get # bytes in expanded image */
|
|
cbRepeat += (WORD) (0xf & (0x10 - (0xf & cTmp)));
|
|
/* Make it look like image ends on
|
|
* paragraph boundary */
|
|
if (State == FINDREPEAT)
|
|
{
|
|
fputc(lastc, bsRunfile); /* Update last enum record */
|
|
cbEnum++;
|
|
}
|
|
minRpt = 1; /* Force final repeat rec. out */
|
|
EmitRecords(); /* Output the final record(s) */
|
|
|
|
cparExp = (short) ((cTmp + 0xf) >> 4);/* Save # paras in image */
|
|
|
|
/*
|
|
* Append the unpacking module (relocator-expander)
|
|
*/
|
|
fpos = ftell(bsRunfile); /* Save where Unpack begins */
|
|
|
|
/* Align Unpack on paragraph boundary */
|
|
while (fpos & 0x0f)
|
|
{
|
|
fpos++;
|
|
fputc(0xff, bsRunfile);
|
|
}
|
|
|
|
/* Make sure User stack won't stomp on unpack code */
|
|
|
|
us = toAdr20(prun->saStack, prun->raStackInit);
|
|
los = toAdr20((cTmp >> 4), raBadStack);
|
|
|
|
while ( us > los && us - los < szBadStack )
|
|
{
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
us--;
|
|
fpos++;
|
|
fputc(0xff, bsRunfile);
|
|
}
|
|
}
|
|
|
|
fflush(bsRunfile);
|
|
|
|
cparPacked = (WORD) ((fpos >> 4) - prun->cparDirectory);
|
|
if (cTmp < ((long)cparPacked << 4) + raMoveUnpack)
|
|
Fatal(ER_badpack);
|
|
|
|
/* Append relocator module (Unpack). This code depends
|
|
* closely on the structure of unpack.asm. */
|
|
|
|
/* Get length of relocation area */
|
|
for (crle = 0, i = 0; i < 16; i++)
|
|
crle += (mpframeRlc[i].count + 1) << 1;
|
|
|
|
/* Initialize stack of relocator module */
|
|
ipsave = prun->raStart;
|
|
cssave = prun->saStart;
|
|
#if defined( _WIN32 )
|
|
if (cbUnpack != sizeof(unpack))
|
|
Fatal(ER_badpack);
|
|
#endif
|
|
i = cbUnpack;
|
|
cbUnpack += (WORD)crle;
|
|
spsave = prun->raStackInit;
|
|
sssave = prun->saStack;
|
|
#ifdef M_BYTESWAP
|
|
bswap(Unpack, 7);
|
|
#endif
|
|
#if DOSX32
|
|
WriteExe(&SegStart, i);
|
|
#else
|
|
fwrite(UnpackModule, 1, i, bsRunfile);
|
|
#endif
|
|
/* Append optimized relocation records */
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
crle = mpframeRlc[i].count;
|
|
OutWord((WORD) crle);
|
|
WriteExe(mpframeRlc[i].rgRlc, crle * sizeof(WORD));
|
|
}
|
|
|
|
/* Correct header values */
|
|
fpos += cbUnpack;
|
|
prun->cbLastp = (WORD) (fpos & 0x1ff);
|
|
prun->cpnRes = (WORD) ((fpos + 511) >> 9);
|
|
prun->irleMax = 0;
|
|
cparUnpack = (cbUnpack + 0xf) >> 4;
|
|
prun->cparMinAlloc = (cparExp + max(prun->cparMinAlloc,(cparUnpack+8)))
|
|
- (cparPacked + cparUnpack);
|
|
if (prun->cparMaxAlloc < prun->cparMinAlloc)
|
|
prun->cparMaxAlloc = prun->cparMinAlloc;
|
|
prun->saStack = cparExp + cparUnpack;
|
|
prun->raStackInit = 128;
|
|
prun->raStart = raStartUnpack;
|
|
prun->saStart = cparPacked;
|
|
fseek(bsRunfile, 0L, 0);
|
|
OutHeader((struct exe_hdr *) prun);
|
|
fseek(bsRunfile, fpos, 0);
|
|
}
|
|
|
|
#ifdef M_BYTESWAP
|
|
/*
|
|
* Swap bytes for 1st n words in buffer.
|
|
*/
|
|
LOCAL bswap (buf, n)
|
|
REGISTER char *buf;
|
|
REGISTER int n;
|
|
{
|
|
REGISTER char swapb;
|
|
|
|
for ( ; n-- > 0 ; buf += 2)
|
|
{
|
|
swapb = buf[0];
|
|
buf[0] = buf[1];
|
|
buf[1] = swapb;
|
|
}
|
|
}
|
|
#endif
|
|
#endif /*FEXEPACK AND ODOS3EXE*/
|
|
|
|
/*
|
|
* The following routines concern packing segmented-executable format
|
|
* files.
|
|
*/
|
|
#if FALSE
|
|
#define MINREPEAT 32 /* Min length of iteration cousing compression */
|
|
|
|
LOCAL long vaLast; /* Virtual address */
|
|
LOCAL long vaStart; /* Virtual scanning start address */
|
|
LOCAL long BufEnd; /* Virtual address of buffer end */
|
|
#if EXE386
|
|
LOCAL long ra; /* Offset within packed segment */
|
|
#endif
|
|
LOCAL BYTE LastB;
|
|
LOCAL BYTE CurrentB;
|
|
LOCAL long VPageAddress; /* Virtual address of current page */
|
|
LOCAL WORD VPageOffset; /* Current position within virtual page */
|
|
LOCAL BYTE *PageBuffer; /* Virtual page buffer */
|
|
|
|
|
|
LOCAL BYTE NEAR GetFromVM()
|
|
{
|
|
|
|
if (VPageOffset == PAGLEN)
|
|
{
|
|
PageBuffer = mapva(VPageAddress, FALSE);
|
|
/* Fetch page */
|
|
VPageAddress += PAGLEN; /* Update page virtual address */
|
|
VPageOffset = 0; /* Init page offset */
|
|
}
|
|
return(PageBuffer[VPageOffset++]);
|
|
}
|
|
|
|
|
|
LOCAL WORD NEAR ScanWhileSame()
|
|
{
|
|
long l;
|
|
|
|
l = 2L; /* We are looking at two bytes in buffer */
|
|
while (CurrentB == LastB)
|
|
{
|
|
if (vaStart + l >= BufEnd)
|
|
return((WORD) l); /* We bump the buffer end */
|
|
|
|
CurrentB = GetFromVM();
|
|
l++;
|
|
}
|
|
return(l == 2L ? 0 : (WORD) (l - 1));
|
|
/* We went one byte too far to detect they are different */
|
|
}
|
|
|
|
|
|
LOCAL WORD NEAR ScanWhileDifferent()
|
|
{
|
|
long l;
|
|
|
|
l = 2L; /* We are looking at two bytes in buffer */
|
|
while (CurrentB != LastB)
|
|
{
|
|
if (vaStart + l >= BufEnd)
|
|
return((WORD) l); /* We bump the buffer end */
|
|
|
|
LastB = CurrentB;
|
|
CurrentB = GetFromVM();
|
|
l++;
|
|
}
|
|
return((WORD) (l - 2)); /* We went two bytes too far to detect they are the same */
|
|
}
|
|
|
|
|
|
LOCAL WORD NEAR AfterScanning(l)
|
|
WORD l; /* Length of scanned bytes */
|
|
{
|
|
vaStart += l; /* Update scan start address */
|
|
#if EXE386
|
|
ra += l; /* Update offset in segment */
|
|
#endif
|
|
if (vaStart + 2 >= BufEnd)
|
|
{ /* We need at least to bytes remaining */
|
|
return(FALSE); /* Buffer end */
|
|
}
|
|
else
|
|
{
|
|
if (LastB != CurrentB)
|
|
{ /* We stop at iterated and enumerated */
|
|
LastB = CurrentB; /* byte sequence, so we have move */
|
|
CurrentB = GetFromVM(); /* one byte forward */
|
|
}
|
|
return((WORD) TRUE);
|
|
}
|
|
}
|
|
|
|
|
|
LOCAL void NEAR OutEnum(void)
|
|
{
|
|
#if EXE386
|
|
if (ExeFormat == Exe386)
|
|
OutVm(vaLast, vaStart - vaLast);
|
|
else
|
|
{
|
|
#endif
|
|
OutWord(1);
|
|
OutWord((WORD) (vaStart - vaLast));
|
|
OutVm(vaLast, vaStart - vaLast);
|
|
#if EXE386
|
|
}
|
|
#endif
|
|
|
|
PageBuffer = mapva((VPageAddress - PAGLEN), FALSE);
|
|
/* Refetch page */
|
|
}
|
|
|
|
|
|
LOCAL void NEAR OutIter(SATYPE sa, WORD length)
|
|
{
|
|
#if EXE386
|
|
ITER idata; /* Iterated data description for range */
|
|
|
|
|
|
if (ExeFormat == Exe386)
|
|
{
|
|
idata.iterations = (DWORD) length;
|
|
idata.length = (DWORD) 1;
|
|
idata.data = (DWORD) LastB;
|
|
UpdateRanges(ShortIterData, sa, ra, &idata);
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
OutWord(length);
|
|
OutWord(1);
|
|
OutByte(bsRunfile, LastB);
|
|
#if EXE386
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Out5Pack - Run a buffer through the compactor. Return value is
|
|
* starting position where data was written to output file.
|
|
*/
|
|
|
|
|
|
long Out5Pack (sa, packed)
|
|
SATYPE sa; /* File segment to be packed */
|
|
WORD *packed; /* TRUE if iterated records written */
|
|
{
|
|
WORD proceed; /* True if there are bytes to scan */
|
|
WORD length; /* Scanned bytes length */
|
|
long lfaStart; /* Starting file address */
|
|
|
|
|
|
lfaStart = ftell(bsRunfile); /* Get the starting address */
|
|
VPageAddress = AREASA(sa);
|
|
VPageOffset = PAGLEN;
|
|
*packed = FALSE;
|
|
if (mpsacbinit[sa] > 1L)
|
|
{ /* If buffer is big enough */
|
|
#if EXE386
|
|
ra = 0L; /* Offset within segment */
|
|
#endif
|
|
vaStart = VPageAddress;
|
|
vaLast = vaStart;
|
|
BufEnd = vaStart + mpsacbinit[sa];
|
|
LastB = GetFromVM();
|
|
CurrentB = GetFromVM();
|
|
proceed = (WORD) TRUE; /* Initialize */
|
|
|
|
while (proceed)
|
|
{
|
|
length = ScanWhileDifferent();
|
|
if (!(proceed = AfterScanning(length)))
|
|
break;
|
|
|
|
if ((length = ScanWhileSame()) > MINREPEAT)
|
|
{ /* If there are enough same bytes */
|
|
if (vaLast != vaStart)
|
|
OutEnum(); /* First write out preceeding diff. bytes */
|
|
|
|
OutIter(sa, length); /* Now write out iterated record */
|
|
proceed = AfterScanning(length);
|
|
*packed = (WORD) TRUE;
|
|
vaLast = vaStart;
|
|
}
|
|
else proceed = AfterScanning(length);
|
|
/* Otherwise enumerated record swallow this */
|
|
} /* small repeated record */
|
|
}
|
|
if (*packed)
|
|
{
|
|
if (vaLast != BufEnd)
|
|
{
|
|
vaStart = BufEnd;
|
|
OutEnum(); /* Write out any remainig bytes */
|
|
}
|
|
|
|
mpsacbinit[sa] = ftell(bsRunfile) - lfaStart;
|
|
/* Return number of written bytes */
|
|
return(lfaStart);
|
|
}
|
|
else
|
|
return(OutVm(AREASA(sa),mpsacbinit[sa]));
|
|
}
|
|
#endif /*FEXEPACK AND OSEGEXE*/
|