// Основная процедура

?define SafeSize        4096
?define ResAllign       0x2

void fastcall PatchResources(EDX);
void fastcall StartPatch(ESI);
dword RebuildResources(ESI,dword ResDst,ResReal,RsRVA,Src);
dword	fastcall Compress(ESI,EDI,ECX);
dword	fastcall CompressRLE(ESI,EDI,ECX);
void fastcall CheckJumps(EDI,ECX,EDX);

dword xrDst,xrSze,xrSrc;
dword NewRR2;
dword ResRVA,ResPhys;
dword Delta;
dword CurDc;

?include "mzhdr.h--";
?include "pehdr.h--";
?include "unpack.h--"
?include "lzss.h--";
?include "lzssrle.h--";
?include "jumps.h--";
?include "resrc.h--";

#define SIZE2OBJENT 2*sizeof(OBJECT_ENTRY)
#define SIZE_HEADOBJ SIZE2OBJENT+sizeof(PE_HEADER)

void BackChar(int count)
{
	loop(count)printf("\x08");
}

dword Protect(dword Src,Dst,Cnt)
{
dword ExpRva,ExpPhys;
dword ImpPos;
dword PxSize;
dword PackedS;
dword xPart;
dword NewSize;
dword TLSRva,TLSPhys;
dword NewRR;
dword dPzzr;	//последний адрес выходного файла
dword dPll;
dword dPzt,dPos;
dword i,j;
dword startjmp;
dword monoblock;
	xrSze=Cnt;
	ESI=xrSrc=Src;
	monoblock=startjmp=Jmpn=ResRVA=PxSize=0;
	EBX=DSDWORD[ESI+0x3C]+ESI;
	IF(DSWORD[ESI]!='ZM')||(DSWORD[EBX]!='EP'){
		MessageErr("Given file not an valid executable");
		return(0);
	}
	IF(cfgpack.DosStub==0){
		ECX=DSDWORD[ESI+0x3C];
	}
	ELSE{
		wsprintf(#MzHdr+32,"EPACK v%s %s",VERSION,__DATE__);
		ECX=MzLen;
		ESI=#MzHdr;				// Copy own MZ header
	}
	EDI=Dst;
	$cld
	$rep $movsb
	xPart=EDI;
	ESI=EBX+sizeof(PE_HEADER);
	IF(DSDWORD[ESI+#OBJECT_ENTRY.name]!='ssb.')&&(DSDWORD[ESI+#OBJECT_ENTRY.psize]==0){
		MessageErr("But file looks like already compressed");
		return(0);
	}
	ECX=i=DSWORD[EBX+#PE_HEADER.numobj];
	EDX=0;
	loop(ECX){
		EAX=DSDWORD[ESI+#OBJECT_ENTRY.pOffset]+DSDWORD[ESI+#OBJECT_ENTRY.psize];
		IF(EAX>EDX)EDX><EAX;
		ESI+=sizeof(OBJECT_ENTRY);
	}
	IF(consolemode){
		$push EBX
		printf("\nPacking 0%%   %s",#cFile);
		$pop EBX
		BackChar(strlen(#cFile)+5);
	}
	pe_head=0;
	pe_head.sign='EP';
	pe_head.cpu=0x14c;
	pe_head.numobj=2;
	pe_head.Magic=0x10b;
	pe_head.LinkVer=0x101;
	pe_head.objAlig=4096;
	pe_head.fileAlig=512;
	pe_head.OSver=4;
	pe_head.numRVA=0x10;
	InitDecompressor(EBX);
	ESI=EBX+sizeof(PE_HEADER);
	xrDst=dPzzr=dPzt=GlobalAlloc(GMEM_FIXED,DSDWORD[EBX+#PE_HEADER.imagesize]+SafeSize);
	loop(i){
		IF(!consolemode){
			$push ESI
			SendMessageA(hStatus,SB_SETTEXT,0x00FF0000,ESI);
			$pop ESI
		}
		EDX=DSDWORD[ESI+#OBJECT_ENTRY.sectionRVA];
		j=EDX+DSDWORD[ESI+#OBJECT_ENTRY.psize];	//конец данных в секции
		if(EDX!=EAX){
			EAX=DSDWORD[EBX+#PE_HEADER.resourRVA];
			IF(EAX>=EDX)&&(EAX<j){   // Resource section ?
				IF(EAX!=EDX){	//ресурсы находится в секции не одни
					ResRVA=EAX;
					ResPhys=EAX-EDX+DSDWORD[ESI+#OBJECT_ENTRY.pOffset];
					DSDWORD[ESI+#OBJECT_ENTRY.psize]=EAX-DSDWORD[ESI+#OBJECT_ENTRY.pOffset];
					monoblock=TRUE;
					GOTO CONTOBJ;
				}
				ResRVA=DSDWORD[ESI+#OBJECT_ENTRY.sectionRVA];
				ResPhys=DSDWORD[ESI+#OBJECT_ENTRY.pOffset];
			}
			else{
CONTOBJ:
				EAX=DSDWORD[EBX+#PE_HEADER.tlsRVA];
				IF(EAX>=EDX)&&(EAX<j){
					TLSRva=DSDWORD[ESI+#OBJECT_ENTRY.sectionRVA];
					TLSPhys=DSDWORD[ESI+#OBJECT_ENTRY.pOffset];
				}
				EAX=DSDWORD[EBX+#PE_HEADER.exportRVA];
				IF(EAX>=EDX)&&(EAX<j){		// Export
					ExpRva=DSDWORD[ESI+#OBJECT_ENTRY.sectionRVA];
					ExpPhys=DSDWORD[ESI+#OBJECT_ENTRY.pOffset];
					RealLen=RealLenFix;
				}
				EAX=DSDWORD[EBX+#PE_HEADER.importRVA];
				IF(EAX<EDX)||(EAX>j){	//не импорт
					EAX=DSDWORD[EBX+#PE_HEADER.fixupRVA];
					IF(DSDWORD[EBX+#PE_HEADER.exportRVA]==0)&&			// Reloc
					 (EAX>=EDX)&&(EAX<j)goto SkipSection;	//пропустить fixup если нет export
					EAX=DSDWORD[EBX+#PE_HEADER.exportRVA];
					IF(EAX>=EDX)&&(EAX<j)GOTO SkipSection;		// Export ???
				}
				EAX=DSDWORD[EBX+#PE_HEADER.EntryRVA];
				IF(EAX>=EDX)||(EAX<j){	//секция кода
					EDI=DSDWORD[ESI+#OBJECT_ENTRY.pOffset]+Src;
					ECX=DSDWORD[ESI+#OBJECT_ENTRY.psize];
					IF(monoblock)ECX=DSDWORD[EBX+#PE_HEADER.importRVA]-DSDWORD[ESI+#OBJECT_ENTRY.sectionRVA];
					ECX+=EDI;
					CheckJumps(EDI,ECX,DSDWORD[ESI+#OBJECT_ENTRY.sectionRVA]);
					IF(startjmp==0)startjmp=DSDWORD[ESI+#OBJECT_ENTRY.sectionRVA];
				}
				$pushad
				ECX=DSDWORD[ESI+#OBJECT_ENTRY.psize];        // Place Section
				EDI=dPzzr;
				EAX=DSDWORD[ESI+#OBJECT_ENTRY.sectionRVA];
				ESI=DSDWORD[ESI+#OBJECT_ENTRY.pOffset]+Src;
				$stosd			  // Section Length
				$stosd
				SWITCH(cfgpack.CompressionType){
					CASE 0:
						Compress(ESI,EDI,ECX);
						BREAK;
					CASE 1:
						CompressRLE(ESI,EDI,ECX);
				}
				dPll=EAX;
				$popad
				EDX=dPzzr+4;
				DSDWORD[EDX]=dPll;
				dPzzr+=dPll+8;
			}
		}
SkipSection:
		ESI+=sizeof(OBJECT_ENTRY);
	}
	object.sectionRVA=object[1].sectionRVA=DSDWORD[EBX+sizeof(PE_HEADER)+#OBJECT_ENTRY.sectionRVA];
	EAX=DSDWORD[EBX+#PE_HEADER.imagesize]+511&0xFFFFFE00;
	object.vsize=EAX;
	EAX=EAX+4095&0xFFFFF000;
	object[1].sectionRVA+=EAX;	//RVA 2 объекта
	EDX=ResRVA;	//RVA секции ресурсов
	if(EDX){	//есть секция ресурсов
		$push EBX
		ESI=EDX+Src-ResRVA+ResPhys;	//смещение ресурсов в файле
		ECX=DSDWORD[EBX+#PE_HEADER.resourSize];	//размер ресурсов
		$push ECX,ECX
		NewRR=GlobalAlloc(GMEM_FIXED,ECX);
		$pop ECX
		NewRR2=GlobalAlloc(GMEM_FIXED,ECX);
		pe_head.resourSize=RebuildResources(ESI,NewRR,NewRR2,object[1].sectionRVA,Src);
		EDI=dPzzr;
		ESI=NewRR2;
		$pop ECX
		EAX=ResRVA;
		$stosd
		$stosd
		rescmp++;
		SWITCH(cfgpack.CompressionType){
			CASE 0:
				Compress(ESI,EDI,ECX);
				BREAK;
			CASE 1:
				CompressRLE(ESI,EDI,ECX);
		}
		dPll=EAX;
		EDX=dPzzr+4;
		DSDWORD[EDX]=dPll;
		dPzzr+=dPll+8;
		$pop EBX
	}
	EDI=dPzzr;
	EAX=0;
	$stosd
	NewSize=EDI-dPzt;
	IF(EAX&1)NewSize++;
	IF(ResRVA){
		EDI+=RealLen;	//размер сжатый образ + декомпресор
		EDX=EDI-dPzt;
		EDX&=ResAllign-1;	//выравненый на 2.
		PxSize=EDX;
		ECX=ResAllign-EDX+EDI;
		EDI=ECX;
		EDX=EDI-dPzt;
		$push EDX
		pe_head.resourRVA=EDI-dPzt+object[1].sectionRVA;
		ESI=NewRR;
		ECX=pe_head.resourSize;
		$push EDI
		$rep $movsb	//перенести то что не сжималось в ресурсах
		$pop ESI
		$pop EDX
		$push EBX
		PatchResources(EDX);
		$pop EBX
	}
	EDX=CurDc;
	dPos=dPzt+NewSize;
	ImpPos=EAX+DeLen;
	if(DSDWORD[EBX+#PE_HEADER.exportRVA]){	//секция экспорта
		EDI=EDX+DSDWORD[EDX+#UCHEADER.SPos];
		DSDWORD[EDI]=dPos-dPzt+object[1].sectionRVA+DSDWORD[EBX+#PE_HEADER.ImageBase]+6;
		EDI=EDX+DSDWORD[EDX+#UCHEADER.ReloOld];
		DSDWORD[EDI]=DSDWORD[EBX+#PE_HEADER.fixupRVA]+DSDWORD[EBX+#PE_HEADER.ImageBase];
		EDI=EDX+DSDWORD[EDX+#UCHEADER.ReloLen];
		DSDWORD[EDI]=DSDWORD[EBX+#PE_HEADER.fixupSize];
		EDI=dPzt+NewSize+pe_head.resourSize+RealLen;
		pe_head.fixupRVA=EDI-dPzt+object[1].sectionRVA-pe_head.fixupSize;
/*
		ESI=#DLLReloc;
		EAX=dPos-dPzt+object[1].sectionRVA+DSDWORD[EDX+ReloPos]-RealDC;
		EDX=EAX;
		EAX&=0xFFF;
		ECX=EAX+0x3000;
		DLLReloc.ReloPatch=CX;
		ECX=0x1000-EDX;
		ECX=EDX-ECX;
		DLLReloc.ReloOfs=ECX;
		ECX=sizeof(DLLReloc);
		pe_head.fixupSize=ECX;
		pe_head.fixupRVA=EDI-dPzt+object[1].sectionRVA;
		$rep $movsb*/
		ECX=pe_head.exportSize=DSDWORD[EBX+#PE_HEADER.exportSize];
		pe_head.exportRVA=EDI-dPzt+object[1].sectionRVA;
		ESI=DSDWORD[EBX+#PE_HEADER.exportRVA]-ExpRva+ExpPhys+Src;
		$push EDI
		$rep $movsb
		$pop EDI
		EDX=pe_head.exportRVA-DSDWORD[EBX+#PE_HEADER.exportRVA];		// Delta
		DSDWORD[EDI+0xC]+=EDX;
		DSDWORD[EDI+0x1C]+=EDX;
		DSDWORD[EDI+0x20]+=EDX;
		DSDWORD[EDI+0x24]+=EDX;
		ECX=DSDWORD[EDI+0x18];
		EAX=DSDWORD[EDI+0x20]+dPzt-object[1].sectionRVA;
		EDI=EAX;
		loop(ECX){
			DSDWORD[EDI]+=EDX;
			EDI+=4;
		}
	}
	EDX=CurDc;
	IF(DSDWORD[EBX+#PE_HEADER.tlsRVA]){	//секция TSL
		EDI=dPzt+NewSize+pe_head.resourSize+RealLen+pe_head.fixupSize;
		ESI=DSDWORD[EBX+#PE_HEADER.tlsRVA]-TLSRva+TLSPhys+Src;
		ECX=DSDWORD[EBX+#PE_HEADER.tlsSize];
		pe_head.tlsSize=ECX;
		pe_head.tlsRVA=EDI-dPzt+object[1].sectionRVA;
		$rep $movsb
	}
	PackedS=NewSize+pe_head.resourSize+RealLen+pe_head.fixupSize+pe_head.tlsSize+pe_head.exportSize;
// PE Header Reconstruction
	object[1].vsize=EAX+4095&0xFFFFF000;
	EAX=SIZE_HEADOBJ+xPart-Dst+511&0xFFFFFE00;
	object.pOffset=object[1].pOffset=pe_head.sizecode=pe_head.sizeinitdata=EAX;
	pe_head.date_time=DSDWORD[EBX+#PE_HEADER.date_time];
	pe_head.NTheadsize=DSWORD[EBX+#PE_HEADER.NTheadsize];
	pe_head.flags=DSWORD[EBX+#PE_HEADER.flags];
	pe_head.sizeuninitdata=object.vsize;
	pe_head.EntryRVA=dPos-dPzt+object[1].sectionRVA;
	EAX=DSDWORD[EBX+sizeof(PE_HEADER)+#OBJECT_ENTRY.sectionRVA];
	pe_head.basecode=pe_head.basedata=EAX;
	pe_head.ImageBase=DSDWORD[EBX+#PE_HEADER.ImageBase];
	pe_head.OSver=DSDWORD[EBX+#PE_HEADER.OSver];
	pe_head.headsize=DSDWORD[Src+0x3C]+SIZE_HEADOBJ+255&0xFFFFFF00;
	pe_head.SubSys=DSDWORD[EBX+#PE_HEADER.SubSys];
	pe_head.stackRezSize=DSDWORD[EBX+#PE_HEADER.stackRezSize];
	pe_head.stackComSize=DSDWORD[EBX+#PE_HEADER.stackComSize];
	pe_head.heapRezSize=DSDWORD[EBX+#PE_HEADER.heapRezSize];
	pe_head.heapComSize=DSDWORD[EBX+#PE_HEADER.heapComSize];
	pe_head.SubSysVer=DSDWORD[EBX+#PE_HEADER.SubSysVer];
	pe_head.importRVA=ImpPos-dPzt+object[1].sectionRVA;
	RelocateDC(EAX-DImport);
	EDX=CurDc;
	EAX=PackedS+PxSize;
	EAX&=511;
	ECX=512-EAX;
	ECX+=PackedS;
	object[1].psize=ECX;
	ESI=EDX+RealDC;
	EDI=dPos;
	ECX=RealLen;
	$rep $movsb	//скопировать декомпресор
	EDI=dPos+DSDWORD[EDX+#UCHEADER.uSrc]-RealDC;
	DSDWORD[EDI]=object[1].sectionRVA+pe_head.ImageBase;
	EDI=dPos+DSDWORD[EDX+#UCHEADER.JumpRVA]-RealDC;
	DSDWORD[EDI]=startjmp+pe_head.ImageBase;
	EDI=dPos+DSDWORD[EDX+#UCHEADER.JumpNum]-RealDC;
	DSDWORD[EDI]=Jmpn;
	EDI=dPos+DSDWORD[EDX+#UCHEADER.OldImp]-RealDC;
	DSDWORD[EDI]=DSDWORD[EBX+#PE_HEADER.importRVA]+pe_head.ImageBase;
	EDI=dPos+DSDWORD[EDX+#UCHEADER.OldEnt]-RealDC;
	DSDWORD[EDI]=DSDWORD[EBX+#PE_HEADER.EntryRVA]+pe_head.ImageBase;
	EDI=dPos+DSDWORD[EDX+#UCHEADER.MyIBase]-RealDC;
	DSDWORD[EDI]=pe_head.ImageBase;
	pe_head.imagesize=object[1].sectionRVA+object[1].vsize;
	ESI=#pe_head;
	EDI=xPart;
	ECX=sizeof(PE_HEADER);
	$rep $movsb	//PE -заголовок
	EDI=xPart+sizeof(PE_HEADER);
	ESI=#object;
	ECX=sizeof(object);
	$rep $movsb	// таблица объектов
	ESI=dPzt;
	EDI=Dst+object.pOffset;
	ECX=object[1].psize;
	$rep $movsb	//сжатый образ
	GlobalFree(NewRR);
	GlobalFree(NewRR2);
	GlobalFree(dPzt);
	return(EDI-Dst);
}

void fastcall PatchResources(EDX)
{
	EBX=ESI;
	Delta=EDX;
	StartPatch(ESI);
}

void PATCH1()
{
	LOOPNZ(ECX){
		$lodsd
		$lodsd
		EDX=EAX;
		EAX<<=1;
		IF(CARRYFLAG){
			$push ECX
			$push ESI
			EAX>>=1;
			EAX+=EBX;
			StartPatch(EAX);
			$pop ESI
			$pop ECX
		}
		ELSE{
			EDX+=EBX;
			IF(DSDWORD[EDX]>object[1].sectionRVA)DSDWORD[EDX]+=Delta;
		}
	}
}

void fastcall StartPatch(ESI)
{
	$lodsd   // Flags
	$lodsd   // Time/Date stamp
	$lodsd   // Minor/Major version
	EAX=0;
	$lodsw
	ECX=EAX;
	$lodsw
	$push EAX
	PATCH1();
	$pop ECX
	PATCH1();
}
