You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
xrdp-proprietary/uirdesktop/uimain.c

1129 lines
25 KiB

19 years ago
/* -*- c-basic-offset: 8 -*-
rdesktop: A Remote Desktop Protocol client.
Main ui file
Copyright (C) Jay Sorg 2006
This program 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 of the License, or
(at your option) any later version.
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "rdesktop.h"
#include "bsops.h"
#include "uimain.h"
char g_username[256] = "";
char g_hostname[256] = "";
char g_servername[256] = "";
char g_password[256] = "";
char g_shell[256] = "";
char g_directory[256] = "";
char g_domain[256] = "";
char * g_bs = 0; /* created in hardware file, used in bsops.c */
int g_bs_Bpp = 4;
int g_bs_bpp = 32;
BOOL g_desktop_save = False; /* desktop save order */
BOOL g_polygon_ellipse_orders = False; /* polygon / ellipse orders */
BOOL g_bitmap_compression = True;
uint32 g_rdp5_performanceflags =
RDP5_NO_WALLPAPER | RDP5_NO_FULLWINDOWDRAG | RDP5_NO_MENUANIMATIONS;
BOOL g_bitmap_cache_persist_enable = False;
BOOL g_bitmap_cache_precache = True;
BOOL g_bitmap_cache = True;
BOOL g_encryption = True;
int g_server_depth = 8;
BOOL g_use_rdp5 = False;
int g_width = 800;
int g_height = 600;
uint32 g_keylayout = 0x409; /* Defaults to US keyboard layout */
int g_keyboard_type = 0x4; /* Defaults to US keyboard layout */
int g_keyboard_subtype = 0x0; /* Defaults to US keyboard layout */
int g_keyboard_functionkeys = 0xc; /* Defaults to US keyboard layout */
BOOL g_console_session = False;
/* can't be static, hardware file or bsops need these */
int g_tcp_sck = 0;
int pal_entries[256];
/* Session Directory redirection */
BOOL g_redirect = False;
char g_redirect_server[64];
char g_redirect_domain[16];
char g_redirect_password[64];
char g_redirect_username[64];
char g_redirect_cookie[128];
uint32 g_redirect_flags = 0;
extern int g_tcp_port_rdp;
static int g_deactivated = 0;
static uint32 g_ext_disc_reason = 0;
struct bitmap
{
uint8 * data;
uint32 width;
uint32 height;
};
/* in ui specific file eg win32.c, qt.c, dfb.c, ... */
int
mi_create_window(void);
void
mi_invalidate(int x, int y, int cx, int cy);
int
mi_create_bs(void);
int
mi_main_loop(void);
void
mi_error(char * msg);
void
mi_warning(char * msg);
void
ui_invalidate(int x, int y, int cx, int cy);
void
mi_paint_rect(char * data, int width, int height, int x, int y, int cx, int cy);
void
mi_begin_update(void);
void
mi_end_update(void);
void
mi_fill_rect(int x, int y, int cx, int cy, int colour);
void
mi_screen_copy(int x, int y, int cx, int cy, int srcx, int srcy);
void
mi_set_clip(int x, int y, int cx, int cy);
void
mi_reset_clip(void);
void
mi_line(int x1, int y1, int x2, int y2, int colour);
mi_create_cursor(unsigned int x, unsigned int y,
int width, int height,
unsigned char * andmask, unsigned char * xormask);
void
mi_destroy_cursor(void * cursor);
void
mi_set_cursor(void * cursor);
void
mi_set_null_cursor(void);
int
mi_read_keyboard_state(void);
/*****************************************************************************/
/* put part of the screen from the backing store to the display */
void
ui_invalidate(int x, int y, int cx, int cy)
{
if (cx < 1 || cy < 1 || g_bs == 0)
{
return;
}
if (bs_warp_coords(&x, &y, &cx, &cy, 0, 0))
{
mi_invalidate(x, y, cx, cy);
}
/*
if (bs_warp_coords(&x, &y, &cx, &cy, 0, 0))
{
cx = (cx + 3) & ~3;
data = (char *) xmalloc(cx * cy * 4);
bs_copy_box(data, x, y, cx, cy, cx * ((g_server_depth + 7) / 8));
mi_paint_rect(data, cx, cy, x, y, cx, cy);
xfree(data);
}
*/
}
/*****************************************************************************/
static int
convert_colour(int in_colour)
{
int r;
int g;
int b;
if (g_server_depth == 8)
{
r = (pal_entries[in_colour & 0xff] & 0xff0000) >> 16;
g = (pal_entries[in_colour & 0xff] & 0xff00) >> 8;
b = pal_entries[in_colour & 0xff] & 0xff;
}
else if (g_server_depth == 15)
{
SPLIT_COLOUR15(in_colour, r, g, b);
}
else if (g_server_depth == 16)
{
SPLIT_COLOUR16(in_colour, r, g, b);
}
else if (g_server_depth == 24)
{
SPLIT_COLOUR32(in_colour, r, g, b);
}
if (g_bs_bpp == 32)
{
MAKE_COLOUR32(in_colour, r, g, b);
}
return in_colour;
}
/*****************************************************************************/
void
ui_bell(void)
{
}
/*****************************************************************************/
int
ui_select(int in)
{
if (g_tcp_sck == 0)
{
g_tcp_sck = in;
}
return 1;
}
/*****************************************************************************/
void *
ui_create_cursor(uint32 x, uint32 y,
int width, int height,
uint8 * andmask, uint8 * xormask)
{
int i;
int j;
char am[32 * 4];
char xm[32 * 4];
if (width != 32 || height != 32)
{
return 0;
}
memset(am, 0, 32 * 4);
memset(xm, 0, 32 * 4);
for (i = 0; i < 32; i++)
{
for (j = 0; j < 32; j++)
{
if (bs_is_pixel_on(andmask, j, i, 32, 1))
{
bs_set_pixel_on(am, j, 31 - i, 32, 1, 1);
}
if (bs_is_pixel_on(xormask, j, i, 32, 24))
{
bs_set_pixel_on(xm, j, 31 - i, 32, 1, 1);
}
}
}
return (void *) mi_create_cursor(x, y, width, height, am, xm);
}
/*****************************************************************************/
void
ui_destroy_cursor(void * cursor)
{
mi_destroy_cursor(cursor);
}
/*****************************************************************************/
void
ui_set_cursor(void * cursor)
{
mi_set_cursor(cursor);
}
/*****************************************************************************/
void
ui_set_null_cursor(void)
{
mi_set_null_cursor();
}
/*****************************************************************************/
void *
ui_create_glyph(int width, int height, uint8 * data)
{
int i;
int j;
char * glyph_data;
struct bitmap * the_glyph;
glyph_data = (char *) xmalloc(width * height);
memset(glyph_data, 0, width * height);
the_glyph = (struct bitmap *) xmalloc(sizeof(struct bitmap));
memset(the_glyph, 0, sizeof(struct bitmap));
the_glyph->width = width;
the_glyph->height = height;
the_glyph->data = glyph_data;
for (i = 0; i < height; i++)
{
for (j = 0; j < width; j++)
{
if (bs_is_pixel_on(data, j, i, width, 1))
{
bs_set_pixel_on(glyph_data, j, i, width, 8, 255);
}
}
}
return the_glyph;
}
/*****************************************************************************/
void
ui_destroy_glyph(void * glyph)
{
struct bitmap * the_glyph;
the_glyph = glyph;
if (the_glyph != 0)
{
xfree(the_glyph->data);
}
xfree(the_glyph);
}
/*****************************************************************************/
void *
ui_create_bitmap(int width, int height, uint8 * data)
{
#if 0
struct bitmap * b;
int size;
size = width * height * ((g_server_depth + 7) / 8);
b = (struct bitmap *) xmalloc(sizeof(struct bitmap));
b->data = (uint8 *) xmalloc(size);
memcpy(b->data, data, size);
b->width = width;
b->height = height;
return b;
#endif
return 0;
}
/*****************************************************************************/
void *
ui_create_bitmap_ex(int width, int height, uint8 * data, int data_size,
int compressed)
{
struct bitmap * b;
int size;
int i;
int j;
int pixel;
int red;
int green;
int blue;
unsigned short * s16;
unsigned char * s8;
unsigned int * d32;
size = width * height * 4;
b = (struct bitmap *) xmalloc(sizeof(struct bitmap));
b->data = (uint8 *) xmalloc(size);
if (compressed)
{
bitmap_decompress_ex(b->data, width, height, data, data_size,
g_server_depth, 32);
}
else
{
if (g_server_depth == 16 && g_bs_bpp == 32)
{
for (i = 0; i < height; i++)
{
s16 = ((unsigned short *) data) + (i * width);
d32 = ((unsigned int *) b->data) + (height - i - 1) * width;
for (j = 0; j < width; j++)
{
pixel = *s16;
s16++;
SPLIT_COLOUR16(pixel, red, green, blue);
MAKE_COLOUR32(pixel, red, green, blue);
*d32 = pixel;
d32++;
}
}
}
else if (g_server_depth == 24 && g_bs_bpp == 32)
{
for (i = 0; i < height; i++)
{
s8 = ((unsigned char *) data) + (i * width * 3);
d32 = ((unsigned int *) b->data) + (height - i - 1) * width;
for (j = 0; j < width; j++)
{
blue = *s8;
s8++;
green = *s8;
s8++;
red = *s8;
s8++;
MAKE_COLOUR32(pixel, red, green, blue);
*d32 = pixel;
d32++;
}
}
}
}
b->width = width;
b->height = height;
return b;
}
/*****************************************************************************/
void
ui_destroy_bitmap(void * bmp)
{
struct bitmap * b;
b = (struct bitmap *) bmp;
if (b != 0)
{
xfree(b->data);
}
xfree(b);
}
/*****************************************************************************/
void
ui_paint_bitmap(int x, int y, int cx, int cy,
int width, int height, uint8 * data)
{
/* struct bitmap b;
b.width = width;
b.height = height;
b.data = data;
ui_memblt(12, x, y, cx, cy, &b, 0, 0); */
}
/*****************************************************************************/
void
ui_paint_bitmap_ex(int x, int y, int cx, int cy,
int width, int height, uint8 * data, int data_size,
int compressed)
{
struct bitmap * b;
b = ui_create_bitmap_ex(width, height, data, data_size, compressed);
ui_memblt(12, x, y, cx, cy, b, 0, 0);
ui_destroy_bitmap(b);
}
/*****************************************************************************/
void
ui_set_clip(int x, int y, int cx, int cy)
{
bs_set_clip(x, y, cx, cy);
mi_set_clip(x, y, cx, cy);
}
/*****************************************************************************/
void
ui_reset_clip(void)
{
bs_reset_clip();
mi_reset_clip();
}
/*****************************************************************************/
void *
ui_create_colourmap(COLOURMAP * colours)
{
int i;
int n;
n = MIN(256, colours->ncolours);
memset(pal_entries, 0, sizeof(pal_entries));
for (i = 0; i < n; i++)
{
pal_entries[i] = (colours->colours[i].red << 16) |
(colours->colours[i].green << 8) |
colours->colours[i].blue;
}
return 0;
}
/*****************************************************************************/
void
ui_set_colourmap(void * map)
{
}
/*****************************************************************************/
/* don't convert colour here */
static void
draw_glyph(int x, int y, void * glyph, int fgcolor)
{
struct bitmap * b;
b = glyph;
bs_draw_glyph(x, y, b->data, b->width, b->height, fgcolor);
}
/*****************************************************************************/
#define DO_GLYPH(ttext,idx) \
{ \
glyph = cache_get_font(font, ttext[idx]); \
if (!(flags & TEXT2_IMPLICIT_X)) \
{ \
xyoffset = ttext[++idx]; \
if (xyoffset & 0x80) \
{ \
if (flags & TEXT2_VERTICAL) \
{ \
y += ttext[idx + 1] | (ttext[idx + 2] << 8); \
} \
else \
{ \
x += ttext[idx + 1] | (ttext[idx + 2] << 8); \
} \
idx += 2; \
} \
else \
{ \
if (flags & TEXT2_VERTICAL) \
{ \
y += xyoffset; \
} \
else \
{ \
x += xyoffset; \
} \
} \
} \
if (glyph != NULL) \
{ \
draw_glyph(x + glyph->offset, y + glyph->baseline, glyph->pixmap, \
fgcolour); \
if (flags & TEXT2_IMPLICIT_X) \
{ \
x += glyph->width; \
} \
} \
}
/*****************************************************************************/
void
ui_draw_text(uint8 font, uint8 flags, uint8 opcode, int mixmode,
int x, int y,
int clipx, int clipy, int clipcx, int clipcy,
int boxx, int boxy, int boxcx, int boxcy, BRUSH * brush,
int bgcolour, int fgcolour, uint8 * text, uint8 length)
{
int i;
int j;
int xyoffset;
DATABLOB * entry;
FONTGLYPH * glyph;
fgcolour = convert_colour(fgcolour);
bgcolour = convert_colour(bgcolour);
if (boxx + boxcx > g_width)
{
boxcx = g_width - boxx;
}
if (boxcx > 1)
{
bs_rect(boxx, boxy, boxcx, boxcy, bgcolour, 0xc);
}
else
{
if (mixmode == MIX_OPAQUE)
{
bs_rect(clipx, clipy, clipcx, clipcy, bgcolour, 0xc);
}
}
/* Paint text, character by character */
for (i = 0; i < length;)
{
switch (text[i])
{
case 0xff:
if (i + 2 < length)
{
cache_put_text(text[i + 1], text, text[i + 2]);
}
else
{
error("this shouldn't be happening\n");
exit(1);
}
/* this will move pointer from start to first character after */
/* FF command */
length -= i + 3;
text = &(text[i + 3]);
i = 0;
break;
case 0xfe:
entry = cache_get_text(text[i + 1]);
if (entry != NULL)
{
if ((((uint8 *) (entry->data))[1] == 0) &&
(!(flags & TEXT2_IMPLICIT_X)))
{
if (flags & TEXT2_VERTICAL)
{
y += text[i + 2];
}
else
{
x += text[i + 2];
}
}
for (j = 0; j < entry->size; j++)
{
DO_GLYPH(((uint8 *) (entry->data)), j);
}
}
if (i + 2 < length)
{
i += 3;
}
else
{
i += 2;
}
length -= i;
/* this will move pointer from start to first character after */
/* FE command */
text = &(text[i]);
i = 0;
break;
default:
DO_GLYPH(text, i);
i++;
break;
}
}
if (boxcx > 1)
{
ui_invalidate(boxx, boxy, boxcx, boxcy);
}
else
{
ui_invalidate(clipx, clipy, clipcx, clipcy);
}
}
/*****************************************************************************/
void
ui_line(uint8 opcode, int startx, int starty, int endx, int endy,
PEN * pen)
{
int x;
int y;
int cx;
int cy;
int colour;
colour = convert_colour(pen->colour);
bs_line(opcode, startx, starty, endx, endy, pen->width, pen->style,
colour);
if (pen->style == 0 && pen->width < 2 && opcode == 12)
{
mi_line(startx, starty, endx, endy, colour);
}
else
{
x = MIN(startx, endx);
y = MIN(starty, endy);
cx = (MAX(startx, endx) + 1) - x;
cy = (MAX(starty, endy) + 1) - y;
ui_invalidate(x, y, cx, cy);
}
}
/*****************************************************************************/
void
ui_triblt(uint8 opcode, int x, int y, int cx, int cy,
void * src, int srcx, int srcy,
BRUSH* brush, int bgcolour, int fgcolour)
{
/* not used */
}
/*****************************************************************************/
void
ui_memblt(uint8 opcode, int x, int y, int cx, int cy,
void * src, int srcx, int srcy)
{
struct bitmap* b;
b = (struct bitmap *) src;
bs_memblt(opcode, x, y, cx, cy, b->data, b->width, b->height,
srcx, srcy);
ui_invalidate(x, y, cx, cy);
}
/*****************************************************************************/
void
ui_desktop_restore(uint32 offset, int x, int y, int cx, int cy)
{
}
/*****************************************************************************/
void
ui_desktop_save(uint32 offset, int x, int y, int cx, int cy)
{
}
/*****************************************************************************/
void
ui_rect(int x, int y, int cx, int cy, int colour)
{
colour = convert_colour(colour);
bs_rect(x, y, cx, cy, colour, 12);
mi_fill_rect(x, y, cx, cy, colour);
}
/*****************************************************************************/
void
ui_screenblt(uint8 opcode, int x, int y, int cx, int cy,
int srcx, int srcy)
{
bs_screenblt(opcode, x, y, cx, cy, srcx, srcy);
if (opcode == 12)
{
mi_screen_copy(x, y, cx, cy, srcx, srcy);
}
else
{
ui_invalidate(x, y, cx, cy);
}
}
/*****************************************************************************/
void
ui_patblt(uint8 opcode, int x, int y, int cx, int cy,
BRUSH * brush, int bgcolour, int fgcolour)
{
bgcolour = convert_colour(bgcolour);
fgcolour = convert_colour(fgcolour);
bs_patblt(opcode, x, y, cx, cy, brush->style, brush->pattern,
brush->xorigin, brush->yorigin, bgcolour, fgcolour);
ui_invalidate(x, y, cx, cy);
}
/*****************************************************************************/
void
ui_destblt(uint8 opcode, int x, int y, int cx, int cy)
{
bs_rect(x, y, cx, cy, 0, opcode);
ui_invalidate(x, y, cx, cy);
/* todo */
}
/*****************************************************************************/
void
ui_move_pointer(int x, int y)
{
}
/*****************************************************************************/
uint16
ui_get_numlock_state(uint32 state)
{
return (uint16) state;
}
/*****************************************************************************/
/* get the num, caps, and scroll lock state */
/* scroll lock is 1, num lock is 2 and caps lock is 4 */
/* just returning 0, the hardware specific file is responsable for this */
uint32
read_keyboard_state(void)
{
return (uint32) mi_read_keyboard_state();
}
/*****************************************************************************/
void
ui_set_modifier_state(int code)
{
//error("%8.8x", code);
rdp_send_input(0, RDP_INPUT_SYNCHRONIZE, 0, (uint16) code, 0);
}
/*****************************************************************************/
void
ui_resize_window(void)
{
}
/*****************************************************************************/
void
ui_begin_update(void)
{
mi_begin_update();
}
/*****************************************************************************/
void
ui_end_update(void)
{
mi_end_update();
}
/*****************************************************************************/
void
ui_polygon(uint8 opcode, uint8 fillmode, POINT * point, int npoints,
BRUSH * brush, int bgcolour, int fgcolour)
{
/* not used */
}
/*****************************************************************************/
void
ui_polyline(uint8 opcode, POINT * points, int npoints, PEN * pen)
{
int i, x, y, dx, dy;
if (npoints > 0)
{
x = points[0].x;
y = points[0].y;
for (i = 1; i < npoints; i++)
{
dx = points[i].x;
dy = points[i].y;
ui_line(opcode, x, y, x + dx, y + dy, pen);
x = x + dx;
y = y + dy;
}
}
}
/*****************************************************************************/
void
ui_ellipse(uint8 opcode, uint8 fillmode,
int x, int y, int cx, int cy,
BRUSH * brush, int bgcolour, int fgcolour)
{
/* not used */
}
/*****************************************************************************/
/* get a 32 byte random */
void
generate_random(uint8 * random)
{
int i;
rand();
rand();
for (i = 0; i < 32; i++)
{
random[i] = rand() >> 16; /* higher bits are more random */
}
}
/*****************************************************************************/
void
save_licence(uint8 * data, int length)
{
}
/*****************************************************************************/
int
load_licence(uint8 ** data)
{
return 0;
}
/*****************************************************************************/
void *
xrealloc(void * in, int size)
{
if (size < 1)
{
size = 1;
}
return realloc(in, size);
}
/*****************************************************************************/
void *
xmalloc(int size)
{
if (size < 1)
{
size = 1;
}
return malloc(size);
}
/*****************************************************************************/
void
xfree(void * in)
{
if (in != 0)
{
free(in);
}
}
/*****************************************************************************/
char *
xstrdup(const char * s)
{
int len;
char * p;
if (s == 0)
{
return 0;
}
len = strlen(s);
p = (char *) xmalloc(len + 1);
strcpy(p, s);
return p;
}
/*****************************************************************************/
void
warning(char * format, ...)
{
va_list ap;
char text[512];
char text1[512];
sprintf(text1, "WARNING: ");
va_start(ap, format);
vsprintf(text, format, ap);
va_end(ap);
strcat(text1, text);
mi_warning(text1);
}
/*****************************************************************************/
void
unimpl(char * format, ...)
{
va_list ap;
char text[512];
char text1[512];
sprintf(text1, "UNIMPL: ");
va_start(ap, format);
vsprintf(text, format, ap);
va_end(ap);
strcat(text1, text);
mi_warning(text1);
}
/*****************************************************************************/
void
error(char * format, ...)
{
va_list ap;
char text[512];
char text1[512];
sprintf(text1, "ERROR: ");
va_start(ap, format);
vsprintf(text, format, ap);
va_end(ap);
strcat(text1, text);
mi_error(text1);
}
/*****************************************************************************/
BOOL
rd_pstcache_mkdir(void)
{
return 0;
}
/*****************************************************************************/
int
rd_open_file(char * filename)
{
return 0;
}
/*****************************************************************************/
void
rd_close_file(int fd)
{
return;
}
/*****************************************************************************/
int
rd_read_file(int fd, void * ptr, int len)
{
return 0;
}
/*****************************************************************************/
int
rd_write_file(int fd, void * ptr, int len)
{
return 0;
}
/*****************************************************************************/
int
rd_lseek_file(int fd, int offset)
{
return 0;
}
/*****************************************************************************/
BOOL
rd_lock_file(int fd, int start, int len)
{
return False;
}
/*****************************************************************************/
void
ui_mouse_move(int x, int y)
{
rdp_send_input(0, RDP_INPUT_MOUSE, MOUSE_FLAG_MOVE, (uint16) x, (uint16) y);
}
/*****************************************************************************/
void
ui_mouse_button(int button, int x, int y, int down)
{
uint16 flags;
flags = 0;
if (down)
{
flags |= MOUSE_FLAG_DOWN;
}
switch (button)
{
case 1:
flags |= MOUSE_FLAG_BUTTON1;
break;
case 2:
flags |= MOUSE_FLAG_BUTTON2;
break;
case 3:
flags |= MOUSE_FLAG_BUTTON3;
break;
case 4:
flags |= MOUSE_FLAG_BUTTON4;
break;
case 5:
flags |= MOUSE_FLAG_BUTTON5;
break;
}
rdp_send_input(0, RDP_INPUT_MOUSE, flags, (uint16) x, (uint16) y);
}
/*****************************************************************************/
void
ui_key_down(int key, int ext)
{
rdp_send_input(0, RDP_INPUT_SCANCODE, (uint16) (RDP_KEYPRESS | ext),
(uint16) key, 0);
}
/*****************************************************************************/
void
ui_key_up(int key, int ext)
{
rdp_send_input(0, RDP_INPUT_SCANCODE, (uint16) (RDP_KEYRELEASE | ext),
(uint16) key, 0);
}
/*****************************************************************************/
/* returns boolean, non zero is good */
int
ui_read_wire(void)
{
return rdp_loop(&g_deactivated, &g_ext_disc_reason);
}
/*****************************************************************************/
/* called after the command line parameters are processed */
/* returns boolean, non zero is ok */
int
ui_main(void)
{
uint32 flags;
/* try to connect */
flags = RDP_LOGON_NORMAL;
if (g_password[0] != 0)
{
flags |= RDP_LOGON_AUTO;
}
if (!rdp_connect(g_servername, flags, g_domain, g_password,
g_shell, g_directory))
{
return 0;
}
/* create the window */
if (!mi_create_window())
{
error("mi_create_window failed\r\n");
return 0;
}
/* create backingstore stuff for use in bsops.c */
if (!mi_create_bs())
{
error("mi_create_bs failed\r\n");
return 0;
}
/* init backingstore */
bs_init();
/* if all ok, enter main loop */
return mi_main_loop();
}
/*****************************************************************************/
/* produce a hex dump */
void
hexdump(uint8 * p, uint32 len)
{
uint8 * line = p;
int i, thisline, offset = 0;
while (offset < (int)len)
{
printf("%04x ", offset);
thisline = len - offset;
if (thisline > 16)
thisline = 16;
for (i = 0; i < thisline; i++)
printf("%02x ", line[i]);
for (; i < 16; i++)
printf(" ");
for (i = 0; i < thisline; i++)
printf("%c", (line[i] >= 0x20 && line[i] < 0x7f) ? line[i] : '.');
printf("\n");
offset += thisline;
line += thisline;
}
}