EuroAssembler Index Manual Download Source Macros


Sitemap Links Forum Tests Projects

pfrsrc.htm
Structures
PFRSRC_DATA_ENTRY
PFRSRC_GRPICONDIR
PFRSRC_GRPICONDIRENTRY
PFRSRC_ICONDIR
PFRSRC_ICONDIRENTRY
PFRSRC_RES_HEADER
PFRSRC_RES_RECORD
PFRSRC_RESOURCE_DIRECTORY
PFRSRC_RESOURCE_ENTRY
Encodings
PfrsrcEncoding
Procedures
PfrsrcCompile
PfrsrcLoadIconFile
PfrsrcLoadPgm
PfrsrcStoreRecord
PfrsrcStoreUstring

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
INCLUDEHEAD "euroasm.htm" ; Interface (structures, symbols and macros) of other modules.
INCLUDEHEAD  \  ; Include headers of another modules used in this module.
ea.htm,      \
eaopt.htm,   \
exp.htm,     \
msg.htm,     \
pf.htm,      \
pgm.htm,     \
pgmopt.htm,  \
reloc.htm,   \
sss.htm,     \
syswin.htm,  \
;;
 pfrsrc HEAD ; Start module interface.
↑ PFRSRC_RES_HEADER
Linkable compiled resource file, usually with extension .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.
There are two kinds of resource types: user-defined and standard (defined by Microsoft). Name and user-defined type may be alternatively identified by string name rather than by 16bit number.
Resource header is used by compiled resource file. This structure has in fact variable size, unless actual ResTypeLength=ResNameLength=2.
When the first WORD in .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.
When the first WORD in .ResName is 0xFFFF, ResNameLength is 2 and the second word specifies resource name identifier (numeric id). Otherwise it is a zero-terminated Unichar string.
When the offset following .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.
↑ PFRSRC_RES_RECORD
This is an internal €ASM structure derived from PFRSRC_RES_HEADER used for construction of resource tree.
Interpretation of members .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.
↑ PFRSRC_RESOURCE_DIRECTORY
Resource Directory is a 16byte structure used in [.rsrc] section in executable file.
It belongs to the resource tree.
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.
↑ PFRSRC_RESOURCE_ENTRY
Resource Entry is a 8byte structure used in [.rsrc] section in executable file.
Theese entries belong to the resource tree where they immediately follow PFRSRC_RESOURCE_DIRECTORY.
Their most significant bit selects their interpretation.
Offsets are related to the beginning of [.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
Data Entry describes one unit of raw resource data, e.g. bitmap or menu.
It belongs to the resource tree.
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.
↑ PFRSRC_ICONDIR
MS Windows ICO files begin with this 6bytes icon directory, which is followed by .idCount icon entries and then followed with .idCount image raw data (bitmaps).
See also
ICO file format and The format of icon resources
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.
↑ PFRSRC_ICONDIRENTRY
Icon entry which describes property of icon image built in *.ico file.
.bColorCount = 1 << (.wBitCount * .wPlanes)
.bColorCount = 0 when (.wBitCount * wPlanes)>= 8.
See also
PFRSRC_ICONDIR
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.
↑ PFRSRC_GRPICONDIR
Resource raw data of type pfrsrcGROUP_ICON in RES files and PE executables.
Identical with PFRSRC_ICONDIR. It is followed with .idCount of .idCount group-icon direntries.
See also
PFRSRC_ICONDIR, ICO file format and The format of icon resources
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.
↑ PFRSRC_GRPICONDIRENTRY
Resource raw dataIcon of type pfrsrcGROUP_ICON in PE executables.
Similar to PFRSRC_ICONDIRENTRY but the last member.
See also
PFRSRC_GRPICONDIR
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.
↑ PfrsrcEncoding
Encoding of memory status used in PFRSRC_RES_HEADER and standard resource type identifiers.
; 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 OutputStream, Pgm
PfrsrcCompile is a dummy procedure because EuroAssembler does not compile resource files.
Input
OutputStream is pointer to a STREAM for the output file contents.
Pgm is pointer to PGM representing completely assembled program.
Output
-
Error
DB '7827 EuroAssembler does not compile format RSRC, use resource compiler instead.
Invoked from
PfOutput
PfrsrcCompile  Procedure OutputStream, Pgm
   Msg '7827' ; EuroAssembler does not compile to format RSRC, use resource compiler instead.
  EndProcedure PfrsrcCompile
↑ PfrsrcStoreUstring StringPtr, StringEnd, StringNameBuffer
PfrsrcStoreUstring will convert input zero-terminated Unichar string to Pascal Unichar string and compare it with all strings already stored in StringNameBuffer. If it wasn't there, the string will be stored to the buffer. In both cases the procedure returns offset of the string relative to the bottom of StringNameBuffer.
Input
StringPtr is pointer to the zero terminated Unichar string.
StringEnd is pointer to the end of memory area where it is allowed to search for terminator 0.
StringNameBuffer is pointer to BUFFER reserved by caller, where the converted string will be stored, if it wasn't stored before. This pointer may be 0 when the resource file is verified only rather than loaded.
Output
CF=0, EAX= offset of the Pascal Unichar string in the buffer.
ESI= pointer behind the input String.
Error
CF=1, EAX=0
ESI=StringEnd when the terminating zero is missing.
Invoked by
PfrsrcStoreRecord
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]
    JECXZ .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
↑ PfrsrcStoreRecord ResPtr, ResEnd, RecBuffer, StrNameBuffer
PfrsrcStoreRecord will convert information about one resource item specified by PFRSRC_RES_HEADER to a form of PFRSRC_RES_RECORD and store the record to RecBuffer. String identifiers of resource type and name is converted from C-style to Pascal-style and stored to StrNameBuffer only if the same string was not already stored.
Empty resource items (PFRSRC_RES_HEADER.DataSize=0) are not stored.
Both buffers may be NULL if this procedure is used only to verify validity of resource file.
Input
ResPtr is pointer to PFRSRC_RES_HEADER header in resource file mapped in memory. When it is not DWORD aligned, the header will be read from the nearest dword.
ResEnd is pointer to the end of resource file.
RecBuffer is pointer to BUFFER reserved by caller, where the converted record will be stored. It may be 0 if the file is verified only.
StrNameBuffer is pointer to BUFFER reserved by caller, where the Pascal Unicode strings will be stored. It may be 0 if the file is verified only.
Output
CF=0, ZF=0, EAX=pointer to the next header. One record PFRSRC_RES_RECORD was written to RecBuffer.
CF=0, ZF=1, EAX=ResPtr when ResPtr pointed to the end of file. Nothing stored to RecBuffer.
Error
CF=1, EAX=ResPtr if the resource file .res has invalid structure.
Invoked by
PfDetect PfrsrcLoadPgm
Invokes
PfrsrcStoreUstring
PfrsrcStoreRecord Procedure ResPtr, ResEnd, RecBuffer, StrNameBuffer
ResRecord LocalVar Size=SIZE# PFRSRC_RES_RECORD  ; Room for the record before it is stored to buffer.
    MOV ESI,[%ResPtr]
    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
↑ PfrsrcLoadPgm BasePgm, ObjBegin, ObjSize, FileNamePtr
PfrsrcLoadPgm converts compiled resources to a format accepted by MS Windows in resource section of PE image. Segment contents has three divisions:
  1. binary tree which consists of resource directories, resource entries, data entries,
  2. string names, if used instead of numeric identificators,
  3. raw data of all resources.
Converted data and relocations are stored to .EmitBuffer and .RelocBuffer of the new segment [.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.
Pointer to resources will be also stored to PFPE_OPTIONAL_HEADER32.DataDirectory or PFPE_OPTIONAL_HEADER64.DataDirectory.
The input compiled resource file (usualy with extension .res) is assumed to be formally verified by PfDetect.
Input
BasePgm is pointer to an existing PGM which the resources are linked to.
ObjBegin is pointer to the contents of linked file mapped in memory by the caller.
ObjSize is number of bytes in the file.
FileNamePtr is pointer to zero-terminated file name (used in error reports).
Output
New segment [.rsrc] with PURPOSE=RESOURCE is created and put on %BaseProgram.SssList.
Error
Errors are reported with macro Msg.
Invoked from
PfLoad
Invoked by
PfrsrcLoadIconFile
Invokes
EaBufferAlign EaBufferRelease EaBufferReserve PfrsrcStoreRecord SssCreateSe
Tested by
t9385 t9436 t9485 t9536
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:Invoke PfrsrcStoreRecord,EAX,EDX,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
↑ PfrsrcLoadIconFile Program
PfrsrcLoadIconFile is invoked when a PE or DLL executable program is linked. If no segment with PURPOSE=RESOURCE exists in program yet, it will create an implicit segment [.rsrc] with just one resource: the icon specified by program option IconFile=.
If the IconFile= option is empty or if the icon file was not found, no resource segment is created.
Input
Programis pointer to PGM in PE of DLL format.
Output
CF=0 when segment [.rsrc] was created succesfully.
Error
CF=1 Errors are reported with macro Msg.
Depends on
PfQueryChar
Invokes
EaBufferAlign EaBufferRelease EaBufferReserve EaoptGetOnePath PfrsrcLoadPgm
Invoked by
PfpeCompile
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,MAX_PATH_SIZE
    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

▲Back to the top▲