| Index | Manual | Download | Source | Macros | Embedded word Case ins.
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
           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.

↑ 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]
        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

↑ PfrsrcStoreRecord ResPtr, ResEnd, MinPtr, MaxEnd, 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.
  MinPtr is minimal value of ResPtr.
  MaxEnd is maximal value of 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, 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

↑ 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: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

↑ 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
  Program is a 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,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 .80:
        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
          PUSH ESI
           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.
          POP ESI
          BufferStore [%ResBuffer],ECX,EAX           ; Store the raw image data.
          ADD ESI,EAX
          Invoke EaBufferAlign::,[%ResBuffer],4
        POP ECX
        LOOP .71:
    .80: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▲