'+--------------------------------------------------------------------------+
'|                              BBplugin.inc                                |
'|                                                                          |
'|                   BassBox Plugin interface definition.                   |
'|                                                                          |
'+--------------------------------------------------------------------------+
'|                                                                          |
'|                         Author Patrice TERRIER                           |
'|                            copyright(c) 2007                             |
'|                           www.zapsolution.com                            |
'|                        pterrier@zapsolution.com                          |
'|                                                                          |
'+--------------------------------------------------------------------------+
'|                  Project started on : 10-23-2007 (MM-DD-YYYY)            |
'|                        Last revised : 01-24-2010 (MM-DD-YYYY)            |
'+--------------------------------------------------------------------------+

#IF NOT %DEF(%BBP_NO_IOFILE)
    %BBP_NO_IOFILE = 0
#ENDIF
#IF NOT %DEF(%BBP_NO_REGISTRY)
    %BBP_NO_REGISTRY = 0
#ENDIF
#IF NOT %DEF(%BBP_NO_GLFONT)
    %BBP_NO_GLFONT = 0
#ENDIF

%BBP_RENDER   = 1               '// Render the scene.
%BBP_CREATE   = 2               '// Retrieve Title, Name, Version, Render mode.
%BBP_INIT     = 3               '// Init the OpenGL.
%BBP_SIZE     = 4               '// The size of the control has changed.
%BBP_KEYBOARD = 5               '// All keyborad message.
%BBP_MOUSE    = 6               '// All mouse messages.
%BBP_DESTROY  = 7               '// Free Up resources.

%BBP_GDIPLUS  = 0                '// GDImage GDIPLUS compatible mode.
%BBP_OPENGL   = 1                '// OpenGL mode.
%BBP_DIRECTX  = 2                '// DirectX mode (for future extension).

%BBP_SUCCESS  = 0
%BBP_ERROR    = -1

$BBP_PLUGIN_FOLDER = "BBplugin\"

TYPE DEVMODE
    dmDeviceName AS ASCIIZ * 32
    dmSpecVersion AS WORD
    dmDriverVersion AS WORD
    dmSize AS WORD
    dmDriverExtra AS WORD
    dmFields AS DWORD
    dmOrientation AS INTEGER
    dmPaperSize AS INTEGER
    dmPaperLength AS INTEGER
    dmPaperWidth AS INTEGER
    dmScale AS INTEGER
    dmCopies AS INTEGER
    dmDefaultSource AS INTEGER
    dmPrintQuality AS INTEGER
    dmColor AS INTEGER
    dmDuplex AS INTEGER
    dmYResolution AS INTEGER
    dmTTOption AS INTEGER
    dmCollate AS INTEGER
    dmFormName AS ASCIIZ * 32
    dmLogPixels AS WORD
    dmBitsPerPel AS DWORD
    dmPelsWidth AS DWORD
    dmPelsHeight AS DWORD
    dmDisplayFlags AS DWORD
    dmDisplayFrequency AS DWORD
    dmICMMethod AS DWORD
    dmICMIntent AS DWORD
    dmMediaType AS DWORD
    dmDitherType AS DWORD
    dmICCManufacturer AS DWORD
    dmICCModel AS DWORD
    dmPanningWidth AS DWORD
    dmPanningHeight AS DWORD
END TYPE

TYPE BBPLUGIN                   '// 256 bytes
    Msg          AS LONG        '// The plugin's message (see above constant list).
    ParentWindow AS LONG        '// The parent window handle.
    DC           AS LONG        '// The parent window DC (while in play mode).
    RC           AS LONG        '// The parent OpenGL RC (while in play mode).
    Lpeak        AS WORD        '// The left audio channel peak value (while in play mode).
    Rpeak        AS WORD        '// The right audio channel peak value (while in play mode).
    Title        AS ASCIIZ * 32 '// Plugin's name or title.
    Author       AS ASCIIZ * 64 '// Plugin's author name.
    Version      AS DWORD       '// LOWRD major, HIWRD minor.
    RenderTo     AS LONG        '// %BBP_GDIPLUS, %BBP_OPENGL, %BBP_DIRECTX.
    BackARGB     AS LONG        '// Default ARGB color background.
    FFTdata      AS DWORD       '// DWORD pointer to the FFT() AS SINGLE array.
    FFTsize      AS WORD        '// Size of the FFT array.

    WinMsg       AS LONG        '// True Windows message.
    wParam       AS LONG        '// wParam 
    lParam       AS LONG        '// lParam
    
    WIMdata      AS DWORD       '// DWORD pointer to the wave MM_WIM_DATA.
    MediaLength  AS DWORD       '// Media length.
    MediaPos     AS DWORD       '// Media pos.

    Reserved     AS ASCIIZ * 98 '// Reserved for future extension.
END TYPE

TYPE BBPTEXTURE
    ID       AS LONG
    FullName AS ASCIIZ * 260
    Texture  AS LONG
    Square   AS LONG            '// boolean TRUE or FALSE value 
END TYPE

' Custom types
TYPE COLORBYTES
    B AS BYTE
    G AS BYTE
    R AS BYTE
    A AS BYTE
END TYPE

TYPE COLORLONG
    longval AS LONG
END TYPE

$Anti                = "\"
$Dot                 = "."
%MAX_PATH            = 260 ' max. length of full pathname
%UnitPixel           = 2   ' Each unit is one device pixel.
%DLL_PROCESS_DETACH  = 0
%DLL_PROCESS_ATTACH  = 1

TYPE FILETIME
    dwLowDateTime AS DWORD
    dwHighDateTime AS DWORD
END TYPE

TYPE WIN32_FIND_DATA
    dwFileAttributes AS DWORD
    ftCreationTime AS FILETIME
    ftLastAccessTime AS FILETIME
    ftLastWriteTime AS FILETIME
    nFileSizeHigh AS DWORD
    nFileSizeLow AS DWORD
    dwReserved0 AS DWORD
    dwReserved1 AS DWORD
    cFileName AS ASCIIZ * %MAX_PATH
    cAlternateFileName AS ASCIIZ * 14
END TYPE

TYPE BITMAP
    bmType AS LONG
    bmWidth AS LONG
    bmHeight AS LONG
    bmWidthBytes AS LONG
    bmPlanes AS WORD
    bmBitsPixel AS WORD
    bmBits AS BYTE PTR
END TYPE

TYPE BITMAPINFOHEADER
    biSize AS DWORD
    biWidth AS LONG
    biHeight AS LONG
    biPlanes AS WORD
    biBitCount AS WORD
    biCompression AS DWORD
    biSizeImage AS DWORD
    biXPelsPerMeter AS LONG
    biYPelsPerMeter AS LONG
    biClrUsed AS DWORD
    biClrImportant AS DWORD
END TYPE

TYPE RGBQUAD
    rgbBlue AS BYTE
    rgbGreen AS BYTE
    rgbRed AS BYTE
    rgbReserved AS BYTE
END TYPE

TYPE MYBITMAPINFO
    bmiHeader AS BITMAPINFOHEADER
    bmiColors(256) AS RGBQUAD ' Patrice
END TYPE

DECLARE FUNCTION CreateCompatibleDC LIB "GDI32.DLL" ALIAS "CreateCompatibleDC" (BYVAL hdc AS DWORD) AS DWORD
DECLARE FUNCTION CreateIC LIB "GDI32.DLL" ALIAS "CreateICA" (lpDriverName AS ASCIIZ, lpDeviceName AS ASCIIZ, lpOutput AS ASCIIZ, lpInitData AS DEVMODE) AS DWORD
DECLARE FUNCTION DeleteDC LIB "GDI32.DLL" ALIAS "DeleteDC" (BYVAL hdc AS DWORD) AS LONG
DECLARE FUNCTION DeleteObject LIB "GDI32.DLL" ALIAS "DeleteObject" (BYVAL hObject AS DWORD) AS LONG
DECLARE FUNCTION FindClose LIB "KERNEL32.DLL" ALIAS "FindClose" (BYVAL hFindFile AS DWORD) AS LONG
DECLARE FUNCTION FindFirstFile LIB "KERNEL32.DLL" ALIAS "FindFirstFileA" (lpFileName AS ASCIIZ, lpFindFileData AS WIN32_FIND_DATA) AS DWORD
DECLARE FUNCTION GetDIBits LIB "GDI32.DLL" ALIAS "GetDIBits" (BYVAL hdc AS DWORD, BYVAL hBitmap AS DWORD, BYVAL nStartScan AS DWORD, BYVAL nNumScans AS DWORD, lpBits AS ANY, lpBI AS MYBITMAPINFO, BYVAL wUsage AS DWORD) AS LONG
DECLARE FUNCTION GetObject LIB "GDI32.DLL" ALIAS "GetObjectA" (BYVAL hObject AS DWORD, BYVAL nCount AS LONG, lpObject AS ANY) AS LONG
DECLARE FUNCTION SelectObject LIB "GDI32.DLL" ALIAS "SelectObject" (BYVAL hdc AS DWORD, BYVAL hObject AS DWORD) AS DWORD
DECLARE FUNCTION MyCreateDIBSection LIB "GDI32.DLL" ALIAS "CreateDIBSection" (BYVAL hdc AS DWORD, pbmi AS MYBITMAPINFO, BYVAL dwUsage AS DWORD, BYVAL ppvBits AS DWORD, BYVAL hSection AS DWORD, BYVAL dwOffset AS DWORD) AS DWORD
DECLARE FUNCTION GetModuleFileName LIB "KERNEL32.DLL" ALIAS "GetModuleFileNameA" (BYVAL hModule AS DWORD, lpFileName AS ASCIIZ, BYVAL nSize AS DWORD) AS DWORD
DECLARE FUNCTION GetCurrentDirectory LIB "KERNEL32.DLL" ALIAS "GetCurrentDirectoryA" (BYVAL nBufferLength AS LONG, lpBuffer AS ASCIIZ) AS LONG

TYPE GdiplusStartupInput
   GdiplusVersion AS LONG             ' Must be 1
   DebugEventCallback AS LONG         ' Ignored on free builds
   SuppressBackgroundThread AS LONG   ' FALSE unless you're prepared to call
                                      ' the hook/unhook functions properly
   SuppressExternalCodecs AS LONG     ' FALSE unless you want GDI+ only to use
                                      ' its internal image codecs.
END TYPE

DECLARE FUNCTION GdipLoadImageFromFile LIB "gdiplus.dll" ALIAS "GdipLoadImageFromFile" (zFilName AS ASCIIZ, nImage&) AS LONG
DECLARE FUNCTION GdipGetImageWidth LIB "gdiplus.dll" ALIAS "GdipGetImageWidth" (BYVAL nImage&, nWidth&) AS LONG
DECLARE FUNCTION GdipGetImageHeight LIB "gdiplus.dll" ALIAS "GdipGetImageHeight" (BYVAL nImage&, Height&) AS LONG
DECLARE FUNCTION GdipCreateFromHDC LIB "gdiplus.dll" ALIAS "GdipCreateFromHDC" (BYVAL hdc&, graphics&) AS LONG
DECLARE FUNCTION GdipSetInterpolationMode LIB "gdiplus.dll" ALIAS "GdipSetInterpolationMode" (BYVAL graphics&, BYVAL interpolation&) AS LONG
DECLARE FUNCTION GdipBitmapGetPixel LIB "gdiplus.dll" ALIAS "GdipBitmapGetPixel" (BYVAL nBitmap&, BYVAL x&, BYVAL y&, Colr&) AS LONG
DECLARE FUNCTION GdipDeleteGraphics LIB "gdiplus.dll" ALIAS "GdipDeleteGraphics" (BYVAL graphics&) AS LONG
DECLARE FUNCTION GdipDisposeImage LIB "gdiplus.dll" ALIAS "GdipDisposeImage" (BYVAL nImage&) AS LONG
DECLARE FUNCTION GdiplusStartup LIB "gdiplus.dll" ALIAS "GdiplusStartup" (token&, inputbuf AS GdiplusStartupInput, OPTIONAL BYVAL outputbuf&) AS LONG
DECLARE FUNCTION GdiplusShutdown LIB "gdiplus.dll" ALIAS "GdiplusShutdown" (BYVAL token&) AS LONG
DECLARE FUNCTION GdipDrawImageRectRectI LIB "gdiplus.dll" ALIAS "GdipDrawImageRectRectI" (BYVAL graphics&, BYVAL nImage&, BYVAL dstx&, BYVAL dsty&, BYVAL dstwidth&, BYVAL dstheight&, BYVAL srcx&, BYVAL srcy&, BYVAL srcwidth&, BYVAL srcheight&, _
                                                                                          BYVAL srcUnit&, OPTIONAL BYVAL imageAttributes&, OPTIONAL BYVAL pCALLBACK&, OPTIONAL BYVAL callbackData&) AS LONG

FUNCTION BBP_GdipStart() AS LONG
  ' Load the GDI+ Dll
    DIM GpInput AS GdiplusStartupInput
    GpInput.GdiplusVersion = 1
    IF GdiplusStartup(hGDIplus&, GpInput) = 0 THEN FUNCTION = hGDIplus&
END FUNCTION

' GDIPLUS unload (unload the GDIPLUS.DLL)
SUB BBP_GdipEnd(BYVAL hGDIplus&)
  ' Unload the GDI+ Dll
    IF hGDIplus& THEN CALL GdiplusShutdown(hGDIplus&): hGDIplus& = 0
END SUB

' Returns current Disk and Path
SUB BBP_DskPath (Dsk$, Path$)
'   Dsk$ and Path$ are ready to be concatened with a filename
'   Dsk$ and Paths correspond to the current directory
    LOCAL zTmp AS ASCIIZ * %MAX_PATH
    IF GetCurrentDirectory(BYVAL SIZEOF(zTmp), zTmp) THEN
       Dsk$ = LEFT$(zTmp, 2)
       Path$ = RTRIM$(MID$(zTmp, 3), ANY CHR$(0,92)) + $Anti
    END IF
END SUB

' Splits a full path name, and return the current disk and directory
SUB BBP_SplitN (BYVAL sFullName$, sPathName$, sFilName$)
' If no path is specified then the current directory is being used.
    sPathName$ = "": sFilName$ = ""
    Length& = LEN(sFullName$)
    IF Length& THEN
       CALL BBP_DskPath(Dsk$, ThisPath$): sFilName$ = sFullName$
       So& = INSTR(-1, sFullName$, $Anti)
       IF So& THEN
          sPathName$ = LEFT$(sFullName$, So&)
       ELSE
          So& = INSTR(sFullName$, ":")
       END IF
       IF sPathName$ = Dsk$ OR LEN(sPathName$) = 0 THEN
          sPathName$ = Dsk$ + ThisPath$
       END IF
       IF So& THEN sFilName$ = MID$(sFullName$, So& + 1, Length& - So&)
    END IF
END SUB

FUNCTION BBP_ExeName () AS STRING
    LOCAL zTmp AS ASCIIZ * 260
    LenExeName& = GetModuleFileName(BYVAL 0, zTmp, SIZEOF(zTmp))
    IF LenExeName& THEN
       LenExeName& = MIN&(LenExeName&, SIZEOF(zTmp))
       FUNCTION = LEFT$(zTmp, LenExeName&)
    END IF
END FUNCTION

FUNCTION BBP_Exepath() AS STRING
    LOCAL sExePath AS STRING
    CALL BBP_SplitN(BBP_ExeName(), sExePath, "")
    FUNCTION = sExePath
END FUNCTION

'// Split a true 32-bit ARGB color into its four components.
SUB BBP_SplitColorARGB (BYVAL ARGB AS LONG, A AS BYTE, R AS BYTE, G AS BYTE, B AS BYTE)
    DIM bytestruct AS COLORBYTES
    LSET bytestruct = MKL$(ARGB)
    A = bytestruct.A
    R = bytestruct.R
    G = bytestruct.G
    B = bytestruct.B
END SUB

FUNCTION BBP_GetRValue (BYVAL colrRGB???) AS BYTE
    FUNCTION = (colrRGB??? AND &H000000FF???)
END FUNCTION

'// Get Green byte from a RGB color.
FUNCTION BBP_GetGValue (BYVAL colrRGB???) AS BYTE
    SHIFT RIGHT colrRGB???, 8
    FUNCTION = (colrRGB??? AND &H000000FF???)
END FUNCTION

'// Get Blue byte from a RGB color.
FUNCTION BBP_GetBValue (BYVAL colrRGB???) AS BYTE
    SHIFT RIGHT colrRGB???, 16
    FUNCTION = (colrRGB??? AND &H000000FF???)
END FUNCTION

FUNCTION BBP_ColorARGB (BYVAL AlphaChannel AS BYTE, BYVAL ColrRGB AS DWORD) AS LONG
    DIM bytestruct AS COLORBYTES
    DIM result AS COLORLONG

    bytestruct.A = AlphaChannel
    bytestruct.R = BBP_GetRValue(ColrRGB)
    bytestruct.G = BBP_GetGValue(ColrRGB)
    bytestruct.B = BBP_GetBValue(ColrRGB)

    LSET result = bytestruct
    FUNCTION = result.longval
END FUNCTION

FUNCTION BBP_Exist (zFileSpec AS ASCIIZ) AS LONG
    LOCAL fd AS WIN32_FIND_DATA, hFind AS LONG
    IF LEN(zFileSpec) THEN
       hFind = FindFirstFile(zFileSpec, fd)
       IF hFind <> -1 THEN
          CALL FindClose(hFind&)
          FUNCTION = -1
       END IF
    END IF
END FUNCTION

FUNCTION BBP_CreateGLTextureFromFile (zFullPath AS ASCIIZ, Xin AS LONG, Yin AS LONG, PixelArray() AS BYTE, BYVAL SquareTexture AS LONG) AS LONG
    LOCAL bi AS MYBITMAPINFO, bm AS BITMAP
    LOCAL pBits AS BYTE PTR, scale AS SINGLE
    LOCAL K, xP, yP, xS, yS, So, hDIB, hIC, hDC, Img, imgW, imgH, ImgAttr AS LONG
    LOCAL sImgName, sPathName AS STRING
    LOCAL ARGBcolor AS LONG, A, R, G, B AS BYTE
    LOCAL WasSquareTexture AS LONG '// 02-03-2009 to strecth OpenGL texture to the size of the memory bitmap target

    Xin = 0: Yin = 0: imgW = 0: imgH = 0
    WasSquareTexture = SquareTexture
    
  ' Make sure that a full path is being used.
    sImgName = zFullPath: REPLACE "/" WITH $Anti IN sImgName
    CALL BBP_SplitN(BBP_ExeName, sPathName, "")
    IF INSTR(sImgName, sPathName) = 0 THEN sImgName = sPathName + sImgName 

    So = INSTR(-1, sImgName, $Dot)
    IF So AND NOT BBP_Exist((sImgName)) THEN So = 0
    IF So THEN
       ImgType$ = UCASE$(MID$(sImgName, So)) + $Dot
       ImageType& = INSTR(".BMP.DIB.GIF.ICO.JPG.JPEG.PNG.TIF.TIFF.", ImgType$)
       IF ImageType& THEN
        ' Load the image
          IF GdipLoadImageFromFile((UCODE$(sImgName)), Img) = 0 THEN
           ' Get the image height and width
             CALL GdipGetImageWidth(Img, imgW)
             CALL GdipGetImageHeight(Img, imgH)
   
             DIM P(1 TO 12) AS LONG
             ARRAY ASSIGN P() = 2,4,8,16,32,64,128,256,512,1024,2048,4096
             FOR K& = UBOUND(P) TO LBOUND(P) STEP -1
                 IF Xin = 0 AND ImgW > P(K&) - 1 THEN Xin = P(K&)
                 IF Yin = 0 AND ImgH > P(K&) - 1 THEN Yin = P(K&)
             NEXT
         
             IF SquareTexture AND Xin <> Yin THEN
                Xin = MAX&(Xin, Yin): Yin = Xin
             ELSE
                SquareTexture = 0
             END IF
      
           ' Perform scale adjustment (zIconise(imgW, imgH, Xin, Yin, xP, yP, xS, yS))
           ' ------------------------------------------------------------------------
             IF imgW THEN scale = Xin / imgW
             IF scale > 1 THEN scale = 1
              xS = imgW * scale: yS = imgH * scale
             ' In case Height > Yin compute new scale factor
             IF yS > Yin THEN
                IF imgH THEN scale = Yin / imgH
                xS = imgW * scale: yS = imgH * scale
             END IF
             xP = (Xin - xS) \ 2: yP = (Yin - yS) \ 2

             IF WasSquareTexture = 2 THEN xP = 0: yP = 0: xS = Xin: yS = Yin ' Strecth the texture to the size of the memory bitmap target

            ' ------------------------------------------------------------------------
      
             hIC = CreateIC("DISPLAY", BYVAL 0, BYVAL 0, BYVAL 0)
             hDC = CreateCompatibleDC(hIC)
      
             bi.bmiHeader.biSize = SIZEOF(bi.bmiHeader)
             bi.bmiHeader.biWidth = Xin
             bi.bmiHeader.biHeight = Yin
             bi.bmiHeader.biPlanes = 1
             bi.bmiHeader.biBitCount = 32
             bi.bmiHeader.biCompression = 0 ' %BI_RGB
             hDIB = MyCreateDIBSection(hDC, bi, 0, 0, 0, 0)
      
             CALL SelectObject(hDC, hDIB)
           ' Draw image
             IF GdipCreateFromHDC(hDC, graphics&) = 0 THEN
         
              ' Set up stretching QualityMode
                CALL GdipSetInterpolationMode(graphics&, 2)

              ' Fill the background with color of pixel(0,0)
                IF SquareTexture THEN
                   CALL GdipBitmapGetPixel(img, 0, 0, ARGBcolor)
                   CALL BBP_SplitColorARGB(ARGBcolor, A, R, G, B)
                   CALL GetObject(hDIB, SIZEOF(bm), bm)
                   pBits = bm.bmBits
                   FOR K = (Xin * Yin) TO 1 STEP - 1
                       @pBits[3] = A
                       @pBits[2] = R
                       @pBits[1] = G
                       @pBits[0] = B
                       pBits = pBits + 4
                   NEXT
                END IF
                
              ' Draw image
                IF GdipDrawImageRectRectI(graphics&, Img, _
                                    xP, yP, xS, yS, _
                                    0, 0, ImgW, ImgH, %UnitPixel, _
                                    ImgAttr) = 0 THEN ' This is an optional parameter
                   bi.bmiHeader.biSize        = SIZEOF(bi.bmiHeader)
                   bi.bmiHeader.biWidth       = Xin
                   bi.bmiHeader.biHeight      = Yin
                   bi.bmiHeader.biPlanes      = 1
                   bi.bmiHeader.biBitCount    = 32
                   bi.bmiHeader.biCompression = 0 ' %BI_RGB
                   REDIM PixelArray(0 TO Xin * Yin * 4 - 1) AS BYTE
                   IF GetDIBits(hDC, hDIB, 0, Yin, PixelArray(0), BYVAL VARPTR(bi), 0) THEN
                   '  Perform Red and Green permutation
                      pBits = VARPTR(PixelArray(0))
                      FOR K = (Xin * Yin) TO 1 STEP - 1
                          R = @pBits[2]: @pBits[2] = @pBits[0]: @pBits[0] = R: pBits = pBits + 4
                      NEXT
                      FUNCTION = -1
                   END IF
                END IF
              ' Cleanup
                'IF ImgAttr THEN CALL GdipDisposeImageAttributes(ImgAttr)
                CALL GdipDeleteGraphics(graphics&)
             END IF
             CALL DeleteObject(hDIB)
             IF hDC THEN CALL DeleteDC(hDC)
             IF hIC THEN CALL DeleteDC(hIC)
          END IF
          CALL GdipDisposeImage(Img): Img = 0
       END IF   
    END IF

END FUNCTION

'''''''''''''''''''''''''''''''''''''''
%GL_LIGHTING           = &H0B50
%GL_TEXTURE_2D         = &H0DE1
%GL_MODELVIEW          = &H1700
%GL_PROJECTION         = &H1701
%GL_LIST_BIT           = &H00020000
%GL_UNSIGNED_BYTE      = &H1401
%GL_LINEAR             = &H2601
%GL_RGBA               = &H1908
%GL_INVALID_ENUM       = &H0500
%GL_TEXTURE_MAG_FILTER = &H2800
%GL_TEXTURE_MIN_FILTER = &H2801
%GL_QUADS              = &h0007&

#IF %BBP_NO_GLFONT = 0
    TYPE ZGLFONT
        fontName   AS ASCIIZ * 64 ' The True Type font name.
        fontBase   AS LONG        ' The OpenGL FontBase (do not set this).
        charStart  AS LONG        ' OpenGL starting ASCII character (usually 32).
        charNum    AS LONG        ' OpenGL number of glyphe characters to create(usually 96).
        fontHeight AS LONG        ' The font size (72 DPI).
        fontWeight AS LONG        ' The font weigt.
        fontHandle AS LONG        ' The Win32 font handle
    END TYPE
    
    TYPE SIZEL
        cx AS LONG
        cy AS LONG
    END TYPE
    
    TYPE RECT
        nLeft AS LONG
        nTop AS LONG
        nRight AS LONG
        nBottom AS LONG
    END TYPE
    
    %FW_BOLD             = 700
    %OUT_TT_PRECIS       = 4
    %ANTIALIASED_QUALITY = 4
    %LF_FACESIZE         = 32

    TYPE LOGFONT
       lfHeight AS LONG
       lfWidth AS LONG
       lfEscapement AS LONG
       lfOrientation AS LONG
       lfWeight AS LONG
       lfItalic AS BYTE
       lfUnderline AS BYTE
       lfStrikeOut AS BYTE
       lfCharSet AS BYTE
       lfOutPrecision AS BYTE
       lfClipPrecision AS BYTE
       lfQuality AS BYTE
       lfPitchAndFamily AS BYTE
       lfFaceName AS ASCIIZ * %LF_FACESIZE
    END TYPE
    
    DECLARE FUNCTION GetStockObject LIB "GDI32.DLL" ALIAS "GetStockObject" (BYVAL nIndex AS LONG) AS DWORD
    DECLARE FUNCTION GetDeviceCaps LIB "GDI32.DLL" ALIAS "GetDeviceCaps" (BYVAL hdc AS DWORD, BYVAL nIndex AS LONG) AS LONG
    DECLARE FUNCTION CreateFontIndirect LIB "GDI32.DLL" ALIAS "CreateFontIndirectA" (lpLogFont AS LOGFONT) AS DWORD
'    DECLARE FUNCTION CreateFont LIB "GDI32.DLL" ALIAS "CreateFontA" (BYVAL nHeight AS LONG, BYVAL nWidth AS LONG, BYVAL nEscapement AS LONG, BYVAL nOrientation AS LONG, BYVAL fnWeight AS LONG, BYVAL fdwItalic AS DWORD, _
'            BYVAL fdwUnderline AS DWORD, BYVAL fdwStrikeOut AS DWORD, BYVAL fdwCharSet AS DWORD, BYVAL fdwOutputPrecision AS DWORD, BYVAL fdwClipPrecision AS DWORD, BYVAL fdwQuality AS DWORD, _
'            BYVAL fdwPitchAndFamily AS DWORD, lpszFace AS ASCIIZ) AS DWORD
    DECLARE FUNCTION glGenLists LIB "opengl32.dll" ALIAS "glGenLists" (BYVAL range&) AS DWORD
    DECLARE FUNCTION wglUseFontBitmaps LIB "OPENGL32.DLL" ALIAS "wglUseFontBitmapsA" (BYVAL hdc AS DWORD, BYVAL dFirst AS DWORD, BYVAL dCount AS DWORD, BYVAL dListBase AS DWORD) AS LONG
    DECLARE FUNCTION GetTextExtentPoint32 LIB "GDI32.DLL" ALIAS "GetTextExtentPoint32A" (BYVAL hdc AS DWORD, lpsz AS ASCIIZ, BYVAL cbString AS LONG, lpSize AS SIZEL) AS LONG
    DECLARE SUB glDeleteLists LIB "opengl32.dll" ALIAS "glDeleteLists" (BYVAL list AS DWORD, BYVAL range&)
    DECLARE FUNCTION GetClientRect LIB "USER32.DLL" ALIAS "GetClientRect" (BYVAL hwnd AS DWORD, lpRect AS RECT) AS LONG
    DECLARE FUNCTION glIsEnabled LIB "opengl32.dll" ALIAS "glIsEnabled" (BYVAL cap AS DWORD) AS BYTE
    DECLARE SUB glDisable LIB "opengl32.dll" ALIAS "glDisable" (BYVAL cap AS DWORD)
    DECLARE SUB glMatrixMode LIB "opengl32.dll" ALIAS "glMatrixMode" (BYVAL mode AS DWORD)
    DECLARE SUB glPushMatrix LIB "opengl32.dll" ALIAS "glPushMatrix" ()
    DECLARE SUB glLoadIdentity LIB "opengl32.dll" ALIAS "glLoadIdentity" ()
    DECLARE SUB glOrtho LIB "opengl32.dll" ALIAS "glOrtho" (BYVAL nleft AS DOUBLE, BYVAL nright AS DOUBLE, BYVAL bottom AS DOUBLE, BYVAL top AS DOUBLE, BYVAL zNear AS DOUBLE, BYVAL zFar AS DOUBLE)
    DECLARE SUB glColor3f LIB "opengl32.dll" ALIAS "glColor3f" (BYVAL red AS SINGLE, BYVAL green AS SINGLE, BYVAL blue AS SINGLE)
    DECLARE SUB glRasterPos2i LIB "opengl32.dll" ALIAS "glRasterPos2i" (BYVAL x&, BYVAL y&)
    DECLARE SUB glPushAttrib LIB "opengl32.dll" ALIAS "glPushAttrib" (BYVAL mask AS DWORD)
    DECLARE SUB glListBase LIB "opengl32.dll" ALIAS "glListBase" (BYVAL nbase AS DWORD)
    DECLARE SUB glCallLists LIB "opengl32.dll" ALIAS "glCallLists" (BYVAL n&, BYVAL ntype AS DWORD, lists AS ANY)
    DECLARE SUB glPopAttrib LIB "opengl32.dll" ALIAS "glPopAttrib" ()
    DECLARE SUB glPopMatrix LIB "opengl32.dll" ALIAS "glPopMatrix"
    DECLARE SUB glTexParameteri LIB "opengl32.dll" ALIAS "glTexParameteri" (BYVAL ntarget AS DWORD, BYVAL pname AS DWORD, BYVAL param&)
    DECLARE FUNCTION glIsTexture LIB "opengl32.dll" ALIAS "glIsTexture" (BYVAL texture AS DWORD) AS BYTE
    DECLARE FUNCTION glGetError LIB "opengl32.dll" ALIAS "glGetError" () AS DWORD
    DECLARE SUB glColor4ub LIB "OPENGL32.DLL" ALIAS "glColor4ub" (BYVAL red AS BYTE, BYVAL green AS BYTE, BYVAL blue AS BYTE, BYVAL alpha AS BYTE)
    DECLARE SUB glVertex2i LIB "OPENGL32.DLL" ALIAS "glVertex2i" (BYVAL x AS LONG, BYVAL y AS LONG)

    FUNCTION BBP_DefaultFont() AS LONG
        STATIC hDefault AS LONG
        IF hDefault = 0 THEN hDefault = GetStockObject(12)      ' %ANSI_VAR_FONT
        FUNCTION = hDefault
    END FUNCTION
    
    SUB BBP_BuildGLfont (BYVAL hDC AS LONG, UseFont AS ZGLFONT)
        LOCAL OldFont AS LONG, lpSize AS SIZEL, hFont AS LONG, LenFontName AS LONG
        DIM LF AS LogFont, sUseFont AS STRING
    
        UseFont.charStart = 32
        UseFont.charNum   = 96
        UseFont.fontBase  = glGenLists(UseFont.charNum)         ' Storage For 96 Characters
        LenFontName = LEN(UseFont.fontName)
        IF hDC = 0 THEN LenFontName = 0
        IF LenFontName THEN
           IF UseFont.fontHeight < 1 THEN UseFont.fontHeight = 10
           UseFont.fontHeight = -1 * (UseFont.fontHeight * GetDeviceCaps(hDC, 90)) \ 72
           IF UseFont.fontWeight = 0 THEN UseFont.fontWeight = %FW_BOLD

           LF.lfHeight         = UseFont.fontHeight '-12'-13'1'-16
           LF.lfWidth          = 0
           LF.lfCharSet        = 1 '%DEFAULT_CHARSET
           LF.lfOutPrecision   = 6'0'%OUT_DEFAULT_PRECIS
           LF.lfClipPrecision  = 0'%OUT_DEFAULT_PRECIS
           LF.lfQuality        = 5'CLEAR_TYPE
           LF.lfPitchAndFamily = 0' %DEFAULT_PITCH OR %FF_DONTCARE
           sUseFont = UseFont.fontName: IF LEN(sUseFont) = 0 THEN sUseFont = "Trebuchet MS"
           LF.lfFaceName       = sUseFont
           LF.lfWeight         = UseFont.fontWeight
           hFont = CreateFontIndirect(LF)

           'hFont = CreateFont(UseFont.fontHeight, _             ' Height Of Font
           '                   0, _                              ' Width Of Font
           '                   0, _                              ' Angle Of Escapement
           '                   0, _                              ' Orientation Angle
           '                   UseFont.fontWeight, _             ' Font Weight
           '                   0, _                              ' Italic
           '                   0, _                              ' Underline
           '                   0, _                              ' Strikeout
           '                   0, _                              ' %ANSI_CHARSET Character Set Identifier
           '                   %OUT_TT_PRECIS, _                 ' Output Precision
           '                   0, _                              ' %CLIP_DEFAULT_PRECIS Clipping Precision
           '                   %ANTIALIASED_QUALITY, _           ' Output Quality
           '                   0, _                              ' %FF_DONTCARE OR %DEFAULT_PITCH Family And Pitch
           '                   UseFont.fontName)                 ' Font Name
        ELSE
           hFont = BBP_DefaultFont()
        END IF
        IF hFont THEN
           OldFont = SelectObject(hDC, hFont)                   ' Selects our font
         ' Builds 96 Characters Starting At Character 32
           CALL wglUseFontBitmaps(hDC, UseFont.charStart, UseFont.charNum, UseFont.fontBase)
         ' Get font height
           CALL GetTextExtentPoint32(hDC, "W", 1, lpSize)
           UseFont.fontHeight = lpSize.cy
           CALL SelectObject(hDC, oldfont)                      ' Restore previous selected font
           IF LenFontName THEN UseFont.fontHandle = hFont       ' Save the Win32 font handle
        END IF
    END SUB
    
    SUB BBP_DeleteGLFont (UseFont AS ZGLFONT)
        IF UseFont.fontBase THEN
           CALL glDeleteLists(UseFont.fontBase, UseFont.charNum)       ' Delete All 96 Characters
           IF UseFont.fontHandle THEN DeleteObject(UseFont.fontHandle) ' The Win32 font handle
        END IF
    END SUB
    
    SUB BBP_DrawGLText (BYVAL glWnd AS LONG, UseFont AS ZGLFONT, BYVAL x AS LONG, BYVAL y AS LONG, zTxt AS ASCIIZ, BYVAL ARGB AS LONG)
        LOCAL rc AS RECT, A, R, G, B AS BYTE, WasTexture, WasLighting AS LONG
        LOCAL AlphaCoef AS SINGLE
        
        CALL GetClientRect(glWnd, rc) 
    
        WasTexture = glIsEnabled(%GL_TEXTURE_2D)
        IF WasTexture THEN CALL glDisable(%GL_TEXTURE_2D)        ' Turn off textures, didn't want our text textured
        WasLighting = glIsEnabled(%GL_LIGHTING)
        IF WasLighting THEN CALL glDisable(%GL_LIGHTING)         ' Disable Lighting
    
        CALL glMatrixMode(%GL_PROJECTION)
        CALL glPushMatrix()
        CALL glLoadIdentity()
';;     Changed 01-23-2010
';;     CALL glOrtho(0.0, rc.nRight, 0.0, rc.nBottom, -1.0, 1.0)
        CALL glOrtho(0, rc.nRight, rc.nBottom, 0, 0, 1)

        CALL glMatrixMode(%GL_MODELVIEW)
        CALL glPushMatrix()
        CALL glLoadIdentity()

';;     Added 01-23-2010
        CALL glTranslatef(0.375, 0.375, 0)
    
      ' Set color
        CALL BBP_SplitColorARGB(ARGB, A, R, G, B)
        'AlphaCoef = A / 255
        'R = R / AlphaCoef
        'G = G / AlphaCoef
        'B = B / AlphaCoef
        CALL glColor4ub(R, G, B, A)

      ' Set X,Y location
';;     Changed 01-23-2010
';;     CALL glRasterPos2i(x, rc.nBottom - (UseFont.fontHeight - 4) - y)
        CALL glRasterPos2i(x, y)
      ' Draw the text
        CALL glPushAttrib(%GL_LIST_BIT)                           ' Pushes The Display List Bits
             CALL glListBase(UseFont.fontBase - UseFont.charStart)' Sets The Base Character to 0
             CALL glCallLists(LEN(zTxt), %GL_UNSIGNED_BYTE, zTxt) ' Draws The Display List Text
        CALL glPopAttrib()                                        ' Pops The Display List Bits
    
        CALL glPopMatrix()
        CALL glMatrixMode(%GL_PROJECTION)
        CALL glPopMatrix()
        CALL glMatrixMode(%GL_MODELVIEW)
    
        IF WasTexture THEN CALL glEnable(%GL_TEXTURE_2D)          ' Enable texture mode
        IF WasLighting THEN CALL glEnable(%GL_LIGHTING)           ' Enable Lighting
    
        CALL glColor3f(1, 1, 1)                                   ' Back to default color
    
    END SUB
#ENDIF

#IF %BBP_NO_REGISTRY = 0
    TYPE SECURITY_ATTRIBUTES
        nLength AS DWORD
        lpSecurityDescriptor AS LONG
        bInheritHandle AS LONG
    END TYPE
    
    DECLARE FUNCTION RegCloseKey LIB "ADVAPI32.DLL" ALIAS "RegCloseKey" (BYVAL hKey AS DWORD) AS LONG
    DECLARE FUNCTION RegCreateKeyEx LIB "ADVAPI32.DLL" ALIAS "RegCreateKeyExA" (BYVAL hKey AS DWORD, lpSubKey AS ASCIIZ, BYVAL Reserved AS LONG, lpClass AS ASCIIZ, BYVAL dwOptions AS DWORD, BYVAL samDesired AS DWORD, _
                     lpSecurityAttributes AS SECURITY_ATTRIBUTES, phkResult AS DWORD, lpdwDisposition AS DWORD) AS LONG
    DECLARE FUNCTION RegOpenKeyEx LIB "ADVAPI32.DLL" ALIAS "RegOpenKeyExA" (BYVAL hKey AS DWORD, lpSubKey AS ASCIIZ, BYVAL ulOptions AS DWORD, BYVAL samDesired AS DWORD, phkResult AS DWORD) AS LONG
    DECLARE FUNCTION RegQueryValueEx LIB "ADVAPI32.DLL" ALIAS "RegQueryValueExA" (BYVAL hKey AS DWORD, lpValueName AS ASCIIZ, BYVAL lpReserved AS LONG, lpType AS LONG, lpData AS ANY, lpcbData AS LONG) AS LONG
    DECLARE FUNCTION RegSetValueEx LIB "ADVAPI32.DLL" ALIAS "RegSetValueExA" (BYVAL hKey AS DWORD, lpValueName AS ASCIIZ, BYVAL dwReserved AS DWORD, BYVAL dwType AS DWORD, lpData AS ANY, BYVAL cbData AS DWORD) AS LONG
    
    %HKEY_CURRENT_USER        = &H80000001???
    %HKEY_LOCAL_MACHINE       = &H80000002???
    %REG_SZ                   = 1     ' Unicode nul terminated string
    %STANDARD_RIGHTS_READ     = &H00020000
    %STANDARD_RIGHTS_WRITE    = &H00020000
    %KEY_QUERY_VALUE          = &H1
    %KEY_SET_VALUE            = &H2
    %KEY_CREATE_SUB_KEY       = &H4
    %KEY_ENUMERATE_SUB_KEYS   = &H8
    %KEY_NOTIFY               = &H10
    %SYNCHRONIZE              = &H00100000
    
    %KEY_READ                 = %STANDARD_RIGHTS_READ OR %KEY_QUERY_VALUE OR %KEY_ENUMERATE_SUB_KEYS OR %KEY_NOTIFY AND (NOT %SYNCHRONIZE)
    %KEY_WRITE                = %STANDARD_RIGHTS_WRITE OR %KEY_SET_VALUE OR %KEY_CREATE_SUB_KEY AND (NOT %SYNCHRONIZE)
    
    $RegistryKey              = "SOFTWARE\BassBox"
    $RegistryPluginCXYZ       = "PluginCXYZ"
    
    ' Returns a registry string
    FUNCTION BBP_GetReg (BYVAL hLocation AS DWORD, zSubKeys AS ASCIIZ, zValueName AS ASCIIZ) AS STRING
        LOCAL hKey AS DWORD, zRegVal AS ASCIIZ * %MAX_PATH, dwType AS DWORD, dwSize AS DWORD
        IF hLocation = 0 THEN hLocation = %HKEY_CURRENT_USER
        IF (RegOpenKeyEx(hLocation, TRIM$(zSubKeys, $Anti), 0, %KEY_READ, hKey) = 0) THEN
           dwType = %REG_SZ
         ' Set length of buffer...
           dwSize = SIZEOF(zRegVal)
           CALL RegQueryValueEx(hKey, zValueName, 0, dwType, zRegVal, dwSize)
           CALL RegCloseKey(hKey)
        END IF
        FUNCTION = zRegVal
    END FUNCTION
    
    ' Saves a string value to the registry, returns nonzero if successful
    FUNCTION BBP_SetReg (BYVAL hLocation AS DWORD, zSubKeys AS ASCIIZ, zRegName AS ASCIIZ, zRegVal AS ASCIIZ) AS LONG
        LOCAL hKey AS DWORD, dwType AS DWORD, dwSize AS DWORD
        nRet& = 0
        IF hLocation = 0 THEN hLocation = %HKEY_CURRENT_USER
        IF RegCreateKeyEx(hLocation, TRIM$(zSubKeys, $Anti), 0, "", 0, %KEY_WRITE, BYVAL 0&, hKey, BYVAL 0&) = 0 THEN
         ' Set length of data...
           dwSize = LEN(zRegVal)
           dwType = %REG_SZ
           IF RegSetValueEx(hKey, zRegName, 0, dwType, zRegVal, dwSize) = 0 THEN nRet& = -1
           RegCloseKey hKey
        END IF
        FUNCTION = nRet&
    END FUNCTION
#ENDIF
'///////////////////////////////////

#IF %BBP_NO_IOFILE = 0
    TYPE OVERLAPPED
        Internal AS DWORD
        InternalHigh AS DWORD
        offset AS DWORD
        OffsetHigh AS DWORD
        hEvent AS DWORD
    END TYPE
    
    DECLARE FUNCTION CloseHandle LIB "KERNEL32.DLL" ALIAS "CloseHandle" (BYVAL hObject AS DWORD) AS LONG
    DECLARE FUNCTION CreateFile LIB "KERNEL32.DLL" ALIAS "CreateFileA" (lpFileName AS ASCIIZ, BYVAL dwDesiredAccess AS DWORD, BYVAL dwShareMode AS DWORD, lpSecurityAttributes AS SECURITY_ATTRIBUTES, BYVAL dwCreationDisposition AS DWORD, _
                     BYVAL dwFlagsAndAttributes AS DWORD, BYVAL hTemplateFile AS DWORD) AS DWORD
    DECLARE FUNCTION GetFileSize LIB "KERNEL32.DLL" ALIAS "GetFileSize" (BYVAL hFile AS DWORD, lpFileSizeHigh AS LONG) AS LONG
    DECLARE FUNCTION GetLastError LIB "KERNEL32.DLL" ALIAS "GetLastError" () AS DWORD
    DECLARE FUNCTION ReadFile LIB "KERNEL32.DLL" ALIAS "ReadFile" (BYVAL hFile AS DWORD, lpBuffer AS ANY, BYVAL nNumberOfBytesToRead AS DWORD, lpNumberOfBytesRead AS DWORD, lpOverlapped AS OVERLAPPED) AS LONG
    DECLARE FUNCTION SetFilePointer LIB "KERNEL32.DLL" ALIAS "SetFilePointer" (BYVAL hFile AS DWORD, BYVAL lDistanceToMove AS LONG, lpDistanceToMoveHigh AS LONG, BYVAL dwMoveMethod AS DWORD) AS LONG
    
    ' Enhanced sequential LINEINPUT
    FUNCTION BBP_Bufin (zFileName AS ASCIIZ, nDone AS LONG) STATIC AS STRING
    DIM sCr AS STRING, sBuffer AS STRING
    DIM hFile AS LONG, nLocation AS LONG, nBufSize AS LONG, nBufPos AS LONG, nErrCode AS LONG, nRemaining AS LONG
    DIM nCr AS LONG, nSlop AS LONG, nReading AS LONG, nLenCr AS LONG, nSizeFile AS LONG, nSaveCr AS LONG
    LOCAL ByttesReaded AS LONG
    IF LEN(zFileName) THEN
       IF NOT nReading THEN
          nReading = -1: nDone = 0
          nLenCr = 0: sCr = CHR$(13)
          nCr = 0: nLocation = 0
          IF NOT BBP_Exist(zFileName) THEN GOTO BufEnd  ' Nothing to do
          hFile = CreateFile(zFileName, &H80000000&, &H00000001, BYVAL 0&, 4, &H00000080, BYVAL 0&)
          IF hFile = -1 THEN                ' -1 Fail to create the file
             nDone = GetLastError           ' Set the error code
             hFile = 0: nReading = 0        ' Reset handle number
             EXIT FUNCTION
          END IF
        ' Bytes count to be read
          nRemaining = GetFileSize(hFile, BYVAL 0&): IF nRemaining = -1 THEN nRemaining = 0
          nSizeFile = nRemaining                     ' Remember this
          IF nRemaining = 0 GOTO BufEnd              ' Nothing to read
          nBufSize = 16384
          sBuffer = SPACE$(nBufSize)
       END IF
       DO WHILE nRemaining                           ' While more in the file
          IF nCr = 0 THEN                            ' If no return was found
             IF nRemaining < nBufSize THEN           ' read only what remains.
                nBufSize = nRemaining                ' Resize the buffer.
                IF nBufSize < 1 THEN EXIT DO         ' Possible only if EOF 26.
                sBuffer = SPACE$(nBufSize)
             END IF
    
             ' Get the string data
             IF SetFilePointer(hFile, nLocation, BYVAL 0&, 0) > -1 THEN
                IF nBufSize AND ReadFile(hFile, BYVAL STRPTR(sBuffer), nBufSize, ByttesReaded, BYVAL 0&) = 0 THEN
                   nErrCode = GetLastError
                 END IF
             END IF
    
             nLocation = nLocation + nBufSize        ' Here we are.
             nBufPos = 1                             ' Start at the beginning
          END IF                                     ' of that bloc.
          DO                                         ' Walk through buffer.
             nCr = INSTR(nBufPos, sBuffer, sCr)      ' Look for a Return.
             IF nLenCr = 0 THEN                      ' Do this to read files with
                nLenCr = 1                           ' or without LF after CR.
                IF ASC(sBuffer, nCr + 1) = 10 THEN nLenCr = 2
             END IF
             IF nCr THEN                             ' We found one.
                nSaveCr = nCr                        ' Save where
                FUNCTION = MID$(sBuffer, nBufPos, nCr - nBufPos)
                nBufPos = nCr + nLenCr               ' In case of LF skip it
                EXIT FUNCTION                        ' all done for now.
             ELSE                                    ' Back up in the file.
              ' If we reached the end of file and no nCr
              ' was found, return what remains in the string.
                IF nLocation >= nSizeFile THEN
                 ' Assign function and trap ^Z
                   FUNCTION = RTRIM$(MID$(sBuffer, nSaveCr + nLenCr), CHR$(26))
                   nRemaining = nBufSize
                   EXIT DO
                END IF
              ' 05-06-1998 () were missing on each side of (nLenCr - 1)
                nSlop = nBufSize - nSaveCr - (nLenCr - 1) ' Calc buffer excess.
                nRemaining = nRemaining + nSlop           ' Calc file excess.
                nLocation = nLocation - nSlop
             END IF
          LOOP WHILE nCr                                 ' While more in buffer.
          nRemaining = nRemaining - nBufSize
          nSaveCr = 0
       LOOP
    END IF
    
    BufEnd:
    nReading = 0: nDone = -1: IF hFile THEN CALL CloseHandle(hFile)
    hFile = 0
    END FUNCTION
#ENDIF

'''''''''''''''''''''''''''''''''''''''
'// Use a generic "BBPLUGIN" folder that would store all the bbp plugins,
'// each one could have its own embedded subfolders inside of "BBPLUGIN" to store local resources.

'// The main exported plugin's function would be bbProc, like this:
'FUNCTION BBProc ALIAS "BBProc" (BYREF BBP AS BBPLUGIN) EXPORT AS LONG
'    LOCAL nRet AS LONG
'    nRet = %BBP_SUCCESS
'    SELECT CASE LONG BBP.Msg
'    CASE %BBP_RENDER
'         '// Draw the scene using BBP.LeftPeak and BBP.RightPeak
'    CASE %BBP_CREATE
'         '// Retrieve plugin details
'         BBP.Title    = "My BB plugin"
'         BBP.Author   = "My Name"
'         BBP.Version  = MAKDWD(1, 0) '// Version 1.0"
'         BBP.RenderTo = %BBP_OPENGL    '// or %BBP_GDIPLUS, or %BBP_DIRECTX
'    CASE %BBP_INIT
'         '// Do your code initialisation there
'    CASE %BBP_SIZE
'         '// The size of the view port has changed.
'    CASE %BBP_KEYBOARD
'         '// Handle all Windows keyboard messages there
'         Msg = BBP.WinMsg
'         wParam = BBP.wParam
'         lParam = BBP.lParam
'    CASE %BBP_MOUSE
'         '// Handle all Windows mouse messages there
'         Msg = BBP.WinMsg
'         wParam = BBP.wParam
'         lParam = BBP.lParam
'    CASE %BBP_DESTROY
'         '// Free up your resources there
'    CASE ELSE
'        nRet = %BBP_ERROR
'    END SELECT
'    FUNCTION = nRet
'END FUNCTION

DECLARE FUNCTION GetAsyncKeyState LIB "USER32.DLL" ALIAS "GetAsyncKeyState" (BYVAL vKey AS LONG) AS INTEGER

FUNCTION BBP_IsKeyDown (BYVAL CheckKey AS LONG) AS LONG
    KeyDown& = GetAsyncKeyState(CheckKey)
    FUNCTION = BIT(KeyDown&, 15)
END FUNCTION

FUNCTION BBP_IsShiftKeyPressed () AS LONG
    FUNCTION = BBP_IsKeyDown(16)
END FUNCTION

FUNCTION BBP_IsCtrlKeyPressed () AS LONG
    FUNCTION = BBP_IsKeyDown(17)
END FUNCTION

FUNCTION BBP_IsLButtonDown () AS LONG
    IF BBP_IsKeyDown(1) THEN FUNCTION = -1
END FUNCTION

FUNCTION BBP_IsRButtonDown () AS LONG
    IF BBP_IsKeyDown(2) THEN FUNCTION = -1
END FUNCTION

FUNCTION BBP_UpdateNamedGLTextureFromFile (zFullName AS ASCIIZ, BYVAL NamedTexture AS LONG, BYVAL nSquareMode AS LONG) AS LONG
    LOCAL nRet, xSize, ySize AS LONG
    REDIM PixelArray(0) AS BYTE
    IF glIsTexture(NamedTexture) THEN
       IF BBP_CreateGLTextureFromFile(zFullName, xSize, ySize, PixelArray(), nSquareMode) THEN
          CALL glBindTexture(%GL_TEXTURE_2D, NamedTexture): nRet = glGetError()
          IF nRet = 0 THEN
             CALL glTexParameteri(%GL_TEXTURE_2D, %GL_TEXTURE_MAG_FILTER, %GL_LINEAR)
             CALL glTexParameteri(%GL_TEXTURE_2D, %GL_TEXTURE_MIN_FILTER, %GL_LINEAR)
             CALL glTexImage2D(%GL_TEXTURE_2D, 0, 4, xSize, ySize, 0, %GL_RGBA, %GL_UNSIGNED_BYTE, PixelArray(0))
          END IF
       END IF
    ELSE
       nRet = %GL_INVALID_ENUM
    END IF
    FUNCTION = nRet
END FUNCTION

%LB_ADDSTRING          = &H180
%LB_INSERTSTRING       = &H181
%LB_FINDSTRING         = &H18F
%LB_GETTEXT            = &H189
%LB_GETTEXTLEN         = &H18A
%LB_DELETESTRING       = &H182
%LB_RESETCONTENT       = &H184
%LB_SETCURSEL          = &H186
%LB_GETCURSEL          = &H188
%LB_GETCOUNT           = &H18B
%LBS_HASSTRINGS        = &H40&
%LBS_SORT              = &H2&

DECLARE FUNCTION DestroyWindow LIB "USER32.DLL" ALIAS "DestroyWindow" (BYVAL hWnd AS DWORD) AS LONG
DECLARE FUNCTION GetDlgItem LIB "USER32.DLL" ALIAS "GetDlgItem" (BYVAL hDlg AS DWORD, BYVAL nIDDlgItem AS LONG) AS DWORD
DECLARE FUNCTION FindNextFile LIB "KERNEL32.DLL" ALIAS "FindNextFileA" (BYVAL hFindFile AS DWORD, lpFindFileData AS WIN32_FIND_DATA) AS LONG
DECLARE FUNCTION SendMessage LIB "USER32.DLL" ALIAS "SendMessageA" (BYVAL hWnd AS DWORD, BYVAL dwMsg AS DWORD, BYVAL wParam AS DWORD, BYVAL lParam AS LONG) AS LONG
DECLARE FUNCTION CharLower LIB "USER32.DLL" ALIAS "CharLowerA" (lpsz AS ASCIIZ) AS DWORD

SUB BBP_ListDeleteAll(BYVAL hList AS LONG)
    IF hList THEN CALL SendMessage(hList, %LB_RESETCONTENT, 0, 0)
END SUB

SUB BBP_ListSelectPlus(BYVAL hList AS LONG, BYVAL nSelected AS LONG)
    IF nSelected > 0 THEN DECR nSelected '// Because ListBox is zero based
    IF hList THEN CALL SendMessage(hList, %LB_SETCURSEL, nSelected, 0)
END SUB

FUNCTION BBP_ListGetCursel(BYVAL hList AS LONG) AS LONG
    LOCAL nSelected AS LONG
    IF hList THEN
       nSelected = SendMessage(hList, %LB_GETCURSEL, 0, 0)
       IF nSelected > -1 THEN INCR nSelected '// Because ListBox is zero based
    END IF
    FUNCTION = nSelected
END FUNCTION

FUNCTION BBP_ListAdd(BYVAL hList AS LONG, zTmp AS ASCIIZ) AS LONG
    IF hList THEN
       IF LEN(zTmp) THEN FUNCTION = SendMessage(hList, %LB_ADDSTRING, 0, VARPTR(zTmp))
    END IF
END FUNCTION

FUNCTION BBP_ListGetText(BYVAL hList AS LONG, BYVAL Item AS LONG) AS STRING
    LOCAL sItem AS STRING, nLEN AS LONG
    IF Item > 0 THEN
       DECR Item
       nLEN = SendMessage(hList, %LB_GETTEXTLEN, Item, 0)
       sItem = SPACE$(nLEN)
       CALL SendMessage(hList, %LB_GETTEXT, Item, BYVAL STRPTR(sItem))
       FUNCTION = sItem
    END IF
END FUNCTION

FUNCTION BBP_ListCount(BYVAL hList AS LONG) AS LONG
    IF hList THEN FUNCTION = SendMessage(hList, %LB_GETCOUNT, 0, 0)
END FUNCTION

SUB BBP_DetectTexture(BYVAL sFilter AS STRING, BYVAL hList AS LONG, BYVAL nUseItem AS LONG)

    IF hList = 0 THEN EXIT SUB

    LOCAL nCount, WashFind, hFind AS LONG
    LOCAL fd AS WIN32_FIND_DATA
    LOCAL zTmp AS ASCIIZ * %MAX_PATH
    LOCAL sBackgroundPath AS STRING

    sBackgroundPath = PATHNAME$(PATH, sFilter)

    CALL BBP_ListDeleteAll(hList)

    WashFind = FindFirstFile((sFilter), fd)
    hFind = WashFind
    nCount = 0
    DO WHILE hFind > 0
       IF ASC(fd.cFileName) <> 46 THEN
          IF INSTR(CHR$(0,1,32,33,128), CHR$(fd.dwFileAttributes)) THEN
             zTmp = sBackgroundPath + fd.cFileName: CALL CharLower(zTmp)
             IF INSTR(".gif.bmp.png.jpg.", RIGHT$(zTmp, 4) + ".") THEN
                IF BBP_ListAdd(hList, zTmp) > -1 THEN
                   nCount = nCount + 1
                END IF
             END IF
          END IF
       END IF
       hFind = FindNextFile(WashFind, fd)
    LOOP
    CALL FindClose(WashFind)

    IF nCount THEN
       IF nUseItem > nCount THEN nUseItem = 1
       CALL BBP_ListSelectPlus(hList, nUseItem)
    END IF

END SUB

SUB BBP_DrawObject2D(BYVAL x1 AS LONG, BYVAL y1 AS LONG, BYVAL x2 AS LONG, BYVAL y2 AS LONG)
    CALL glBegin(%GL_QUADS)
         CALL glTexCoord2f(0.0, 1.0): CALL glVertex2i(x1, y1)
         CALL glTexCoord2f(1.0, 1.0): CALL glVertex2i(x2, y1)
         CALL glTexCoord2f(1.0, 0.0): CALL glVertex2i(x2, y2)
         CALL glTexCoord2f(0.0, 0.0): CALL glVertex2i(x1, y2)
    CALL glEnd()
END SUB
