This source PFRSRC is able to load compiled resource file and convert it to a special segment [.rsrc], which will be linked to output executable PE or DLL format.
EUROASM NOWARN=2101
pfrsrc PROGRAM FORMAT=COFF,MODEL=FLAT,WIDTH=32
INCLUDEHEAD1 euroasm.htm, \ Interface (structures, symbols and macros) of other modules used in this source.
ea.htm,eaopt.htm,exp.htm,msg.htm,pf.htm,pgm.htm,pgmopt.htm,reloc.htm,sss.htm
pfrsrc HEAD ; Start module interface.
.res, consists of raw resource data chunks, each preceeded by resource header. Resource has three main properties encoded as unsigned 16bit integer number: type, name and language.
.ResType is 0xFFFF, ResTypeLength is 2 and the second word
specifies resource type identifier (standard or custom numeric id). Otherwise it is a zero-terminated Unichar string.
.ResName is 0xFFFF, ResNameLength is 2 and the second word
specifies resource name identifier (numeric id). Otherwise it is a zero-terminated Unichar string.
.ResName member is not DWORD aligned, one padding zero-word must be added.
ResTypeLength EQU 2 ResNameLength EQU 2 PFRSRC_RES_HEADER STRUC .DataSize D DWORD ; Size of raw data which immediately follow this header. .HeaderSize D DWORD ; Size of this header. .ResType D ResTypeLength * UNICHAR .ResName D ResNameLength * UNICHAR .DataVersion D DWORD ; Version of following raw data, should be 0. .MemoryFlags D WORD ; Resource status, see PfrsrcEncoding. .LanguageID D WORD ; Language and sublanguage which strings are written in. .Version D DWORD ; Version of the resource editor. .Characteristics D DWORD ; Flags specified by resource editor. ENDSTRUC PFRSRC_RES_HEADER ; Size=20h=32 or more when strings are used.
.Type and .Name depends
on their most significant bit. Offsets are relative to the start of segment [.rsrc].
PFRSRC_RES_RECORD STRUC
.Type D D ; MSbit=0: Offset of pascal unicode string identifier of resource type.
; MSbit=1: Value of numeric identifier of resource type in the lower word.
.Name D D ; MSbit=0: Offset of pascal unicode string identifier of resource name.
; MSbit=1: Value of numeric identifier of resource name in the lower word.
.Lang D D ; Value of the language and sublanguage identifier in the lower word.
.Data D D ; Pointer to PFRSRC_RES_HEADER which defines raw resource data following the header.
; Following offsets are used for building the tree.
.TypeDir D D ; Offset of PFRSRC_RESOURCE_DIRECTORY which specifies resource type.
.NameDir D D ; Offset of PFRSRC_RESOURCE_DIRECTORY which specifies resource name.
.LangDir D D ; Offset of PFRSRC_RESOURCE_DIRECTORY which specifies resource language.
.DataDir D D ; Offset of PFRSRC_DATA_ENTRY which specifies resource raw data.
ENDSTRUC PFRSRC_RES_RECORD ; Size=20h=32.
[.rsrc] section in executable file.
PFRSRC_RESOURCE_DIRECTORY STRUC .Characteristics D D ; Reserved. .TimeDateStamp D D ; Time of resource creation. Number of seconds since midnight 1.1.1970 .MajorVersion D W ; Resource version. .MinorVersion D W .NumberOfNamedEntries D W ; How many string-identified PFRSRC_RESOURCE_ENTRY objects follow the directory. .NumberOfIdEntries D W ; How many numeric-identified PFRSRC_RESOURCE_ENTRY objects follow the directory. ENDSTRUC PFRSRC_RESOURCE_DIRECTORY ; Size=10h=16.
[.rsrc] section in executable file.
[.rsrc] section.
PFRSRC_RESOURCE_ENTRY STRUC
.IdOrName D D ; MSbit=0: Resource numeric identifier in lower word.
; MSbit=1: Offset of UNICHAR Pascal-string carrying the typename or resource name.
.DataOrDir D D ; MSbit=0: Offset of PFRSRC_DATA_ENTRY, i.e. the final tree leaf describing the raw data.
; MSbit=1: Offset of another PFRSRC_RESOURCE_DIRECTORY (one level lower).
ENDSTRUC PFRSRC_RESOURCE_ENTRY ; Size=8.
PFRSRC_DATA_ENTRY STRUC .OffsetToData D D ; RVA of the raw data unit. .Size D D ; Numer of bytes in the raw data unit. .CodePage D D ; Encoding of characters in the raw data unit. .Reserved D D ; 0. ENDSTRUC PFRSRC_DATA_ENTRY ; Size=10h=16.
.idCount icon entries and then followed with
.idCount image raw data (bitmaps).PFRSRC_ICONDIR STRUC .idReserved D W ; Always 0. .idType D W ; 1=icon image (*.ico). 2=cursor image (*.cur). .idCount D W ; Number of icon images in this file. ENDSTRUC PFRSRC_ICONDIR ; Size=6.
.bColorCount = 1 << (.wBitCount * .wPlanes)
.bColorCount = 0 when (.wBitCount * wPlanes)>= 8.PFRSRC_ICONDIRENTRY STRUC .bWidth D B ; Image width in pixels. 0 when 256px or more. .bHeight D B ; Image height in pixels. 0 when 256px or more. .bColorCount D B ; Number of colors in palette, see above. .bReserved D B ; 0. .wPlanes D W ; Number of color planes (0 or 1). .wBitCount D W ; Number of bits per pixel. .dwBytesInRes D D ; Size of icon raw data. .dwImageOffset D D ; File address of raw data from the beginning of the file. ENDSTRUC PFRSRC_ICONDIRENTRY ; Size=16=10h.
pfrsrcGROUP_ICON in RES files and PE executables.
.idCount group-icon direntries. PFRSRC_GRPICONDIR STRUC .idReserved D W ; Always 0. .idType D W ; 1=icon image (*.ico). 2=cursor image (*.cur). .idCount D W ; Number of GRPICONDIRENTRY objects in this resource object. ENDSTRUC PFRSRC_GRPICONDIR ; Size=6.
pfrsrcGROUP_ICON in PE executables.
PFRSRC_GRPICONDIRENTRY STRUC .bWidth D B ; Image width in pixels. 0 when 256px or more. .bHeight D B ; Image height in pixels. 0 when 256px or more. .bColorCount D B ; Number of colors in palette. 0 when palette is not used. .bReserved D B ; 0. .wPlanes D W ; Number of color planes (0 or 1). .wBitCount D W ; Number of bits per pixel. .dwBytesInRes D D ; Size of icon raw data. .nId D W ; Numeric name identifier of this icon variant. ENDSTRUC PFRSRC_GRPICONDIRENTRY ; Size=14=0Eh.
; Memory flags: pfrsrcResMOVABLE = 0x0010 ; Loader may move the resource in memory. pfrsrcResPURE = 0x0020 ; Resource contain DWORD-aligned data, no alignment needed. pfrsrcResPRELOAD = 0x0040 ; Resource must be loaded in memory on load time. pfrsrcResDISCARDABLE = 0x1000 ; Resource may be removed and reloaded on demand. ; Standard resource type numeric identifiers: pfrsrcCURSOR = 0x0001 pfrsrcBITMAP = 0x0002 pfrsrcICON = 0x0003 pfrsrcMENU = 0x0004 pfrsrcDIALOG = 0x0005 pfrsrcSTRING = 0x0006 pfrsrcFONTDIR = 0x0007 pfrsrcFONT = 0x0008 pfrsrcACCELERATOR = 0x0009 pfrsrcRCDATA = 0x000A pfrsrcMESSAGETABLE = 0x000B pfrsrcGROUP_CURSOR = 0x000C pfrsrcGROUP_ICON = 0x000E pfrsrcVERSION = 0x0010
ENDHEAD pfrsrc ; End of module interface.
PfrsrcCompile Procedure OutputStream, Pgm Msg '7827' ; EuroAssembler does not compile to format RSRC, use resource compiler instead. EndProcedure PfrsrcCompile
PfrsrcStoreUstring Procedure StringPtr, StringEnd, StringNameBuffer
XOR EAX,EAX
MOV EDI,[%StringPtr]
MOV ECX,[%StringEnd]
MOV [%ReturnEAX],EAX
SUB ECX,EDI
MOV ESI,EDI
REPNE SCASW
MOV [%ReturnESI],EDI
STC
JNE .90: ; Return with error if terminator missing.
SUB EDI,ESI ; Brutto string size.
SAR EDI,1
LEA EDX,[EDI-1] ; EDX is now netto string length in Unichars.
CMP EDX,0x0000_FFFE
CMC
JC .90: ; Error - string is too long.
MOV ECX,[%StringNameBuffer]
TEST ECX
JZ .90:
BufferRetrieve ECX ; ESI is now pointer to older Pascal Unichar string on buffer. Length=[ESI].
LEA EBX,[ESI+ECX] ; EBX points now to the end of buffer data.
.20:MOV [%ReturnEAX],ESI ; Assume that it might be found.
MOV EDI,[%StringPtr] ; EDI is now pointer to the new string. Length=EDX.
CMP ESI,EBX
JNB .40: ; If the string EDI,EDX was not found on buffer.
LODSW ; AX is now the old string length.
CMP EAX,EDX
JNE .30: ; Skip to the next string when lengths are different.
MOV ECX,EDX
REPE CMPSW
JNE .30:
BufferRetrieve [%StringNameBuffer] ; The same string was found at [%ReturnEAX].
SUB [%ReturnEAX],ESI ; Return buffer offset instead of pointer.
JMP .90:
.30:MOV ESI,[%ReturnEAX]
LODSW
ADD ESI,EAX
ADD ESI,EAX ; ESI now points to the next string on buffer.
JMP .20:
.40:MOV EBX,[%StringNameBuffer]
BufferRetrieve EBX
MOV [%ReturnEAX],ECX
BufferStoreWord EBX,EDX
SAL EDX,1
BufferStore EBX,EDI,EDX
.90:EndProcedure PfrsrcStoreUstring
.reshas invalid structure.
PfrsrcStoreRecord Procedure ResPtr, ResEnd, MinPtr, MaxEnd, RecBuffer, StrNameBuffer
ResRecord LocalVar Size=SIZE# PFRSRC_RES_RECORD ; Room for the record before it is stored to buffer.
MOV ESI,[%ResPtr]
CMP ESI,[%MinPtr]
JC .90:
MOV EAX,[%MaxEnd]
CMP EAX,[%ResEnd]
JC .90:
LEA EBX,[%ResRecord]
MOV [%ReturnEAX],ESI ; Preinitialize returned EAX for the case of error or end of file.
CMP ESI,[%ResEnd]
JE .90: ; If EOF.
ADD ESI,3
AND ESI,-4 ; DWORD alignment.
MOV [%ReturnEAX],ESI ; Preinitialize returned aligned EAX for the case of error or end of file.
CMP ESI,[%ResEnd]
JE .90: ; If EOF.
LEA EAX,[ESI+SIZE#PFRSRC_RES_HEADER]
CMP [%ResEnd],EAX
JC .90: ; When the file is too short.
; Record raw data.
MOV [EBX+PFRSRC_RES_RECORD.Data],ESI
; Record type.
ADD ESI,8
LODSD
MOV ECX,EAX
SHR EAX,16
BTS EAX,31 ; Set MSbit.
INC CX
JZ .20: ; Record type started with CX=0xFFFF.
SUB ESI,4
Invoke PfrsrcStoreUstring,ESI,[%ResEnd],[%StrNameBuffer]
JC .90:
.20:MOV [EBX+PFRSRC_RES_RECORD.Type],EAX
; Record name.
LODSD
MOV ECX,EAX
SHR EAX,16
BTS EAX,31 ; Set MSbit.
INC CX
JZ .40: ; Record name started with CX=0xFFFF.
SUB ESI,4
Invoke PfrsrcStoreUstring,ESI,[%ResEnd],[%StrNameBuffer]
JC .90:
.40:MOV [EBX+PFRSRC_RES_RECORD.Name],EAX
ADD ESI,3 ; ESI may be DWORD misaligned.
AND ESI,-4
ADD ESI,6 ; Skip .DataVersion and .MemoryFlags.
; Record language. MSbit is not set, though it is never identified by a string.
MOVZXW EAX,[ESI]
MOV [EBX+PFRSRC_RES_RECORD.Lang],EAX
; Tree entries.
LEA EDI,[EBX+PFRSRC_RES_RECORD.TypeDir]
XOR EAX,EAX
STOSD
STOSD
STOSD
STOSD
MOV ESI,[%ReturnEAX] ; Aligned ResPtr.
MOV ECX,[ESI+PFRSRC_RES_HEADER.DataSize]
JECXZ .80: ; Skip the whole record if raw data size is empty.
MOV ECX,[%RecBuffer]
JECXZ .80: ; Skip if no buffer is provided.
; Store the record EBX to buffer ECX.
BufferStore ECX,EBX,SIZE#PFRSRC_RES_RECORD
.80:; Return pointer to the next header in EAX.
MOV ECX,[ESI+PFRSRC_RES_HEADER.DataSize]
ADD ESI,[ESI+PFRSRC_RES_HEADER.HeaderSize]
ADD ESI,ECX ; End of raw data is pointer to the next header.
MOV [%ReturnEAX],ESI ; It may be unaligned.
.90:EndProcedure PfrsrcStoreRecord
[.rsrc], with PURPOSE=RESOURCE
, which will be appended to other segments of the BasePgm.
When segment with this name already exists in BasePgm and when its Status is
sssImplicit, its old contents (which was specified by program option ICONFILE=) will be overwritten.
PFPE_OPTIONAL_HEADER32.DataDirectory or
PFPE_OPTIONAL_HEADER64.DataDirectory.
.res) is assumed to be formally verified by PfDetect.
[.rsrc] with PURPOSE=RESOURCE is created and put on %BaseProgram.SssList.
PfrsrcLoadPgm Procedure BasePgm, ObjBegin, ObjSize, FileNamePtr
ObjEnd LocalVar ; End of input data in memory.
TypeCnt LocalVar ; Number of Type entries (tree level 1).
NameCnt LocalVar ; Number of Name entries (tree level 2).
LangCnt LocalVar ; Number of Lang entries (tree level 3).
ResRecordBuf LocalVar ; Pointer to BUFFER which keeps PFRSRC_RES_RECORD.
ResRecord LocalVar Size=SIZE# PFRSRC_RES_RECORD ; Room for temporary resource record.
TreeBuf LocalVar ; Pointer to BUFFER which keeps PFRSRC_RESOURCE_DIRECTORY and PFRSRC_RESOURCE_ENTRY.
TreeBottom LocalVar ; Pointer to the start of tree in TreeBuf.
TreePtr LocalVar ; Pointer to current PFRSRC_RESOURCE_DIRECTORY inside %TreeBuf.
TreeSize LocalVar ; Size of the binary tree division. Naturally QWORD aligned.
DataEntryBuf LocalVar ; Pointer to BUFFER which keeps PFRSRC_DATA_ENTRY.
DataEntryPtr LocalVar ; Pointer to the current PFRSRC_DATA_ENTRY inside %DataEntryBuf.
DataEntrySize LocalVar ; Size of the entire PFRSRC_DATA_ENTRY division. Naturally QWORD aligned.
StrNameBuf LocalVar ; Pointer to BUFFER which keeps Pascal UNICHAR string.
StrNamePtr LocalVar ; Pointer to the current free Pascal Unichar string inside StrNameBuf.
StrNameSize LocalVar ; Size of Pascal Unichar strings. Naturally WORD aligned, at the end stuffed to DWORD.
RawDataOffset LocalVar ; Offset of current raw data in output section.
DirLevel2Ptr LocalVar ; Pointer to first resource directory inside tree which specifies resource name.
DirLevel3Ptr LocalVar ; Pointer to first resource directory inside tree which specifies resource language.
DirLevel4Ptr LocalVar ; Pointer to first data entry inside tree which specifies resource data.
ClearLocalVar
Invoke EaBufferReserve::,PfrsrcLoadPgm
MOV [%ResRecordBuf],EAX
MOV EBX,EAX
Invoke EaBufferReserve::,PfrsrcLoadPgm
MOV [%TreeBuf],EAX
Invoke EaBufferReserve::,PfrsrcLoadPgm
MOV [%StrNameBuf],EAX
MOV EDI,EAX
Invoke EaBufferReserve::,PfrsrcLoadPgm
MOV [%DataEntryBuf],EAX
MOV EAX,[%ObjBegin]
MOV ECX,[%ObjSize]
LEA EDX,[EAX+ECX]
MOV [%ObjEnd],EDX
; Resource file will be indexed into temporary array of PFRSRC_RES_RECORDs and sorted.
.05:MOV ECX,[%ObjSize]
ADD ECX,[%ObjBegin]
Invoke PfrsrcStoreRecord,EAX,EDX,[%ObjBegin],ECX,EBX,EDI
JC .90:
JNZ .05: ; Store the next resource record.
BufferRetrieve EBX ; ESI,ECX now contains resource headers converted to RecRecords. They will be sorted by type, name, language.
SAR ECX,5 ; SIZE# PFRSRC_RES_RECORD = 25.
JZ .90: ; If no resource were present in the file.
ShellSort ESI,ECX,SIZE#PFRSRC_RES_RECORD,.CmpRecords
Invoke EaBufferAlign::,EDI,4 ; Strings in StrNameBuf might have been unaligned.
.10:; ESI now points to ECX sorted records PFRSRC_RES_RECORD, one for each resource.
; This array will be passed several times to create the tree.
; Pass 1: examine size of the three-level binary tree.
LEA EDI,[%ResRecord] ; This room will be used as a memory of previous values.
CALL .ResetPrev:
.12:PUSH ESI,EDI
CMPSD ; Has the Type changed?
POP EDI,ESI
JE .14:
INCD [%TypeCnt]
.14:PUSH ESI,EDI
CMPSD
JNE .16:
CMPSD ; Has the Type or Name changed?
.16:POP EDI,ESI
JE .18:
INCD [%NameCnt]
.18:INCD [%LangCnt] ; LangCnt is identical with total RecRecord count,
; even when two resources have the same .Type, .Name, .Lang.
.19:MOVSD
MOVSD ; Copy the current record ESI to be used as the previous ResRecord.
SUB EDI,8
ADD ESI,SIZE#PFRSRC_RES_RECORD - 8
DEC ECX
JNZ .12: ; The next sorted record.
MOV ECX,[%TypeCnt] ; Number of PFRSRC_RESOURCE_ENTRY = %TypeCnt + %NameCnt + %LangCnt.
ADD ECX,[%NameCnt]
ADD ECX,[%LangCnt]
SAL ECX,3 ; Each entry takes 8 bytes.
MOV EDX,[%TypeCnt] ; Number of PFRSRC_RESOURCE_DIRECTORY = %TypeCnt + %NameCnt +1.
ADD EDX,[%NameCnt]
INC EDX
SAL EDX,4 ; Each directory takes 16 bytes.
ADD ECX,EDX
MOV EDX,[%LangCnt] ; Number of PFRSRC_DATA_ENTRY = %LangCnt.
SAL EDX,4 ; Each data entry takes 16 bytes.
ADD ECX,EDX
MOV [%TreeSize],ECX
BufferNew [%TreeBuf],ECX ; Reserve space for the tree.
MOV [%TreeBottom],EAX
MOV [%TreePtr],EAX
MOV EDI,EAX
SHR ECX,2
XOR EAX,EAX
REP STOSD ; Clear the entire binary tree division.
BufferRetrieve [%StrNameBuf] ; ECX is now dword aligned total size of string division.
ADD ECX,[%TreeSize] ; Add the size of tree division.
MOV [%RawDataOffset],ECX
; Pass 2: invert MSbit and relocate offsets in StrNameBuf by [%TreeSize].
BufferRetrieve [%ResRecordBuf]
MOV EDI,[%TreeSize]
MOV EDX,SIZE#PFRSRC_RES_RECORD
MOV EBX,0x8000_0000 ; Inverted MSbit.
.21:MOV EAX,[ESI+PFRSRC_RES_RECORD.Type] ; Loop .21: .. .29: through all records.
XOR EAX,EBX ; Invert MSbit of .Type.
TEST EAX ; It's a string if MSbit=1.
JNS .25: ; No relocation if it is a numeric value.
ADD EAX,EDI ; Relocate.
.25:MOV [ESI+PFRSRC_RES_RECORD.Type],EAX
MOV EAX,[ESI+PFRSRC_RES_RECORD.Name]
XOR EAX,EBX ; Invert MSbit of .Name.
TEST EAX ; It's a string if MSbit=1.
JNS .29: ; No relocation if it is a numeric value.
ADD EAX,EDI ; Relocate.
.29:MOV [ESI+PFRSRC_RES_RECORD.Name],EAX
ADD ESI,EDX ; MSbit of .Lang is not inverted because language is never identified by string.
SUB ECX,EDX
JA .21:
LEA EDI,[%ResRecord] ; EDI points to the record with previous values.
; Pass 3: tree level 1 - resource types.
SUB EBX,EBX ; Tree level 1.
CALL .ResetPrev:
BufferRetrieve [%ResRecordBuf] ; ESI is now one ResRecord from sorted table.
CALL .NewResDir: ; Create level 1 directory EDX for type entries (offset %TreePtr=0).
.31:PUSH ESI,EDI
CMPSD ; Has the Type changed?
POP EDI,ESI
JE .39: ; Skip creating resource entry if type did not change.
CALL .NewResEntry: ; Create resource entry for the type.
.39:MOVSD ; Copy current record .Type to the previous ones.
SUB EDI,4
ADD ESI,SIZE#PFRSRC_RES_RECORD - 4
SUB ECX,SIZE#PFRSRC_RES_RECORD
JA .31: ; The next record.
; Pass 4: tree level 2 - resource name.
MOV BL,4 ; Tree level 2.
MOV EAX,[%TreePtr]
MOV [%DirLevel2Ptr],EAX
CALL .ResetPrev:
BufferRetrieve [%ResRecordBuf] ; ESI is now one ResRecord from sorted table.
.41:PUSH ESI,EDI
CMPSD ; Has the Type changed?
.43:POP EDI,ESI
JE .45:
CALL .NewResDir: ; Type changed. Create level 2 directory EDX for name entries.
JMP .47:
.45:PUSH ESI,EDI
CMPSD
CMPSD ; Has the Name changed?
POP EDI,ESI
JE .49:
.47:CALL .NewResEntry: ; Create resource entry for the name.
.49:MOVSD ; Copy current record .Type to the previous ones.
MOVSD ; Copy current record .Name to the previous ones.
SUB EDI,8
ADD ESI,SIZE#PFRSRC_RES_RECORD - 8
SUB ECX,SIZE#PFRSRC_RES_RECORD
JA .41: ; The next record.
; Pass 5: tree level 3 - resource language.
MOV BL,8 ; Tree level 3.
MOV EAX,[%TreePtr]
MOV [%DirLevel3Ptr],EAX
CALL .ResetPrev:
BufferRetrieve [%ResRecordBuf] ; ESI is now one ResRecord from sorted table.
.51:PUSH ESI,EDI
CMPSD ; Has the Type changed?
JNE .55:
CMPSD ; Has the Name changed?
.55:POP EDI,ESI
JE .59: ; Skip creating resource dir if type and name did not change.
CALL .NewResDir: ; Type or name changed. Create level 3 directory EDX for lang entries.
.59:CALL .NewResEntry: ; Create resource entry for the language.
MOVSD
MOVSD ; Copy current record values to the previous ones.
SUB EDI,8
ADD ESI,SIZE#PFRSRC_RES_RECORD - 8
SUB ECX,SIZE#PFRSRC_RES_RECORD
JA .51: ; The next record.
; Pass 6: tree level 4 - data entries.
BufferRetrieve [%ResRecordBuf] ; ESI is now one ResRecord from the sorted table.
MOV EDI,[%TreePtr] ; EDI is now pointer to PFRSRC_DATA_ENTRY.
MOV [%DirLevel4Ptr],EDI
MOV EAX,[%RawDataOffset] ; Offset in soon-to-be compiled segment.
.61:Align2Dword EAX ; Each record creates one data entry.
MOV EBX,[ESI+PFRSRC_RES_RECORD.Data] ; EBX is now pointer to PFRSRC_RES_HEADER.
MOV EDX,[EBX+PFRSRC_RES_HEADER.DataSize] ; Raw data netto size, unaligned.
MOV [EDI+PFRSRC_DATA_ENTRY.OffsetToData],EAX
MOV [EDI+PFRSRC_DATA_ENTRY.Size],EDX
ADD EAX,EDX
MOV [%RawDataOffset],EAX
MOV EDX,EDI
SUB EDX,[%TreeBottom]
MOV [ESI+PFRSRC_RES_RECORD.DataDir],EDX
ADD EDI,SIZE# PFRSRC_DATA_ENTRY
ADD ESI,SIZE#PFRSRC_RES_RECORD
SUB ECX,SIZE#PFRSRC_RES_RECORD
JA .61:
; Walk the tree and resolve links.
MOV ESI,[%TreeBottom]
.71:CMP ESI,[%DirLevel2Ptr] ; ESI now points to resource directory level 1.
JNB .73:
MOVZX ECX,[ESI+PFRSRC_RESOURCE_DIRECTORY.NumberOfNamedEntries]
ADD CX,[ESI+PFRSRC_RESOURCE_DIRECTORY.NumberOfIdEntries]
ADD ESI,SIZE#PFRSRC_RESOURCE_DIRECTORY
.72:; ESI now points to ECX resource entries.
MOV EDX,[ESI+PFRSRC_RESOURCE_ENTRY.DataOrDir]; Temporary pointer to resource record.
MOV EAX,[EDX+PFRSRC_RES_RECORD.NameDir]
BTS EAX,31 ; MSbit=1.
MOV [ESI+PFRSRC_RESOURCE_ENTRY.DataOrDir],EAX
ADD ESI,SIZE#PFRSRC_RESOURCE_ENTRY
LOOP .72:
JMP .71:
.73:CMP ESI,[%DirLevel3Ptr] ; ESI now points to resource directory level 2.
JNB .76:
MOVZX ECX,[ESI+PFRSRC_RESOURCE_DIRECTORY.NumberOfNamedEntries]
ADD CX,[ESI+PFRSRC_RESOURCE_DIRECTORY.NumberOfIdEntries]
ADD ESI,SIZE#PFRSRC_RESOURCE_DIRECTORY
.75:; ESI now points to ECX resource entries.
MOV EDX,[ESI+PFRSRC_RESOURCE_ENTRY.DataOrDir]; Temporary pointer to resource record.
MOV EAX,[EDX+PFRSRC_RES_RECORD.LangDir]
BTS EAX,31 ; MSbit=1.
MOV [ESI+PFRSRC_RESOURCE_ENTRY.DataOrDir],EAX
ADD ESI,SIZE#PFRSRC_RESOURCE_ENTRY
LOOP .75:
JMP .73:
.76:CMP ESI,[%DirLevel4Ptr] ; ESI now points to resource directory level 3.
JNB .79:
MOVZX ECX,[ESI+PFRSRC_RESOURCE_DIRECTORY.NumberOfNamedEntries]
ADD CX,[ESI+PFRSRC_RESOURCE_DIRECTORY.NumberOfIdEntries]
ADD ESI,SIZE#PFRSRC_RESOURCE_DIRECTORY
.78:; ESI now points to ECX resource entries.
MOV EDX,[ESI+PFRSRC_RESOURCE_ENTRY.DataOrDir]; Temporary pointer to resource record.
MOV EAX,[EDX+PFRSRC_RES_RECORD.DataDir] ; Offset of data entry (leaf), MSbit=0.
MOV [ESI+PFRSRC_RESOURCE_ENTRY.DataOrDir],EAX
ADD ESI,SIZE#PFRSRC_RESOURCE_ENTRY
LOOP .78:
JMP .76:
.79: ; Tree in [%TreeBuf] and names in [%StrNameBuf] is now completed.
; Create a new resource segment in the current program, i.e. BasePgm.
MOV EBX,[%BasePgm]
Invoke SssCreateSe::,[EBX+PGM.CurrentStm],0,=B".rsrc",5, \
sssSegment+sssPrivate+sssNotBSS+sssWidth32+sssUsed,sssPurposeRESOURCE,16
JC .90:
MOV EDX,EAX ; Segment [.rsrc] wih empty .EmitBuffer and .RelocBuffer.
; Compile emitted contents of resource segment EDX.
MOV ECX,[%RawDataOffset]
MOV [EDX+SSS.TopLow],ECX
BufferNew [EDX+SSS.EmitBuffer],ECX
MOV EDI,EAX ; Pointer to .EmitBuffer bottom.
BufferRetrieve [%TreeBuf]
REP MOVSB ; Copy the tree division.
BufferRetrieve [%StrNameBuf]
REP MOVSB ; Copy the string names division.
BufferRetrieve [%ResRecordBuf]
XOR EAX,EAX
.82:PUSH ECX,ESI
MOV ECX,EDI
Align2Dword ECX
SUB ECX,EDI
JZ .83:
REP STOSB ; Align raw data to dword.
.83: MOV ESI,[ESI+PFRSRC_RES_RECORD.Data] ; Pointer to the resource header.
MOV ECX,[ESI+PFRSRC_RES_HEADER.DataSize]
ADD ESI,[ESI+PFRSRC_RES_HEADER.HeaderSize]
REP MOVSB ; Copy raw data.
.84:POP ESI,ECX
ADD ESI,SIZE#PFRSRC_RES_RECORD
SUB ECX,SIZE#PFRSRC_RES_RECORD
JA .82:
MOV EBX,EDX ; ^SSS [.rsrc].
; Create relocations in resource segment EBX.
MOV ECX,[%LangCnt] ; Number of relocations.
MOV EAX,SIZE# RELOC
MUL ECX
BufferNew [EBX+SSS.RelocBuffer],EAX, Zeroed=Yes
MOV EDI,EAX ; Pointer to the future RELOC objects.
MOV ESI,[%DirLevel4Ptr] ; ESI is now pointer to ECX data entries.
.86:MOV EAX,ESI ; PFRSRC_DATA_ENTRY.OffsetToData.
SUB EAX,[%TreeBottom]
MOV [EDI+RELOC.OrgLow],EAX ; Offset of PFRSRC_DATA_ENTRY in level 4.
MOV [EDI+RELOC.Section],EBX
MOV EAX,[EBX+SSS.SymPtr] ; symSe reprezents the beginning of [.rsrc] section.
MOV [EDI+RELOC.Symbol],EAX
SetSt [EDI+RELOC.Status],relocAbsRVA+relocWidth32
ADD ESI,SIZE#PFRSRC_DATA_ENTRY
ADD EDI,SIZE#RELOC
LOOP .86: ; The next RELOC.
.CmpRecords PROC1 ; Callback subprocedure to compare two records pointed by ESI,EDI dword by dword.
PUSH ECX
MOV ECX,SIZE#PFRSRC_RES_RECORD / 4
MOV EAX,ESI
MOV EDX,EDI
REPE CMPSD
MOV ESI,EAX
MOV EDI,EDX
CMC ; Ascending order.
JNC .C9:
MOV ECX,SIZE#PFRSRC_RES_RECORD / 4 ; Swap records ESI with EDI, return with CF.
.C2: MOV EDX,[EDI]
LODSD
MOV [ESI-4],EDX
STOSD
LOOP .C2:
STC
.C9:POP ECX
RET
ENDP1 .CmpRecords
.ResetPrev:PROC1 ; Subprocedures which create the binary tree.
; Initializes record at [%ResRecord] to never used values -1.
; Input: EDI=[%ResRecord].
; Output: All registers are preserved.
PUSH EAX,EDI
XOR EAX,EAX
NOT EAX
STOSD ; PFRSRC_RES_RECORD.Type=-1.
STOSD ; PFRSRC_RES_RECORD.Name=-1.
STOSD ; PFRSRC_RES_RECORD.Lang=-1.
POP EDI,EAX
RET
ENDP1 .ResetPrev:
.NewResDir:PROC1 ; Creates empty resource directory at [%TreePtr], increments [%TreePtr] by dirsize=16.
; Input: ESI=resouce record, EBX=0,4,8,12 for tree level 1,2,3,4
; Returns EDX=created directory, other registers are preserved.
PUSH EAX
MOV EAX,[%BasePgm]
MOV EDX,[%TreePtr]
MOV EAX,[Ea.Eaopt.TimeStamp::]
MOV [EDX+PFRSRC_RESOURCE_DIRECTORY.TimeDateStamp],EAX
MOV EAX,EDX
SUB EAX,[%TreeBottom]
MOV [ESI+PFRSRC_RES_RECORD.TypeDir+EBX],EAX
ADDD [%TreePtr],SIZE#PFRSRC_RESOURCE_DIRECTORY
POP EAX
RET
ENDP1 .NewResDir:
.NewResEntry:PROC1 ; Creates resource entry at [%TreePtr], increments [%TreePtr] by entrysize=8.
; The entry is counted to .NrOfNamedEntries or .NrOfIdEntries in resource directory EDX.
; Input: ESI=Resource record, EDX=resource directory, EBX=0,4,8,12 for tree level 1,2,3,4.
; Output: All registers are preserved.
PUSH EAX,EDI
MOV EDI,[%TreePtr] ; Pointer to PFRSRC_RESOURCE_ENTRY.IdOrName.
MOV EAX,[ESI+PFRSRC_RES_RECORD.Type+EBX] ; Value of type, name or language.
STOSD ; Store PFRSRC_RESOURCE_ENTRY.IdOrName.
XCHG EAX,ESI
STOSD ; Store temporary pointer to the current resource record to .DataOrDir.
XCHG ESI,EAX
MOV [%TreePtr],EDI ; The next vacant resource entry or dir.
SAR EAX,31 ; MSbit selects numeric or string typename.
AND AL,-2 ; EAX=-2 for string type, EAX=0 for numeric type.
INCW [EDX+PFRSRC_RESOURCE_DIRECTORY.NumberOfIdEntries+EAX] ; Increment one of two counters.
POP EDI,EAX
RET
ENDP1 .NewResEntry:
JMP .90:
.90:Invoke EaBufferRelease::, [%DataEntryBuf]
Invoke EaBufferRelease::, [%StrNameBuf]
Invoke EaBufferRelease::, [%TreeBuf]
Invoke EaBufferRelease::, [%ResRecordBuf]
EndProcedure PfrsrcLoadPgm
PURPOSE=RESOURCE exists in program yet, it will create an implicit segment
[.rsrc] with just one resource: the icon specified by program option
IconFile=.
[.rsrc] was created succesfully.
PfrsrcLoadIconFile Procedure Program
PathNr LocalVar ; Ordinal number of LINKPATH.
RsrcSss LocalVar ; Pointer to the section [.rsrc].
IcoPtr LocalVar ; Pointer to the beginning of icon file mapped in memory.
IcoSize LocalVar ; Netto size of icon file.
IcoEnd LocalVar ; Pointer to the end of *.ico file in memory.
ResBuffer LocalVar ; BUFFER for compiled resource.
IcoImgBuf LocalVar ; BUFFER with DD Ptr,Size pairs.
IcoImgQW LocalVar ; Pointer to DD Ptr,Size pairs inside %IcoImgBuf.
NameId LocalVar ; Numeric name of icon (1,2,3..).
ResHeader LocalVar Size=SIZE#PFRSRC_RES_HEADER ; Working space for resource header.
IcoFile LocalVar Size=SIZE#FILE ; Linked icon file.
ClearLocalVar
MOV EBX,[%Program]
; Check already linked segments on sssPurposeRESOURCE.
ListGetFirst [EBX+PGM.SssList]
JZ .90:
.10:JSt [EAX+SSS.Purpose],sssPurposeRESOURCE,.90: ; If a resource section already exists, do nothing.
ListGetNext EAX
JNZ .10:
; Check if icon file is specified.
MOV EDX,[EBX+PGM.Pgmopt.IconFileSize]
MOV ESI,[EBX+PGM.Pgmopt.IconFilePtr]
TEST EDX
JZ .90: ; When IconFile is empty, no resource section will be linked.
; Query if Filemask ESI,EDX was specified with path (it contains slash or colon).
PfQueryChar '\'
JE .50:
PfQueryChar '/'
JE .50:
PfQueryChar ':'
JE .50:
SUB EAX,EAX ; When there is no path in ICONFILE=, try all pathes from LINKPATH=.
MOV [%PathNr],EAX
.20:Invoke EaoptGetOnePath::,[Ea.Eaopt.LinkPathPtr::],[Ea.Eaopt.LinkPathSize::],[%PathNr]
JC .E7752: ; IconFile=!1$ was no found in LinkPath="!2S".
INCD [%PathNr] ; Prepare for the next path. One path is in ESI,ECX.
MOV EBX,[%Program]
MOV EDX,[EBX+PGM.Pgmopt.IconFileSize]
LEA EAX,[EDX+ECX+1]
CMP EAX,260
JA .E6953: ; Size of LinkPath "!1_" + size of filename exceeded 256 characters.
LEA EDI,[%IcoFile+FILE.Name] ; Assign path+iconfile name to %IcoFile.
REP MOVSB
MOV AX,'\/'
CMPB [Ea.EuroasmOS::],'W' ; Choose slash or backslash.
JE .30:
XCHG AL,AH
.30:CMP AL,[EDI-1]
JE .40:
CMP AH,[EDI-1]
JE .40:
STOSB ; If the path was not terminated with slash or backslash, add it.
.40:MOV ESI,[EBX+PGM.Pgmopt.IconFilePtr]
MOV ECX,EDX
REP MOVSB
SUB EAX,EAX
STOSB ; Zero terminate the filename.
LEA EDI,[%IcoFile] ; IconFile is now assigned with path and filemask.
LEA EDX,[EDI+FILE.Name]
SysOpenFileMap EDI,EDX
JC .20: ; If not found, try the next IncludePath.
JMP .60:
.50:LEA EDI,[%IcoFile]
SysOpenFileMap EDI,ESI,FileNameSize=EDX ; Path was found in filename ESI.
JC .E7751: ; IconFile=!1$ was not found.
.60:MOV ESI,[EDI+FILE.BufPtr]
MOV ECX,[EDI+FILE.BufSize]
MOV [%IcoPtr],ESI
CMP ECX,SIZE# PFRSRC_ICONDIR + SIZE# PFRSRC_ICONDIRENTRY
JNA .E7753: ; Invalid format of IconFile="!1$".
Invoke EaBufferReserve::,PfrsrcLoadIconFile
MOV [%IcoImgBuf],EAX
; Icon file is now mapped to memory at ESI. Let's create linkable compiled resource "file" in memory.
ADD ECX,ESI
MOV [%IcoEnd],ECX
MOVZXW ECX,[ESI+PFRSRC_ICONDIR.idCount] ; Number of icon images in the file.
TEST ECX
JZ .E7753: ; Invalid format of IconFile="!1$".
CMP ECX,16
JA .E7753: ; Invalid format of IconFile="!1$".
INC ECX,ECX ; That number of RES_HEADER will be in resource "file".
SAL ECX,5 ; Multiply with SIZE# PFRSRC_RES_HEADER.
ADD ECX,[EDI+FILE.BufSize] ; Plus icon file size.
BufferCreate [EBX+PGM.Pool],Size=ECX ; Resource "file" will be compiled here.
MOV [%ResBuffer],EAX
; Store initial resource header.
LEA EDI,[%ResHeader]
XOR EAX,EAX
NOT EAX
MOVD [EDI+PFRSRC_RES_HEADER.HeaderSize],SIZE# PFRSRC_RES_HEADER
MOV [EDI+PFRSRC_RES_HEADER.ResType],AX ; Use numeric Id rather than Unicode strings.
MOV [EDI+PFRSRC_RES_HEADER.ResName],AX
BufferStore [%ResBuffer],EDI,SIZE# PFRSRC_RES_HEADER
; Store resource header for GROUP_ICON.
MOVZXW ECX,[ESI+PFRSRC_ICONDIR.idCount] ; Number of icon images in the file.
MOV EAX,SIZE# PFRSRC_GRPICONDIRENTRY ; 14.
MUL ECX
ADD EAX,SIZE# PFRSRC_GRPICONDIR ; 6.
MOV [EDI+PFRSRC_RES_HEADER.DataSize],EAX
MOVW [EDI+PFRSRC_RES_HEADER.ResType+2],pfrsrcGROUP_ICON ; 0x000E.
MOVW [EDI+PFRSRC_RES_HEADER.ResName+2],1 ; GROUP_ICON #1.
MOVW [EDI+PFRSRC_RES_HEADER.MemoryFlags],pfrsrcResMOVABLE+pfrsrcResPURE+pfrsrcResDISCARDABLE
BufferStore [%ResBuffer],EDI,SIZE# PFRSRC_RES_HEADER
; Store resource data for GROUP_ICON. Plain copy ICONDIR to GRPINCONDIR.
BufferStore [%ResBuffer],ESI,SIZE# PFRSRC_ICONDIR
ADD ESI,SIZE# PFRSRC_ICONDIR ; ESI now points to ICONDIRENTRY.
; Store ICONDIRENTRY to GRPICONDIRENTRY with DD .dwImageOffset modified to DW .nId.
; Repeat for each image (ECX-times) in the loop .61: .. .69:
.61:CMP ESI,[%IcoEnd]
JNB .E7753: ; Invalid format of IconFile="!1$".
PUSH ECX
BufferNew [%ResBuffer],SIZE# PFRSRC_GRPICONDIRENTRY - 2 ; Omit the last member (.nId).
MOV EDI,EAX
MOV EAX,[ESI+PFRSRC_ICONDIRENTRY.dwImageOffset] ; File address.
ADD EAX,[%IcoPtr] ; Convert FA to memory pointer.
BufferStoreDword [%IcoImgBuf],EAX ; Temporary save pointer and then size of image.
MOV EAX,[ESI+PFRSRC_ICONDIRENTRY.dwBytesInRes]
BufferStoreDword [%IcoImgBuf],EAX ; Size of image.
MOV ECX,SIZE# PFRSRC_ICONDIRENTRY - 4 ; Omit the last member (.dwImageOffset).
REP MOVSB
MOV EAX,[%NameId]
INC EAX
MOV [%NameId],EAX
BufferStoreWord [%ResBuffer],EAX ; Store PFRSRC_GRPICONDIRENTRY.nId.
LODSD ; Skip void PFRSRC_ICONDIRENTRY.dwImageOffset.
.69:POP ECX
LOOP .61:
Invoke EaBufferAlign::,[%ResBuffer],4
; Store resource header and data for each icon image in the loop .71: .. .79:.
BufferRetrieve [%IcoImgBuf]
MOV [%IcoImgQW],ESI ; Prepare pointer to DD Ptr,Size pairs with raw image data.
MOV ESI,[%IcoPtr] ; Restore pointer to icon file mapped in memory.
MOVZXW ECX,[ESI+PFRSRC_ICONDIR.idCount] ; Number of icon images in the file.
MOVD [%NameId],0
.71:CMP ESI,[%IcoEnd]
JNB .E7753: ; Invalid format of IconFile="!1$".
PUSH ECX
LEA EDI,[%ResHeader]
MOVW [EDI+PFRSRC_RES_HEADER.ResType+2],pfrsrcICON ; 3.
MOV EAX,[%NameId]
INC EAX
MOV [%NameId],EAX
MOV [EDI+PFRSRC_RES_HEADER.ResName+2],AX ; Numeric icon Id.
MOV EDX,[%IcoImgQW]
MOV EAX,[EDX+4]
MOV [EDI+PFRSRC_RES_HEADER.DataSize],EAX
BufferStore [%ResBuffer],EDI,SIZE# PFRSRC_RES_HEADER
MOV ESI,EDX
LODSD ; Load pointer to the raw data.
MOV ECX,EAX
LODSD ; Load size of raw data.
MOV [%IcoImgQW],ESI ; Prepare for the next image.
BufferStore [%ResBuffer],ECX,EAX ; Store the raw image data.
Invoke EaBufferAlign::,[%ResBuffer],4
POP ECX
LOOP .71:
BufferRetrieve [%ResBuffer] ; ESI,ECX now contain the contents of resource file.
; Convert resource ESI,ECX to binary tree in [.rsrc] section.
LEA EDX,[%IcoFile]
Invoke PfrsrcLoadPgm,EBX,ESI,ECX,[EDX+FILE.Name]
JMP .90:
.E6953:Msg '6953',ESI ; Size of LinkPath "!1_" + size of filename exceeded 256 characters.
JMP .Error:
.E7751:MOV ESI,[EBX+PGM.Pgmopt.IconFilePtr]
Msg '7751',ESI ; IcoFile=!1$ was not found.
JMP .Error:
.E7752:MOV ESI,[EBX+PGM.Pgmopt.IconFilePtr]
LEA EAX,[Ea.Eaopt.LinkPathPtr]
Msg '7752',ESI,EAX ; IconFile=!1$ was no found in LinkPath="!2S".
JMP .Error:
.E7753:MOV ESI,[EBX+PGM.Pgmopt.IconFilePtr]
Msg '7753',ESI ; Invalid format of IconFile="!1$".
.Error:STC
.90: Invoke EaBufferRelease::,[%IcoImgBuf]
EndProcedure PfrsrcLoadIconFile
ENDPROGRAM pfrsrc