From 620b251aaf5038f522f76bcd5d7dd9aba0c9ddd4 Mon Sep 17 00:00:00 2001 From: mathias Date: Sat, 30 Apr 2005 00:35:03 +0000 Subject: [PATCH] initial import --HG-- extra : convert_revision : svn%3Aeebe1cee-a9af-4fe4-bd26-ad572b19c5ab/trunk%401 --- CHANGELOG | 7 + README | 84 +++++++++ SConstruct | 79 +++++++++ aklock.c | 482 ++++++++++++++++++++++++++++++++++++++++++++++++++++ aklock.man | 54 ++++++ lock.bitmap | 19 +++ mask.bitmap | 19 +++ 7 files changed, 744 insertions(+) create mode 100644 CHANGELOG create mode 100644 README create mode 100644 SConstruct create mode 100644 aklock.c create mode 100644 aklock.man create mode 100644 lock.bitmap create mode 100644 mask.bitmap diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..6fbb587 --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,7 @@ +Version 0.1 + +2005-04-28: + * first release (0.1) + * added pam-support (works ok with linux + freebsd so far) + * use scons for building the stuff + diff --git a/README b/README new file mode 100644 index 0000000..cbfee9b --- /dev/null +++ b/README @@ -0,0 +1,84 @@ +-+------------------------------------------------------------------+- + aklock - a slim screenlocker for X +-+------------------------------------------------------------------+- + +1. about +2. installation +3. usage +4. todo +5. author + + +1. about +-------- + + 'aklock' locks your current session in X. its not a fullfeatured + screensaver-distribution like 'xlockmore' or 'xscreensaver' but just a + tiny locker: "uh, i need coffee .. lock" .. speaking of size: binary takes + only 11kb. 'aklock' shows the content of the locked screen .. so you can + see whats going on, even when you are not able to modify what you see. + this is pretty usefull for public terminals where a demo should run which + shouldnt interupted (you know those little kids who press every button and + click everywhere ...). + + an irc-friend of mine (izhirahider) is using 'xtrlock' and had some + problems with it .. no wonder, development stopped some years ago + and it doesnt support pam. since i like the minimalism of 'xtrlock' + but wanted to add some minor things to it i "forked" 'aklock' from + 'xtrlock' and modified it a bit :) + +2. installation +--------------- + + to install / compile 'aklock' you need 'scons': + + http://www.scons.org + + its a clever buildsystem which i prefer over make/autotools. + after installing 'scons' you just type: + + $> scons + + and it should build your personal version of 'aklock' + you can influence the build-process by some variables, check + + $> scons -h + + to finally install it to your system use: + + $> scons install + + and thats it. + + tested platforms (so far): + + gentoo-linux (pc and ppc with gcc3.4.3) + freebsd5.3 (pc gcc3.4.2) + +3. usage +-------- + + aklock [-vh] [-blank] + + -h displays a little help + -v displays version number + + -blank hides the content of the screen with a big, black area + + after locking 'aklock' will show a modified mousecursor. you have to + enter your password blindly. if its the correct password the screen will + unlock + +4. todo +------- + + - check on irix, sun, hp etc etc + - users wants to specify lockcursor + background for -blank + +5. author +--------- + + 2005 mathias gumz aka akira < akira at fluxbox dot org> + 1993 - 1994 ian jackson (xtrlock) + + diff --git a/SConstruct b/SConstruct new file mode 100644 index 0000000..403becd --- /dev/null +++ b/SConstruct @@ -0,0 +1,79 @@ +#!/bin/env python +########################################################## +# +# scons - buildsystem for aklock +# +########################################################## + +import sys + +aklock_version = '0.1' +aklock_optfile = 'scons.opts' + +aklock_distfiles = ['aklock.c', + 'lock.bitmap', 'mask.bitmap', + 'SConstruct', 'README', 'CHANGELOG' ] +aklock_sources = [ 'aklock.c' ] +aklock_target = 'aklock' +Default(aklock_target) + + +aklock_options = Options(aklock_optfile) +aklock_options.AddOptions( + BoolOption('debug', 'build debug version', 0), + BoolOption('shadow', 'support for shadowpasswords', 0), + BoolOption('pam', 'support for pam', 1), + PathOption('prefix', 'install-path base', '/usr/local') +) + +aklock_env = Environment(options = aklock_options, + TARFLAGS = '-c -z', + TARSUFFIX = '.tgz') +Help(aklock_options.GenerateHelpText(aklock_env)) + +########################################################### +# +# +build = aklock_env.Copy() + +if sys.platform == "linux2" or sys.platform == "linux-i386": + build.AppendUnique( + CPPDEFINES = [ 'LINUX' ]) + +build.AppendUnique( + CPPDEFINES = [ 'VERSION=\\"'+aklock_version+'\\"' ], + CPPFLAGS = [ '-Wall' ], + CPPPATH = [ '/usr/X11R6/include' ], + LIBPATH = ['/usr/X11R6/lib'], + LIBS = [ 'X11', 'crypt' ]) + +if build['debug']: + build.AppendUnique( + CPPDEFINES = [ 'DEBUG' ], + LINKFLAGS = [ '-g' ], + CPPFLAGS = [ '-g' ]) + +if build['pam']: + build.AppendUnique( + CPPDEFINES = [ 'PAM_PWD' ], + LIBS = [ 'pam' ]) + + if sys.platform == 'linux2' or sys.platform == 'linux-i386': + build.AppendUnique(LIBS = ['pam_misc']) + + +if build['shadow']: + build.AppendUnique( + CPPDEFINES = [ 'SHADOW_PWD' ]) + +aklock_program = build.Program(aklock_target, aklock_sources) +build.AddPostAction(aklock_program, Chmod(aklock_target, 0755)) + +aklock_env.Install(aklock_env['prefix']+'/bin', aklock_program) +aklock_env.Alias('install', aklock_env['prefix']+'/bin') + +# TODO: add a "scons dist" command which builds a propper tarball +#aklock_env.Alias('dist', aklock_env.Tar(aklock_target + '-' + aklock_version, +# aklock_distfiles)) + +# vim:ft=python diff --git a/aklock.c b/aklock.c new file mode 100644 index 0000000..f7dce4e --- /dev/null +++ b/aklock.c @@ -0,0 +1,482 @@ +/*------------------------------------------------------------------*\ + + file: aklock.c + + X Transparent Lock + + copyright: + + Copyright (C)1993,1994 Ian Jackson (xtrlock) + Copyright (C)2005 Mathias Gumz (forked aklock) + + license: + + This is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +\*------------------------------------------------------------------*/ + +/*------------------------------------------------------------------*\ +\*------------------------------------------------------------------*/ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef SHADOW_PWD +# include +#endif /* SHADOW_PWD */ + +#ifdef PAM_PWD +# include +# ifdef LINUX +# include +# endif +#endif /* PAM_PWD */ + +/*----------------------------------------------*\ +\*----------------------------------------------*/ + +#include "lock.bitmap" +#include "mask.bitmap" + +/*------------------------------------------------------------------*\ + globals +\*------------------------------------------------------------------*/ + +#define TIMEOUTPERATTEMPT 30000 +#define MAXGOODWILL (TIMEOUTPERATTEMPT*5) +#define INITIALGOODWILL MAXGOODWILL +#define GOODWILLPORTION 0.3 + +#ifdef DEBUG +# define DBGMSG fprintf(stderr, "%s : %d\n", __FUNCTION__, __LINE__); fflush(stderr) +#else +# define DBGMSG +#endif // DEBUG + +struct passwd *pw; + + + +/*------------------------------------------------------------------*\ +\*------------------------------------------------------------------*/ + +void displayUsage() { + + printf("\naklock [-hv] [-blank]\n\n" +" -h shows this little help\n" +" -v shows the version number\n" +" -blank hides the content of the screen\n" +"\n" +"when the screen is locked, just enter your password.\n"); +} + + + + + +/*------------------------------------------------------------------*\ + pam-related stuff + + taken from pure-ftpd's authstuff, but you can see similar stuff + in xlockmore, openssh and basicly all pam-related apps :) +\*------------------------------------------------------------------*/ +#ifdef PAM_PWD +# define PAM_YN { \ + if (PAM_error != 0 || pam_error != PAM_SUCCESS) { \ + fprintf(stderr, "pam error:%s\n", pam_strerror(pam_handle, pam_error)); \ + pam_end(pam_handle, pam_error); \ + PAM_username = NULL; \ + PAM_password = NULL; \ + return 0;\ + } \ + } + +# define GET_MEM \ + size += sizeof(struct pam_response); \ + if ((reply = realloc(reply, size)) == NULL) { \ + PAM_error = 1; \ + return PAM_CONV_ERR; \ + } + +static const char* PAM_username = NULL; +static const char* PAM_password = NULL; +static int PAM_error = 0; +static int pam_error = PAM_SUCCESS; + +static int PAM_conv(int num_msg, const struct pam_message **msgs, + struct pam_response **resp, void *appdata_ptr) { + + int count = 0; + unsigned int replies = 0U; + struct pam_response *reply = NULL; + size_t size = (size_t) 0U; + + (void) appdata_ptr; + *resp = NULL; + for (count = 0; count < num_msg; count++) { + switch (msgs[count]->msg_style) { + case PAM_PROMPT_ECHO_ON: + GET_MEM; + memset(&reply[replies], 0, sizeof reply[replies]); + if ((reply[replies].resp = strdup(PAM_username)) == NULL) { +# ifdef PAM_BUF_ERR + reply[replies].resp_retcode = PAM_BUF_ERR; +# endif + PAM_error = 1; + return PAM_CONV_ERR; + } + reply[replies++].resp_retcode = PAM_SUCCESS; + /* PAM frees resp */ + break; + case PAM_PROMPT_ECHO_OFF: + GET_MEM; + memset(&reply[replies], 0, sizeof reply[replies]); + if ((reply[replies].resp = strdup(PAM_password)) == NULL) { +# ifdef PAM_BUF_ERR + reply[replies].resp_retcode = PAM_BUF_ERR; +# endif + PAM_error = 1; + return PAM_CONV_ERR; + } + reply[replies++].resp_retcode = PAM_SUCCESS; + /* PAM frees resp */ + break; + case PAM_TEXT_INFO: + /* ignore it... */ + break; + case PAM_ERROR_MSG: + default: + /* Must be an error of some sort... */ + free(reply); + PAM_error = 1; + return PAM_CONV_ERR; + } + } + *resp = reply; + return PAM_SUCCESS; +} + +static struct pam_conv PAM_conversation = { + &PAM_conv, NULL +}; +#endif /* PAM_PWD */ +/*------------------------------------------------------------------*\ +\*------------------------------------------------------------------*/ + +int passwordOk(const char *s) { +#if 0 + char key[3]; + char *encr; + + key[0] = *(pw->pw_passwd); + key[1] = (pw->pw_passwd)[1]; + key[2] = 0; + encr = crypt(s, key); + return !strcmp(encr, pw->pw_passwd); +#else + /* simpler, and should work with crypt() algorithms using longer + salt strings (like the md5-based one on freebsd). --marekm */ +#ifdef PAM_PWD + pam_handle_t* pam_handle = NULL; + PAM_username = pw->pw_name; + PAM_password = s; + pam_error = pam_start("login", PAM_username, &PAM_conversation, &pam_handle); + PAM_YN; + pam_error = pam_authenticate(pam_handle, 0); + PAM_YN; + pam_error = pam_end(pam_handle, pam_error); + PAM_YN; + return 1; +#else + return !strcmp(crypt(s, pw->pw_passwd), pw->pw_passwd); +#endif /* PAM_PWD */ +#endif /* 0 */ +} + +/*------------------------------------------------------------------*\ + check if the system would be able to authentificate the user +\*------------------------------------------------------------------*/ +void checkAuth() { + +#ifdef SHADOW_PWD + struct spwd *sp; +#endif + + errno = 0; + pw = getpwuid(getuid()); + if (!pw) { + perror("password entry for uid not found"); + exit(1); + } + +#ifdef SHADOW_PWD + sp = getspnam(pw->pw_name); + if (sp) + pw->pw_passwd = sp->sp_pwdp; + endspent(); +#endif + /* we can be installed setuid root to support shadow passwords, + and we don't need root privileges any longer. --marekm */ + setuid(getuid()); +#ifndef PAM_PWD + if (strlen(pw->pw_passwd) < 13) { + perror("aklock: password entry has no pwd\n"); + exit(1); + } +#endif /* PAM_PWD */ + +} + + + +/*------------------------------------------------------------------*\ +\*------------------------------------------------------------------*/ + +struct XInfo { + + Display* display; + Window root; + Window window; + + Cursor cursor; + + int width; + int height; +}; + +struct Opts { + + char use_blank; + + char* color_fg; + char* color_bg; +}; + + +/*------------------------------------------------------------------*\ +\*------------------------------------------------------------------*/ + +void initOpts(struct Opts* opts) { + + opts->use_blank = 0; + + opts->color_bg = "steelblue3"; + opts->color_fg = "grey25"; + +} + +void initXInfo(struct XInfo* xinfo, struct Opts* opts) { + + + Display* dpy = XOpenDisplay(NULL); + XColor color_fg; + XColor color_bg; + XColor tmp_color; + Colormap color_map; + Pixmap pixmap_cursor; + Pixmap pixmap_cursor_mask; + XWindowAttributes xgwa; + + if (!dpy) { + perror("aklock: error, can't open connection to X"); + exit(1); + } + + xinfo->display = dpy; + xinfo->window = 0; + + xinfo->root = DefaultRootWindow(dpy); + color_map = DefaultColormap(dpy, DefaultScreen(dpy)); + + XGetWindowAttributes(dpy, xinfo->root, &xgwa); + + xinfo->width = xgwa.width; + xinfo->height = xgwa.height; + + pixmap_cursor = XCreateBitmapFromData(dpy, xinfo->root, lock_bits, lock_width, lock_height); + pixmap_cursor_mask = XCreateBitmapFromData(dpy, xinfo->root, mask_bits, mask_width, mask_height); + + if((XAllocNamedColor(dpy, color_map, opts->color_bg, &tmp_color, &color_bg)) == 0) + XAllocNamedColor(dpy, color_map, "black", &tmp_color, &color_bg); + if((XAllocNamedColor(dpy, color_map, opts->color_fg, &tmp_color, &color_fg)) == 0) + XAllocNamedColor(dpy, color_map, "white", &tmp_color, &color_fg); + + xinfo->cursor = XCreatePixmapCursor(dpy, + pixmap_cursor, pixmap_cursor_mask, + &color_fg, &color_bg, + lock_x_hot, lock_y_hot); + + +} + +int main(int argc, char **argv) { + + XEvent ev; + KeySym ks; + char cbuf[10], rbuf[50]; + int clen, rlen = 0; + long goodwill = INITIALGOODWILL, timeout = 0; + + XSetWindowAttributes xswa; + long xsmask = 0; + + struct XInfo xinfo; + struct Opts opts; + + int arg = 0; + + initOpts(&opts); + + // parse options + if (argc != 1) { + for(arg = 1; arg <= argc; arg++) { + if (!strcmp(argv[arg - 1], "-blank")) { + opts.use_blank = 1; + } else if (!strcmp(argv[arg - 1], "-h")) { + displayUsage(); + exit(0); + } else if (!strcmp(argv[arg - 1], "-v")) { + printf("aklock-%s by m.gumz 2005\n", VERSION); + exit(0); + } + } + } + + checkAuth(); + + initXInfo(&xinfo, &opts); + + + /* create the windows */ + xswa.override_redirect = True; + xsmask |= CWOverrideRedirect; + + if (opts.use_blank) { + + xswa.background_pixel = BlackPixel(xinfo.display, DefaultScreen(xinfo.display)); + xsmask |= CWBackPixel; + + xinfo.window = XCreateWindow(xinfo.display, xinfo.root, + 0, 0, xinfo.width, xinfo.height, + 0, /* borderwidth */ + CopyFromParent, /* depth */ + InputOutput, /* class */ + CopyFromParent, /* visual */ + xsmask, &xswa); + } else { + + xinfo.window = XCreateWindow(xinfo.display, xinfo.root, + 0, 0, 1, 1, + 0, /* borderwidth */ + CopyFromParent, /* depth */ + InputOnly, /* class */ + CopyFromParent, /* visual */ + xsmask, &xswa); + } + + XSelectInput(xinfo.display, xinfo.window, KeyPressMask|KeyReleaseMask); + XMapWindow(xinfo.display, xinfo.window); + XRaiseWindow(xinfo.display, xinfo.window); + + /* try to grab 2 times, another process (windowmanager) may have grabbed + * the keyboard already */ + if ((XGrabKeyboard(xinfo.display, xinfo.window, True, GrabModeAsync, GrabModeAsync, + CurrentTime)) != GrabSuccess) { + sleep(1); + if ((XGrabKeyboard(xinfo.display, xinfo.window, True, GrabModeAsync, GrabModeAsync, + CurrentTime)) != GrabSuccess) { + perror("aklock: couldnt grab the keyboard.\n"); + exit(1); + } + } + + if (XGrabPointer(xinfo.display, xinfo.window, False, (KeyPressMask|KeyReleaseMask) & 0, + GrabModeAsync, GrabModeAsync, None, xinfo.cursor, CurrentTime) != GrabSuccess) { + XUngrabKeyboard(xinfo.display, CurrentTime); + perror("aklock: couldnt grab the pointer.\n"); + exit(1); + } + + /* eventhandling */ + for(;;) { + XNextEvent(xinfo.display, &ev); + switch (ev.type) { + case KeyPress: + if (ev.xkey.time < timeout) { + XBell(xinfo.display, 0); + break; + } + clen = XLookupString(&ev.xkey, cbuf, 9, &ks, 0); + switch (ks) { + case XK_Escape: + case XK_Clear: + rlen = 0; + break; + case XK_Delete: + case XK_BackSpace: + if (rlen > 0) + rlen--; + break; + case XK_Linefeed: + case XK_Return: + if (rlen == 0) + break; + rbuf[rlen] = 0; + if (passwordOk(rbuf)) + goto exit; + XBell(xinfo.display, 0); + rlen = 0; + if (timeout) { + goodwill += ev.xkey.time - timeout; + if (goodwill > MAXGOODWILL) { + goodwill = MAXGOODWILL; + } + } + timeout = -goodwill * GOODWILLPORTION; + goodwill += timeout; + timeout += ev.xkey.time + TIMEOUTPERATTEMPT; + break; + default: + if (clen != 1) + break; + if (rlen < sizeof(rbuf)) + rbuf[rlen] = cbuf[0]; + rlen++; + break; + } + break; + default: + break; + } + } + +exit: + XDestroyWindow(xinfo.display, xinfo.window); + XCloseDisplay(xinfo.display); + + return 0; +} + diff --git a/aklock.man b/aklock.man new file mode 100644 index 0000000..63e6a5f --- /dev/null +++ b/aklock.man @@ -0,0 +1,54 @@ +.TH AKLOCK 1 +.SH NAME +aklock \- Lock X display until password supplied +.SH SYNOPSIS +.B aklock +.SH DESCRIPTION +.B aklock +locks the X server till the user enters their password at the keyboard. + +While +.B aklock +is running, the mouse and keyboard are grabbed and the mouse cursor +becomes a padlock. Output displayed by X programs, and windows put up +by new X clients, continue to be visible, and any new output is +displayed normally, except you run in -blank mode. Then the content +of the Screen is hidden behind a black Window. + +The mouse and keyboard are returned when the user types their +password, followed by Enter or Newline. If an incorrect password is +entered the bell is sounded. Pressing Backspace or Delete erases one +character of a password partially typed; pressing Escape or Clear +clears anything that has been entered. + +If too many attempts are made in too short a time further keystrokes +generate bells and are otherwise ignored until a timeout has expired. + +The X server screen saver continues to operate normally; if it comes +into operation the display may be restored by the usual means of +touching a key (Shift, for example) or the mouse. +.SH OPTIONS, X RESOURCES, CONFIGURATION +None. +.SH BUGS +Additional input devices other than the keyboard and mouse are not +disabled. + +The timeouts, bitmaps and mouse cursor colour cannot be modifed. +.SH SEE ALSO +.BR X "(1), " xlock "(1), Xlib Documentation." +.SH AUTHOR +Mathias Gumz +Ian Jackson +.SH COPYRIGHT +Copyright (C) 2005 Mathias Gumz +Copyright (C) 1993, 1994 by Ian Jackson. + +Permission to use, copy, modify and distribute this software and its +documentation is granted under the terms of the GNU General Public +Licence, version 2 or later, as published by the Free Software +Foundation. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. diff --git a/lock.bitmap b/lock.bitmap new file mode 100644 index 0000000..f953bc5 --- /dev/null +++ b/lock.bitmap @@ -0,0 +1,19 @@ +#define lock_width 28 +#define lock_height 40 +#define lock_x_hot 14 +#define lock_y_hot 21 +static char lock_bits[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xf8, 0xff, 0x7f, 0x00, 0xe0, 0xff, + 0x3f, 0x00, 0xc0, 0xff, 0x1f, 0x00, 0x80, 0xff, 0x0f, 0xfc, 0x03, 0xff, + 0x0f, 0xfe, 0x07, 0xff, 0x0f, 0xff, 0x0f, 0xff, 0x07, 0xff, 0x0f, 0xfe, + 0x87, 0xff, 0x1f, 0xfe, 0x87, 0xff, 0x1f, 0xfe, 0x87, 0xff, 0x1f, 0xfe, + 0x87, 0xff, 0x1f, 0xfe, 0x87, 0xff, 0x1f, 0xfe, 0x87, 0xff, 0x1f, 0xfe, + 0x87, 0xff, 0x1f, 0xfe, 0x87, 0xff, 0x1f, 0xfe, 0x87, 0xff, 0x1f, 0xfe, + 0x87, 0xff, 0x1f, 0xfe, 0x01, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0xf8, + 0x01, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0xf8, 0x01, 0xf0, 0x00, 0xf8, + 0x01, 0xf8, 0x01, 0xf8, 0x01, 0xf8, 0x01, 0xf8, 0x01, 0xf8, 0x01, 0xf8, + 0x01, 0xf8, 0x01, 0xf8, 0x01, 0xf0, 0x00, 0xf8, 0x01, 0x60, 0x00, 0xf8, + 0x01, 0x60, 0x00, 0xf8, 0x01, 0x60, 0x00, 0xf8, 0x01, 0x60, 0x00, 0xf8, + 0x01, 0x60, 0x00, 0xf8, 0x01, 0x60, 0x00, 0xf8, 0x01, 0x00, 0x00, 0xf8, + 0x01, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0xf8, + 0xff, 0xff, 0xff, 0xff}; diff --git a/mask.bitmap b/mask.bitmap new file mode 100644 index 0000000..96bb4d8 --- /dev/null +++ b/mask.bitmap @@ -0,0 +1,19 @@ +#define mask_width 28 +#define mask_height 40 +#define mask_x_hot 14 +#define mask_y_hot 21 +static char mask_bits[] = { + 0x00, 0xfe, 0x07, 0x00, 0x80, 0xff, 0x1f, 0x00, 0xc0, 0xff, 0x3f, 0x00, + 0xe0, 0xff, 0x7f, 0x00, 0xf0, 0xff, 0xff, 0x00, 0xf8, 0xff, 0xff, 0x01, + 0xf8, 0x03, 0xfc, 0x01, 0xf8, 0x01, 0xf8, 0x01, 0xfc, 0x01, 0xf8, 0x03, + 0xfc, 0x00, 0xf0, 0x03, 0xfc, 0x00, 0xf0, 0x03, 0xfc, 0x00, 0xf0, 0x03, + 0xfc, 0x00, 0xf0, 0x03, 0xfc, 0x00, 0xf0, 0x03, 0xfc, 0x00, 0xf0, 0x03, + 0xfc, 0x00, 0xf0, 0x03, 0xfc, 0x00, 0xf0, 0x03, 0xfc, 0x00, 0xf0, 0x03, + 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x0f, + 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x0f, + 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x0f, + 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x0f, + 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x0f, + 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x0f, + 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x0f, 0xff, 0xff, 0xff, 0x0f, + 0xff, 0xff, 0xff, 0x0f};