Commit 456c9189 authored by Libretro-Admin's avatar Libretro-Admin
Browse files

Use libretro VFS

parent 1e9b9ff1
Pipeline #64782 failed with stages
in 2 minutes and 15 seconds
......@@ -44,7 +44,7 @@ static const int Volumes[16] =
/** Reset the sound chip and use sound channels from the **/
/** one given in First. **/
/*************************************************************/
void Reset8910(register AY8910 *D,int ClockHz,int First)
void Reset8910(AY8910 *D,int ClockHz,int First)
{
static byte RegInit[16] =
{
......@@ -108,9 +108,9 @@ byte RdData8910(AY8910 *D)
/** Call this function to output a value V into the sound **/
/** chip. **/
/*************************************************************/
void Write8910(register AY8910 *D,register byte R,register byte V)
void Write8910(AY8910 *D,byte R,byte V)
{
register int J,I;
int J,I;
switch(R)
{
......@@ -240,9 +240,9 @@ void Write8910(register AY8910 *D,register byte R,register byte V)
/** envelopes. Use mS to pass the time since the last call **/
/** of Loop8910() in milliseconds. **/
/*************************************************************/
void Loop8910(register AY8910 *D,int mS)
void Loop8910(AY8910 *D,int mS)
{
register int J,I;
int J,I;
/* Exit if no envelope running */
if(!D->EPeriod) return;
......@@ -282,9 +282,9 @@ void Loop8910(register AY8910 *D,int mS)
/** noise channels with MIDI drums, OR second argument with **/
/** AY8910_DRUMS. **/
/*************************************************************/
void Sync8910(register AY8910 *D,register byte Sync)
void Sync8910(AY8910 *D,byte Sync)
{
register int J,I;
int J,I;
/* Hit MIDI drums for noise channels, if requested */
if(Sync&AY8910_DRUMS)
......
......@@ -51,13 +51,13 @@ typedef struct
/** Reset the sound chip and use sound channels from the **/
/** one given in First. **/
/*************************************************************/
void Reset8910(register AY8910 *D,int ClockHz,int First);
void Reset8910(AY8910 *D,int ClockHz,int First);
/** Write8910() **********************************************/
/** Call this function to output a value V into the sound **/
/** chip. **/
/*************************************************************/
void Write8910(register AY8910 *D,register byte R,register byte V);
void Write8910(AY8910 *D,byte R,byte V);
/** WrCtrl8910() *********************************************/
/** Write a value V to the PSG Control Port. **/
......@@ -82,14 +82,14 @@ byte RdData8910(AY8910 *D);
/** noise channels with MIDI drums, OR second argument with **/
/** AY8910_DRUMS. **/
/*************************************************************/
void Sync8910(register AY8910 *D,register byte Sync);
void Sync8910(AY8910 *D,byte Sync);
/** Loop8910() ***********************************************/
/** Call this function periodically to update volume **/
/** envelopes. Use mS to pass the time since the last call **/
/** of Loop8910() in milliseconds. **/
/*************************************************************/
void Loop8910(register AY8910 *D,int mS);
void Loop8910(AY8910 *D,int mS);
#ifdef __cplusplus
}
......
......@@ -22,6 +22,8 @@
#endif
#include <ctype.h>
#include <streams/file_stream.h>
#define IMAGE_SIZE(Fmt) \
(Formats[Fmt].Sides*Formats[Fmt].Tracks* \
Formats[Fmt].Sectors*Formats[Fmt].SecSize)
......@@ -69,6 +71,16 @@ static const byte TRDDiskInfo[] =
0x61,0x6D,0x65,0x00,0x00,0x00,0x46,0x55
};
/* Forward declarations */
RFILE* rfopen(const char *path, const char *mode);
int rfclose(RFILE* stream);
int64_t rfread(void* buffer,
size_t elem_size, size_t elem_count, RFILE* stream);
int64_t rftell(RFILE* stream);
int64_t rfseek(RFILE* stream, int64_t offset, int origin);
int64_t rfwrite(void const* buffer,
size_t elem_size, size_t elem_count, RFILE* stream);
static int stricmpn(const char *S1,const char *S2,int Limit)
{
for(;*S1&&*S2&&Limit&&(toupper(*S1)==toupper(*S2));++S1,++S2,--Limit);
......@@ -194,7 +206,7 @@ int LoadFDI(FDIDisk *D,const char *FileName,int Format)
byte Buf[256],*P,*DDir;
const char *T;
int J,I,K,L,N;
FILE *F;
RFILE *F;
/* If just ejecting a disk, drop out */
if(!FileName) { EjectFDI(D);return(0); }
......@@ -233,19 +245,19 @@ int LoadFDI(FDIDisk *D,const char *FileName,int Format)
}
/* Open file and find its size */
if(!(F=fopen(FileName,"rb"))) return(0);
if(fseek(F,0,SEEK_END)<0) { fclose(F);return(0); }
if((J=ftell(F))<=0) { fclose(F);return(0); }
rewind(F);
if(!(F=rfopen(FileName,"rb"))) return(0);
if(rfseek(F,0,SEEK_END)<0) { rfclose(F);return(0); }
if((J=rftell(F))<=0) { rfclose(F);return(0); }
filestream_rewind(F);
switch(Format)
{
case FMT_FDI: /* If .FDI format... */
/* Allocate memory and read file */
if(!(P=(byte *)malloc(J))) { fclose(F);return(0); }
if(fread(P,1,J,F)!=J) { free(P);fclose(F);return(0); }
if(!(P=(byte *)malloc(J))) { rfclose(F);return(0); }
if(rfread(P,1,J,F)!=J) { free(P);rfclose(F);return(0); }
/* Verify .FDI format tag */
if(memcmp(P,"FDI",3)) { free(P);fclose(F);return(0); }
if(memcmp(P,"FDI",3)) { free(P);rfclose(F);return(0); }
/* Eject current disk image */
EjectFDI(D);
/* Read disk dimensions */
......@@ -273,11 +285,11 @@ int LoadFDI(FDIDisk *D,const char *FileName,int Format)
case FMT_DSK: /* If .DSK format... */
/* Read header */
if(fread(Buf,1,32,F)!=32) { fclose(F);return(0); }
if(rfread(Buf,1,32,F)!=32) { rfclose(F);return(0); }
/* Check magic number */
if((Buf[0]!=0xE9)&&(Buf[0]!=0xEB)) { fclose(F);return(0); }
if((Buf[0]!=0xE9)&&(Buf[0]!=0xEB)) { rfclose(F);return(0); }
/* Check media descriptor */
if(Buf[21]<0xF8) { fclose(F);return(0); }
if(Buf[21]<0xF8) { rfclose(F);return(0); }
/* Compute disk geometry */
K = Buf[26]+((int)Buf[27]<<8); /* Heads */
N = Buf[24]+((int)Buf[25]<<8); /* Sectors */
......@@ -288,23 +300,23 @@ int LoadFDI(FDIDisk *D,const char *FileName,int Format)
K = I&&N&&L? J/I/N/L:0;
/* Create a new disk image */
P = NewFDI(D,K,I,N,L);
if(!P) { fclose(F);return(0); }
if(!P) { rfclose(F);return(0); }
/* Make sure we do not read too much data */
I = K*I*N*L;
J = J>I? I:J;
/* Read disk image file (ignore short reads!) */
rewind(F);
fread(P,1,J,F);
filestream_rewind(F);
rfread(P,1,J,F);
/* Done */
P = D->Data;
break;
case FMT_CPCDSK: /* If Amstrad CPC .DSK format... */
/* Read header (first track is next) */
if(fread(Buf,1,256,F)!=256) { fclose(F);return(0); }
if(rfread(Buf,1,256,F)!=256) { rfclose(F);return(0); }
/* Check magic string */
if(memcmp(Buf,"MV - CPC",8)&&memcmp(Buf,"EXTENDED CPC DSK File",21))
{ fclose(F);return(0); }
{ rfclose(F);return(0); }
/* Compute disk geometry */
I = Buf[48]; /* Tracks */
K = Buf[49]; /* Heads */
......@@ -318,9 +330,9 @@ int LoadFDI(FDIDisk *D,const char *FileName,int Format)
L = (L-0x100+N-1)/N;
/* Check geometry */
//printf("Tracks=%d, Heads=%d, Sectors=%d, SectorSize=%d\n",I,K,N,L);
if(!K||!N||!L||!I) { fclose(F);return(0); }
if(!K||!N||!L||!I) { rfclose(F);return(0); }
/* Create a new disk image */
if(!NewFDI(D,K,I,N,L)) { fclose(F);return(0); }
if(!NewFDI(D,K,I,N,L)) { rfclose(F);return(0); }
/* Sectors-per-track and bytes-per-sector may vary */
D->Sectors = 0;
D->SecSize = 0;
......@@ -328,12 +340,12 @@ int LoadFDI(FDIDisk *D,const char *FileName,int Format)
DDir = FDI_DIR(D->Data);
P = FDI_DATA(D->Data);
/* Skip to the first track info block */
fseek(F,0x100,SEEK_SET);
rfseek(F,0x100,SEEK_SET);
/* Read tracks */
for(I*=K;I;--I)
{
/* Read track header */
if(fread(Buf,1,0x18,F)!=0x18) break;
if(rfread(Buf,1,0x18,F)!=0x18) break;
/* Check magic string */
if(memcmp(Buf,"Track-Info\r\n",12)) break;
/* Compute track geometry */
......@@ -349,7 +361,7 @@ int LoadFDI(FDIDisk *D,const char *FileName,int Format)
DDir[5] = 0;
DDir[6] = N;
/* Read sector headers */
for(DDir+=7,J=N,K=0;J&&(fread(Buf,8,1,F)==8);DDir+=7,--J,K+=SecSizes[L])
for(DDir+=7,J=N,K=0;J&&(rfread(Buf,8,1,F)==8);DDir+=7,--J,K+=SecSizes[L])
{
/* Create .FDI sector entry */
DDir[0] = Buf[0];
......@@ -361,9 +373,9 @@ int LoadFDI(FDIDisk *D,const char *FileName,int Format)
DDir[6] = K>>8;
}
/* Seek to the track data */
if(fseek(F,0x100-0x18-8*N,SEEK_CUR)<0) break;
if(rfseek(F,0x100-0x18-8*N,SEEK_CUR)<0) break;
/* Read track data */
if(fread(P,1,K,F)!=K) break; else P+=K;
if(rfread(P,1,K,F)!=K) break; else P+=K;
}
/* Done */
P = D->Data;
......@@ -380,11 +392,11 @@ int LoadFDI(FDIDisk *D,const char *FileName,int Format)
Formats[Format].Sectors,
Formats[Format].SecSize
);
if(!P) { fclose(F);return(0); }
if(!P) { rfclose(F);return(0); }
/* Make sure we do not read too much data */
J = J>IMAGE_SIZE(Format)? IMAGE_SIZE(Format):J;
/* Read disk image file (ignore short reads!) */
fread(P,1,J,F);
rfread(P,1,J,F);
/* Done */
P = D->Data;
break;
......@@ -398,13 +410,13 @@ int LoadFDI(FDIDisk *D,const char *FileName,int Format)
Formats[Format].Sectors,
Formats[Format].SecSize
);
if(!P) { fclose(F);return(0); }
if(!P) { rfclose(F);return(0); }
/* Read disk image file track-by-track */
K = Formats[Format].Tracks;
L = Formats[Format].Sectors*Formats[Format].SecSize;
I = Formats[Format].Tracks*Formats[Format].Sides;
for(J=0;J<I;++J)
if(fread(P+L*2*(J%K)+(J>=K? L:0),1,L,F)!=L) break;
if(rfread(P+L*2*(J%K)+(J>=K? L:0),1,L,F)!=L) break;
/* Done */
P = D->Data;
break;
......@@ -412,9 +424,9 @@ int LoadFDI(FDIDisk *D,const char *FileName,int Format)
case FMT_SCL: /* If .SCL format... */
/* @@@ NEED TO CHECK CHECKSUM AT THE END */
/* Read header */
if(fread(Buf,1,9,F)!=9) { fclose(F);return(0); }
if(rfread(Buf,1,9,F)!=9) { rfclose(F);return(0); }
/* Verify .SCL format tag and the number of files */
if(memcmp(Buf,"SINCLAIR",8)||(Buf[8]>128)) { fclose(F);return(0); }
if(memcmp(Buf,"SINCLAIR",8)||(Buf[8]>128)) { rfclose(F);return(0); }
/* Create a new disk image */
P = NewFDI(
D,
......@@ -423,14 +435,14 @@ int LoadFDI(FDIDisk *D,const char *FileName,int Format)
Formats[Format].Sectors,
Formats[Format].SecSize
);
if(!P) { fclose(F);return(0); }
if(!P) { rfclose(F);return(0); }
/* Compute the number of free sectors */
I = D->Sides*D->Tracks*D->Sectors;
/* Build directory, until we run out of disk space */
for(J=0,K=D->Sectors,DDir=P;(J<Buf[8])&&(K<I);++J)
{
/* Read .SCL directory entry */
if(fread(DDir,1,14,F)!=14) break;
if(rfread(DDir,1,14,F)!=14) break;
/* Compute sector and track */
DDir[14] = K%D->Sectors;
DDir[15] = K/D->Sectors;
......@@ -439,7 +451,7 @@ int LoadFDI(FDIDisk *D,const char *FileName,int Format)
DDir += 16;
}
/* Skip over remaining directory entries */
if(J<Buf[8]) fseek(F,(Buf[8]-J)*14,SEEK_CUR);
if(J<Buf[8]) rfseek(F,(Buf[8]-J)*14,SEEK_CUR);
/* Build disk information */
memset(P+J*16,0,D->Sectors*D->SecSize-J*16);
memcpy(P+0x08E2,TRDDiskInfo,sizeof(TRDDiskInfo));
......@@ -457,7 +469,7 @@ int LoadFDI(FDIDisk *D,const char *FileName,int Format)
I = (DDir[15]*D->Sectors+DDir[14])*D->SecSize;
N = DDir[13]*D->SecSize;
/* Read .SCL data (ignore short reads!) */
fread(P+I,1,N,F);
rfread(P+I,1,N,F);
}
/* Done */
P = D->Data;
......@@ -472,9 +484,9 @@ int LoadFDI(FDIDisk *D,const char *FileName,int Format)
Formats[Format].Sectors,
Formats[Format].SecSize
);
if(!P) { fclose(F);return(0); }
if(!P) { rfclose(F);return(0); }
/* Read header */
if(fread(P,1,17,F)!=17) { fclose(F);return(0); }
if(rfread(P,1,17,F)!=17) { rfclose(F);return(0); }
/* Determine data offset and size */
I = D->Sectors*D->SecSize;
N = P[13]+((int)P[14]<<8);
......@@ -492,7 +504,7 @@ int LoadFDI(FDIDisk *D,const char *FileName,int Format)
P[0x8E6] = J>>8;
N = N*D->SecSize; /* N is now in bytes */
/* Read data (ignore short reads!) */
fread(P+I,1,N,F);
rfread(P+I,1,N,F);
/* Compute and check checksum */
for(L=I=0;I<15;++I) L+=P[I];
L = ((L*257+105)&0xFFFF)-P[15]-((int)P[16]<<8);
......@@ -518,7 +530,7 @@ int LoadFDI(FDIDisk *D,const char *FileName,int Format)
);
/* Done */
fclose(F);
rfclose(F);
D->Data = P;
D->Format = Format;
return(Format);
......@@ -534,7 +546,7 @@ int SaveFDI(FDIDisk *D,const char *FileName,int Format)
{
byte S[32];
int I,J,K,C,L;
FILE *F;
RFILE *F;
byte *P,*T;
/* Must have a disk to save */
......@@ -542,15 +554,15 @@ int SaveFDI(FDIDisk *D,const char *FileName,int Format)
/* Use original format if requested */
if(!Format) Format=D->Format;
/* Open file for writing */
if(!(F=fopen(FileName,"wb"))) return(0);
if(!(F=rfopen(FileName,"wb"))) return(0);
/* Depending on the format... */
switch(Format)
{
case FMT_FDI:
if(fwrite(D->Data,1,D->DataSize,F)!=D->DataSize)
if(rfwrite(D->Data,1,D->DataSize,F)!=D->DataSize)
{
fclose(F);
rfclose(F);
unlink(FileName);
return(0);
}
......@@ -560,7 +572,7 @@ int SaveFDI(FDIDisk *D,const char *FileName,int Format)
/* Check the number of tracks and sides */
if((FDI_TRACKS(D->Data)!=Formats[Format].Tracks)||(FDI_SIDES(D->Data)!=Formats[Format].Sides))
{
fclose(F);
rfclose(F);
unlink(FileName);
return(0);
}
......@@ -572,12 +584,12 @@ int SaveFDI(FDIDisk *D,const char *FileName,int Format)
{
P = SeekFDI(D,I,J,I,J,K+1);
C = D->SecSize<L? D->SecSize:L;
if(!P||(fwrite(P,1,C,F)!=C)) break;
if(!P||(rfwrite(P,1,C,F)!=C)) break;
}
/* If failed to write all sectors, clean up */
if(I<Formats[Format].Sides)
{
fclose(F);
rfclose(F);
unlink(FileName);
return(0);
}
......@@ -589,7 +601,7 @@ int SaveFDI(FDIDisk *D,const char *FileName,int Format)
/* Check the number of tracks and sides */
if((FDI_TRACKS(D->Data)!=Formats[Format].Tracks)||(FDI_SIDES(D->Data)!=Formats[Format].Sides))
{
fclose(F);
rfclose(F);
unlink(FileName);
return(0);
}
......@@ -608,15 +620,15 @@ int SaveFDI(FDIDisk *D,const char *FileName,int Format)
K = FDI_SECSIZE(T);
K = K>L? L:K;
L-= K;
if(fwrite(FDI_SECTOR(D->Data,P,T),1,K,F)!=K)
if(rfwrite(FDI_SECTOR(D->Data,P,T),1,K,F)!=K)
{
fclose(F);
rfclose(F);
unlink(FileName);
return(0);
}
}
/* Fill remaining track length with zeros */
if(L>0) fseek(F,L,SEEK_CUR);
if(L>0) rfseek(F,L,SEEK_CUR);
}
/* Done */
break;
......@@ -632,16 +644,16 @@ int SaveFDI(FDIDisk *D,const char *FileName,int Format)
||(T[0x8E3]!=0x16)
)
{
fclose(F);
rfclose(F);
unlink(FileName);
return(0);
}
/* Write header */
strcpy((char *)S,"SINCLAIR");
S[8]=T[0x8E4];
if(fwrite(S,1,9,F)!=9)
if(rfwrite(S,1,9,F)!=9)
{
fclose(F);
rfclose(F);
unlink(FileName);
return(0);
}
......@@ -649,9 +661,9 @@ int SaveFDI(FDIDisk *D,const char *FileName,int Format)
/* Write directory entries */
for(J=0,P=T;J<T[0x8E4];++J,P+=16)
{
if(fwrite(P,1,14,F)!=14)
if(rfwrite(P,1,14,F)!=14)
{
fclose(F);
rfclose(F);
unlink(FileName);
return(0);
}
......@@ -664,9 +676,9 @@ int SaveFDI(FDIDisk *D,const char *FileName,int Format)
K = (P[15]*D->Sectors+P[14])*D->SecSize;
I = P[13]*D->SecSize;
/* Write data */
if(fwrite(T+K,1,I,F)!=I)
if(rfwrite(T+K,1,I,F)!=I)
{
fclose(F);
rfclose(F);
unlink(FileName);
return(0);
}
......@@ -678,9 +690,9 @@ int SaveFDI(FDIDisk *D,const char *FileName,int Format)
S[1] = (C>>8)&0xFF;
S[2] = (C>>16)&0xFF;
S[3] = (C>>24)&0xFF;
if(fwrite(S,1,4,F)!=4)
if(rfwrite(S,1,4,F)!=4)
{
fclose(F);
rfclose(F);
unlink(FileName);
return(0);
}
......@@ -698,7 +710,7 @@ int SaveFDI(FDIDisk *D,const char *FileName,int Format)
||(T[0x8E3]!=0x16)
)
{
fclose(F);
rfclose(F);
unlink(FileName);
return(0);
}
......@@ -707,7 +719,7 @@ int SaveFDI(FDIDisk *D,const char *FileName,int Format)
/* If not found, drop out */
if(J>=T[0x8E4])
{
fclose(F);
rfclose(F);
unlink(FileName);
return(0);
}
......@@ -723,16 +735,16 @@ int SaveFDI(FDIDisk *D,const char *FileName,int Format)
S[15] = C&0xFF;
S[16] = (C>>8)&0xFF;
/* Write header */
if(fwrite(S,1,17,F)!=17)
if(rfwrite(S,1,17,F)!=17)
{
fclose(F);
rfclose(F);
unlink(FileName);
return(0);
}
/* Write file data */
if(fwrite(P,1,I,F)!=I)
if(rfwrite(P,1,I,F)!=I)
{
fclose(F);
rfclose(F);
unlink(FileName);
return(0);
}
......@@ -741,13 +753,13 @@ int SaveFDI(FDIDisk *D,const char *FileName,int Format)
default:
/* Can't save this format for now */
fclose(F);
rfclose(F);
unlink(FileName);
return(0);
}
/* Done */
fclose(F);
rfclose(F);
return(Format);
}
......
......@@ -20,10 +20,11 @@
#if defined(_WIN32)
// Copied from linux libc sys/stat.h:
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
#include <retro_dirent.h>
#include <file/file_path.h>
#include <streams/file_stream.h>
#define DSK_RESERVED_SECS 0
#define DSK_FATS_PER_DISK 2
......@@ -54,6 +55,14 @@
+ ((ID)*32) \
)
/* Forward declarations */
RFILE* rfopen(const char *path, const char *mode);
int rfclose(RFILE* stream);
int64_t rfread(void* buffer,
size_t elem_size, size_t elem_count, RFILE* stream);
int64_t rfwrite(void const* buffer,
size_t elem_size, size_t elem_count, RFILE* stream);
/** FindFreeCluster() ****************************************/
/** Finds first free cluster starting from the given one. **/
/** Returns cluster number on success or 0 on failure. **/
......@@ -365,7 +374,7 @@ byte *DSKLoad(const char *Name,byte *Dsk)
{
byte *Dsk1,*Buf,FN[32],*Path;
struct stat FS;
FILE *F;
RFILE *F;
struct RDIR *D;
int J,I;
......@@ -374,7 +383,7 @@ byte *DSKLoad(const char *Name,byte *Dsk)
if(!Dsk1) return(0);
/* If <Name> is a directory... */
if(!stat(Name,&FS)&&S_ISDIR(FS.st_mode))
if (path_is_directory(Name))
{
/* Open directory */
D = retro_opendir(Name);
......@@ -404,13 +413,13 @@ byte *DSKLoad(const char *Name,byte *Dsk)
/* Open input file */
if(!stat((const char*)Path,&FS)&&S_ISREG(FS.st_mode)&&FS.st_size)
if((F = fopen((const char*)Path,"rb")))
if((F = rfopen((const char*)Path,"rb")))
{
/* Allocate input buffer */
if((Buf = malloc(FS.st_size)))
{
/* Read file into the buffer */
if(fread(Buf,1,FS.st_size,F)==FS.st_size)
if(rfread(Buf,1,FS.st_size,F)==FS.st_size)
{
/* Create and write floppy file */
if((I = DSKFile(Dsk1,(const char*)FN)))
......@@ -421,7 +430,7 @@ byte *DSKLoad(const char *Name,byte *Dsk)
free(Buf);
}
/* Done with the input file */
fclose(F);
rfclose(F);
}
/* Done with the full input file name */
......@@ -434,15 +443,15 @@ byte *DSKLoad(const char *Name,byte *Dsk)
}
/* Assume <Name> to be a disk image file */
F=fopen(Name,"rb");
F=rfopen(Name,"rb");
if(!F) { if(!Dsk) free(Dsk1);return(0); }
/* Read data */
if(fread(Dsk1,1,DSK_DISK_SIZE,F)!=DSK_DISK_SIZE)
{ if(!Dsk) free(Dsk1);fclose(F);return(0); }
if(rfread(Dsk1,1,DSK_DISK_SIZE,F)!=DSK_DISK_SIZE)
{ if(!Dsk) free(Dsk1);rfclose(F);return(0); }
/* Done */
fclose(F);
rfclose(F);
return(Dsk1);
}
......@@ -451,11 +460,11 @@ const byte *DSKSave(const char *Name,const byte *Dsk)
const char *T;
byte *Path,*P;
struct stat FS;
FILE *F;
RFILE *F;
int J,I,K;
/* If <Name> is a directory... */
if(!stat(Name,&FS)&&S_ISDIR(FS.st_mode))
if (path_is_directory(Name))
{