home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
rexx
/
library2
/
gbmrexx
/
gbm
/
gbmbmp.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-09-12
|
18KB
|
873 lines
/*
GBMBMP.C OS/2 1.1, 1.2, 2.0 and Windows 3.0 support
Reads and writes any OS/2 1.x bitmap.
Will also read uncompressed, RLE4 and RLE8 Windows 3.x bitmaps too.
There are horrific file structure alignment considerations hence each
word,dword is read individually.
Input options: index=# (default: 0)
*/
/*...sincludes:0:*/
#include <stdio.h>
#include <ctype.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <memory.h>
#include <malloc.h>
#ifdef AIX
#include <unistd.h>
#else
#include <io.h>
#endif
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "standard.h"
#include "gbm.h"
/*...vgbm\46\h:0:*/
#ifndef min
#define min(a,b) (((a)<(b))?(a):(b))
#endif
/*...e*/
/*...suseful:0:*/
#define low_byte(w) ((byte) ((w)&0x00ff) )
#define high_byte(w) ((byte) (((w)&0xff00)>>8))
#define make_word(a,b) (((word)a) + (((word)b) << 8))
/*...e*/
/*...ssame:0:*/
static BOOLEAN same(char *s1, char *s2, int n)
{
for ( ; n--; s1++, s2++ )
if ( tolower(*s1) != tolower(*s2) )
return ( FALSE );
return ( TRUE );
}
/*...e*/
/*...sfind_word:0:*/
static char *find_word(char *str, char *substr)
{
char buf [100+1], *s;
int len = strlen(substr);
for ( s = strtok(strcpy(buf, str), " \t,");
s != NULL;
s = strtok(NULL, " \t,") )
if ( same(s, substr, len) && s [len] == '\0' )
return ( str + (s - buf) );
return ( NULL );
}
/*...e*/
/*...sfind_word_prefix:0:*/
static char *find_word_prefix(char *str, char *substr)
{
char buf [100+1], *s;
int len = strlen(substr);
for ( s = strtok(strcpy(buf, str), " \t,");
s != NULL;
s = strtok(NULL, " \t,") )
if ( same(s, substr, len) )
return ( str + (s - buf) );
return ( NULL );
}
/*...e*/
/*...sread_word:0:*/
static BOOLEAN read_word(int fd, word *w)
{
byte low = 0, high = 0;
read(fd, (char *) &low, 1);
read(fd, (char *) &high, 1);
*w = (word) (low + ((word) high << 8));
return ( TRUE );
}
/*...e*/
/*...sread_dword:0:*/
static BOOLEAN read_dword(int fd, dword *d)
{
word low, high;
read_word(fd, &low);
read_word(fd, &high);
*d = low + ((dword) high << 16);
return ( TRUE );
}
/*...e*/
/*...swrite_word:0:*/
static BOOLEAN write_word(int fd, word w)
{
byte low = (byte) w;
byte high = (byte) (w >> 8);
write(fd, &low, 1);
write(fd, &high, 1);
return ( TRUE );
}
/*...e*/
/*...swrite_dword:0:*/
static BOOLEAN write_dword(int fd, dword d)
{
write_word(fd, (word) d);
write_word(fd, (word) (d >> 16));
return ( TRUE );
}
/*...e*/
/*...sreading ahead:0:*/
#define AHEAD_BUF 0x4000
typedef struct
{
byte buf [AHEAD_BUF];
int inx, cnt;
int fd;
} AHEAD;
static AHEAD *create_ahead(int fd)
{
AHEAD *ahead;
if ( (ahead = malloc(sizeof(AHEAD))) == NULL )
return ( NULL );
ahead -> inx = 0;
ahead -> cnt = 0;
ahead -> fd = fd;
return ( ahead );
}
static void destroy_ahead(AHEAD *ahead)
{
free(ahead);
}
static byte next(AHEAD *ahead)
{
if ( ahead -> inx >= ahead -> cnt )
{
ahead -> cnt = read(ahead -> fd, (char *) ahead -> buf, AHEAD_BUF);
ahead -> inx = 0;
}
return ( ahead -> buf [ahead -> inx++] );
}
/*...e*/
static GBMFT bmp_gbmft =
{
"Bitmap",
"OS/2 1.1, 1.2, 2.0 / Windows 3.0 bitmap",
"BMP VGA BGA RLE DIB RL4 RL8",
GBM_FT_R1|GBM_FT_R4|GBM_FT_R8|GBM_FT_R24|
GBM_FT_W1|GBM_FT_W4|GBM_FT_W8|GBM_FT_W24,
};
#define GBM_ERR_BMP_PLANES ((GBM_ERR) 300)
#define GBM_ERR_BMP_BITCOUNT ((GBM_ERR) 301)
#define GBM_ERR_BMP_CBFIX ((GBM_ERR) 302)
#define GBM_ERR_BMP_COMP ((GBM_ERR) 303)
#define GBM_ERR_BMP_OFFSET ((GBM_ERR) 304)
typedef struct
{
dword base;
BOOLEAN windows;
dword cbFix;
dword ulCompression;
dword cclrUsed;
dword offBits;
BOOLEAN inv, invb;
} BMP_PRIV;
#define BFT_BMAP 0x4d42
#define BFT_BITMAPARRAY 0x4142
#define BCA_UNCOMP 0x00000000L
#define BCA_RLE8 0x00000001L
#define BCA_RLE4 0x00000002L
#define BCA_HUFFFMAN1D 0x00000003L
#define BCA_RLE24 0x00000004L
#define MSWCC_EOL 0
#define MSWCC_EOB 1
#define MSWCC_DELTA 2
/*...sinvert:0:*/
static void invert(byte *buffer, unsigned count)
{
while ( count-- )
*buffer++ ^= (byte) 0xff;
}
/*...e*/
/*...sswap_pal:0:*/
static void swap_pal(GBMRGB *gbmrgb)
{
GBMRGB tmp = gbmrgb [0];
gbmrgb [0] = gbmrgb [1];
gbmrgb [1] = tmp;
}
/*...e*/
/*...sbmp_qft:0:*/
GBM_ERR bmp_qft(GBMFT *gbmft)
{
*gbmft = bmp_gbmft;
return ( GBM_ERR_OK );
}
/*...e*/
/*...sbmp_rhdr:0:*/
GBM_ERR bmp_rhdr(char *fn, int fd, GBM *gbm, char *opt)
{
word usType, xHotspot, yHotspot;
dword cbSize, offBits, cbFix;
BMP_PRIV *bmp_priv = (BMP_PRIV *) gbm -> priv;
bmp_priv -> inv = ( find_word(opt, "inv" ) != NULL );
bmp_priv -> invb = ( find_word(opt, "invb") != NULL );
fn=fn; /* Suppress 'unref arg' compiler warnings */
lseek(fd, 0L, SEEK_SET);
read_word(fd, &usType);
if ( usType == BFT_BITMAPARRAY )
/*...shandle bitmap arrays:16:*/
{
char *index;
int i;
if ( (index = find_word_prefix(opt, "index=")) != NULL )
sscanf(index + 6, "%d", &i);
else
i = 0;
while ( i-- > 0 )
{
dword cbSize2, offNext;
read_dword(fd, &cbSize2);
read_dword(fd, &offNext);
if ( offNext == 0L )
return ( GBM_ERR_BMP_OFFSET );
lseek(fd, (long) offNext, SEEK_SET);
read_word(fd, &usType);
if ( usType != BFT_BITMAPARRAY )
return ( GBM_ERR_BAD_MAGIC );
}
lseek(fd, 4L + 4L + 2L + 2L, SEEK_CUR);
read_word(fd, &usType);
}
/*...e*/
if ( usType != BFT_BMAP )
return ( GBM_ERR_BAD_MAGIC );
bmp_priv -> base = lseek(fd, 0L, SEEK_CUR) - 2L;
read_dword(fd, &cbSize);
read_word(fd, &xHotspot);
read_word(fd, &yHotspot);
read_dword(fd, &offBits);
read_dword(fd, &cbFix);
bmp_priv -> offBits = offBits;
if ( cbFix == 12 )
/*...sOS\47\2 1\46\1\44\ 1\46\2:16:*/
/* OS/2 1.x uncompressed bitmap */
{
word cx, cy, cPlanes, cBitCount;
read_word(fd, &cx);
read_word(fd, &cy);
read_word(fd, &cPlanes);
read_word(fd, &cBitCount);
if ( cx == 0 || cy == 0 )
return ( GBM_ERR_BAD_SIZE );
if ( cPlanes != 1 )
return ( GBM_ERR_BMP_PLANES );
if ( cBitCount != 1 && cBitCount != 4 && cBitCount != 8 && cBitCount != 24 )
return ( GBM_ERR_BMP_BITCOUNT );
gbm -> w = (int) cx;
gbm -> h = (int) cy;
gbm -> bpp = (int) cBitCount;
bmp_priv -> windows = FALSE;
}
/*...e*/
else if ( cbFix >= 16 && cbFix <= 64 &&
((cbFix & 3) == 0 || cbFix == 42 || cbFix == 46) )
/*...sOS\47\2 2\46\0 and Windows 3\46\0:16:*/
{
word cPlanes, cBitCount, usUnits, usReserved, usRecording, usRendering;
dword ulWidth, ulHeight, ulCompression;
dword ulSizeImage, ulXPelsPerMeter, ulYPelsPerMeter;
dword cclrUsed, cclrImportant, cSize1, cSize2, ulColorEncoding, ulIdentifier;
read_dword(fd, &ulWidth);
read_dword(fd, &ulHeight);
read_word(fd, &cPlanes);
read_word(fd, &cBitCount);
if ( cbFix > 16 )
read_dword(fd, &ulCompression);
else
ulCompression = BCA_UNCOMP;
if ( cbFix > 20 )
read_dword(fd, &ulSizeImage);
if ( cbFix > 24 )
read_dword(fd, &ulXPelsPerMeter);
if ( cbFix > 28 )
read_dword(fd, &ulYPelsPerMeter);
if ( cbFix > 32 )
read_dword(fd, &cclrUsed);
else
cclrUsed = ( 1 << cBitCount );
if ( cBitCount != 24 && cclrUsed == 0 )
cclrUsed = ( 1 << cBitCount );
if ( cbFix > 36 )
read_dword(fd, &cclrImportant);
if ( cbFix > 40 )
read_word(fd, &usUnits);
if ( cbFix > 42 )
read_word(fd, &usReserved);
if ( cbFix > 44 )
read_word(fd, &usRecording);
if ( cbFix > 46 )
read_word(fd, &usRendering);
if ( cbFix > 48 )
read_dword(fd, &cSize1);
if ( cbFix > 52 )
read_dword(fd, &cSize2);
if ( cbFix > 56 )
read_dword(fd, &ulColorEncoding);
if ( cbFix > 60 )
read_dword(fd, &ulIdentifier);
if ( ulWidth == 0L || ulHeight == 0L )
return ( GBM_ERR_BAD_SIZE );
if ( cPlanes != 1 )
return ( GBM_ERR_BMP_PLANES );
if ( cBitCount != 1 && cBitCount != 4 && cBitCount != 8 && cBitCount != 24 )
return ( GBM_ERR_BMP_BITCOUNT );
gbm -> w = (int) ulWidth;
gbm -> h = (int) ulHeight;
gbm -> bpp = (int) cBitCount;
bmp_priv -> windows = TRUE;
bmp_priv -> cbFix = cbFix;
bmp_priv -> ulCompression = ulCompression;
bmp_priv -> cclrUsed = cclrUsed;
}
/*...e*/
else
return ( GBM_ERR_BMP_CBFIX );
return ( GBM_ERR_OK );
}
/*...e*/
/*...sbmp_rpal:0:*/
GBM_ERR bmp_rpal(int fd, GBM *gbm, GBMRGB *gbmrgb)
{
BMP_PRIV *bmp_priv = (BMP_PRIV *) gbm -> priv;
if ( gbm -> bpp != 24 )
{
int i;
byte b [4];
if ( bmp_priv -> windows )
/*...sOS\47\2 2\46\0 and Windows 3\46\0:24:*/
{
lseek(fd, bmp_priv -> base + 14L + bmp_priv -> cbFix, SEEK_SET);
for ( i = 0; i < (int) bmp_priv -> cclrUsed; i++ )
{
read(fd, b, 4);
gbmrgb [i].b = b [0];
gbmrgb [i].g = b [1];
gbmrgb [i].r = b [2];
}
}
/*...e*/
else
/*...sOS\47\2 1\46\1\44\ 1\46\2:24:*/
{
lseek(fd, bmp_priv -> base + 26L, SEEK_SET);
for ( i = 0; i < (1 << gbm -> bpp); i++ )
{
read(fd, b, 3);
gbmrgb [i].b = b [0];
gbmrgb [i].g = b [1];
gbmrgb [i].r = b [2];
}
}
/*...e*/
}
if ( gbm -> bpp == 1 && !bmp_priv -> inv )
swap_pal(gbmrgb);
return ( GBM_ERR_OK );
}
/*...e*/
/*...sbmp_rdata:0:*/
GBM_ERR bmp_rdata(int fd, GBM *gbm, byte *data)
{
BMP_PRIV *bmp_priv = (BMP_PRIV *) gbm -> priv;
int cLinesWorth = ((gbm -> bpp * gbm -> w + 31) / 32) * 4;
if ( bmp_priv -> windows )
/*...sOS\47\2 2\46\0 and Windows 3\46\0:16:*/
{
lseek(fd, bmp_priv -> offBits, SEEK_SET);
switch ( (int) bmp_priv -> ulCompression )
{
/*...sBCA_UNCOMP:24:*/
case BCA_UNCOMP:
read(fd, data, gbm -> h * cLinesWorth);
break;
/*...e*/
/*...sBCA_RLE8:24:*/
case BCA_RLE8:
{
AHEAD *ahead;
int stride = ((gbm -> w + 3) & ~3);
int x = 0, y = 0;
BOOLEAN eof8 = FALSE;
if ( (ahead = create_ahead(fd)) == NULL )
return ( GBM_ERR_MEM );
while ( !eof8 )
{
byte c = next(ahead);
byte d = next(ahead);
if ( c )
{
memset(data, d, c);
x += c;
data += c;
}
else
switch ( d )
{
/*...sMSWCC_EOL:56:*/
case MSWCC_EOL:
{
int to_eol = stride - x;
memset(data, 0, to_eol);
x = 0; y++;
data += to_eol;
}
break;
/*...e*/
/*...sMSWCC_EOB:56:*/
case MSWCC_EOB:
if ( y < gbm -> h )
{
int to_eol = stride - x;
memset(data, 0, to_eol);
x = 0; y++;
data += to_eol;
while ( y < gbm -> h )
{
memset(data, 0, stride);
data += stride;
y++;
}
}
eof8 = TRUE;
break;
/*...e*/
/*...sMSWCC_DELTA:56:*/
case MSWCC_DELTA:
{
byte dx = next(ahead);
byte dy = next(ahead);
int fill = dx + dy * stride;
x += dx; y += dy;
memset(data, 0, fill);
data += fill;
}
break;
/*...e*/
/*...sdefault:56:*/
default:
{
int n = (int) d;
while ( n-- > 0 )
*data++ = next(ahead);
x += d;
if ( d & 1 )
next(ahead); /* Align */
}
break;
/*...e*/
}
}
destroy_ahead(ahead);
}
break;
/*...e*/
/*...sBCA_RLE4:24:*/
case BCA_RLE4:
{
AHEAD *ahead;
int x = 0, y = 0;
BOOLEAN eof4 = FALSE;
int inx = 0;
if ( (ahead = create_ahead(fd)) == NULL )
return ( GBM_ERR_MEM );
memset(data, 0, gbm -> h * cLinesWorth);
while ( !eof4 )
{
byte c = next(ahead);
byte d = next(ahead);
if ( c )
{
byte h, l;
int i;
if ( x & 1 )
{
h = (byte) (d >> 4); l = (byte) (d << 4);
}
else
{
h = (byte) (d & 0xf0); l = (byte) (d & 0x0f);
}
for ( i = 0; i < (int) c; i++, x++ )
if ( x & 1 )
data [inx++] |= l;
else
data [inx] |= h;
}
else
switch ( d )
{
/*...sMSWCC_EOL:56:*/
case MSWCC_EOL:
for ( ; x < gbm -> w; x++ )
if ( x & 1 )
inx++;
x = 0; y++;
inx = ((inx + 3) & ~3); /* Align output */
break;
/*...e*/
/*...sMSWCC_EOB:56:*/
case MSWCC_EOB:
eof4 = TRUE;
break;
/*...e*/
/*...sMSWCC_DELTA:56:*/
case MSWCC_DELTA:
{
byte dx = next(ahead);
byte dy = next(ahead);
y += dy;
inx += dy * cLinesWorth;
if ( dx > 0 )
{
if ( x & 1 )
{
inx++;
x++;
dx--;
}
inx += (dx / 2);
x += dx;
}
}
break;
/*...e*/
/*...sdefault:56:*/
default:
{
int i, nr = 0;
if ( x & 1 )
{
for ( i = 0; i < (int) d; i += 2 )
{
byte b = next(ahead);
data [inx++] |= (b >> 4);
data [inx ] |= (b << 4);
nr++;
}
if ( i < (int) d )
{
data [inx++] |= (next(ahead) >> 4);
nr++;
}
}
else
{
for ( i = 0; i < (int) d; i += 2 )
{
data [inx++] = next(ahead);
nr++;
}
if ( i < (int) d )
{
data [inx] = next(ahead);
nr++;
}
}
x += d;
if ( nr & 1 )
next(ahead); /* Align input stream to next word */
}
break;
/*...e*/
}
}
destroy_ahead(ahead);
}
break;
/*...e*/
/*...sdefault:24:*/
default:
return ( GBM_ERR_BMP_COMP );
/*...e*/
}
}
/*...e*/
else
/*...sOS\47\2 1\46\1\44\ 1\46\2:16:*/
{
lseek(fd, bmp_priv -> offBits, SEEK_SET);
read(fd, data, cLinesWorth * gbm -> h);
}
/*...e*/
if ( bmp_priv -> invb )
invert(data, cLinesWorth * gbm -> h);
return ( GBM_ERR_OK );
}
/*...e*/
/*...sbmp_w:0:*/
/*...swrite_inv:0:*/
static unsigned write_inv(int fd, char *buffer, unsigned count)
{
char small_buf [1024];
unsigned so_far = 0, this_go, written;
while ( so_far < count )
{
this_go = min(count - so_far, 1024);
memcpy(small_buf, buffer + so_far, this_go);
invert(small_buf, this_go);
if ( (written = write(fd, small_buf, this_go)) != this_go )
return ( so_far + written );
so_far += written;
}
return ( so_far );
}
/*...e*/
GBM_ERR bmp_w(char *fn, int fd, GBM *gbm, GBMRGB *gbmrgb, byte *data, char *opt)
{
BOOLEAN pm11 = ( find_word(opt, "1.1" ) != NULL );
BOOLEAN win = ( find_word(opt, "win" ) != NULL ||
find_word(opt, "2.0" ) != NULL );
BOOLEAN inv = ( find_word(opt, "inv" ) != NULL );
BOOLEAN invb = ( find_word(opt, "invb") != NULL );
int cRGB;
GBMRGB gbmrgb_1bpp [2];
if ( pm11 && win )
return ( GBM_ERR_BAD_OPTION );
fn=fn; /* Suppress 'unref arg' compiler warning */
cRGB = ( (1 << gbm -> bpp) & 0x1ff );
/* 1 -> 2, 4 -> 16, 8 -> 256, 24 -> 0 */
if ( cRGB == 2 )
/*...shandle messy 1bpp case:16:*/
{
/*
The palette entries inside a 1bpp PM bitmap are not honored, or handled
correctly by most programs. Current thinking is that they have no actual
meaning. Under OS/2 PM, bitmap 1's re fg and 0's are bg, and it is the job of
the displayer to pick fg and bg. We will pick fg=black, bg=white in the bitmap
file we save. If we do not write black and white, we find that most programs
will incorrectly honor these entries giving unpredicatable (and often black on
a black background!) results.
*/
gbmrgb_1bpp [0].r = gbmrgb_1bpp [0].g = gbmrgb_1bpp [0].b = 0xff;
gbmrgb_1bpp [1].r = gbmrgb_1bpp [1].g = gbmrgb_1bpp [1].b = 0x00;
/*
We observe these values must be the wrong way around to keep most PM
programs happy, such as WorkPlace Shell WPFolder backgrounds.
*/
if ( !inv )
swap_pal(gbmrgb_1bpp);
gbmrgb = gbmrgb_1bpp;
}
/*...e*/
if ( pm11 )
/*...sOS\47\2 1\46\1:16:*/
{
word usType, xHotspot, yHotspot, cx, cy, cPlanes, cBitCount;
dword cbSize, offBits, cbFix;
int cLinesWorth, i;
usType = BFT_BMAP;
xHotspot = 0;
yHotspot = 0;
cbFix = 12L;
cx = gbm -> w;
cy = gbm -> h;
cPlanes = 1;
cBitCount = gbm -> bpp;
cLinesWorth = (((cBitCount * cx + 31) / 32) * cPlanes) * 4;
offBits = 26L + cRGB * 3L;
cbSize = offBits + (dword) cy * (dword) cLinesWorth;
write_word(fd, usType);
write_dword(fd, cbSize);
write_word(fd, xHotspot);
write_word(fd, yHotspot);
write_dword(fd, offBits);
write_dword(fd, cbFix);
write_word(fd, cx);
write_word(fd, cy);
write_word(fd, cPlanes);
write_word(fd, cBitCount);
for ( i = 0; i < cRGB; i++ )
{
byte b [3];
b [0] = gbmrgb [i].b;
b [1] = gbmrgb [i].g;
b [2] = gbmrgb [i].r;
write(fd, b, 3);
}
if ( invb )
write_inv(fd, data, gbm -> h * cLinesWorth);
else
write(fd, data, gbm -> h * cLinesWorth);
}
/*...e*/
else
/*...sOS\47\2 2\46\0 and Windows 3\46\0:16:*/
{
word usType, xHotspot, yHotspot, cPlanes, cBitCount;
dword cx, cy, cbSize, offBits, cbFix, ulCompression, cbImage;
dword cxResolution, cyResolution, cclrUsed, cclrImportant;
int cLinesWorth, i;
usType = BFT_BMAP;
xHotspot = 0;
yHotspot = 0;
cbFix = 40L;
cx = gbm -> w;
cy = gbm -> h;
cPlanes = 1;
cBitCount = gbm -> bpp;
cLinesWorth = (((cBitCount * (int) cx + 31) / 32) * cPlanes) * 4;
offBits = 54L + cRGB * 4L;
cbSize = offBits + (dword) cy * (dword) cLinesWorth;
ulCompression = BCA_UNCOMP;
cbImage = cLinesWorth * gbm -> h;
cxResolution = 0;
cyResolution = 0;
cclrUsed = 0;
cclrImportant = 0;
write_word(fd, usType);
write_dword(fd, cbSize);
write_word(fd, xHotspot);
write_word(fd, yHotspot);
write_dword(fd, offBits);
write_dword(fd, cbFix);
write_dword(fd, cx);
write_dword(fd, cy);
write_word(fd, cPlanes);
write_word(fd, cBitCount);
write_dword(fd, ulCompression);
write_dword(fd, cbImage);
write_dword(fd, cxResolution);
write_dword(fd, cyResolution);
write_dword(fd, cclrUsed);
write_dword(fd, cclrImportant);
for ( i = 0; i < cRGB; i++ )
{
byte b [4];
b [0] = gbmrgb [i].b;
b [1] = gbmrgb [i].g;
b [2] = gbmrgb [i].r;
b [3] = 0;
write(fd, b, 4);
}
if ( invb )
write_inv(fd, data, gbm -> h * cLinesWorth);
else
write(fd, data, gbm -> h * cLinesWorth);
}
/*...e*/
return ( GBM_ERR_OK );
}
/*...e*/
/*...sbmp_err:0:*/
char *bmp_err(GBM_ERR rc)
{
switch ( (int) rc )
{
case GBM_ERR_BMP_PLANES:
return ( "number of bitmap planes is not 1" );
case GBM_ERR_BMP_BITCOUNT:
return ( "bit count not 1, 4, 8 or 24" );
case GBM_ERR_BMP_CBFIX:
return ( "cbFix bad" );
case GBM_ERR_BMP_COMP:
return ( "compression type not uncompressed, RLE4 or RLE8" );
case GBM_ERR_BMP_OFFSET:
return ( "less bitmaps in file than index requested" );
}
return ( NULL );
}
/*...e*/