home *** CD-ROM | disk | FTP | other *** search
- {$S-,I-,R-}
- {$M 3000, 30000, 200000}
-
- program Pack;
- {-Packs EXE file header structure}
-
- function StUpcase(S : string) : string;
- {-Return uppercase of string}
- var
- I : integer;
- begin
- for I := 1 to length(S) do
- S[I] := upcase(S[I]);
- StUpcase := S;
- end;
-
- function HasExtension(Name : string; var DotPos : Word) : Boolean;
- {-Return whether and position of extension separator dot in a pathname}
- var
- I : Word;
- begin
- DotPos := 0;
- for I := Length(Name) downto 1 do
- if (Name[I] = '.') and (DotPos = 0) then
- DotPos := I;
- HasExtension := (DotPos > 0) and (Pos('\', Copy(Name, Succ(DotPos), 64)) = 0);
- end;
-
- function ForceExtension(Name, Ext : string) : string;
- {-Return a pathname with the specified extension attached}
- var
- DotPos : Word;
- begin
- if HasExtension(Name, DotPos) then
- ForceExtension := Copy(Name, 1, DotPos)+Ext
- else
- ForceExtension := Name+'.'+Ext;
- end;
-
- procedure Error(Msg : string);
- {-Write error message and halt}
- begin
- if Msg <> '' then
- WriteLn(^M^J, Msg);
- Halt(1);
- end;
-
- function BlkRead(var F : file; var Buffer; Size : Word) : Boolean;
- {-Convenient shell around BlockRead}
- var
- BytesRead : Word;
- begin
- BlockRead(F, Buffer, Size, BytesRead);
- BlkRead := (IoResult = 0) and (BytesRead = Size);
- end;
-
- function BlkWrite(var F : file; var Buffer; Size : Word) : Boolean;
- {-Convenient shell around BlockWrite}
- var
- BytesWritten : Word;
- begin
- BlockWrite(F, Buffer, Size, BytesWritten);
- BlkWrite := (IoResult = 0) and (BytesWritten = Size);
- end;
-
- procedure PackExe(ExeName, OutName : string);
- {-Squeeze an EXE file by packing fixups into segment groups}
- const
- MaxRWbufSize = $8000; {Max size of read/write buffer for EXE copying}
- FlagWord = $FFFF; {Flag segment changes in packed relocation table}
- OrigIPofs = 3; {Position of first patch word in NewLoader}
- ShowRLEeffect = False; {True to show value of run length encoding}
- Threshold = 4; {Bytes of overhead per RLE block}
- MaxReloc = $3FFC; {Maximum allowable relocation items}
-
- NewLoaderSize = 82;
- NewLoader : array[1..NewLoaderSize] of Byte =
- (
- $EB, $08, $00, $00, $00, $00, $00, $00, $00, $00, $2E, $8C, $1E, $06, $00, $2E,
- $8C, $06, $08, $00, $8C, $C3, $83, $C3, $10, $8C, $C8, $8E, $D8, $BE, $52, $00,
- $FC, $AD, $3D, $FF, $FF, $75, $0B, $AD, $3D, $FF, $FF, $74, $0C, $03, $C3, $8E,
- $C0, $AD, $8B, $F8, $26, $01, $1D, $EB, $E8, $2E, $8E, $06, $08, $00, $2E, $8E,
- $1E, $06, $00, $8B, $C3, $2E, $03, $06, $04, $00, $50, $2E, $A1, $02, $00, $50,
- $CB, $90
- );
-
- type
- ExeHeaderRec = {Information describing EXE file}
- record
- Signature : Word; {EXE file signature}
- LengthRem : Word; {Number of bytes in last page of EXE image}
- LengthPages : Word; {Number of 512 byte pages in EXE image}
- NumReloc : Word; {Number of relocation items}
- HeaderSize : Word; {Number of paragraphs in EXE header}
- MinHeap, MaxHeap : Word; {Paragraphs to keep beyond end of image}
- StackSeg, StackPtr : Word; {Initial SS:SP, StackSeg relative to image base}
- CheckSum : Word; {EXE file check sum, not used}
- IpInit, CodeSeg : Word; {Initial CS:IP, CodeSeg relative to image base}
- RelocOfs : Word; {Bytes into EXE for first relocation item}
- OverlayNum : Word; {Overlay number, not used here}
- end;
- RelocRec =
- record
- Offset : Word;
- Segment : Word;
- end;
- RelocArray = array[1..MaxReloc] of RelocRec;
- PackedTable = array[1..$7FF0] of Word;
- ReadWriteBuffer = array[1..MaxRWbufSize] of Byte;
-
- var
- ExeF, OutF : file;
- BytesRead, BytesWritten, RWbufSize,
- I, TableSize, TablePos, LastSeg,
- BlockSize, OldNumReloc, OldHeaderSize : Word;
- OldExeSize, ExeSize, RLEbytes : LongInt;
- LastByte : Byte;
- ExeHeader : ExeHeaderRec;
- RA : ^RelocArray; {Old relocation table from input file}
- PT : ^PackedTable; {New relocation table after packing}
- RWbuf : ^ReadWriteBuffer; {Read/write buffer for file copy}
-
- procedure SetTable(var TA : PackedTable; var TablePos : Word; Value : Word);
- {-Put a value into packed table and increment the index}
- begin
- TA[TablePos] := Value;
- Inc(TablePos);
- end;
-
- begin
-
- {Make sure we don't overwrite the input}
- if StUpcase(ExeName) = StUpcase(OutName) then
- Error('Input and output files must differ');
-
- {Open the existing EXE file}
- Assign(ExeF, ExeName);
- Reset(ExeF, 1);
- if IoResult <> 0 then
- Error(ExeName+' not found');
-
- {Read the existing EXE header}
- if not BlkRead(ExeF, ExeHeader, SizeOf(ExeHeaderRec)) then
- Error('Error reading EXE file');
-
- with ExeHeader do begin
-
- {Assure it's a real EXE file}
- if Signature <> $5A4D then
- Error('File is not in EXE format');
-
- {Check the number of relocation items}
- if NumReloc = 0 then
- Error('No packing can be done. No output written');
- if NumReloc > MaxReloc then
- Error('Number of relocation items exceeds capacity of PACK');
- if NumReloc shl 2 > MaxAvail then
- Error('Insufficient memory');
-
- {Read the relocation items into memory}
- GetMem(RA, NumReloc shl 2);
- Seek(ExeF, RelocOfs);
- if not BlkRead(ExeF, RA^, NumReloc shl 2) then
- Error('Error reading EXE file');
-
- {Determine size of packed relocation table in bytes}
- LastSeg := $FFFF;
- TableSize := 0;
- for I := 1 to NumReloc do
- with RA^[I] do begin
- if Segment <> LastSeg then begin
- LastSeg := Segment;
- {Table will hold FFFF as a flag, followed by new segment}
- Inc(TableSize, 4);
- end;
- {Space for the offset in this record}
- Inc(TableSize, 2);
- end;
- {Termination record}
- Inc(TableSize, 4);
-
- {Build the packed relocation table in memory}
- if TableSize > MaxAvail then
- Error('Insufficient memory');
-
- GetMem(PT, TableSize);
- LastSeg := $FFFF;
- TablePos := 1;
- for I := 1 to NumReloc do
- with RA^[I] do begin
- if Segment <> LastSeg then begin
- LastSeg := Segment;
- {Flag that the segment is changing}
- SetTable(PT^, TablePos, FlagWord);
- {Write the new segment}
- SetTable(PT^, TablePos, Segment);
- end;
- {Write the offset in the segment}
- SetTable(PT^, TablePos, Offset);
- end;
- {Write a termination record}
- for I := 1 to 2 do
- SetTable(PT^, TablePos, FlagWord);
-
- {Deallocate space for the old relocation array}
- FreeMem(RA, NumReloc shl 2);
-
- {Allocate space for the read/write buffer}
- if MaxAvail > MaxRWbufSize then
- RWbufSize := MaxRWbufSize
- else
- RWbufSize := MaxAvail;
- GetMem(RWbuf, RWbufSize);
-
- {Save some items we'll need later}
- OldNumReloc := NumReloc; {items}
- OldHeaderSize := HeaderSize; {paragraphs}
- if LengthRem = 0 then
- OldExeSize := LongInt(LengthPages) shl 9
- else
- OldExeSize := (LongInt(Pred(LengthPages)) shl 9)+LongInt(LengthRem);
-
- {Change the header to accomodate the packing}
- {No fixups remain after packing}
- NumReloc := 0;
- {Headersize shrinks to size of header record, rounded to para boundary}
- HeaderSize := (SizeOf(ExeHeaderRec)+15) shr 4; {paragraphs}
- {Patch initial CS:IP into the new loader}
- Move(IpInit, NewLoader[OrigIPofs], 4);
- {Set up so our loader executes first}
- IpInit := 0;
- CodeSeg := Succ(OldExeSize shr 4)-OldHeaderSize; {paragraphs}
-
- {Compute new exesize}
- ExeSize := (LongInt(HeaderSize)+LongInt(CodeSeg)) shl 4
- +LongInt(NewLoaderSize)+LongInt(TableSize); {bytes}
- if ExeSize >= OldExeSize then
- Error('Packed size exceeds original. No output written');
-
- if (ExeSize and 511) = 0 then begin
- {An exact number of pages}
- LengthPages := ExeSize shr 9;
- LengthRem := 0;
- end else begin
- LengthPages := Succ(ExeSize shr 9);
- LengthRem := ExeSize-LongInt(Pred(LongInt(LengthPages)) shl 9);
- end;
-
- {Create the new EXE file}
- Assign(OutF, OutName);
- Rewrite(OutF, 1);
- if IoResult <> 0 then
- Error('Could not create '+OutName);
-
- {Write the new header}
- if not BlkWrite(OutF, ExeHeader, (HeaderSize shl 4)) then
- Error('Error writing EXE file');
-
- {Transfer the code from old to new program}
- Seek(ExeF, OldHeaderSize shl 4);
-
- {Initialize parameters for run length encoding}
- LastByte := 0;
- BlockSize := 0;
- RLEbytes := 00;
-
- repeat
- BlockRead(ExeF, RWbuf^, RWbufSize, BytesRead);
- if IoResult <> 0 then
- Error('Error reading EXE file');
- if BytesRead <> 0 then begin
- if not BlkWrite(OutF, RWbuf^, BytesRead) then
- Error('Error writing EXE file');
-
- if ShowRLEeffect then
- {Check to see how much run length packing would save}
- for I := 1 to BytesRead do
- if RWbuf^[I] = LastByte then
- Inc(BlockSize)
- else begin
- LastByte := RWbuf^[I];
- if BlockSize > Threshold then
- Inc(RLEbytes, BlockSize-Threshold);
- BlockSize := 0;
- end;
- end;
- until BytesRead = 0;
-
- if ShowRLEeffect then
- if BlockSize > Threshold then
- Inc(RLEbytes, BlockSize-Threshold);
-
- {Write the loader to the new program}
- Seek(OutF, (LongInt(HeaderSize)+LongInt(CodeSeg)) shl 4);
- if not BlkWrite(OutF, NewLoader, NewLoaderSize) then
- Error('Error writing EXE file');
-
- {Write the packed loader table to the program}
- if not BlkWrite(OutF, PT^, TableSize) then
- Error('Error writing EXE file');
-
- if ShowRLEeffect then
- WriteLn('Run length packing would save ', RLEbytes, ' bytes');
-
- end;
-
- {Release heap space we allocated}
- FreeMem(PT, TableSize);
- FreeMem(RWbuf, RWbufSize);
-
- {Close up the files}
- Close(ExeF);
- Close(OutF);
- end;
-
- begin
- if ParamCount < 2 then
- Error('Usage: PACK OldExeName NewExeName');
- {Modify the EXE file}
- PackExe(ForceExtension(ParamStr(1), 'EXE'), ForceExtension(ParamStr(2), 'EXE'));
- end.