
IMAGE_FILE_MACHINE_UNKNOWN   = 0x0
IMAGE_FILE_MACHINE_AM33      = 0x1D3
IMAGE_FILE_MACHINE_AMD64     = 0x8664
IMAGE_FILE_MACHINE_ARM	     = 0x1C0
IMAGE_FILE_MACHINE_ARMNT     = 0x1C4
IMAGE_FILE_MACHINE_ARM64     = 0xAA64
IMAGE_FILE_MACHINE_EBC	     = 0xEBC
IMAGE_FILE_MACHINE_I386      = 0x14C
IMAGE_FILE_MACHINE_IA64      = 0x200
IMAGE_FILE_MACHINE_M32R      = 0x9041
IMAGE_FILE_MACHINE_MIPS16    = 0x266
IMAGE_FILE_MACHINE_MIPSFPU   = 0x366
IMAGE_FILE_MACHINE_MIPSFPU16 = 0x466
IMAGE_FILE_MACHINE_POWERPC   = 0x1F0
IMAGE_FILE_MACHINE_POWERPCFP = 0x1F1
IMAGE_FILE_MACHINE_R4000     = 0x166
IMAGE_FILE_MACHINE_SH3	     = 0x1A2
IMAGE_FILE_MACHINE_SH3DSP    = 0x1A3
IMAGE_FILE_MACHINE_SH4	     = 0x1A6
IMAGE_FILE_MACHINE_SH5	     = 0x1A8
IMAGE_FILE_MACHINE_THUMB     = 0x1C2
IMAGE_FILE_MACHINE_WCEMIPSV2 = 0x169

IMAGE_FILE_RELOCS_STRIPPED	    = 0x0001
IMAGE_FILE_EXECUTABLE_IMAGE	    = 0x0002
IMAGE_FILE_LINE_NUMS_STRIPPED	    = 0x0004
IMAGE_FILE_LOCAL_SYMS_STRIPPED	    = 0x0008
IMAGE_FILE_AGGRESSIVE_WS_TRIM	    = 0x0010
IMAGE_FILE_LARGE_ADDRESS_AWARE	    = 0x0020
IMAGE_FILE_BYTES_REVERSED_LO	    = 0x0080
IMAGE_FILE_32BIT_MACHINE	    = 0x0100
IMAGE_FILE_DEBUG_STRIPPED	    = 0x0200
IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP  = 0x0400
IMAGE_FILE_NET_RUN_FROM_SWAP	    = 0x0800
IMAGE_FILE_SYSTEM		    = 0x1000
IMAGE_FILE_DLL			    = 0x2000
IMAGE_FILE_UP_SYSTEM_ONLY	    = 0x4000
IMAGE_FILE_BYTES_REVERSED_HI	    = 0x8000

IMAGE_DLLCHARACTERISTICS_HIGH_ENTROPY_VA       = 0x0020
IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE	       = 0x0040
IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY       = 0x0080
IMAGE_DLLCHARACTERISTICS_NX_COMPAT	       = 0x0100
IMAGE_DLLCHARACTERISTICS_NO_ISOLATION	       = 0x0200
IMAGE_DLLCHARACTERISTICS_NO_SEH 	       = 0x0400
IMAGE_DLLCHARACTERISTICS_NO_BIND	       = 0x0800
IMAGE_DLLCHARACTERISTICS_APPCONTAINER	       = 0x1000
IMAGE_DLLCHARACTERISTICS_WDM_DRIVER	       = 0x2000
IMAGE_DLLCHARACTERISTICS_GUARD_CF	       = 0x4000
IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = 0x8000

IMAGE_SUBSYSTEM_UNKNOWN 		= 0
IMAGE_SUBSYSTEM_NATIVE			= 1
IMAGE_SUBSYSTEM_WINDOWS_GUI		= 2
IMAGE_SUBSYSTEM_WINDOWS_CUI		= 3
IMAGE_SUBSYSTEM_POSIX_CUI		= 7
IMAGE_SUBSYSTEM_WINDOWS_CE_GUI		= 9
IMAGE_SUBSYSTEM_EFI_APPLICATION 	= 10
IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER = 11
IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER	= 12
IMAGE_SUBSYSTEM_EFI_ROM 		= 13
IMAGE_SUBSYSTEM_XBOX			= 14

IMAGE_SCN_TYPE_NO_PAD		 = 0x00000008
IMAGE_SCN_CNT_CODE		 = 0x00000020
IMAGE_SCN_CNT_INITIALIZED_DATA	 = 0x00000040
IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080
IMAGE_SCN_LNK_OTHER		 = 0x00000100
IMAGE_SCN_LNK_INFO		 = 0x00000200
IMAGE_SCN_LNK_REMOVE		 = 0x00000800
IMAGE_SCN_LNK_COMDAT		 = 0x00001000
IMAGE_SCN_GPREL 		 = 0x00008000
IMAGE_SCN_MEM_PURGEABLE 	 = 0x00020000
IMAGE_SCN_MEM_16BIT		 = 0x00020000
IMAGE_SCN_MEM_LOCKED		 = 0x00040000
IMAGE_SCN_MEM_PRELOAD		 = 0x00080000
IMAGE_SCN_ALIGN_1BYTES		 = 0x00100000
IMAGE_SCN_ALIGN_2BYTES		 = 0x00200000
IMAGE_SCN_ALIGN_4BYTES		 = 0x00300000
IMAGE_SCN_ALIGN_8BYTES		 = 0x00400000
IMAGE_SCN_ALIGN_16BYTES 	 = 0x00500000
IMAGE_SCN_ALIGN_32BYTES 	 = 0x00600000
IMAGE_SCN_ALIGN_64BYTES 	 = 0x00700000
IMAGE_SCN_ALIGN_128BYTES	 = 0x00800000
IMAGE_SCN_ALIGN_256BYTES	 = 0x00900000
IMAGE_SCN_ALIGN_512BYTES	 = 0x00A00000
IMAGE_SCN_ALIGN_1024BYTES	 = 0x00B00000
IMAGE_SCN_ALIGN_2048BYTES	 = 0x00C00000
IMAGE_SCN_ALIGN_4096BYTES	 = 0x00D00000
IMAGE_SCN_ALIGN_8192BYTES	 = 0x00E00000
IMAGE_SCN_LNK_NRELOC_OVFL	 = 0x01000000
IMAGE_SCN_MEM_DISCARDABLE	 = 0x02000000
IMAGE_SCN_MEM_NOT_CACHED	 = 0x04000000
IMAGE_SCN_MEM_NOT_PAGED 	 = 0x08000000
IMAGE_SCN_MEM_SHARED		 = 0x10000000
IMAGE_SCN_MEM_EXECUTE		 = 0x20000000
IMAGE_SCN_MEM_READ		 = 0x40000000
IMAGE_SCN_MEM_WRITE		 = 0x80000000

IMAGE_REL_BASED_ABSOLUTE = 0
IMAGE_REL_BASED_HIGH	 = 1
IMAGE_REL_BASED_LOW	 = 2
IMAGE_REL_BASED_HIGHLOW  = 3
IMAGE_REL_BASED_HIGHADJ  = 4
IMAGE_REL_BASED_DIR64	 = 10

calminstruction align? boundary,value:?
	check	$ relativeto 0 | ( $ relativeto PE.RELOCATION & PE.SECTION_ALIGNMENT mod boundary = 0 )
	jyes	allowed
	err	'section not aligned enough'
	exit
    allowed:
	compute boundary, (boundary-1)-((0 scaleof $)+boundary-1) mod boundary
	arrange value, =db boundary =dup value
	assemble value
end calminstruction

PE::

namespace PE

	if defined Settings.Magic
		MAGIC = Settings.Magic
	else
		MAGIC = 0x10B
	end if

	if defined Settings.Machine
		MACHINE = Settings.Machine
	else
		MACHINE = IMAGE_FILE_MACHINE_I386
	end if

	if defined Settings.Characteristics
		CHARACTERISTICS = Settings.Characteristics
	else
		CHARACTERISTICS = IMAGE_FILE_EXECUTABLE_IMAGE or IMAGE_FILE_32BIT_MACHINE
	end if

	if defined Settings.DllCharacteristics
		DLL_CHARACTERISTICS = Settings.DllCharacteristics
	else
		DLL_CHARACTERISTICS = 0
	end if

	if defined Settings.Subsystem
		SUBSYSTEM = Settings.Subsystem
	else
		SUBSYSTEM = IMAGE_SUBSYSTEM_WINDOWS_CUI
	end if

	if defined Settings.MajorSubsystemVersion
		MAJOR_SUBSYSTEM_VERSION = Settings.MajorSubsystemVersion
	else
		MAJOR_SUBSYSTEM_VERSION = 3
	end if

	if defined Settings.MinorSubsystemVersion
		MINOR_SUBSYSTEM_VERSION = Settings.MinorSubsystemVersion
	else
		MINOR_SUBSYSTEM_VERSION = 10
	end if

	if defined Fixups
		element RELOCATION
		DLL_CHARACTERISTICS = DLL_CHARACTERISTICS or IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
	else
		RELOCATION := 0
		CHARACTERISTICS = CHARACTERISTICS or IMAGE_FILE_RELOCS_STRIPPED
	end if

	if defined Settings.ImageBase
		IMAGE_BASE := RELOCATION + Settings.ImageBase
	else
		IMAGE_BASE := RELOCATION + 400000h
	end if

	if defined Settings.SectionAlignment
		SECTION_ALIGNMENT := Settings.SectionAlignment
	else
		SECTION_ALIGNMENT := 1000h
	end if

	if defined Settings.FileAlignment
		FILE_ALIGNMENT := Settings.FileAlignment
	else
		FILE_ALIGNMENT := 512
	end if

	if defined Settings.LegacyHeaders
		LEGACY_HEADERS := Settings.LegacyHeaders
	else
		LEGACY_HEADERS := 1
	end if

	NUMBER_OF_DIRECTORIES := 16

	if defined Settings.Stub

		virtual at 0
			file Settings.Stub
			if $ >= 1Ch
				load SIGNATURE : word from 0
				if SIGNATURE = "MZ" | SIGNATURE = "ZM"
					StubTemplate::
				end if
			end if
		end virtual

		if defined StubTemplate

			load .BYTES_IN_LAST_PAGE : word from StubTemplate:2
			load .NUMBER_OF_PAGES : word from StubTemplate:4
			.TEMPLATE_LENGTH = .NUMBER_OF_PAGES shl 9 - (-.BYTES_IN_LAST_PAGE) and 1FFh

			load .RELOCATIONS_OFFSET : word from StubTemplate:18h
			if .RELOCATIONS_OFFSET >= 40h
				file Settings.Stub,.TEMPLATE_LENGTH
			else
				load .NUMBER_OF_RELOCATIONS : word from StubTemplate:6
				.RELOCATIONS_LENGTH = .NUMBER_OF_RELOCATIONS shl 2
				load .NUMBER_OF_HEADER_PARAGRAPHS : word from StubTemplate:8
				.TEMPLATE_HEADER_LENGTH = .NUMBER_OF_HEADER_PARAGRAPHS shl 4

				file Settings.Stub,1Ch
				rb 40h - $
				file Settings.Stub:.RELOCATIONS_OFFSET,.RELOCATIONS_LENGTH
				align 16
				.HEADER_LENGTH = $
				file Settings.Stub:.TEMPLATE_HEADER_LENGTH,.TEMPLATE_LENGTH-.TEMPLATE_HEADER_LENGTH
				.LENGTH = $

				store 40h : word at 18h
				store .HEADER_LENGTH shr 4 : word at 8
				store .LENGTH and 1FFh : word at 2
				store (.LENGTH-1) shr 9 + 1 : word at 4
			end if

			store Header : dword at 3Ch

		else

			Stub:
			.signature		       dw "MZ"
			.bytes_in_last_page	       dw .LENGTH and 1FFh
			.number_of_pages	       dw (.LENGTH-1) shr 9 + 1
			.number_of_relocations	       dw 0
			.number_of_header_paragraphs   dw .HEADER_LENGTH shr 4
			.minimum_heap		       dw (10000h - (.LENGTH-.HEADER_LENGTH)) shr 4
			.maximum_heap		       dw 0FFFFh
			.initial_ss		       dw (-100h) shr 4
			.initial_sp		       dw 0FFFEh
			.checksum		       dw 0
			.initial_ip		       dw 100h
			.initial_cs		       dw (-100h) shr 4
			.relocations_offset	       dw 40h
			.overlay_number 	       dw 0
						       rb 3Ch - $
			.new_header_offset	       dd Header

			.HEADER_LENGTH = $

			file Settings.Stub

			.LENGTH = $

		end if

	else

		Stub:
		.signature		       dw "MZ"
		.bytes_in_last_page	       dw .LENGTH and 1FFh
		.number_of_pages	       dw (.LENGTH-1) shr 9 + 1
		.number_of_relocations	       dw 0
		.number_of_header_paragraphs   dw .HEADER_LENGTH shr 4
		.minimum_heap		       dw .STACK_LENGTH shr 4
		.maximum_heap		       dw 0FFFFh
		.initial_ss		       dw 0
		.initial_sp		       dw .LENGTH - .HEADER_LENGTH + .STACK_LENGTH
		.checksum		       dw 0
		.initial_ip		       dw 0
		.initial_cs		       dw 0
		.relocations_offset	       dw 40h
		.overlay_number 	       dw 0
					       rb 3Ch - $
		.new_header_offset	       dd Header

		.HEADER_LENGTH = $
		.STACK_LENGTH = 100h

		namespace Stub

		      include '../8086.inc'

		start:
		      push    cs
		      pop     ds
		      mov     dx,message - start
		      mov     ah,9
		      int     21h
		      mov     ax,4C01h
		      int     21h

		message db 'This program cannot be run in DOS mode.',0Dh,0Ah,24h

		end namespace

		align 16

		.LENGTH = $

	end if

	if defined Settings.Stamp
		TIMESTAMP := Settings.Stamp
	else
		TIMESTAMP := __TIME__
	end if

	align 8

	Header:
	.Signature		      dw "PE",0
	.Machine		      dw MACHINE
	.NumberOfSections	      dw NUMBER_OF_SECTIONS
	.TimeDateStamp		      dd TIMESTAMP
	.PointerToSymbolTable	      dd 0
	.NumberOfSymbols	      dd 0
	.SizeOfOptionalHeader	      dw SectionTable - OptionalHeader
	.Characteristics	      dw CHARACTERISTICS

	OptionalHeader:
	.Magic			      dw MAGIC
	.MajorLinkerVersion	      db 0
	.MinorLinkerVersion	      db 0
	.SizeOfCode		      dd 0
	.SizeOfInitializedData	      dd 0
	.SizeOfUninitializedData      dd 0
	.AddressOfEntryPoint	      dd 0
	.BaseOfCode		      dd 0
	if MAGIC <> 0x20B
	 .BaseOfData		      dd 0
	 .ImageBase		      dd IMAGE_BASE - RELOCATION
	else
	 .ImageBase		      dq IMAGE_BASE - RELOCATION
	end if
	.SectionAlignment	      dd SECTION_ALIGNMENT
	.FileAlignment		      dd FILE_ALIGNMENT
	.MajorOperatingSystemVersion  dw 1
	.MinorOperatingSystemVersion  dw 0
	.MajorImageVersion	      dw 0
	.MinorImageVersion	      dw 0
	.MajorSubsystemVersion	      dw MAJOR_SUBSYSTEM_VERSION
	.MinorSubsystemVersion	      dw MINOR_SUBSYSTEM_VERSION
	.Win32VersionValue	      dd 0
	.SizeOfImage		      dd SIZE_OF_IMAGE
	.SizeOfHeaders		      dd SIZE_OF_HEADERS
	.CheckSum		      dd 0
	.Subsystem		      dw SUBSYSTEM
	.DllCharacteristics	      dw DLL_CHARACTERISTICS
	if MAGIC <> 0x20B
	 .SizeOfStackReserve	      dd 1000h
	 .SizeOfStackCommit	      dd 1000h
	 .SizeOfHeapReserve	      dd 10000h
	 .SizeOfHeapCommit	      dd 0
	else
	 .SizeOfStackReserve	      dq 1000h
	 .SizeOfStackCommit	      dq 1000h
	 .SizeOfHeapReserve	      dq 10000h
	 .SizeOfHeapCommit	      dq 0
	end if
	.LoaderFlags		      dd 0
	.NumberOfRvaAndSizes	      dd NUMBER_OF_DIRECTORIES
	RvaAndSizes:
	.Rva			      dd 0
	.Size			      dd 0
	.ENTRY_LENGTH = $ - RvaAndSizes
				      db (NUMBER_OF_DIRECTORIES-1)*RvaAndSizes.ENTRY_LENGTH dup 0
	SectionTable:
	.Name			      dq '.flat'
	.VirtualSize		      dd 0
	.VirtualAddress 	      dd 0
	.SizeOfRawData		      dd 0
	.PointerToRawData	      dd 0
	.PointerToRelocations	      dd 0
	.PointerToLineNumbers	      dd 0
	.NumberOfRelocations	      dw 0
	.NumberOfLineNumbers	      dw 0
	.Characteristics	      dd IMAGE_SCN_MEM_EXECUTE + IMAGE_SCN_MEM_READ + IMAGE_SCN_MEM_WRITE
	.ENTRY_LENGTH = $ - SectionTable
				      db (NUMBER_OF_SECTIONS-1)*SectionTable.ENTRY_LENGTH dup 0

	HeadersEnd:
	define CheckSumBlocks PE,0,HeadersEnd

	SECTION_INDEX = 0
	RELOCATION_INDEX = 0
	DEFINED_SECTION = 0
	SECTION_DIRECTORIES = 0
	align SECTION_ALIGNMENT
	FIRST_SECTION_RVA:
	section $%%
	align FILE_ALIGNMENT,0
	SIZE_OF_HEADERS = $%
	FILE_OFFSET = $%
	SECTION_BASE = IMAGE_BASE + FIRST_SECTION_RVA
	org SECTION_BASE

	store SECTION_BASE-IMAGE_BASE at PE:OptionalHeader.AddressOfEntryPoint
	store SECTION_BASE-IMAGE_BASE at PE:SectionTable.VirtualAddress
	store FILE_OFFSET at PE:SectionTable.PointerToRawData

	virtual at 0
	     relocated_addresses:: rd NUMBER_OF_RELOCATIONS
	end virtual

	virtual at 0
	     relocation_types:: rw NUMBER_OF_RELOCATIONS
	end virtual

end namespace

RVA? equ -PE.IMAGE_BASE +

macro entry? address*
	namespace PE
		store address-IMAGE_BASE at PE:OptionalHeader.AddressOfEntryPoint
	end namespace
end macro

macro stack? reserve*,commit:1000h
	namespace PE
		store reserve at PE:OptionalHeader.SizeOfStackReserve
		store commit at PE:OptionalHeader.SizeOfStackCommit
	end namespace
end macro

macro heap? reserve*,commit:0
	namespace PE
		store reserve at PE:OptionalHeader.SizeOfHeapReserve
		store commit at PE:OptionalHeader.SizeOfHeapCommit
	end namespace
end macro

calminstruction calminstruction?.initsym? var*, val&
	publish var, val
end calminstruction

calminstruction calminstruction?.asm? line&
	local	name, i
	initsym name, name.0
	match	name.i, name
	compute i, i+1
	arrange name, name.i
	publish name, line
	arrange line, =assemble name
	assemble line
end calminstruction

macro section?
	namespace PE

		repeat SECTION_DIRECTORIES
			end data
		end repeat

		local AREA,DATA_START,DATA_END
		AREA::
		DATA_START = $$
		DATA_END = $-($%-$%%)
		CheckSumBlocks reequ CheckSumBlocks,AREA,DATA_START,DATA_END

		SECTION_SIZE = $ - SECTION_BASE
		store SECTION_SIZE at PE:SectionTable.VirtualSize+SECTION_INDEX*SectionTable.ENTRY_LENGTH
		align SECTION_ALIGNMENT
		SECTION_BASE = $
		section $%%
		align FILE_ALIGNMENT,0
		RAW_DATA_SIZE = $% - FILE_OFFSET
		store RAW_DATA_SIZE at PE:SectionTable.SizeOfRawData+SECTION_INDEX*SectionTable.ENTRY_LENGTH
		FILE_OFFSET = $%
		org SECTION_BASE

		load SECTION_CHARACTERISTICS from PE:SectionTable.Characteristics+SECTION_INDEX*SectionTable.ENTRY_LENGTH
		if SECTION_SIZE > 0 & RAW_DATA_SIZE = 0
			SECTION_CHARACTERISTICS = SECTION_CHARACTERISTICS or IMAGE_SCN_CNT_UNINITIALIZED_DATA
			store SECTION_CHARACTERISTICS at PE:SectionTable.Characteristics+SECTION_INDEX*SectionTable.ENTRY_LENGTH
		end if

		if LEGACY_HEADERS
			if SECTION_CHARACTERISTICS and IMAGE_SCN_CNT_CODE & RAW_DATA_SIZE > 0
				load CODE_SIZE from PE:OptionalHeader.SizeOfCode
				if CODE_SIZE = 0
					load CODE_BASE from PE:SectionTable.VirtualAddress+SECTION_INDEX*SectionTable.ENTRY_LENGTH
					store CODE_BASE at PE:OptionalHeader.BaseOfCode
				end if
				CODE_SIZE = CODE_SIZE + RAW_DATA_SIZE
				store CODE_SIZE at PE:OptionalHeader.SizeOfCode
			end if
			if SECTION_CHARACTERISTICS and IMAGE_SCN_CNT_INITIALIZED_DATA & RAW_DATA_SIZE > 0
				load DATA_SIZE from PE:OptionalHeader.SizeOfInitializedData
				if DATA_SIZE = 0 & MAGIC <> 0x20B
					load DATA_BASE from PE:SectionTable.VirtualAddress+SECTION_INDEX*SectionTable.ENTRY_LENGTH
					store DATA_BASE at PE:OptionalHeader.BaseOfData
				end if
				DATA_SIZE = DATA_SIZE + RAW_DATA_SIZE
				store DATA_SIZE at PE:OptionalHeader.SizeOfInitializedData
			end if
			if SECTION_CHARACTERISTICS and IMAGE_SCN_CNT_UNINITIALIZED_DATA
				load BSS_SIZE from PE:OptionalHeader.SizeOfUninitializedData
				BSS_SIZE = BSS_SIZE + SECTION_SIZE
				store BSS_SIZE at PE:OptionalHeader.SizeOfUninitializedData
			end if
		end if

		if DEFINED_SECTION | SECTION_SIZE > 0
			SECTION_INDEX = SECTION_INDEX + 1
		end if

	end namespace
end macro

macro section? declaration*
	namespace PE

		section

		DEFINED_SECTION = 1

		store SECTION_BASE-IMAGE_BASE at PE:SectionTable.VirtualAddress+SECTION_INDEX*SectionTable.ENTRY_LENGTH
		store FILE_OFFSET at PE:SectionTable.PointerToRawData+SECTION_INDEX*SectionTable.ENTRY_LENGTH

		SECTION_DIRECTORIES = 0

		match name attributes, declaration

			store name:qword at PE:SectionTable.Name+SECTION_INDEX*SectionTable.ENTRY_LENGTH

			SECTION_CHARACTERISTICS = 0

			local seq
			define seq attributes:
			while 1
				match :, seq
					break
				else match =readable? tail, seq
					redefine seq tail
					SECTION_CHARACTERISTICS = SECTION_CHARACTERISTICS or IMAGE_SCN_MEM_READ
				else match =writeable? tail, seq
					redefine seq tail
					SECTION_CHARACTERISTICS = SECTION_CHARACTERISTICS or IMAGE_SCN_MEM_WRITE
				else match =writable? tail, seq
					redefine seq tail
					SECTION_CHARACTERISTICS = SECTION_CHARACTERISTICS or IMAGE_SCN_MEM_WRITE
				else match =executable? tail, seq
					redefine seq tail
					SECTION_CHARACTERISTICS = SECTION_CHARACTERISTICS or IMAGE_SCN_MEM_EXECUTE
				else match =discardable? tail, seq
					redefine seq tail
					SECTION_CHARACTERISTICS = SECTION_CHARACTERISTICS or IMAGE_SCN_MEM_DISCARDABLE
				else match =shareable? tail, seq
					redefine seq tail
					SECTION_CHARACTERISTICS = SECTION_CHARACTERISTICS or IMAGE_SCN_MEM_SHARED
				else match =import? tail, seq
					redefine seq tail
					SECTION_DIRECTORIES = SECTION_DIRECTORIES + 1
					data import
				else match =export? tail, seq
					redefine seq tail
					SECTION_DIRECTORIES = SECTION_DIRECTORIES + 1
					data export
				else match =resource? =from? path tail, seq
					redefine seq tail
					SECTION_DIRECTORIES = SECTION_DIRECTORIES + 1
					data resource from path
				else match =resource? tail, seq
					redefine seq tail
					SECTION_DIRECTORIES = SECTION_DIRECTORIES + 1
					data resource
				else match =fixups? tail, seq
					redefine seq tail
					SECTION_DIRECTORIES = SECTION_DIRECTORIES + 1
					data fixups
				else match =code? tail, seq
					redefine seq tail
					SECTION_CHARACTERISTICS = SECTION_CHARACTERISTICS or IMAGE_SCN_CNT_CODE
				else match =data? tail, seq
					redefine seq tail
					SECTION_CHARACTERISTICS = SECTION_CHARACTERISTICS or IMAGE_SCN_CNT_INITIALIZED_DATA
				else match =udata? tail, seq
					redefine seq tail
					SECTION_CHARACTERISTICS = SECTION_CHARACTERISTICS or IMAGE_SCN_CNT_UNINITIALIZED_DATA
				else match attribute tail, seq
					err 'unknown attribute "',`attribute,'"'
					redefine seq :
				end match
			end while

			store SECTION_CHARACTERISTICS at PE:SectionTable.Characteristics+SECTION_INDEX*SectionTable.ENTRY_LENGTH
		else

			store declaration:qword at PE:SectionTable.Name+SECTION_INDEX*SectionTable.ENTRY_LENGTH

		end match

	end namespace
end macro

macro data? type*
	namespace PE
		local number,content
		define content
		match =export?, type
			number = 0
		else match =import?, type
			number = 1
		else match =resource? =from? path, type
			number = 2
			define content resource_from path
		else match =resource?, type
			number = 2
		else match =fixups?, type
			number = 5
			define content fixups
		else
			number = type
		end match
		define DATA_DIRECTORY number
		load DATA_BASE:dword from PE:RvaAndSizes.Rva+DATA_DIRECTORY*RvaAndSizes.ENTRY_LENGTH
		if DATA_BASE = 0
			store $-IMAGE_BASE:dword at PE:RvaAndSizes.Rva+DATA_DIRECTORY*RvaAndSizes.ENTRY_LENGTH
			match instruction, content
				instruction
			end match
		else
			err 'data already defined'
		end if
	end namespace
end macro

macro end?.data?
	namespace PE
		load DATA_BASE:dword from PE:RvaAndSizes.Rva+DATA_DIRECTORY*RvaAndSizes.ENTRY_LENGTH
		store $-IMAGE_BASE-DATA_BASE:dword at PE:RvaAndSizes.Size+DATA_DIRECTORY*RvaAndSizes.ENTRY_LENGTH
		restore DATA_DIRECTORY
	end namespace
end macro

macro PE.resource_directory?
	namespace PE
		Resource: rb RESOURCE_HEADERS_LENGTH
		Resource.counter = 0
		define RESOURCE_DIRECTORIES_LIST Resource_root
		Resource_root.counter = 0
	end namespace
end macro

macro PE.resource_data? type*,id*,lang*,codepage:0
	namespace PE
		local _type,_id,_lang
		_type = type
		_id = id
		_lang = lang
		if ~ type eqtype 0
			_type = _type shl 32
		end if
		if ~ id eqtype 0
			_id = id shl 32
		end if
		if ~ lang eqtype 0
			_lang = lang shl 32
		end if
		repeat 1, %type:_type, %id:_id, %lang:_lang
			if ~ defined Resource_#%type#_#%id.counter
				if ~ defined Resource_#%type.counter
					repeat 1, i:Resource_root.counter
						Resource_root.entry#i = type
						Resource_root.offset#i =  (Resource_#%type - Resource) or 80000000h
					end repeat
					Resource_root.counter = Resource_root.counter + 1
					match list, RESOURCE_DIRECTORIES_LIST
						define RESOURCE_DIRECTORIES_LIST list,Resource_#%type
					end match
					Resource_#%type.counter = 0
				end if
				repeat 1, i:Resource_#%type.counter
					Resource_#%type.entry#i = id
					Resource_#%type.offset#i =  (Resource_#%type#_#%id - Resource) or 80000000h
				end repeat
				Resource_#%type.counter = Resource_#%type.counter + 1

				match list, RESOURCE_DIRECTORIES_LIST
					define RESOURCE_DIRECTORIES_LIST list,Resource_#%type#_#%id
				end match
				Resource_#%type#_#%id.counter = 0
			end if
			repeat 1, i:Resource_#%type#_#%id.counter
				Resource_#%type#_#%id.entry#i = lang
				Resource_#%type#_#%id.offset#i = Resource_#%type#_#%id#_#%lang - Resource
			end repeat
			Resource_#%type#_#%id.counter = Resource_#%type#_#%id.counter + 1
			repeat 1, i:Resource.counter
				Resource_#%type#_#%id#_#%lang := Resource.entry#i
				Resource.cp#i := codepage
				Resource.data#i:
			end repeat
		end repeat
	end namespace
end macro

macro PE.end_resource_data?
	namespace PE
		repeat 1, i:Resource.counter
			Resource.size#i := $ - Resource.data#i
		end repeat
		Resource.counter = Resource.counter + 1
		align 4
	end namespace
end macro

macro PE.end_resource_directory?
       namespace PE
		RESOURCE_HEADERS_POINTER = 0

		match list, RESOURCE_DIRECTORIES_LIST
			iterate dir, list
				dir := Resource + RESOURCE_HEADERS_POINTER
				RESOURCE_HEADERS_POINTER = RESOURCE_HEADERS_POINTER + 16 + dir.counter * 8
				local x,y,z,a,b
				x = dir.counter shr 1
				while x > 0
					y = x
					while y < dir.counter
						z = y
						while z-x >= 0
							repeat 1, i:z, j:z-x
								if dir.entry#i eqtype 0
									if ~ dir.entry#j eqtype 0 | dir.entry#i >= dir.entry#j
										z = 0
									end if
								else if ~ dir.entry#j eqtype 0
									a = dir.entry#i bswap lengthof dir.entry#i
									b = dir.entry#j bswap lengthof dir.entry#j
									if ( lengthof a >= lengthof b & a shr ((lengthof a - lengthof b)*8) >= b ) | ( lengthof a < lengthof b & a > b shr ((lengthof b - lengthof a)*8) )
										z = 0
									end if
								end if
								if z > 0
									a = dir.entry#i
									b = dir.offset#i
									dir.entry#i = dir.entry#j
									dir.offset#i = dir.offset#j
									dir.entry#j = a
									dir.offset#j = b
									z = z - x
								end if
							end repeat
						end while
						y = y + 1
					end while
					x = x shr 1
				end while
			end iterate
			iterate dir, list
				store __TIME__ : 4 at dir + 4
				dir.names_counter = 0
				repeat dir.counter, i:0
					if dir.entry#i eqtype 0
						store dir.entry#i : 4 at dir + 16 + i * 8
					else
						dir.names_counter = dir.names_counter + 1
						repeat 1, %id:dir.entry#i
							if ~ defined Resource_string#%id
								restore Resource_string#%id
								Resource_string#%id = Resource + RESOURCE_HEADERS_POINTER
								if lengthof dir.entry#i and 1
									err 'a word-aligned string is expected as a name'
								end if
								RESOURCE_HEADERS_POINTER = RESOURCE_HEADERS_POINTER + lengthof dir.entry#i + 2
								store (lengthof dir.entry#i)/2 : 2 at Resource_string#%id
								store dir.entry#i : lengthof dir.entry#i at Resource_string#%id + 2
							end if
							store (Resource_string#%id - Resource) or 80000000h : 4 at dir + 16 + i * 8
						end repeat
					end if
					store dir.offset#i : 4 at dir + 16 + i * 8 + 4
				end repeat
				store dir.names_counter : 2 at dir + 12
				store dir.counter - dir.names_counter : 2 at dir + 14
			end iterate
		end match

		if RESOURCE_HEADERS_POINTER and 11b
			RESOURCE_HEADERS_POINTER = RESOURCE_HEADERS_POINTER + 4 - RESOURCE_HEADERS_POINTER and 11b
		end if

		repeat Resource.counter, i:0
			Resource.entry#i := Resource + RESOURCE_HEADERS_POINTER
			RESOURCE_HEADERS_POINTER = RESOURCE_HEADERS_POINTER + 16
			store RVA(Resource.data#i) : 4 at Resource.entry#i
			store Resource.size#i : 4 at Resource.entry#i + 4
			store Resource.cp#i : 4 at Resource.entry#i + 8
		end repeat

		RESOURCE_HEADERS_LENGTH = RESOURCE_HEADERS_POINTER
	end namespace
end macro

macro PE.resource_from path*

	local res_file,res_size,res_header_size,res_data_size,res_data
	local offset,char,type,id,lang

	virtual at 0
		res_file:: file path
		res_size := $
	end virtual

	PE.resource_directory

		offset = 0
		while offset < res_size
			load res_header_size : 4 from res_file : offset + 4
			load res_data_size : 4 from res_file : offset + 0

			if res_data_size > 0

				offset =: offset + 8
				load char : 2 from res_file : offset
				if char = 0FFFFh
					load char : 2 from res_file : offset + 2
					type = +char
					offset = offset + 4
				else
					while 1
						if char = 0
							load type : (%-1)*2 from res_file : offset
							offset = offset + (% + % and 1)*2
							break
						end if
						load char : 2 from res_file : offset + %*2
					end while
				end if
				load char : 2 from res_file : offset
				if char = 0FFFFh
					load char : 2 from res_file : offset + 2
					id = +char
					offset = offset + 4
				else
					while 1
						if char = 0
							load id : (%-1)*2 from res_file : offset
							offset = offset + (% + % and 1)*2
							break
						end if
						load char : 2 from res_file : offset + %*2
					end while
				end if
				load char : 2 from res_file : offset + 6
				lang = +char

				restore offset

				PE.resource_data type,id,lang
					load res_data : res_data_size from res_file : offset + res_header_size
					db res_data
				PE.end_resource_data

			end if

			offset = offset + res_header_size + res_data_size
			if offset and 11b
				offset = offset + 4 - offset and 11b
			end if
		end while

	PE.end_resource_directory

end macro

macro PE.fixups
	namespace PE
		Fixups:
		calminstruction BuildFixups
			local	PAGE_RVA, BLOCK_HEADER, BLOCK_SIZE
			local	INDEX, ADDRESS, TYPE, FIXUP
			compute PAGE_RVA, -1
			compute BLOCK_HEADER, 0
			compute BLOCK_SIZE, 0
			compute INDEX,0
		    process:
			check	INDEX = NUMBER_OF_RELOCATIONS
			jyes	close_block
			asm	load ADDRESS:4 from relocated_addresses:INDEX shl 2
			check	PAGE_RVA >= 0 & ADDRESS and not 0FFFh = PAGE_RVA
			jyes	append_to_block
		    close_block:
			check	BLOCK_HEADER
			jno	start_new_block
			check	BLOCK_SIZE and 11b
			jno	finish_block
			asm	dw 0
			compute BLOCK_SIZE, BLOCK_SIZE + 2
		    finish_block:
			asm	store BLOCK_SIZE:4 at BLOCK_HEADER+4
		    start_new_block:
			check	INDEX = NUMBER_OF_RELOCATIONS
			jyes	done
			compute PAGE_RVA, ADDRESS and not 0FFFh
			compute BLOCK_HEADER, $
			asm	dd PAGE_RVA, 0
			compute BLOCK_SIZE, 8
		    append_to_block:
			asm	load TYPE:2 from relocation_types:INDEX shl 1
			compute FIXUP, (ADDRESS and 0FFFh) or (TYPE shl 12)
			asm	dw FIXUP
			compute BLOCK_SIZE, BLOCK_SIZE + 2
			compute INDEX, INDEX + 1
			jump	process
		    done:
		end calminstruction
		BuildFixups
	end namespace
end macro

if defined PE.Fixups

	calminstruction dword? value*
		compute value, value
		check	value relativeto 0 | ~ value relativeto PE.RELOCATION
		jyes	plain
		local	offset
		compute offset, $%
		emit	4, value - PE.RELOCATION
		check	$% > offset
		jno	done
		asm	store $-4-PE.IMAGE_BASE:4 at PE.relocated_addresses:PE.RELOCATION_INDEX shl 2
		asm	store IMAGE_REL_BASED_HIGHLOW:2 at PE.relocation_types:PE.RELOCATION_INDEX shl 1
		compute PE.RELOCATION_INDEX, PE.RELOCATION_INDEX + 1
	    done:
		exit
	    plain:
		emit	4, value
	end calminstruction

	calminstruction qword? value*
		compute value, value
		check	value relativeto 0 | ~ value relativeto PE.RELOCATION
		jyes	plain
		local	offset
		compute offset, $%
		emit	8, value - PE.RELOCATION
		check	$% > offset
		jno	done
		asm	store $-8-PE.IMAGE_BASE:4 at PE.relocated_addresses:PE.RELOCATION_INDEX shl 2
		asm	store IMAGE_REL_BASED_DIR64:2 at PE.relocation_types:PE.RELOCATION_INDEX shl 1
		compute PE.RELOCATION_INDEX, PE.RELOCATION_INDEX + 1
	    done:
		exit
	    plain:
		emit	8, value
	end calminstruction

	iterate <dd,dword>, dd,dword, dq,qword

		calminstruction dd? definitions&
			local	value, n
		    start:
			match	value=,definitions, definitions, ()
			jyes	recognize
			match	value, definitions
			arrange definitions,
		    recognize:
			match	n =dup? value, value, ()
			jyes	duplicate
			match	?, value
			jyes	reserve
			call	dword, value
		    next:
			match	, definitions
			jno	start
			take	, definitions
			take	definitions, definitions
			jyes	next
			exit
		    reserve:
			emit	dword
			jump	next
		    duplicate:
			match	(value), value
		    stack:
			check	n
			jno	next
			take	definitions, value
			arrange value, definitions
			compute n, n - 1
			jump	stack
		end calminstruction

		calminstruction (label) dd? definitions&
			local	cmd
			arrange cmd, =label label : =dword
			assemble cmd
			arrange cmd, =dd definitions
			assemble cmd
		end calminstruction

	end iterate

end if

postpone
	purge section?
	section
	namespace PE
		SIZE_OF_IMAGE := SECTION_BASE - IMAGE_BASE
		NUMBER_OF_SECTIONS := SECTION_INDEX
		NUMBER_OF_RELOCATIONS := RELOCATION_INDEX
	end namespace
end postpone

postpone ?
	namespace PE
		CHECKSUM = 0

		calminstruction CheckSum
			local	AREA, DATA_START, DATA_END, POS, H
		    get_block:
			match	AREA=,DATA_START=,DATA_END=,CheckSumBlocks, CheckSumBlocks
			jyes	block_ready
			match	AREA=,DATA_START=,DATA_END, CheckSumBlocks
			jyes	last_block
			exit
		    last_block:
			arrange CheckSumBlocks,
		    block_ready:
			compute POS, DATA_START
		    process_block:
			check	POS + 2 <= DATA_END
			jno	finish_block
			asm	load H:2 from AREA:POS
			compute CHECKSUM, CHECKSUM + H
			compute POS, POS + 2
			jump	process_block
		    finish_block:
			check	POS + 1 = DATA_END
			jno	reduce_checksum
			asm	load H:1 from AREA:POS
			compute CHECKSUM, CHECKSUM + H
		    reduce_checksum:
			check	CHECKSUM shr 16
			jno	get_block
			compute CHECKSUM, CHECKSUM shr 16 + CHECKSUM and 0FFFFh
			jump	reduce_checksum
		    done:
		end calminstruction

		CheckSum
		CHECKSUM = CHECKSUM + $%
		store CHECKSUM at PE:OptionalHeader.CheckSum
	end namespace
end postpone
