PowerBASIC Users Meeting Point
Using #RESOURCE RCDATA to implement the missing #RESOURCE AVI - Printable Version

+- PowerBASIC Users Meeting Point (http://pump.richheimer.de)
+-- Forum: User to User Discussions (http://pump.richheimer.de/forumdisplay.php?fid=3)
+--- Forum: Source Code Library (http://pump.richheimer.de/forumdisplay.php?fid=8)
+--- Thread: Using #RESOURCE RCDATA to implement the missing #RESOURCE AVI (/showthread.php?tid=110)



Using #RESOURCE RCDATA to implement the missing #RESOURCE AVI - George Bleck - 14.01.2026

I posted this on the new community run PB Users Forum: https://pbusers.org/forum/viewtopic.php?t=239
Sharing here as well as I think it is useful for those that might not have joined there yet.

============================


It always bothered me that PowerBASIC never implemented a #RESOURCE AVI metastatement. If you want to include an AVI for the animation control via #RESOURCE metastatements it's impossible as you cannot animate directly from RCDATA (or other resource sections available via the #RESOURCE metastatement). It can only be done from an AVI resource section (or from a supplied file path). Yes, you can compile the AVI into an AVI resource section using an RC file compiler and add it as a PBR (which still works but was technically deprecated) or RES, but "you cannot add any other #RESOURCE metastatements in your program" as per the PowerBASIC documentation.

What this code does is allow you to embed AVIs into your code using #RESOURCE RCDATA. After you compile your code to DLL or EXE you run this patcher by supplying the DLL/EXE file path on the command line (or via drag-and-drop). It proceeds to enumerate the AVIs and moves them to an AVI resource section using UpdateResource.

Be aware that you will need to repatch the DLL/EXE each time you re-compile your code.
As such, this could be included in a tool chain to always run after a compile.

Processing is written to a log (AVIRscPtch_Log.csv) in the directory the targeted module is located. To facilitate running in a toolchain it runs silently if no errors occur, but uses MESSAGEBOX to report any errors (in addition to the log).

Code:
' Compiles under PBWIN or PBCC without changes
#COMPILE EXE "AVIRscPtch.exe"
#DIM ALL

'----------------------------------------------------------------------------(')

%UNICODE = 1

'----------------------------------------------------------------------------(')

$DDQ = $DQ + $DQ

'----------------------------------------------------------------------------(')

#INCLUDE "Win32API.inc" ' Roca Headers III_107

'----------------------------------------------------------------------------(')

GLOBAL v_ghMod AS DWORD
GLOBAL v_ghUpd AS DWORD
GLOBAL v_gsExe AS STRING

'----------------------------------------------------------------------------(')

FUNCTION fn_sCSV(BYVAL v_sText AS WSTRING) AS STRING
  IF INSTR(v_sText, ANY CHR$(34, 44)) THEN
      REPLACE $DQ WITH $DDQ IN v_sText
      FUNCTION = $DQ + v_sText + $DQ
  ELSE
      FUNCTION = v_sText
  END IF
END FUNCTION

'----------------------------------------------------------------------------(')

FUNCTION fn_sTimestamp() AS WSTRING
  LOCAL v_uST AS SYSTEMTIME
  GETSYSTEMTIME v_uST
  FUNCTION = _
      FORMAT$(v_uST.wYear, "0000") + "-" + _
      FORMAT$(v_uST.wMonth, "00") + "-" + _
      FORMAT$(v_uST.wDay, "00") + " " + _
      FORMAT$(v_uST.wHour, "00") + ":" + _
      FORMAT$(v_uST.wMinute, "00") + ":" + _
      FORMAT$(v_uST.wSecond, "00") + "Z"
END FUNCTION

'----------------------------------------------------------------------------(')

FUNCTION fn_sGetErrorText(BYVAL v_lError AS LONG) AS WSTRING
  LOCAL v_lReturn AS LONG
  LOCAL v_szBuffer AS WSTRINGZ * 256
  IF v_lError = %ERROR_SUCCESS THEN EXIT FUNCTION
  v_lReturn = FORMATMESSAGE(%FORMAT_MESSAGE_FROM_SYSTEM, BYVAL 0&, v_lError, BYVAL 0&, v_szBuffer, SIZEOF(v_szBuffer), BYVAL 0&)
  IF v_lReturn > 0 THEN FUNCTION = RTRIM$(LEFT$(v_szBuffer, v_lReturn), ANY $CRLF)
END FUNCTION

'----------------------------------------------------------------------------(')

SUB sub_Log(BYVAL v_bError AS LONG, v_sText AS WSTRING)
  LOCAL v_hFile AS LONG
  LOCAL v_lError AS LONG
  LOCAL v_sLogPath AS WSTRING
  LOCAL v_sTimestamp AS WSTRING
  v_sTimestamp = fn_sTimestamp
  v_sLogPath = PATHNAME$(PATH, v_gsExe) + "\AVIRscPtch_Log.csv"
  TRY
      OPEN v_sLogPath FOR APPEND AS # v_hFile
      PRINT # v_hFile, v_sTimestamp + "," + fn_sCSV(v_sText)
      CLOSE # v_hFile
      IF v_bError THEN MESSAGEBOX %HWND_DESKTOP, "Error during patching:" + $LF + v_sText, EXE.NAMEX$, %MB_ICONEXCLAMATION
  CATCH
      v_lError = ERR
      MESSAGEBOX %HWND_DESKTOP, _
        "Could not record message to the log file." + $LF + $LF + _
        "Timestamp:" + $LF + v_sTimestamp + $LF + $LF + _
        "Log Path:" + $LF + v_sLogPath + $LF + $LF + _
        "Error:" + ERROR$(v_lError) + $LF + $LF + _
        "Msg:" + $LF + v_sText, _
        EXE.NAMEX$, %MB_ICONEXCLAMATION
      IF v_hFile THEN CLOSE # v_hFile
  END TRY
END SUB

'----------------------------------------------------------------------------(')

FUNCTION fn_bIsAVI(BYVAL v_pData AS DWORD, BYVAL v_cbData AS DWORD) AS LONG
  IF v_pData = 0 THEN sub_Log 0, "pdata = 0" : EXIT FUNCTION
  IF v_cbData < 12 THEN sub_Log 0, "cbData < 12" : EXIT FUNCTION
  IF PEEK$(v_pData, 4) <> "RIFF" THEN sub_Log 0, "Not a RIFF" : EXIT FUNCTION
  IF PEEK$(v_pData + 8, 4) <> "AVI " THEN sub_Log 0, "Not an AVI" : EXIT FUNCTION
  FUNCTION = %TRUE
END FUNCTION

'----------------------------------------------------------------------------(')

FUNCTION fn_lEnumLangProc( _
      BYVAL v_hModule AS DWORD, _
      BYVAL v_lpType AS DWORD, _
      BYVAL v_lpName AS DWORD, _
      BYVAL v_wLang AS WORD, _
      BYVAL v_lParam AS LONG) AS LONG
  LOCAL v_bOK AS LONG
  LOCAL v_cbData AS DWORD
  LOCAL v_hData AS DWORD
  LOCAL v_hRes AS DWORD
  LOCAL v_lError AS LONG
  LOCAL v_pData AS DWORD
  LOCAL v_wzTypeAVI AS WSTRINGZ * 4
  ' FindResourceEx
  v_hRes = FINDRESOURCEEX(v_hModule, BYVAL %RT_RCDATA, BYVAL v_lpName, v_wLang)
  v_lError = GETLASTERROR
  IF v_hRes = 0 THEN
      sub_Log 0, "Error calling FindResourceEx: " + fn_sGetErrorText(v_lError)
      FUNCTION = %TRUE
      EXIT FUNCTION
  END IF
  v_cbData = SIZEOFRESOURCE(v_hModule, v_hRes)
  v_hData = LOADRESOURCE(v_hModule, v_hRes)
  v_pData = LOCKRESOURCE(v_hData)
  IF fn_bIsAVI(v_pData, v_cbData) THEN
      v_wzTypeAVI = "AVI"
      ' UpdateResource - Copy resource to new resource type
      v_bOK = UPDATERESOURCE(v_ghUpd, BYVAL VARPTR(v_wzTypeAVI), BYVAL v_lpName, v_wLang, BYVAL v_pData, v_cbData)
      v_lError = GETLASTERROR
      IF v_bOK THEN
        ' UpdateResource - Remove resource from old resource type
        v_bOK = UPDATERESOURCE(v_ghUpd, BYVAL %RT_RCDATA, BYVAL v_lpName, v_wLang, BYVAL %NULL, 0)
        v_lError = GETLASTERROR
        IF v_bOK = %FALSE THEN sub_Log 1, "Error calling UpdateResource (delete old): " + fn_sGetErrorText(v_lError)
      ELSE
        sub_Log 1, "Error calling UpdateResource (copy to new): " + fn_sGetErrorText(v_lError)
      END IF
  END IF
  FUNCTION = %TRUE
END FUNCTION

'----------------------------------------------------------------------------(')

FUNCTION fn_lEnumNameProc( _
      BYVAL v_hModule AS DWORD, _
      BYVAL v_lpType AS DWORD, _
      BYVAL v_lpName AS DWORD, _
      BYVAL v_lParam AS LONG) AS LONG
  ENUMRESOURCELANGUAGES(v_hModule, BYVAL %RT_RCDATA, BYVAL v_lpName, CODEPTR(fn_lEnumLangProc), 0)
  FUNCTION = %TRUE
END FUNCTION

'----------------------------------------------------------------------------(')

FUNCTION PBMAIN() AS LONG
  LOCAL v_bOK AS LONG
  LOCAL v_lError AS LONG
  LOCAL v_sCmd AS STRING
  LOCAL v_wzExe AS WSTRINGZ * %MAX_PATH
  v_sCmd = TRIM$(COMMAND$, ANY CHR$(32, 34))
  IF LEN(v_sCmd) THEN
      v_gsExe = v_sCmd
  ELSE
      sub_Log 1, "Please supply a module (DLL/EXE) path to patch"
      EXIT FUNCTION
  END IF
  IF ISFILE(v_gsExe) = %FALSE THEN
      sub_Log 1, "Module (DLL/EXE) path does not exist"
      EXIT FUNCTION
  END IF
  v_wzExe = v_gsExe
  sub_Log 0, "Attemping to patch module: " + $DQ + v_gsExe + $DQ
  v_ghMod = LOADLIBRARYEX(BYVAL VARPTR(v_wzExe), BYVAL %NULL, %LOAD_LIBRARY_AS_DATAFILE)
  v_lError = GETLASTERROR
  IF v_ghMod = 0 THEN
      sub_Log 1, "Error loading module: " + fn_sGetErrorText(v_lError)
      EXIT FUNCTION
  END IF
  ' BeginUpdateResource
  v_ghUpd = BEGINUPDATERESOURCE(BYVAL VARPTR(v_wzExe), %FALSE)
  v_lError = GETLASTERROR
  IF v_ghUpd = 0 THEN
      sub_Log 1, "Error calling BeginUpdateResource: " + fn_sGetErrorText(v_lError)
      FREELIBRARY v_ghMod
      EXIT FUNCTION
  END IF
  ' EnumResourceNames
  v_bOK = ENUMRESOURCENAMES(v_ghMod, BYVAL %RT_RCDATA, CODEPTR(fn_lEnumNameProc), 0)
  v_lError = GETLASTERROR
  IF v_bOK = %FALSE THEN
      sub_Log 1, "Error calling EnumResourceNames: " + fn_sGetErrorText(v_lError)
      FREELIBRARY v_ghMod
      EXIT FUNCTION
  END IF
  ' Release the module
  FREELIBRARY v_ghMod
  ' EndUpdateResource
  v_bOK = ENDUPDATERESOURCE(v_ghUpd, %FALSE)
  v_lError = GETLASTERROR
  IF v_bOK = %FALSE THEN
      sub_Log 1, "Error calling EndUpdateResource: " + fn_sGetErrorText(v_lError)
      EXIT FUNCTION
  END IF
  sub_Log 0, "Module patched"
END FUNCTION