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.
.res, consists of raw resource data chunks, each preceeded by resource header. Resource has three main properties encoded as unsigned 16bit integer number: type, name and language.
.ResType
is 0xFFFF, ResTypeLength is 2 and the second word
specifies resource type identifier (standard or custom numeric id). Otherwise it is a zero-terminated Unichar string.
.ResName
is 0xFFFF, ResNameLength is 2 and the second word
specifies resource name identifier (numeric id). Otherwise it is a zero-terminated Unichar string.
.ResName
member is not DWORD aligned, one padding zero-word must be added.
ResTypeLength EQU 2 ResNameLength EQU 2 PFRSRC_RES_HEADER STRUC .DataSize D DWORD ; Size of raw data which immediately follow this header. .HeaderSize D DWORD ; Size of this header. .ResType D ResTypeLength * UNICHAR .ResName D ResNameLength * UNICHAR .DataVersion D DWORD ; Version of following raw data, should be 0. .MemoryFlags D WORD ; Resource status, see PfrsrcEncoding. .LanguageID D WORD ; Language and sublanguage which strings are written in. .Version D DWORD ; Version of the resource editor. .Characteristics D DWORD ; Flags specified by resource editor. ENDSTRUC PFRSRC_RES_HEADER ; Size=20h=32 or more when strings are used.
.Type
and .Name
depends
on their most significant bit. Offsets are relative to the start of segment [.rsrc]
.
PFRSRC_RES_RECORD STRUC .Type D D ; MSbit=0: Offset of pascal unicode string identifier of resource type. ; MSbit=1: Value of numeric identifier of resource type in the lower word. .Name D D ; MSbit=0: Offset of pascal unicode string identifier of resource name. ; MSbit=1: Value of numeric identifier of resource name in the lower word. .Lang D D ; Value of the language and sublanguage identifier in the lower word. .Data D D ; Pointer to PFRSRC_RES_HEADER which defines raw resource data following the header. ; Following offsets are used for building the tree. .TypeDir D D ; Offset of PFRSRC_RESOURCE_DIRECTORY which specifies resource type. .NameDir D D ; Offset of PFRSRC_RESOURCE_DIRECTORY which specifies resource name. .LangDir D D ; Offset of PFRSRC_RESOURCE_DIRECTORY which specifies resource language. .DataDir D D ; Offset of PFRSRC_DATA_ENTRY which specifies resource raw data. ENDSTRUC PFRSRC_RES_RECORD ; Size=20h=32.
[.rsrc]
section in executable file.
PFRSRC_RESOURCE_DIRECTORY STRUC .Characteristics D D ; Reserved. .TimeDateStamp D D ; Time of resource creation. Number of seconds since midnight 1.1.1970 .MajorVersion D W ; Resource version. .MinorVersion D W .NumberOfNamedEntries D W ; How many string-identified PFRSRC_RESOURCE_ENTRY objects follow the directory. .NumberOfIdEntries D W ; How many numeric-identified PFRSRC_RESOURCE_ENTRY objects follow the directory. ENDSTRUC PFRSRC_RESOURCE_DIRECTORY ; Size=10h=16.
[.rsrc]
section in executable file.
[.rsrc]
section.
PFRSRC_RESOURCE_ENTRY STRUC .IdOrName D D ; MSbit=0: Resource numeric identifier in lower word. ; MSbit=1: Offset of UNICHAR Pascal-string carrying the typename or resource name. .DataOrDir D D ; MSbit=0: Offset of PFRSRC_DATA_ENTRY, i.e. the final tree leaf describing the raw data. ; MSbit=1: Offset of another PFRSRC_RESOURCE_DIRECTORY (one level lower). ENDSTRUC PFRSRC_RESOURCE_ENTRY ; Size=8.
PFRSRC_DATA_ENTRY STRUC .OffsetToData D D ; RVA of the raw data unit. .Size D D ; Numer of bytes in the raw data unit. .CodePage D D ; Encoding of characters in the raw data unit. .Reserved D D ; 0. ENDSTRUC PFRSRC_DATA_ENTRY ; Size=10h=16.
.idCount
icon entries and then followed with
.idCount
image raw data (bitmaps).PFRSRC_ICONDIR STRUC .idReserved D W ; Always 0. .idType D W ; 1=icon image (*.ico). 2=cursor image (*.cur). .idCount D W ; Number of icon images in this file. ENDSTRUC PFRSRC_ICONDIR ; Size=6.
.bColorCount = 1 << (.wBitCount * .wPlanes)
.bColorCount = 0
when (.wBitCount * wPlanes)>= 8
.PFRSRC_ICONDIRENTRY STRUC .bWidth D B ; Image width in pixels. 0 when 256px or more. .bHeight D B ; Image height in pixels. 0 when 256px or more. .bColorCount D B ; Number of colors in palette, see above. .bReserved D B ; 0. .wPlanes D W ; Number of color planes (0 or 1). .wBitCount D W ; Number of bits per pixel. .dwBytesInRes D D ; Size of icon raw data. .dwImageOffset D D ; File address of raw data from the beginning of the file. ENDSTRUC PFRSRC_ICONDIRENTRY ; Size=16=10h.
pfrsrcGROUP_ICON
in RES files and PE executables.
.idCount
group-icon direntries. PFRSRC_GRPICONDIR STRUC .idReserved D W ; Always 0. .idType D W ; 1=icon image (*.ico). 2=cursor image (*.cur). .idCount D W ; Number of GRPICONDIRENTRY objects in this resource object. ENDSTRUC PFRSRC_GRPICONDIR ; Size=6.
pfrsrcGROUP_ICON
in PE executables.
PFRSRC_GRPICONDIRENTRY STRUC .bWidth D B ; Image width in pixels. 0 when 256px or more. .bHeight D B ; Image height in pixels. 0 when 256px or more. .bColorCount D B ; Number of colors in palette. 0 when palette is not used. .bReserved D B ; 0. .wPlanes D W ; Number of color planes (0 or 1). .wBitCount D W ; Number of bits per pixel. .dwBytesInRes D D ; Size of icon raw data. .nId D W ; Numeric name identifier of this icon variant. ENDSTRUC PFRSRC_GRPICONDIRENTRY ; Size=14=0Eh.
; Memory flags: pfrsrcResMOVABLE = 0x0010 ; Loader may move the resource in memory. pfrsrcResPURE = 0x0020 ; Resource contain DWORD-aligned data, no alignment needed. pfrsrcResPRELOAD = 0x0040 ; Resource must be loaded in memory on load time. pfrsrcResDISCARDABLE = 0x1000 ; Resource may be removed and reloaded on demand. ; Standard resource type numeric identifiers: pfrsrcCURSOR = 0x0001 pfrsrcBITMAP = 0x0002 pfrsrcICON = 0x0003 pfrsrcMENU = 0x0004 pfrsrcDIALOG = 0x0005 pfrsrcSTRING = 0x0006 pfrsrcFONTDIR = 0x0007 pfrsrcFONT = 0x0008 pfrsrcACCELERATOR = 0x0009 pfrsrcRCDATA = 0x000A pfrsrcMESSAGETABLE = 0x000B pfrsrcGROUP_CURSOR = 0x000C pfrsrcGROUP_ICON = 0x000E pfrsrcVERSION = 0x0010
ENDHEAD pfrsrc ; End of module interface.
PfrsrcCompile Procedure OutputStream, Pgm Msg '7827' ; EuroAssembler does not compile to format RSRC, use resource compiler instead. EndProcedure PfrsrcCompile
PfrsrcStoreUstring Procedure StringPtr, StringEnd, StringNameBuffer XOR EAX,EAX MOV EDI,[%StringPtr] MOV ECX,[%StringEnd] MOV [%ReturnEAX],EAX SUB ECX,EDI MOV ESI,EDI REPNE SCASW MOV [%ReturnESI],EDI STC JNE .90: ; Return with error if terminator missing. SUB EDI,ESI ; Brutto string size. SAR EDI,1 LEA EDX,[EDI-1] ; EDX is now netto string length in Unichars. CMP EDX,0x0000_FFFE CMC JC .90: ; Error - string is too long. MOV ECX,[%StringNameBuffer] 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
.reshas invalid structure.
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
[.rsrc]
, with PURPOSE=RESOURCE
, which will be appended to other segments of the BasePgm.
When segment with this name already exists in BasePgm and when its Status is
sssImplicit
, its old contents (which was specified by program option ICONFILE=) will be overwritten.
PFPE_OPTIONAL_HEADER32.DataDirectory
or
PFPE_OPTIONAL_HEADER64.DataDirectory
.
.res) is assumed to be formally verified by PfDetect.
[.rsrc]
with PURPOSE=RESOURCE
is created and put on %BaseProgram.SssList
.PfrsrcLoadPgm Procedure BasePgm, ObjBegin, ObjSize, FileNamePtr ObjEnd LocalVar ; End of input data in memory. TypeCnt LocalVar ; Number of Type entries (tree level 1). NameCnt LocalVar ; Number of Name entries (tree level 2). LangCnt LocalVar ; Number of Lang entries (tree level 3). ResRecordBuf LocalVar ; Pointer to BUFFER which keeps PFRSRC_RES_RECORD. ResRecord LocalVar Size=SIZE# PFRSRC_RES_RECORD ; Room for temporary resource record. TreeBuf LocalVar ; Pointer to BUFFER which keeps PFRSRC_RESOURCE_DIRECTORY and PFRSRC_RESOURCE_ENTRY. TreeBottom LocalVar ; Pointer to the start of tree in TreeBuf. TreePtr LocalVar ; Pointer to current PFRSRC_RESOURCE_DIRECTORY inside %TreeBuf. TreeSize LocalVar ; Size of the binary tree division. Naturally QWORD aligned. DataEntryBuf LocalVar ; Pointer to BUFFER which keeps PFRSRC_DATA_ENTRY. DataEntryPtr LocalVar ; Pointer to the current PFRSRC_DATA_ENTRY inside %DataEntryBuf. DataEntrySize LocalVar ; Size of the entire PFRSRC_DATA_ENTRY division. Naturally QWORD aligned. StrNameBuf LocalVar ; Pointer to BUFFER which keeps Pascal UNICHAR string. StrNamePtr LocalVar ; Pointer to the current free Pascal Unichar string inside StrNameBuf. StrNameSize LocalVar ; Size of Pascal Unichar strings. Naturally WORD aligned, at the end stuffed to DWORD. RawDataOffset LocalVar ; Offset of current raw data in output section. DirLevel2Ptr LocalVar ; Pointer to first resource directory inside tree which specifies resource name. DirLevel3Ptr LocalVar ; Pointer to first resource directory inside tree which specifies resource language. DirLevel4Ptr LocalVar ; Pointer to first data entry inside tree which specifies resource data. ClearLocalVar Invoke EaBufferReserve::,PfrsrcLoadPgm MOV [%ResRecordBuf],EAX MOV EBX,EAX Invoke EaBufferReserve::,PfrsrcLoadPgm MOV [%TreeBuf],EAX Invoke EaBufferReserve::,PfrsrcLoadPgm MOV [%StrNameBuf],EAX MOV EDI,EAX Invoke EaBufferReserve::,PfrsrcLoadPgm MOV [%DataEntryBuf],EAX MOV EAX,[%ObjBegin] MOV ECX,[%ObjSize] LEA EDX,[EAX+ECX] MOV [%ObjEnd],EDX ; Resource file will be indexed into temporary array of PFRSRC_RES_RECORDs and sorted. .05: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
PURPOSE=RESOURCE
exists in program yet, it will create an implicit segment
[.rsrc]
with just one resource: the icon specified by program option
IconFile=
.
[.rsrc]
was created succesfully.PfrsrcLoadIconFile Procedure Program PathNr LocalVar ; Ordinal number of LINKPATH. RsrcSss LocalVar ; Pointer to the section [.rsrc]. IcoPtr LocalVar ; Pointer to the beginning of icon file mapped in memory. IcoSize LocalVar ; Netto size of icon file. IcoEnd LocalVar ; Pointer to the end of *.ico file in memory. ResBuffer LocalVar ; BUFFER for compiled resource. IcoImgBuf LocalVar ; BUFFER with DD Ptr,Size pairs. IcoImgQW LocalVar ; Pointer to DD Ptr,Size pairs inside %IcoImgBuf. NameId LocalVar ; Numeric name of icon (1,2,3..). ResHeader LocalVar Size=SIZE#PFRSRC_RES_HEADER ; Working space for resource header. IcoFile LocalVar Size=SIZE#FILE ; Linked icon file. ClearLocalVar MOV EBX,[%Program] ; Check already linked segments on sssPurposeRESOURCE. ListGetFirst [EBX+PGM.SssList] JZ .90: .10:JSt [EAX+SSS.Purpose],sssPurposeRESOURCE,.90: ; If a resource section already exists, do nothing. ListGetNext EAX JNZ .10: ; Check if icon file is specified. MOV EDX,[EBX+PGM.Pgmopt.IconFileSize] MOV ESI,[EBX+PGM.Pgmopt.IconFilePtr] TEST EDX JZ .90: ; When IconFile is empty, no resource section will be linked. ; Query if Filemask ESI,EDX was specified with path (it contains slash or colon). PfQueryChar '\' JE .50: PfQueryChar '/' JE .50: PfQueryChar ':' JE .50: SUB EAX,EAX ; When there is no path in ICONFILE=, try all pathes from LINKPATH=. MOV [%PathNr],EAX .20:Invoke EaoptGetOnePath::,[Ea.Eaopt.LinkPathPtr::],[Ea.Eaopt.LinkPathSize::],[%PathNr] JC .E7752: ; IconFile=!1$ was no found in LinkPath="!2S". INCD [%PathNr] ; Prepare for the next path. One path is in ESI,ECX. MOV EBX,[%Program] MOV EDX,[EBX+PGM.Pgmopt.IconFileSize] LEA EAX,[EDX+ECX+1] CMP EAX,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