552 lines
14 KiB
C++
552 lines
14 KiB
C++
|
|
/*
|
|
* CWIN3.1 and ETEN format file i/o
|
|
*
|
|
* Copyright (c) 1997-1999 Microsoft Corporation.
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
#pragma pack(2)
|
|
|
|
|
|
#include "extfunc.h"
|
|
#include "eten.h"
|
|
|
|
#define EUDCCODEBASE ((unsigned short)0xe000)
|
|
#define ETENBANKID 0x8001
|
|
/*
|
|
static unsigned short getsval(unsigned char *s);
|
|
static unsigned long getlval(unsigned char *l);
|
|
static void setsval(unsigned char *m,unsigned short s);
|
|
static void setlval(unsigned char *m,unsigned long lval);
|
|
static int readHdr(HANDLE fhdl,struct ETENHEADER *hdr);
|
|
static int updHdr(void);
|
|
static int getETENBankID( HANDLE fh, WORD *BankID);
|
|
int openETENBMP(TCHAR *path,int md);
|
|
int closeETENBMP(void);
|
|
static void setIniHdr(struct R_ETENHEADER *hdr,int width,int height);
|
|
int createETENBMP(TCHAR *path,int wid,int hei);
|
|
int getETENBMPInf(int *n, int *ng, int *wid,int *hei, char *sign,WORD *bID);
|
|
int readETENBMPRec(int rec,LPBYTE buf,int bufsiz,unsigned short *code);
|
|
int appendETENBMP(LPBYTE buf,unsigned short code);
|
|
int isETENBMP(TCHAR *path);
|
|
*/
|
|
static HANDLE bmpFh ;
|
|
static int openmd = -1;
|
|
static int nChar;
|
|
static int width, height;
|
|
static int *recordBuf=0;
|
|
int uNum=0;
|
|
static int *codep=0;
|
|
static char UserFontSign[8];
|
|
static WORD BankID;
|
|
|
|
static unsigned short
|
|
getsval( unsigned char *s)
|
|
{
|
|
unsigned short sval;
|
|
|
|
sval = (unsigned short )*(s+1);
|
|
sval <<=8;
|
|
sval |= (unsigned short )*s;
|
|
return sval;
|
|
}
|
|
static unsigned long
|
|
getlval( unsigned char *l)
|
|
{
|
|
unsigned long lval;
|
|
int i;
|
|
|
|
lval = (unsigned long)*(l+3);
|
|
for ( i=2; i>=0; i--) {
|
|
lval<<=8;
|
|
lval |=(unsigned long)*(l+i);
|
|
}
|
|
return lval;
|
|
}
|
|
static void
|
|
setsval( unsigned char *m, unsigned short s)
|
|
{
|
|
*m = (unsigned char)(s & 0xff);
|
|
*(m+1) = (unsigned char)((s>>8) & 0xff);
|
|
}
|
|
static void
|
|
setlval( unsigned char *m, unsigned long lval)
|
|
{
|
|
int i;
|
|
|
|
for ( i=0; i<4; i++) {
|
|
*m++ = (unsigned char)(lval & 0xff);
|
|
lval>>=8;
|
|
}
|
|
}
|
|
static int
|
|
readHdr( HANDLE fhdl, struct ETENHEADER *hdr)
|
|
{
|
|
struct R_ETENHEADER rhdr;
|
|
DWORD nByte;
|
|
BOOL res;
|
|
|
|
res = ReadFile( fhdl, &rhdr, 256, &nByte, NULL);
|
|
if (!res || nByte !=256)
|
|
goto ERET;
|
|
|
|
memset( hdr, 0, sizeof(struct ETENHEADER));
|
|
|
|
/* Set values to hdr */
|
|
hdr->uHeaderSize = getsval( rhdr.uHeaderSize);
|
|
memcpy( hdr->idUserFontSign, rhdr.idUserFontSign, 8);
|
|
hdr->idMajor = rhdr.idMajor;
|
|
hdr->idMinor = rhdr.idMinor;
|
|
hdr->ulCharCount = getlval( rhdr.ulCharCount);
|
|
hdr->uCharWidth = getsval( rhdr.uCharWidth);
|
|
hdr->uCharHeight = getsval( rhdr.uCharHeight);
|
|
hdr->cPatternSize = getlval( rhdr.cPatternSize);
|
|
hdr->uchBankID = rhdr.uchBankID;
|
|
hdr->idInternalBankID = getsval( rhdr.idInternalBankID);
|
|
hdr->sFontInfo.uInfoSize = getsval(rhdr.sFontInfo.uInfoSize);
|
|
hdr->sFontInfo.idCP = getsval(rhdr.sFontInfo.idCP);
|
|
hdr->sFontInfo.idCharSet = rhdr.sFontInfo.idCharSet;
|
|
hdr->sFontInfo.fbTypeFace = rhdr.sFontInfo.fbTypeFace;
|
|
memcpy( hdr->sFontInfo.achFontName , rhdr.sFontInfo.achFontName,12);
|
|
hdr->sFontInfo.ulCharDefine = getlval(rhdr.sFontInfo.ulCharDefine);
|
|
hdr->sFontInfo.uCellWidth = getsval(rhdr.sFontInfo.uCellWidth);
|
|
hdr->sFontInfo.uCellHeight = getsval(rhdr.sFontInfo.uCellHeight);
|
|
hdr->sFontInfo.uCharHeight = getsval(rhdr.sFontInfo.uCharHeight);
|
|
hdr->sFontInfo.uBaseLine = getsval(rhdr.sFontInfo.uBaseLine);
|
|
hdr->sFontInfo.uUnderLine = getsval(rhdr.sFontInfo.uUnderLine);
|
|
hdr->sFontInfo.uUnlnHeight = getsval(rhdr.sFontInfo.uUnlnHeight);
|
|
hdr->sFontInfo.fchStrokeWeight = rhdr.sFontInfo.fchStrokeWeight;
|
|
hdr->sFontInfo.fCharStyle = getsval(rhdr.sFontInfo.fCharStyle);
|
|
hdr->sFontInfo.fbFontAttrib = rhdr.sFontInfo.fbFontAttrib;
|
|
hdr->sFontInfo.ulCellWidthMax = getlval(rhdr.sFontInfo.ulCellWidthMax);
|
|
hdr->sFontInfo.ulCellHeightMax= getlval(rhdr.sFontInfo.ulCellHeightMax);
|
|
return 0;
|
|
ERET:
|
|
return -1;
|
|
}
|
|
static int
|
|
updHdr( )
|
|
{
|
|
struct R_ETENHEADER rhdr;
|
|
DWORD nByte;
|
|
BOOL res;
|
|
|
|
if ( (long) SetFilePointer( bmpFh, 0L, NULL, FILE_BEGIN)!=0L)
|
|
goto ERET;
|
|
res = ReadFile( bmpFh, &rhdr, 256, &nByte, NULL);
|
|
if (!res || nByte !=256)
|
|
goto ERET;
|
|
|
|
setlval( rhdr.ulCharCount, (long)nChar);
|
|
setlval( rhdr.sFontInfo.ulCharDefine, (long)nChar);
|
|
if ( (long) SetFilePointer( bmpFh, 0L, NULL, FILE_BEGIN)!=0L)
|
|
goto ERET;
|
|
res = WriteFile(bmpFh, (char *)&rhdr, 256,&nByte, NULL);
|
|
if (!res || nByte !=256)
|
|
goto ERET;
|
|
return 0;
|
|
ERET:
|
|
return -1;
|
|
}
|
|
static int
|
|
getETENBankID( HANDLE fh, WORD *BankID)
|
|
{
|
|
struct R_CODEELEMENT cElm;
|
|
long ofs;
|
|
DWORD nByte;
|
|
BOOL res;
|
|
|
|
ofs = sizeof(struct R_ETENHEADER);
|
|
if ((long) SetFilePointer( fh, ofs, NULL, FILE_BEGIN) != ofs)
|
|
goto ERET;
|
|
|
|
res = ReadFile( fh, &cElm, sizeof(struct R_CODEELEMENT), &nByte, NULL);
|
|
if (!res || nByte !=sizeof(struct R_CODEELEMENT))
|
|
goto ERET;
|
|
*BankID = getsval( cElm.nBankID);
|
|
return 0;
|
|
ERET:
|
|
return -1;
|
|
}
|
|
/***
|
|
recBuf
|
|
+-------+
|
|
| rec# | E000
|
|
+-------+
|
|
| | E001
|
|
+-------+
|
|
|
|
|
+-------+
|
|
| | maxUCode
|
|
+-------+
|
|
****/
|
|
static int
|
|
scanETENBMP( int **recBuf, unsigned int maxUCode, int nRec)
|
|
{
|
|
long ofs;
|
|
int recsiz, bmpsiz;
|
|
struct R_CODEELEMENT *bhd;
|
|
int rec;
|
|
char *rbuf;
|
|
unsigned short code;
|
|
unsigned short ucode;
|
|
int urec;
|
|
int *recp;
|
|
DWORD nByte;
|
|
BOOL res;
|
|
|
|
recp = 0;
|
|
rbuf = 0;
|
|
if ( maxUCode < EUDCCODEBASE)
|
|
return -1;
|
|
else if ( nRec <=0)
|
|
return -1;
|
|
uNum = maxUCode - EUDCCODEBASE+1;
|
|
if ( (codep = (int *)malloc( uNum*sizeof(int)))==0)
|
|
goto ERET;
|
|
|
|
ofs = sizeof( struct R_ETENHEADER);
|
|
if ( (long) SetFilePointer( bmpFh, ofs, NULL, FILE_BEGIN)!=ofs)
|
|
goto ERET;
|
|
bmpsiz = (width+7)/8*height;
|
|
recsiz =bmpsiz+sizeof (bhd);
|
|
if ((rbuf = (char *)malloc( recsiz))==(char *)0)
|
|
goto ERET;
|
|
for ( code = EUDCCODEBASE; code <= maxUCode; code++)
|
|
codep[code-EUDCCODEBASE] = -1;
|
|
bhd = (struct R_CODEELEMENT *)rbuf;
|
|
|
|
for ( rec = 0; rec < nRec; rec++) {
|
|
res = ReadFile( bmpFh, rbuf, (unsigned int)recsiz, &nByte, NULL);
|
|
if (!res || nByte !=(unsigned int)recsiz)
|
|
goto ERET;
|
|
code = getsval( bhd->nInternalCode);
|
|
if( memcmp(UserFontSign, "CMEX_PTN", 8) ||
|
|
BankID != ETENBANKID){
|
|
ucode = sjisToUniEUDC( code);
|
|
}else ucode = code;
|
|
|
|
if( ucode > maxUCode || ucode < EUDCCODEBASE)
|
|
continue;
|
|
urec = (int)(ucode - EUDCCODEBASE);
|
|
codep[urec] = rec;
|
|
}
|
|
free( rbuf);
|
|
|
|
if ( (recp = (int *)malloc( nRec*sizeof(int)))==0)
|
|
goto ERET;
|
|
|
|
*recBuf=recp;
|
|
for ( rec=0; rec < uNum; rec++) {
|
|
if ( codep[rec]>0)
|
|
*recp++ = codep[rec];
|
|
}
|
|
return 0;
|
|
ERET:
|
|
if ( codep) free( codep);
|
|
if ( recp) free( recp);
|
|
if ( rbuf) free( rbuf);
|
|
return -1;
|
|
}
|
|
int
|
|
openETENBMP( TCHAR *path, int md)
|
|
{
|
|
HANDLE fh;
|
|
struct ETENHEADER hdr;
|
|
|
|
makeUniCodeTbl();
|
|
if ( md) {
|
|
fh = CreateFile(path,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if ( fh == INVALID_HANDLE_VALUE)
|
|
goto ERET;
|
|
bmpFh = fh;
|
|
openmd = 1;
|
|
}
|
|
else {
|
|
fh = CreateFile(path,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if ( fh == INVALID_HANDLE_VALUE)
|
|
goto ERET;
|
|
bmpFh = fh;
|
|
openmd = 0;
|
|
}
|
|
if (readHdr( fh, &hdr))
|
|
goto ERET;
|
|
if (getETENBankID( fh, &BankID))
|
|
goto ERET;
|
|
|
|
nChar = (int)hdr.ulCharCount;
|
|
width = (int)hdr.uCharWidth;
|
|
height = (int)hdr.uCharHeight;
|
|
memcpy((char *)UserFontSign, hdr.idUserFontSign, 8);
|
|
|
|
if ( scanETENBMP( &recordBuf, getMaxUniCode(), nChar))
|
|
goto ERET;
|
|
|
|
return 0;
|
|
ERET:
|
|
if (fh != INVALID_HANDLE_VALUE)
|
|
CloseHandle(fh);
|
|
return -1;
|
|
}
|
|
int
|
|
closeETENBMP( )
|
|
{
|
|
int sts;
|
|
if ( openmd)
|
|
sts = updHdr();
|
|
else sts = 0;
|
|
if ( bmpFh!=INVALID_HANDLE_VALUE)
|
|
CloseHandle( bmpFh);
|
|
if(recordBuf) {
|
|
free(recordBuf);
|
|
recordBuf = 0;
|
|
}
|
|
if ( codep) {
|
|
free(codep);
|
|
codep = 0;
|
|
}
|
|
return sts;
|
|
}
|
|
static void
|
|
setIniHdr( struct R_ETENHEADER *hdr, int width, int height)
|
|
{
|
|
memset( hdr, 0, sizeof(struct R_ETENHEADER));
|
|
setsval( hdr->uHeaderSize, sizeof(struct R_ETENHEADER));
|
|
memcpy( hdr->idUserFontSign, "CWIN_PTN", 8);
|
|
hdr->idMajor = 1;
|
|
hdr->idMinor = 0;
|
|
setsval( hdr->uCharWidth, (unsigned short)width);
|
|
setsval( hdr->uCharHeight, (unsigned short)height);
|
|
setlval( hdr->cPatternSize, (unsigned long)(((width+7)/8)*height));
|
|
setsval( hdr->sFontInfo.uInfoSize,
|
|
(unsigned short)sizeof(struct R_CFONTINFO));
|
|
setsval( hdr->sFontInfo.idCP, 938);
|
|
hdr->sFontInfo.idCharSet = (char)0x88;
|
|
setsval( hdr->sFontInfo.uCellWidth, (unsigned short)width);
|
|
setsval( hdr->sFontInfo.uCellHeight, (unsigned short)height);
|
|
setsval( hdr->sFontInfo.uCharHeight, (unsigned short)height);
|
|
setlval( hdr->sFontInfo.ulCellWidthMax, (unsigned long)width);
|
|
setlval( hdr->sFontInfo.ulCellHeightMax, (unsigned long)height);
|
|
}
|
|
int
|
|
createETENBMP( TCHAR *path, int wid, int hei)
|
|
{
|
|
HANDLE fh;
|
|
struct R_ETENHEADER hdr;
|
|
DWORD nByte;
|
|
BOOL res;
|
|
|
|
fh = CreateFile(path,
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if ( fh == INVALID_HANDLE_VALUE)
|
|
goto ERET;
|
|
|
|
width = wid;
|
|
height = hei;
|
|
setIniHdr( &hdr, width, height);
|
|
res = WriteFile( fh, (char *)&hdr, sizeof(hdr), &nByte, NULL);
|
|
if (!res || nByte !=sizeof(hdr))
|
|
goto ERET;
|
|
bmpFh = fh;
|
|
openmd = 1;
|
|
nChar =0;
|
|
return 0;
|
|
ERET:
|
|
if (fh != INVALID_HANDLE_VALUE)
|
|
CloseHandle(fh);
|
|
return -1;
|
|
}
|
|
int
|
|
getETENBMPInf( int *nRec, int *nGlyph, int *wid, int *hei, char *sign,WORD *bID)
|
|
{
|
|
if ( bmpFh <0) return -1;
|
|
*nRec = uNum;
|
|
*nGlyph = nChar;
|
|
*wid = width;
|
|
*hei = height;
|
|
*bID = BankID;
|
|
memcpy( sign, UserFontSign, 8);
|
|
return 0;
|
|
}
|
|
int
|
|
readETENBMPRec( int rec, LPBYTE buf, int bufsiz, unsigned short *code)
|
|
{
|
|
long ofs;
|
|
int recsiz;
|
|
struct R_CODEELEMENT bhd;
|
|
int rdsiz;
|
|
int bWid, wWid;
|
|
int y, ylim;
|
|
unsigned char *rbuf;
|
|
DWORD nByte;
|
|
BOOL res;
|
|
|
|
bWid = (width+7)/8;
|
|
wWid = (bWid+1)/2*2;
|
|
recsiz = (width+7)/8*height;
|
|
ofs = sizeof( struct R_ETENHEADER)+(long)(recsiz+sizeof (bhd))*rec;
|
|
if ( (long) SetFilePointer( bmpFh, ofs, NULL, FILE_BEGIN)!=ofs)
|
|
goto ERET;
|
|
res = ReadFile( bmpFh, &bhd, sizeof(bhd), &nByte, NULL);
|
|
if (!res || nByte !=sizeof(bhd))
|
|
goto ERET;
|
|
if ( bufsiz<recsiz) rdsiz = bufsiz;
|
|
else rdsiz = recsiz;
|
|
|
|
if ( bWid!=wWid) {
|
|
BYTE *src, *dst;
|
|
if ((rbuf = (unsigned char *)malloc( recsiz))==(unsigned char *)0)
|
|
goto ERET;
|
|
res = ReadFile( bmpFh, (char *)rbuf, (unsigned int)recsiz, &nByte, NULL);
|
|
if (!res || nByte !=(unsigned int)recsiz) {
|
|
free(rbuf);
|
|
goto ERET;
|
|
}
|
|
ylim = rdsiz / bWid;
|
|
src = (LPBYTE)rbuf;
|
|
dst = buf;
|
|
memset( buf, 0xff, rdsiz);
|
|
for ( y = 0; y < ylim; y++, src+=bWid, dst+=wWid)
|
|
memcpy(dst , src , bWid);
|
|
|
|
free( rbuf);
|
|
}
|
|
else {
|
|
res = ReadFile( bmpFh, (char *)buf, (unsigned int)rdsiz, &nByte, NULL);
|
|
if (!res || nByte !=(unsigned int)rdsiz)
|
|
goto ERET;
|
|
}
|
|
|
|
*code = getsval( bhd.nInternalCode);
|
|
return 0;
|
|
ERET:
|
|
return -1;
|
|
}
|
|
int
|
|
appendETENBMP( LPBYTE buf, unsigned short code)
|
|
{
|
|
struct R_CODEELEMENT bhd;
|
|
int bmpsiz;
|
|
DWORD nByte;
|
|
BOOL res;
|
|
|
|
SetFilePointer( bmpFh, 0L, NULL, FILE_END);
|
|
bmpsiz = (width+7)/8*height;
|
|
setsval( bhd.nBankID, 1);
|
|
setsval( bhd.nInternalCode, code);
|
|
res = WriteFile( bmpFh, (LPBYTE)(&bhd), sizeof( bhd), &nByte, NULL);
|
|
if (!res || nByte !=sizeof(bhd))
|
|
goto ERET;
|
|
res = WriteFile( bmpFh, (LPBYTE)buf, (unsigned int)bmpsiz, &nByte, NULL);
|
|
if (!res || nByte != (unsigned int)bmpsiz)
|
|
goto ERET;
|
|
|
|
nChar++;
|
|
return 0;
|
|
ERET:
|
|
return -1;
|
|
}
|
|
int
|
|
isETENBMP(TCHAR *path)
|
|
{
|
|
struct ETENHEADER hdr;
|
|
HANDLE fhdl;
|
|
fhdl = CreateFile(path,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if ( fhdl == INVALID_HANDLE_VALUE)
|
|
return -1;
|
|
if ( readHdr( fhdl, &hdr)) {
|
|
CloseHandle( fhdl);
|
|
return -1;
|
|
}
|
|
CloseHandle( fhdl);
|
|
/* check Header size and keyWord*/
|
|
if ( hdr.uHeaderSize != sizeof(struct R_ETENHEADER))
|
|
goto NO_ETEN;
|
|
|
|
if ( memcmp(hdr.idUserFontSign, "CWIN_PTN", 8) &&
|
|
memcmp(hdr.idUserFontSign, "CMEX_PTN", 8))
|
|
goto NO_ETEN;
|
|
return 1;
|
|
NO_ETEN:
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ETENrecTbl( int **recTbl)
|
|
{
|
|
*recTbl=codep;
|
|
return 0;
|
|
}
|
|
/* EOF */
|
|
/* For test +/
|
|
static int
|
|
dispHdr( struct ETENHEADER *hdr)
|
|
{
|
|
printf("hdr->uHeaderSize= %d\n", hdr->uHeaderSize );
|
|
printf("hdr->idMajor %d\n", hdr->idMajor );
|
|
printf("hdr->idMinor %d\n", hdr->idMinor );
|
|
printf("hdr->ulCharCout %ld\n", hdr->ulCharCount );
|
|
printf("hdr->uCharWidth %d\n", hdr->uCharWidth );
|
|
printf(" hdr->uCharHeight %d\n", hdr->uCharHeight );
|
|
printf(" hdr->cPatternSize %d\n", hdr->cPatternSize);
|
|
printf("hdr->uchBankID %d\n", hdr->uchBankID);
|
|
printf("hdr->idInternalBankID %d\n", hdr->idInternalBankID);
|
|
printf("hdr->sFontInfo.uInfoSize %d\n", hdr->sFontInfo.uInfoSize);
|
|
printf("hdr->sFontInfo.idCP %d\n", hdr->sFontInfo.idCP);
|
|
printf("hdr->sFontInfo.idCharSet %d\n", hdr->sFontInfo.idCharSet);
|
|
printf("hdr->sFontInfo.fbTypeFace %d\n", hdr->sFontInfo.fbTypeFace);
|
|
printf("hdr->sFontInfo.ulCharDefine %ld\n", hdr->sFontInfo.ulCharDefine);
|
|
printf("hdr->sFontInfo.uCellWidth %d\n", hdr->sFontInfo.uCellWidth);
|
|
printf("hdr->sFontInfo.uCellHeight %d\n", hdr->sFontInfo.uCellHeight);
|
|
printf("hdr->sFontInfo.uCharHeight %d\n", hdr->sFontInfo.uCharHeight);
|
|
printf("hdr->sFontInfo.uBaseLine %d\n", hdr->sFontInfo.uBaseLine);
|
|
printf("hdr->sFontInfo.uUnderLine %d\n", hdr->sFontInfo.uUnderLine);
|
|
printf("hdr->sFontInfo.uUnlnHeight %d\n", hdr->sFontInfo.uUnlnHeight);
|
|
printf("hdr->sFontInfo.fchStrokeWeight %d\n", hdr->sFontInfo.fchStrokeWeight);
|
|
printf("hdr->sFontInfo.fCharStyle %d\n", hdr->sFontInfo.fCharStyle);
|
|
printf("hdr->sFontInfo.fbFontAttrib %d\n", hdr->sFontInfo.fbFontAttrib);
|
|
printf("hdr->sFontInfo.ulCellWidthMax %ld\n", hdr->sFontInfo.ulCellWidthMax);
|
|
printf("hdr->sFontInfo.ulCellHeightMax %ld\n", hdr->sFontInfo.ulCellHeightMax);
|
|
}
|
|
main( int argc, char *argv[])
|
|
{
|
|
int fh;
|
|
struct ETENHEADER hdr;
|
|
fh = _lopen( argv[1], O_RDONLY | O_BINARY);
|
|
|
|
readHdr( fh, &hdr);
|
|
dispHdr( &hdr);
|
|
_lclose(fh);
|
|
exit(0);
|
|
}
|
|
/+ */
|
|
/* EOF */
|