#include "ctlspriv.h"
#include "tracki.h"

#define THUMBSLOP  3
#define TICKHEIGHT 4


#define ABS(X)              (X >= 0) ? X : -X
#define BOUND(x,low,high)   max(min(x, high),low)


TCHAR szSTrackBarClass[] = TRACKBAR_CLASS;

LPARAM FAR CALLBACK TrackBarWndProc(HWND hwnd, WORD message, WPARAM wParam, LPARAM lParam);

//
//  convert a logical scroll-bar position to a physical pixel position
//
int NEAR PASCAL TBLogToPhys(PTrackBar tb, DWORD dwPos)
{
    if (tb->lLogMax == tb->lLogMin)
       return tb->rc.left;

    return (WORD)MulDiv(dwPos - tb->lLogMin, tb->iSizePhys - 1,
                        tb->lLogMax - tb->lLogMin) + tb->rc.left;
}

LONG NEAR PASCAL TBPhysToLog(PTrackBar tb, int iPos)
{
    if (tb->iSizePhys <= 1)
       return tb->lLogMin;

    if (iPos <= tb->rc.left)
       return tb->lLogMin;

    if (iPos >= tb->rc.right)
       return tb->lLogMax;

   return MulDiv(iPos - tb->rc.left, tb->lLogMax - tb->lLogMin,
                 tb->iSizePhys - 1) + tb->lLogMin;
}


/*
   Initialize the trackbar code.
*/

BOOL FAR PASCAL InitTrackBar(HINSTANCE hInstance)
{
    WNDCLASS wc;

    if (!GetClassInfo(hInstance, szSTrackBarClass, &wc))
    {
       // See if we must register a window class

       wc.lpszClassName = szSTrackBarClass;
       wc.lpfnWndProc = (WNDPROC)TrackBarWndProc;
       wc.hCursor = LoadCursor(NULL, IDC_ARROW);
       wc.hIcon = NULL;
       wc.lpszMenuName = NULL;
       wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
       wc.hInstance = hInstance;
       wc.style = CS_DBLCLKS | CS_GLOBALCLASS;
       wc.cbClsExtra = 0;
       wc.cbWndExtra = EXTRA_TB_BYTES;

       if (!RegisterClass(&wc))
          return FALSE;
    }
    return TRUE;
}

#if 0
/* SelectColorObjects() */
/* I don't know what this does. */

HBRUSH NEAR PASCAL SelectColorObjects(PTrackBar tb, BOOL fSelect)
{
   static HBRUSH hbrSave;
   HBRUSH hbr;

   if (fSelect)
      hbr = (HBRUSH)SendMessage(GetParent(tb->hwnd),
                         WM_CTLCOLORSCROLLBAR, tb->hdc, tb->hwnd);
   else
      hbr = hbrSave;

   hbrSave = SelectObject(tb->hdc, hbr);

   return hbr;
}
#endif


void NEAR PASCAL PatRect(HDC hdc,int x,int y,int dx,int dy)
{
    RECT    rc;

    rc.left   = x;
    rc.top    = y;
    rc.right  = x + dx;
    rc.bottom = y + dy;

    ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
}

void NEAR PASCAL DrawTic(PTrackBar tb, int x, int yTic)
{
    SetBkColor(tb->hdc, GetSysColor(COLOR_BTNTEXT));
    PatRect(tb->hdc,(x),yTic,1,TICKHEIGHT);
    SetBkColor(tb->hdc, GetSysColor(COLOR_BTNHIGHLIGHT));
    PatRect(tb->hdc,(x)+1,yTic,1,TICKHEIGHT);
}

/* DrawTics() */
/* There is always a tick at the beginning and end of the bar, but you can */
/* add some more of your own with a TBM_SETTIC message.  This draws them.  */
/* They are kept in an array whose handle is a window word.  The first     */
/* element is the number of extra ticks, and then the positions.           */

void NEAR PASCAL DrawTics(PTrackBar tb)
{
    PDWORD pTics;
    int    iPos;
    int    yTic;
    int    i;

    yTic = tb->rc.bottom + THUMBSLOP + 1;

    DrawTic(tb, tb->rc.left, yTic);               // first
    DrawTic(tb, tb->rc.right-1, yTic);            // last

    // those inbetween
    pTics = tb->pTics;
    if (pTics)
       for (i = 0; i < tb->nTics; ++i) {
          iPos = TBLogToPhys(tb,pTics[i]);
          DrawTic(tb, iPos, yTic);
       }

    // draw the selection range (triangles)

    if ((tb->Flags & TBF_SELECTION) &&
       (tb->lSelStart <= tb->lSelEnd) && (tb->lSelEnd >= tb->lLogMin)) {

       SetBkColor(tb->hdc, GetSysColor(COLOR_BTNTEXT));

       iPos = TBLogToPhys(tb,tb->lSelStart);

       for (i = 0; i < TICKHEIGHT; i++)
          PatRect(tb->hdc, iPos - i, yTic + i, 1, TICKHEIGHT - i);

       iPos = TBLogToPhys(tb,tb->lSelEnd);

       for (i = 0; i < TICKHEIGHT; i++)
           PatRect(tb->hdc, iPos + i, yTic + i, 1, TICKHEIGHT - i);
    }

    // line across the bottom
    SetBkColor(tb->hdc, GetSysColor(COLOR_BTNTEXT));
    PatRect(tb->hdc, tb->rc.left, yTic + TICKHEIGHT, tb->iSizePhys, 1);

    SetBkColor(tb->hdc, GetSysColor(COLOR_BTNHIGHLIGHT));
    PatRect(tb->hdc, tb->rc.left, yTic + TICKHEIGHT + 1, tb->iSizePhys, 1);
}

/* This draws the track bar itself */

void NEAR PASCAL DrawChannel(PTrackBar tb)
{
    HBRUSH hbrTemp;

    // draw the frame around the window
    SetBkColor(tb->hdc, GetSysColor(COLOR_WINDOWFRAME));

    PatRect(tb->hdc, tb->rc.left, tb->rc.top,      tb->iSizePhys, 1);
    PatRect(tb->hdc, tb->rc.left, tb->rc.bottom-2, tb->iSizePhys, 1);
    PatRect(tb->hdc, tb->rc.left, tb->rc.top,      1, tb->rc.bottom-tb->rc.top-1);
    PatRect(tb->hdc, tb->rc.right-1, tb->rc.top, 1, tb->rc.bottom-tb->rc.top-1);

    SetBkColor(tb->hdc, GetSysColor(COLOR_BTNHIGHLIGHT));
    PatRect(tb->hdc, tb->rc.left, tb->rc.bottom-1, tb->iSizePhys, 1);

    SetBkColor(tb->hdc, GetSysColor(COLOR_BTNSHADOW));
    PatRect(tb->hdc, tb->rc.left+1, tb->rc.top + 1, tb->iSizePhys-2,1);

    // draw the bacground in dither gray
    hbrTemp = SelectObject(tb->hdc, hbrDither);
    if (hbrTemp) {
        PatBlt(tb->hdc, tb->rc.left+1, tb->rc.top + 2,
            tb->iSizePhys-2, tb->rc.bottom-tb->rc.top-4, PATCOPY);
        SelectObject(tb->hdc, hbrTemp);
    }

    // now highlight the selection range
    if ((tb->Flags & TBF_SELECTION) &&
       (tb->lSelStart <= tb->lSelEnd) && (tb->lSelEnd > tb->lLogMin)) {

       int iStart, iEnd;

       iStart = TBLogToPhys(tb,tb->lSelStart);
       iEnd   = TBLogToPhys(tb,tb->lSelEnd);

       SetBkColor(tb->hdc, GetSysColor(COLOR_BTNTEXT));
       PatRect(tb->hdc, iStart,tb->rc.top+1,1,tb->rc.bottom-tb->rc.top-2);
       PatRect(tb->hdc, iEnd,  tb->rc.top+1,1,tb->rc.bottom-tb->rc.top-2);

       if (iStart + 2 <= iEnd) {
          SetBkColor(tb->hdc, GetSysColor(COLOR_BTNHIGHLIGHT));
          PatRect(tb->hdc, iStart+1, tb->rc.top + 1, iEnd - iStart - 1, tb->rc.bottom-tb->rc.top - 3);
       }
    }
}

void NEAR PASCAL MoveThumb(PTrackBar tb, LONG lPos)
{
   InvalidateRect(tb->hwnd, &tb->Thumb, TRUE);

   tb->lLogPos  = BOUND(lPos,tb->lLogMin,tb->lLogMax);

   tb->Thumb.left   = TBLogToPhys(tb, tb->lLogPos) - tb->wThumbWidth/2;
   tb->Thumb.right  = tb->Thumb.left + tb->wThumbWidth;
   tb->Thumb.top    = tb->rc.top - THUMBSLOP;
   tb->Thumb.bottom = tb->rc.bottom + THUMBSLOP;

   InvalidateRect(tb->hwnd, &tb->Thumb, TRUE);
   UpdateWindow(tb->hwnd);
}


void NEAR PASCAL DrawThumb(PTrackBar tb)
{
   HBITMAP hbmT;
   HDC     hdcT;
   int     x;

   hdcT = CreateCompatibleDC(tb->hdc);

   if ((tb->Cmd == TB_THUMBTRACK) || !IsWindowEnabled(tb->hwnd))
       x = tb->wThumbWidth;
   else
       x = 0;

   hbmT = SelectObject(hdcT, hbmThumb);
   if (hbmT) {
      BitBlt(tb->hdc,tb->Thumb.left, tb->rc.top-THUMBSLOP,
      tb->wThumbWidth, tb->wThumbHeight, hdcT, x, 0, SRCCOPY);
   }

   SelectObject(hdcT, hbmT);
   DeleteDC(hdcT);
}

/* SetTBCaretPos() */
/* Make the caret flash in the middle of the thumb when it has the focus */

void NEAR PASCAL SetTBCaretPos(PTrackBar tb)
{
   // We only get the caret if we have the focus.
   if (tb->hwnd == GetFocus())
      SetCaretPos(tb->Thumb.left + 3, tb->Thumb.top + 3);
}

LPARAM FAR CALLBACK TrackBarWndProc(HWND hwnd, WORD message, WPARAM wParam, LPARAM lParam)
{
   PTrackBar       tb;
   PAINTSTRUCT     ps;
   BITMAP          bm;
   HANDLE          h;

   tb = TrackBarLock(hwnd);

   switch (message) {
      case WM_CREATE:
         if (!CreateDitherBrush(FALSE))
            return -1;
         // Get us our window structure.
         TrackBarCreate(hwnd);
         tb = TrackBarLock(hwnd);

         tb->hwnd = hwnd;
         tb->Cmd = (WORD)-1;

         /* load the 2 thumb bitmaps (pressed and released) */
         CreateThumb(FALSE);

         GetObject(hbmThumb, sizeof(bm), &bm);

         tb->wThumbWidth  = (WORD)(bm.bmWidth/2);
         tb->wThumbHeight = (WORD)bm.bmHeight;
                        // tb->lLogMax = 100;      // make tracking work by default

         // fall through to WM_SIZE

      case WM_SIZE:
         GetClientRect(hwnd, &tb->rc);

         tb->rc.bottom  = tb->rc.top + tb->wThumbHeight - THUMBSLOP;
         tb->rc.top    += THUMBSLOP;
         tb->rc.left   += tb->wThumbWidth/2;
         tb->rc.right  -= tb->wThumbWidth/2;

         // Figure out how much room we have to move the thumb in
         //!!! -2
         tb->iSizePhys = tb->rc.right - tb->rc.left;

         // Elevator isn't there if there's no room.
         if (tb->iSizePhys == 0) {
            // Lost our thumb.
            tb->Flags |= TBF_NOTHUMB;
            tb->iSizePhys = 1;
         } else {
            // Ah. We have a thumb.
            tb->Flags &= ~TBF_NOTHUMB;
         }
         InvalidateRect(hwnd, NULL, TRUE);
         MoveThumb(tb, tb->lLogPos);
         break;

      case WM_DESTROY:
         TrackBarDestroy(hwnd);
         FreeDitherBrush();

         DestroyThumb();
         break;

      case WM_SETFOCUS:
         // We gots the focus. We need a caret.

         CreateCaret(hwnd, (HBITMAP)1, 3, 13);
         SetTBCaretPos(tb);
         ShowCaret(hwnd);
         break;

      case WM_KILLFOCUS:
         DestroyCaret();
         break;

      case WM_ERASEBKGND:
         break;

      case WM_ENABLE:
         InvalidateRect(hwnd, NULL, FALSE);
         break;

      case WM_PAINT:
         if (wParam == 0L)
             tb->hdc = BeginPaint(hwnd, &ps);
         else
             tb->hdc = (HDC)wParam;

         // Update the dither brush if necessary.
         CheckSysColors();

         DrawTics(tb);
         DrawThumb(tb);
         ExcludeClipRect(tb->hdc, tb->Thumb.left, tb->Thumb.top,
             tb->Thumb.right, tb->Thumb.bottom);
         DrawChannel(tb);
         SetTBCaretPos(tb);

         if (wParam == 0L)
            EndPaint(hwnd, &ps);

         tb->hdc = NULL;
         break;

      case WM_GETDLGCODE:
         return DLGC_WANTARROWS;
         break;

      case WM_LBUTTONDOWN:
         /* Give ourselves focus */
         SetFocus(hwnd);
         TBTrackInit(tb, lParam);
         break;

      case WM_LBUTTONUP:
         // We're through doing whatever we were doing with the
         // button down.
         TBTrackEnd(tb, lParam);
         break;

      case WM_TIMER:
         // The only way we get a timer message is if we're
         // autotracking.
         lParam = GetMessagePos();
         ScreenToClient(tb->hwnd, (LPPOINT)&lParam);

         // fall through to WM_MOUSEMOVE

      case WM_MOUSEMOVE:
         // We only care that the mouse is moving if we're
         // tracking the bloody thing.
         if (tb->Cmd != (WORD)-1)
            TBTrack(tb, lParam);
         return 0L;

      case WM_KEYUP:
         // If key was any of the keyboard accelerators, send end
         // track message when user up clicks on keyboard
         switch (wParam) {
            case VK_HOME:
            case VK_END:
            case VK_PRIOR:
            case VK_NEXT:
            case VK_LEFT:
            case VK_UP:
            case VK_RIGHT:
            case VK_DOWN:
               DoTrack(tb, TB_ENDTRACK, 0);
               break;
            default:
               break;
         }
         break;

      case WM_KEYDOWN:
         switch (wParam) {
            case VK_HOME:
               wParam = TB_TOP;
               goto KeyTrack;

            case VK_END:
               wParam = TB_BOTTOM;
               goto KeyTrack;

            case VK_PRIOR:
               wParam = TB_PAGEUP;
               goto KeyTrack;

            case VK_NEXT:
               wParam = TB_PAGEDOWN;
               goto KeyTrack;

            case VK_LEFT:
            case VK_UP:
               wParam = TB_LINEUP;
               goto KeyTrack;

            case VK_RIGHT:
            case VK_DOWN:
               wParam = TB_LINEDOWN;
KeyTrack:
               DoTrack(tb, wParam, 0);
               break;

            default:
               break;
         }
         break;

      case TBM_GETPOS:
         return tb->lLogPos;

      case TBM_GETSELSTART:
         return tb->lSelStart;

      case TBM_GETSELEND:
         return tb->lSelEnd;

      case TBM_GETRANGEMIN:
         return tb->lLogMin;

      case TBM_GETRANGEMAX:
         return tb->lLogMax;

      case TBM_GETPTICS:
         return (LONG)(LPVOID)tb->pTics;

      case TBM_CLEARSEL:
         tb->Flags &= ~TBF_SELECTION;
         tb->lSelStart = -1;
         tb->lSelEnd   = -1;
         goto RedrawTB;

      case TBM_CLEARTICS:
         if (tb->pTics)
             LocalFree((HLOCAL)tb->pTics);

         tb->nTics = 0;
         tb->pTics = NULL;
         goto RedrawTB;

      case TBM_GETTIC:
         if (tb->pTics == NULL || (int)wParam >= tb->nTics)
             return -1L;

         return tb->pTics[wParam];

      case TBM_GETTICPOS:
         if (tb->pTics == NULL || (int)wParam >= tb->nTics)
             return -1L;

         return TBLogToPhys(tb,tb->pTics[wParam]);

      case TBM_GETNUMTICS:
         return tb->nTics;

      case TBM_SETTIC:
         /* not a valid position */
         if (lParam < 0)
             break;

         if (tb->pTics)
            h = LocalReAlloc((HLOCAL)tb->pTics,
                sizeof(DWORD) * (WORD)(tb->nTics + 1),
                LMEM_MOVEABLE | LMEM_ZEROINIT);
         else
            h = LocalAlloc(LPTR, sizeof(DWORD));

         if (h)
            tb->pTics = (PDWORD)h;
         else
            return (LONG)FALSE;

         tb->pTics[tb->nTics++] = (DWORD)lParam;

         InvalidateRect(hwnd, NULL, TRUE);
         return (LONG)TRUE;
         break;

      case TBM_SETPOS:
         /* Only redraw if it will physically move */
         if (wParam && TBLogToPhys(tb, lParam) !=
                  TBLogToPhys(tb, tb->lLogPos))
             MoveThumb(tb, lParam);
         else
             tb->lLogPos = BOUND(lParam,tb->lLogMin,tb->lLogMax);
         break;

      case TBM_SETSEL:
                        tb->Flags |= TBF_SELECTION;
         tb->lSelStart = LOWORD(lParam);
         tb->lSelEnd   = HIWORD(lParam);
         if (tb->lSelEnd < tb->lSelStart)
             tb->lSelEnd = tb->lSelStart;
         goto RedrawTB;

      case TBM_SETSELSTART:
                        tb->Flags |= TBF_SELECTION;
         tb->lSelStart = lParam;
         if (tb->lSelEnd < tb->lSelStart || tb->lSelEnd == -1)
             tb->lSelEnd = tb->lSelStart;
         goto RedrawTB;

      case TBM_SETSELEND:
                        tb->Flags |= TBF_SELECTION;
         tb->lSelEnd   = lParam;
         if (tb->lSelStart > tb->lSelEnd || tb->lSelStart == -1)
             tb->lSelStart = tb->lSelEnd;
         goto RedrawTB;

      case TBM_SETRANGE:
         tb->lLogMin = LOWORD(lParam);
         tb->lLogMax = HIWORD(lParam);
         goto RedrawTB;

      case TBM_SETRANGEMIN:
         tb->lLogMin = (DWORD)lParam;
         goto RedrawTB;

      case TBM_SETRANGEMAX:
         tb->lLogMax = (DWORD)lParam;

RedrawTB:
         tb->lLogPos = BOUND(tb->lLogPos, tb->lLogMin,tb->lLogMax);
         /* Only redraw if flag says so */
         if (wParam) {
             InvalidateRect(hwnd, NULL, TRUE);
             MoveThumb(tb, tb->lLogPos);
         }
         break;
   }
   return DefWindowProc(hwnd, message, wParam, lParam);
}

/* DoTrack() */

void NEAR PASCAL DoTrack(PTrackBar tb, int cmd, DWORD dwPos)
{
   // note: we only send back a WORD worth of the position.
   SendMessage(GetParent(tb->hwnd), WM_HSCROLL,
      (WPARAM)MAKELONG(cmd,LOWORD(dwPos)), (LPARAM)tb->hwnd);
}

/* WTrackType() */

WORD NEAR PASCAL WTrackType(PTrackBar tb, LONG lParam)
{
   POINT pt;

   LONG2POINT( lParam, pt );

   if (tb->Flags & TBF_NOTHUMB)            // If no thumb, just leave.
       return 0;

   if (PtInRect(&tb->Thumb, pt))
       return TB_THUMBTRACK;

   if (!PtInRect(&tb->rc, pt))
       return 0;

   if (pt.x >= tb->Thumb.left)
       return TB_PAGEDOWN;
   else
       return TB_PAGEUP;
}

/* TBTrackInit() */

void NEAR PASCAL TBTrackInit(PTrackBar tb, LONG lParam)
{
   WORD wCmd;

   if (tb->Flags & TBF_NOTHUMB)         // No thumb:  just leave.
       return;

        wCmd = WTrackType(tb, lParam);
   if (!wCmd)
       return;

   HideCaret(tb->hwnd);
   SetCapture(tb->hwnd);

   tb->Cmd = wCmd;
   tb->dwDragPos = (DWORD)-1;

   // Set up for auto-track (if needed).
   if (wCmd != TB_THUMBTRACK) {
      // Set our timer up
      tb->Timer = (UINT)SetTimer(tb->hwnd, TIMER_ID, REPEATTIME, NULL);
   }

   TBTrack(tb, lParam);
}

/* EndTrack() */

void near PASCAL TBTrackEnd(PTrackBar tb, long lParam)
{
   if (GetCapture() != tb->hwnd)
       return;

   // Let the mouse go.
   ReleaseCapture();

   // Decide how we're ending this thing.
   if (tb->Cmd == TB_THUMBTRACK)
      DoTrack(tb, TB_THUMBPOSITION, tb->dwDragPos);

   if (tb->Timer)
      KillTimer(tb->hwnd, TIMER_ID);

   tb->Timer = 0;

   // Always send TB_ENDTRACK message.
   DoTrack(tb, TB_ENDTRACK, 0);

   // Give the caret back.
   ShowCaret(tb->hwnd);

   // Nothing going on.
   tb->Cmd = (WORD)-1;

   MoveThumb(tb, tb->lLogPos);
}

void NEAR PASCAL TBTrack(PTrackBar tb, LONG lParam)
{
   DWORD dwPos;

   // See if we're tracking the thumb
   if (tb->Cmd == TB_THUMBTRACK) {
      dwPos = TBPhysToLog(tb, LOWORD(lParam));

      // Tentative position changed -- notify the guy.
      if (dwPos != tb->dwDragPos) {
         tb->dwDragPos = dwPos;
         MoveThumb(tb, dwPos);
         DoTrack(tb, TB_THUMBTRACK, dwPos);
      }
   } else {
      if (tb->Cmd != WTrackType(tb, lParam))
          return;

      DoTrack(tb, tb->Cmd, 0);
   }
}


// what is this for?
#if 0

BYTE ThumbBitmap[] = {
    0x28,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x01,0x00,0x04,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xbf,0x00,
    0x00,0xbf,0x00,0x00,0x00,0xbf,0xbf,0x00,0xbf,0x00,0x00,0x00,0xbf,0x00,0xbf,0x00,
    0xbf,0xbf,0x00,0x00,0xc0,0xc0,0xc0,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0xff,0x00,
    0x00,0xff,0x00,0x00,0x00,0xff,0xff,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0xff,0x00,
    0xff,0xff,0x00,0x00,0xff,0xff,0xff,0x00,0xbb,0xbb,0x0b,0xbb,0xbb,0xbb,0xb0,0xbb,
    0xbb,0x00,0x00,0x00,0xbb,0xb0,0x80,0xbb,0xbb,0xbb,0x08,0x0b,0xbb,0x00,0x00,0x00,
    0xbb,0x08,0xf8,0x0b,0xbb,0xb0,0x87,0x70,0xbb,0x00,0x00,0x00,0xb0,0x8f,0xf8,0x80,
    0xbb,0x08,0x77,0x77,0x0b,0x00,0x00,0x00,0x08,0xf8,0x88,0x88,0x00,0x88,0x88,0x87,
    0x70,0x00,0x00,0x00,0x0f,0xf7,0x77,0x88,0x00,0x88,0x77,0x77,0x70,0x00,0x00,0x00,
    0x0f,0xf8,0x88,0x88,0x00,0x88,0x88,0x87,0x70,0x00,0x00,0x00,0x0f,0xf7,0x77,0x88,
    0x00,0x88,0x77,0x77,0x70,0x00,0x00,0x00,0x0f,0xf8,0x88,0x88,0x00,0x88,0x88,0x87,
    0x70,0x00,0x00,0x00,0x0f,0xf7,0x77,0x88,0x00,0x88,0x77,0x77,0x70,0x00,0x00,0x00,
    0x0f,0xf8,0x88,0x88,0x00,0x88,0x88,0x87,0x70,0x00,0x00,0x00,0x0f,0xf7,0x77,0x88,
    0x00,0x88,0x77,0x77,0x70,0x00,0x00,0x00,0x0f,0xf8,0x88,0x88,0x00,0x88,0x88,0x87,
    0x70,0x00,0x00,0x00,0x0f,0xf7,0x77,0x88,0x00,0x88,0x77,0x77,0x70,0x00,0x00,0x00,
    0x0f,0xf8,0x88,0x88,0x00,0x88,0x88,0x87,0x70,0x00,0x00,0x00,0x0f,0xf7,0x77,0x88,
    0x00,0x88,0x77,0x77,0x70,0x00,0x00,0x00,0x0f,0xf8,0x88,0x88,0x00,0x88,0x88,0x87,
    0x70,0x00,0x00,0x00,0x0f,0xf7,0x77,0x78,0x00,0x88,0x77,0x77,0x70,0x00,0x00,0x00,
    0x0f,0xff,0xff,0xff,0x00,0x88,0x88,0x88,0x80,0x00,0x00,0x00,0xb0,0x00,0x00,0x00,
    0xbb,0x00,0x00,0x00,0x0b,0x00,0x00,0x00
};
#endif