LinkRes2Exe by Mike Henning

Started by MrBcx, July 19, 2025, 01:13:13 PM

Previous topic - Next topic

Quin

Ah, fair enough, that makes sense. Well, it works as it is in 32-bit, so no complaints!

MrBcx

Quote from: Quin on July 24, 2025, 04:51:59 PMWhy must I compile this as 32-bit though?


Because binary data alignment is unforgiving and I didn't want to do a lot of testing converting it to 64-bit.

Quin

Ooh, okay now that is cool. now I can use MinGW with res files, as you pointed out windres leaves...a lot to be desired. Why must I compile this as 32-bit though?

jbk


MrBcx

Mike Henning wrote this app in 2006 and it still builds today using the current version of BCX.

It is important that you build this as a 32-bit executable, to minimize warnings and to ensure
its proper operation.  Be certain to build it as a console mode app.

This app can attach a proper Windows binary resource file (*.res) to any 32-bit or 64-bit exe.
This is particularly good news for for folks that prefer working with the MINGW toolchain, as
WINDRES is not particularly adept at dealing with *.res files.


'*********************************************************
'  LinkRes2Exe 1.02  (c)2006 by Mike Henning
'  Usage: LinkRes2Exe  file.res  file.exe [-verbose]
'*********************************************************
' This console mode app should only be compiled to 32-bit
' to minimize warnings and to better ensure proper
' operation.  It provides the easiest way for attaching
' a standard binary Windows resource file (*.res) to a
' MINGW32/64 (GCC) executable, or any other executable
' built with any of the Windows c/c++ compilers.
'*********************************************************
'     It works on 32-bit and 64-bit executable files
'*********************************************************

TYPE ResHead1
    DataSize    AS DWORD
    HeaderSize  AS DWORD
END TYPE

TYPE ResHead2
    DataVersion AS DWORD
    MemFlags    AS WORD
    LangId      AS WORD
    Version     AS DWORD
    Charistics  AS DWORD
END TYPE

GLOBAL Verbose

'************************ Main ************************
IF ARGC > 2 THEN
    IF ARGC = 4 THEN
        IF ARGV$[3] = "-verbose" THEN Verbose = TRUE
    END IF
    ' argv$[1] = res
    ' argv$[2] = exe
    AddRes2File (argv$[1], argv$[2])
ELSE
    PRINT " LinkRes2Exe 1.02  (c)2006 by Mike Henning"
    PRINT " Usage: LinkRes2Exe  file.res  file.exe [-verbose]"
END IF

'******************************************************


FUNCTION AddRes2File (ResFile$, TgtFile$)
    DIM RAW h AS HANDLE
    DIM RAW rh1 AS ResHead1
    DIM RAW rh2 AS ResHead2 PTR
    DIM RAW ResName AS DWORD, szResName$
    DIM RAW ResType AS DWORD, szResType$
    DIM RAW unihead = NULL AS PCHAR
    DIM RAW rdata   = NULL AS PCHAR
    DIM RAW HeadOffset, UniSize
    DIM RAW FileEnd, FilePos = 0, Result = 0

    IF NOT EXIST(ResFile$) THEN
        PRINT "File not found: ", ResFile$
        FUNCTION = 1
    ELSEIF NOT EXIST(TgtFile$) THEN
        PRINT "File not found: ", TgtFile$
        FUNCTION = 1
    END IF

    h = BeginUpdateResource(TgtFile$, TRUE)  ' File to add the resource to
    IF h = NULL THEN
        PRINT "BeginUpdateResource failed..."
        FUNCTION = 1
    END IF

    FileEnd = LOF(ResFile$)

    OPEN ResFile$ FOR BINARY INPUT AS FP1

    WHILE FilePos < FileEnd

        GET$ FP1, &rh1, SIZEOF(rh1)
        INCR FilePos, SIZEOF(rh1)

        UniSize = dwAlign(rh1.HeaderSize - SIZEOF(rh1))
        unihead = (PCHAR)realloc(unihead, UniSize)
        GET$ FP1, unihead, UniSize
        INCR FilePos, UniSize

        rh2 = (LPRESHEAD2)&unihead[UniSize-SIZEOF(*rh2)]

        ' --------------------------------------------
        ' Get the resource type
        ' --------------------------------------------
        HeadOffset = IsUnicode(unihead$)

        IF HeadOffset = 0 THEN
            ResType    = HIWORD(*(DWORD*)unihead)
            szResType$ = RetResType$(ResType)
            HeadOffset = SIZEOF(DWORD)
        ELSE
            szResType$ = WIDETOANSI$((LPWSTR)unihead)
            ResType    = (DWORD)szResType
        END IF

        ' --------------------------------------------
        ' Get the resource name
        ' --------------------------------------------
        IF IsUnicode(&unihead[HeadOffset]) = 0 THEN
            ResName    = HIWORD(*(DWORD*)(unihead + HeadOffset))
            szResName$ = STR$(ResName)
        ELSE
            szResName$ = WIDETOANSI$((LPWSTR)(&unihead[HeadOffset]))
            ResName    = (DWORD)szResName
        END IF

        IF Verbose THEN
            PRINT "DataSize    =" , rh1.DataSize
            PRINT "HeaderSize  =" , rh1.HeaderSize
            PRINT "ResType     = ", szResType$
            PRINT "ResName     = ", szResName$
            PRINT "DataVersion =" , rh2->DataVersion
            PRINT "MemFlags    = ", RetMemFlags$(rh2->MemFlags)
            PRINT "LangId      =" , rh2->LangId
            PRINT "Version     =" , rh2->Version
            PRINT "Charistics  =" , rh2->Charistics

            PRINT STRING$(60, ASC("-"))
        END IF

        HeadOffset = dwAlign(rh1.DataSize)

        rdata = (PCHAR)realloc(rdata, HeadOffset)
        GET$ FP1, rdata$, HeadOffset
        INCR FilePos, HeadOffset

        IF rh1.DataSize THEN
            IF UpdateResource( h, (PCHAR)ResType, (PCHAR)ResName, rh2->LangId, rdata$, rh1.DataSize) = 0 THEN
                PRINT "UpdateResource failed..."
                Result = 1
                EXIT LOOP
            END IF
        END IF

    WEND

    FREE unihead
    FREE rdata
    CLOSE FP1

    IF NOT EndUpdateResource(h, Result) THEN
        PRINT "EndUpdateResource failed..."
        Result = 1
    END IF

    FUNCTION = Result
END FUNCTION




FUNCTION RetResType (ResType) AS LPCSTR
    SELECT CASE ResType
        CASE 0x0000 : FUNCTION = "NULL"
        CASE 0x0001 : FUNCTION = "Cursor"
        CASE 0x0002 : FUNCTION = "Bitmap"
        CASE 0x0003 : FUNCTION = "Icon"
        CASE 0x0004 : FUNCTION = "Menu"
        CASE 0x0005 : FUNCTION = "Dialog"
        CASE 0x0006 : FUNCTION = "String Table"
        CASE 0x0007 : FUNCTION = "Font Directory"
        CASE 0x0008 : FUNCTION = "Font"
        CASE 0x0009 : FUNCTION = "Accelerators Table"
        CASE 0x000A : FUNCTION = "RC Data (custom binary data)"
        CASE 0x000B : FUNCTION = "Message table"
        CASE 0x000C : FUNCTION = "Group Cursor"
        CASE 0x000E : FUNCTION = "Group Icon"
        CASE 0x0010 : FUNCTION = "Version Information"
        CASE 0x0011 : FUNCTION = "Dialog Include"
        CASE 0x0013 : FUNCTION = "Plug'n'Play"
        CASE 0x0014 : FUNCTION = "VXD"
        CASE 0x0015 : FUNCTION = "Animated Cursor"
        CASE 0x0018 : FUNCTION = "Manifest"
        CASE 0x2002 : FUNCTION = "Bitmap (new version)"
        CASE 0x2004 : FUNCTION = "Menu (new version)"
        CASE 0x2005 : FUNCTION = "Dialog (new version)"

    END SELECT

    FUNCTION = "Unknown"
END FUNCTION


FUNCTION RetMemFlags (flags) AS LPSTR
    STATIC MemFlags$ : MemFlags$ = ""
    SELECT CASE BAND flags
        CASE 0x0010 : MemFlags$ = "MOVEABLE"
        CASE 0x0020 : CONCAT(MemFlags$, " PURE")
        CASE 0x0040 : CONCAT(MemFlags$, " PRELOAD")
        CASE 0x1000 : CONCAT(MemFlags$, " DISCARDABLE")
    END SELECT

    IF flags = 0 THEN MemFlags$ = "None"

    FUNCTION = MemFlags
END FUNCTION


'----------------------------------------------------------
' Returns 0 if sz$ is an ordinal (numeric id)
' Returns a DWORD aligned length if a unicode name
'----------------------------------------------------------
FUNCTION IsUnicode(sz$) AS DWORD

    IF LOWORD(*(DWORD*)sz) = 0xFFFF THEN
        FUNCTION = 0
    END IF

    FUNCTION = dwAlign(2 + (2 * wcslen((LPCWSTR)sz)))
END FUNCTION


FUNCTION dwAlign (ul AS DWORD) AS DWORD
    ul+ = 3
    ul>>=2
    ul<<=2
    FUNCTION = ul
END FUNCTION



'==============================================================================
'                         WIN32 Resource file format
'==============================================================================
'
' Offset Field Datatype         Description
'-------------------------------------------------------------------------------
' 0      DataSize  DWORD
' 4 bytes,
'                           32 bit unsigned integer The size of the data that
'                         follows the header
'                                                       (not including any padding).
'-------------------------------------------------------------------------------
' 4 HeaderSize     DWORD
' 4 bytes,
'                           32 bit unsigned integer The size of the header structure.
'-------------------------------------------------------------------------------
' 8 ResType Ordinal or UNICODE string
' Array of 16 bit unsigned
'                               integers (2 bytes each) Resource Type ID (standard or custom).
'-------------------------------------------------------------------------------
' OfsOf(ResType)
'+SizeOf(ResType)
'+Padding ResName Ordinal or UNICODE string
' Array of 16 bit unsigned
'                                integers (2 bytes each) Resource name.
'-------------------------------------------------------------------------------
' OfsOf(ResName) Determines the format of
'+SizeOf(ResName)                                               the information within the
'+Padding DataVersion DWORD                           resource header that follows.
' 4 bytes,                        Not currently used.
'                               32 bit unsigned integer Should be zeroed.
'-------------------------------------------------------------------------------
' OfsOf(DataVersion)+4
'        MemoryFlags WORD
' 2 bytes,
'                                16 bit unsigned integer The state of the resource.
'-------------------------------------------------------------------------------
' OfsOf(MemoryFlags)+2
'        LanguageID WORD
' 2 bytes,
'                                16 bit unsigned integer The language that the strings
' are written with.
'-------------------------------------------------------------------------------
' OfsOf(LanguageID)+2
'        Version DWORD
' 4 bytes,
'                                32 bit unsigned integer It has no significance to the
' system. Used by resource editors.
'                                                               Usually zeroed.
'-------------------------------------------------------------------------------
' OfsOf(Version)+4
'        Characteristics DWORD
' 4 bytes,
'                                32 bit unsigned integer It has no significance to the
' system. Used by resource editors.
'                                                               Usually zeroed.
'-------------------------------------------------------------------------------