/* Copyright 1993 J. Remyn */ /* This program is freely distributable, but there is no warranty */ /* of any kind. */ /* version 0.3 */ /* Modified for aaflip version 1.0 by Jan Hubicka*/ #include #include #include #include #include #include "general.h" #include "fli.h" #define NAMELEN 256 struct OPTIONS { char filename[NAMELEN]; /* filename given on command line */ char fast; /* 1 = play without delays */ char verbose; /* 1 = show info about flic file */ char release; /* 1 = don't keep frames in memory */ char firstp; /* 1 = process frames while loading */ char blank; /* 1 = keep black screen while loading */ char stdinp; /* 1 = take flic file from stdin */ int speed; /* speed gotten from command line (-1 if nothing gotten) */ int playrep; /* nr of times to play animation */ } options; struct FLI_FRAMECHUNK { long size; /* Size of chunk data part */ long file_offset; /* Offset of chunk data part into flic file */ int subchunks; /* Number of subchunks in data part */ void *cd; /* Pointer to memory containing data part, */ /* is NULL if data not kept in memory */ }; struct FLI { char filename[NAMELEN]; /* filename of fli file */ char flc; /* 0 = fli, 1 = flc */ int mode; /* vgalib mode number to be used */ int scr_width; /* width of screen mode used */ int scr_height; /* guess */ int current; /* current frame index */ long time; /* time of last processed frame in usec */ FILE *f; /* file pointer of opened flic file */ struct FLI_HEADER h; /* 128 byte fli file header structure */ struct FLI_FRAMECHUNK *frame; /* pointer to an allocated 'array' of FLI_FRAMECHUNK structs */ } fli; static aa_palette pal; static aa_context *context; static aa_renderparams *params; static char *graph_mem; static void dcd_color_64( char *data ) { uchar start = 0; int ops; int count; int i; /* puts( "color_64" ); */ ops = *(short int *)data; data = (void *)(((short int *)data) + 1); while( ops-- > 0 ) { start += *(uchar *)data; data = (void *) ((uchar *)data + 1); if( (count = (int)*(uchar *)data) == 0 ) count = 256; data = (void *) ((uchar *)data + 1); for( i=0; i 0 ) { start += *(uchar *)data; data = (void *)((uchar *)data + 1); if( (count = (int)*(uchar *)data) == 0 ) count = 256; data = (void *)((uchar *)data + 1); for( i=0; i> 2, *((uchar *)data+1) >> 2, *((uchar *)data+2) >> 2 ); data = (void *)((uchar *)data + 3); } start += count; } } void *memsetw( unsigned short *target, unsigned short w, int count ) { while( count-->0 ) { *target++ = w; } return( target ); } static void dcd_delta_flc( struct FLI *fli, char *data ) { short int line; short int nrlines; char *index; short packets; short type; int x; uchar lastbyte; char setlastbyte; /* puts( "delta flc" ); */ /* get nr of lines in chunk */ nrlines = *(short int *)data; data = (void *)((short int *)data + 1); line = 0; setlastbyte = 0; lastbyte = 0; /* this is just to get rid of a compiler warning */ index = graph_mem; while( nrlines>0 ) { /* don't put nrlines-- here, the continues don't allow it */ type = *(short *)data; data = (void *)((short *)data + 1); /* the 2 highest bits of type indicate how to interpret it */ if( type<0 ) { if( type&0x4000 ) { /* add to the line number and act as if nothing * happened. * documentation says it's all right just to * add the abs. value of the word, which is the * same as subtracting the word as it is always * negative. */ line -= type; index += fli->scr_width * (-type); continue; } /* the low byte contains a lastbyte (the last byte * on a line in odd-width resolution flic files), * this word is followed by the packet count */ setlastbyte = 1; lastbyte = (uchar)(type & 0x00FF); packets = *(short *)data; data = (void *)((short *)data + 1); /* packets can be 0 now if just the last byte * changes for this line */ } if( type>=0 ) { /* the word contains the nr of packets * we can just assign this to packets because the * high bits are zero */ packets = type; } /* decode packets */ x = 0; while( packets-->0 ) { /* get & decode packet */ x += *(uchar *)data; data = (void *)((uchar *)data + 1); type = *(char *)data; data = (void *)((uchar *)data + 1); if( (char)type>=0 ) { /* copy ptype words */ type <<= 1; memcpy( index + x, data, type ); x += type; data = (void *)((uchar *)data + type); continue; } type = -(char)type; memsetw( (ushort *)(index + x), *(ushort *)data, type ); x += type<<1; data = (void *)((ushort *)data + 1); } if( !setlastbyte ) { index = (char *)((uchar *)index + fli->scr_width); nrlines--; continue; } /* put lastbyte at end of line */ *(uchar *)(index + fli->scr_width - 1) = lastbyte; setlastbyte = 0; index = (char *)((uchar *)index + fli->scr_width); nrlines--; } } static void dcd_delta_fli( struct FLI *fli, char *data ) { short int line; short int nrlines; char *index; uchar packets; int index_x; char type; /* puts( "delta fli" ); */ line = *(short int *)data; data = (void *)((short int *)data + 1); index = graph_mem + line * fli->scr_width; nrlines = *(short int *)data; data = (void *)((short int *)data + 1); while( nrlines-- > 0 ) { index_x = 0; packets = *(uchar *)data; data = (void *)((uchar *)data + 1); while( packets > 0 ) { index_x += *(uchar *)data; data = (void *)((uchar *)data + 1); type = *(char *)data; data = (void *)((char *)data + 1); if( type >= 0 ) { memcpy( index + index_x, data, type ); index_x += type; data = (void *)((uchar *)data + type); packets--; continue; } memset( index + index_x, *(uchar *)data, -type ); index_x -= type; data = (void *)((uchar *)data + 1); packets--; } index += fli->scr_width; } } static void dcd_byte_run( struct FLI *fli, char *data ) { int lines; int width; char type; int index; int index_x; /* puts( "byte run" ); */ lines = fli->h.height; width = fli->h.width; index = 0; while( lines-->0 ) { data = (void *)((uchar *)data + 1); /* skip byte containing number of packets */ /* start a loop to decode packets until end of line is reached */ index_x = 0; while ( index_x < width ) { type = *((char *)data++); if( type<0 ) { /* type now contains nr of bytes to copy to video */ memcpy( graph_mem + index + index_x, (uchar *)data, -type ); index_x -= type; data = (void *)((uchar *)data - type); } else { memset( graph_mem + index + index_x, *(uchar *)data++, type ); index_x += type; } } index += fli->scr_width; } } static void dcd_black( struct FLI *fli, char *data ) { /* puts( "black" ); */ int l; uchar *index; l = fli->h.height; index = graph_mem; while( l-- > 0 ) { memset( index, 0, fli->h.width ); index += fli->scr_width; } } static void dcd_literal( struct FLI *fli, char *data ) { int l; /* puts( "literal" ); */ l = fli->h.height; { /* linear video memory */ char *index; index = graph_mem; while( l-- > 0 ) { memcpy( index, data, fli->h.width ); data = (void *)((uchar *)data + fli->h.width); index += fli->scr_width; } } } static void dcd_pstamp( char *data ) { /* puts( "pstamp" ); */ } static void showhelp( void ) { puts( "FLI Player for Linux (version 0.2a) by John Remyn" ); puts( "modified version for aalib by Jan Hubicka" ); puts( "Now it is ascii arted FLI Player for text mode" ); puts( "Usage:" ); puts( " flip [switch] " ); puts( "Valid switches are :" ); puts( " -f Switch off clock synchronization." ); puts( " -v Show information on flic file." ); puts( " -? -h Show help." ); puts( " -a Don't keep frames in memory." ); puts( " -b Process frames when they are loaded." ); puts( " -c Keep a blank screen while frames are being loaded." ); puts( " -n Play the animation sequence times." ); puts( " -s Set delay between frames to miliseconds." ); puts( " - Read flic file from standard input." ); puts( "also standard aalib options are supported" ); puts( " -dim, -bold, -reverse, -normal for enabling attributes"); puts( " -nodim, -nobold, -noreverse, -nonormal for disabling"); puts( "Press H for help on keys while playing." ); } static void parse_cmdln( int argc, char *argv[], struct OPTIONS *options ) { int i,j; options->filename[0] = '\0'; options->fast = 0; options->verbose = 0; options->release = 0; options->firstp = 0; options->blank = 0; options->playrep = 1; options->speed = -1; options->stdinp = 0; for( i=1; istdinp = 1; } j = 1; k = 0; while( (c = *(argv[i]+j))!='\0' ) { switch( c ) { case 'f' : options->fast = 1; break; case 'v' : options->verbose = 1; break; case 'a' : options->release = 1; break; case 'b' : options->firstp = 1; break; case 'c' : options->blank = 1; break; case '?' : case 'h' : showhelp(); exit( 0 ); break; case 'n' : if( i+k+1playrep = abs( atoi( argv[i+k] ) ); } break; case 's' : if( i+k+1speed = abs( atoi( argv[i+k] ) ); /* adjust to some reasonable value */ if (options->speed > 1000) options->speed = 1000; /* convert to usec */ options->speed *= 1000; } break; default : showhelp(); puts( "Unknown option." ); exit( -1 ); } j++; } i = i+k; } else { if( argv[i]!=NULL ) { /* argv[i] is the filename of the flic file */ strncpy( options->filename, argv[i], NAMELEN ); } } } if( options->filename[0]=='\0' && options->stdinp==0 ) { showhelp(); puts( "No filename given." ); exit( -1 ); } } static int open_fli( struct FLI *fli, struct OPTIONS *options ) { /* open file and read the header */ if( !options->stdinp ) { fli->f = fopen( fli->filename, "rb" ); if( !fli->f ) { return 0; } } else { fli->f = stdin; } freadblock( fli->f, sizeof( fli->h.size ), &(fli->h.size) ); freadblock( fli->f, sizeof( fli->h.type ), &(fli->h.type) ); freadblock( fli->f, sizeof( fli->h.frames ), &(fli->h.frames) ); freadblock( fli->f, sizeof( fli->h.width ), &(fli->h.width) ); freadblock( fli->f, sizeof( fli->h.height ), &(fli->h.height) ); freadblock( fli->f, sizeof( fli->h.depth ), &(fli->h.depth) ); freadblock( fli->f, sizeof( fli->h.flags ), &(fli->h.flags) ); freadblock( fli->f, sizeof( fli->h.speed ), &(fli->h.speed) ); freadblock( fli->f, sizeof( fli->h.reserved0 ), &(fli->h.reserved0) ); freadblock( fli->f, sizeof( fli->h.created ), &(fli->h.created) ); freadblock( fli->f, sizeof( fli->h.creator ), &(fli->h.creator) ); freadblock( fli->f, sizeof( fli->h.updated ), &(fli->h.updated) ); freadblock( fli->f, sizeof( fli->h.updater ), &(fli->h.updater) ); freadblock( fli->f, sizeof( fli->h.aspectx ), &(fli->h.aspectx) ); freadblock( fli->f, sizeof( fli->h.aspecty ), &(fli->h.aspecty) ); freadblock( fli->f, sizeof( fli->h.reserved1 ), &(fli->h.reserved1) ); freadblock( fli->f, sizeof( fli->h.oframe1 ), &(fli->h.oframe1) ); freadblock( fli->f, sizeof( fli->h.oframe2 ), &(fli->h.oframe2) ); freadblock( fli->f, sizeof( fli->h.reserved2 ), &(fli->h.reserved2) ); /* now check if it's a flc or a fli and take necessary precautions */ if( (unsigned)fli->h.type==FLITYPE ) { /* it's a fli. convert as much as possible to flc */ fli->flc = 0; /* convert frame rate from 1/70 sec to usec */ fli->h.speed *= 100; fli->h.speed /= 7; fli->h.speed *= 1000; /* ascpect ratio is ignored, but fix it anyway */ fli->h.aspectx = 6; fli->h.aspecty = 5; /* can't convert frame offsets */ } else if( (unsigned)fli->h.type==FLCTYPE ) { fli->flc = 1; fli->h.speed *= 1000; /* convert msec to usec */ } else { puts( "File type not recognized." ); exit( -1 ); } return 1; }; static void describe_fli( struct FLI *fli ) { printf( "Filename %s\n", fli->filename ); printf( "size %li\n", fli->h.size ); printf( "type %x\n", fli->h.type ); printf( "frames %i\n", fli->h.frames ); printf( "width %i\n", fli->h.width ); printf( "height %i\n", fli->h.height ); printf( "depth %i\n", fli->h.depth ); printf( "flags %x\n", fli->h.flags ); printf( "speed %li\n", fli->h.speed ); printf( "aspectx %i\n", fli->h.aspectx ); printf( "aspecty %i\n", fli->h.aspecty ); printf( "oframe1 %li\n", fli->h.oframe1 ); printf( "oframe2 %li\n", fli->h.oframe2 ); }; static void readchunkheader( FILE *f, struct FLI_CHUNK_HEADER *buf ) { freadblock( f, sizeof( buf->size ), &buf->size ); freadblock( f, sizeof( buf->type ), &buf->type ); freadblock( f, sizeof( buf->chunks ), &buf->chunks ); freadblock( f, sizeof( buf->reserved ), &buf->reserved ); } static void scan_to_frame( struct FLI *fli ) { struct FLI_CHUNK_HEADER buf; struct FLI_FRAMECHUNK *fc; do { readchunkheader( fli->f, &buf ); if( buf.type==0xF1FA ) break; fseek( fli->f, buf.size - FLI_CHUNK_HEADERLEN, SEEK_CUR ); } while ( buf.type!=0xF1FA ); fc = fli->frame + fli->current; fc->size = buf.size - FLI_CHUNK_HEADERLEN; fc->subchunks = buf.chunks; fc->file_offset = ftell( fli->f ); fc->cd = NULL; } static void getframechunkdata( struct FLI *fli ) { struct FLI_FRAMECHUNK *fc; void *data; fc = fli->frame + fli->current; data = fc->cd; if( data ) return; /* frame chunk data already loaded */ if(fc->size) { data = malloc( fc->size ); if( !data ) { printf( "cannot allocate memory for frame data\n" ); exit( 1 ); } fseek( fli->f, fc->file_offset, SEEK_SET ); freadblock( fli->f, fc->size, data ); } else data=NULL; fc->cd = data; } static void releaseframechunkdata( struct FLI *fli ) { struct FLI_FRAMECHUNK *fc; fc = fli->frame + fli->current; if( fc->cd ) { free( fc->cd ); fc->cd = NULL; } } static void processframechunk( struct FLI *fli ) { struct FLI_FRAMECHUNK *fc; void *data; int i; fc = fli->frame + fli->current; data = fc->cd; for( i=0; isubchunks; i++ ) { /* switch( chunktype ) */ switch( *((short int *)((char *)data+4)) ) { case COLOR_256 : dcd_color_256( (char *)data + 6 ); break; case DELTA_FLC : dcd_delta_flc( fli, (char *)data + 6 ); break; case COLOR_64 : dcd_color_64( (char *)data + 6 ); break; case DELTA_FLI : dcd_delta_fli( fli, (char *)data + 6 ); break; case BLACK : dcd_black( fli, (char *)data + 6 ); break; case BYTE_RUN : dcd_byte_run( fli, (char *)data + 6 ); break; case LITERAL : dcd_literal( fli, (char *)data + 6 ); break; case PSTAMP : dcd_pstamp( (char *)data + 6 ); break; default : puts( "unknown subchunk" ); } data = (void *)((char *)data + *(long *)data); } } long gettime(void) { struct timeval t; gettimeofday(&t, NULL); return(t.tv_usec); } int await() { #define T 10000 /* process key every 10ms */ int i; long t; t = gettime(); if (t < fli.time) t = t + 1E6; t = fli.h.speed - (t - fli.time); if (t > 0) { if (t > T) { for (i = 0; i < (t / T); i++) { usleep(T); if (f_getkey() == 'q') return(1); } usleep(t % T); } else usleep(t); } return(f_getkey() == 'q'); } static void fastscale(char *b1, char *b2, int x1, int x2, int y1, int y2, int width1, int width2) { int ddx1, ddx, spx = 0, ex; int ddy1, ddy, spy = 0, ey; int x; char *bb1 = b1; width2 -= x2; if (!x1 || !x2 || !y1 || !y2) return; ddx = x1 + x1; ddx1 = x2 + x2; if (ddx1 < ddx) spx = ddx / ddx1, ddx %= ddx1; ddy = y1 + y1; ddy1 = y2 + y2; if (ddy1 < ddy) spy = (ddy / ddy1) * width1, ddy %= ddy1; ey = -ddy1; for (; y2; y2--) { ex = -ddx1; for (x = x2; x; x--) { *b2 = *b1; b2++; b1 += spx; ex += ddx; if (ex > 0) { b1++; ex -= ddx1; } } b2 += width2; bb1 += spy; ey += ddy; if (ey > 0) { bb1 += width1; ey -= ddy1; } b1 = bb1; } } static int resized; static void resize(aa_context * c) { aa_resize(c); resized = 1; } static int yesno(int x, int y, char *string) { char c; aa_puts(context, x, y, AA_SPECIAL, string); aa_flush(context); while ((c = tolower(aa_getkey(context, 1))) != 'y' && c != 'n'); return (c == 'y'); } static int getnum(char *string) { unsigned char c; aa_puts(context, 0, 0, AA_SPECIAL, string); aa_flush(context); while ((c = tolower(aa_getkey(context, 1))) < '0' || c > '9'); return (c - '0'); } static void selectfont(aa_context * c) { int i = 0, i1; char string[255]; for (i = 0; aa_fonts[i] != NULL; i++) { sprintf(string, "%i - %-40s", i, aa_fonts[i]->name); aa_puts(context, 0, i, AA_SPECIAL, string); } i1 = getnum(""); if (i1 < i) aa_setfont(c, *(aa_fonts + i1)); } #if AA_LIB_VERSION==1 && AA_LIB_MINNOR==0 #define SUPPORTED c->driver->params.supported #else #define SUPPORTED c->driverparams.supported #endif static void selectsupported(aa_context * c) { int supported = c->params.supported; char text[40]; int ch; do { char *texts[]={ "Normal characters", "Half bright (dim)", "Double bright (bold)", "Bold font", "Reversed", "8 bit ascii", "Control characters"}; int masks[]={AA_NORMAL_MASK,AA_DIM_MASK,AA_BOLD_MASK,AA_BOLDFONT_MASK,AA_REVERSE_MASK,AA_EIGHT,AA_ALL}; int i; for(i=0;i<7;i++) { sprintf(text,"%i %-20s:%-12s",i+1,texts[i],(SUPPORTED&masks[i])?((supported&masks[i])?"On":"Off"):"Unsupported"); aa_puts(c,0,i,AA_SPECIAL,text); } aa_puts(c,0,i,AA_SPECIAL,"8 Leave this menu"); aa_flush(c); while ((ch = tolower(aa_getkey(context, 1))) < '1' || ch > '8'); ch-='1'; if(ch<7) supported^=masks[ch]; } while (ch<7); aa_setsupported(c, supported); } int f_getkey(void) { int c=aa_getkey(context,0); switch(c) { case 'h': case 'H': aa_puts(context,0,0,AA_SPECIAL," aaflip - an ascii art fli/flc player "); aa_puts(context,0,1,AA_SPECIAL," "); aa_puts(context,0,2,AA_SPECIAL," ';' '\\' - gamma '<' '>' - bright "); aa_puts(context,0,3,AA_SPECIAL," '[' ']' - Random dithering',' '.' - contrast "); aa_puts(context,0,4,AA_SPECIAL," 'I' 'i' - inversion "); aa_puts(context,0,5,AA_SPECIAL," "); aa_puts(context,0,6,AA_SPECIAL," 'm' - Dithering method 'q' - quit "); aa_puts(context,0,7,AA_SPECIAL," 'u' - Select attributes 'g' - font "); aa_flush(context); aa_getkey(context,1); break; case ';': params->gamma /= 1.05; break; case '\'': params->gamma *= 1.05; break; case '<': params->bright -= 4; break; case '>': params->bright += 4; break; case '[': params->randomval -= 4; break; case ']': params->randomval += 4; break; case ',': params->contrast -= 2; break; case '.': params->contrast += 2; break; case 'm': params->dither = (params->dither + 1) % AA_DITHERTYPES; break; case 'i': params->inversion = 1; break; case 'I': params->inversion = 0; break; case 'u': selectsupported(context); break; case 'g': selectfont(context); break; } return(c); } int main( int argc, char *argv[] ) { int quit = 0; int playstartframe = 0; int first=1; long t, tdelta; aa_parseoptions(NULL,NULL,&argc,argv); parse_cmdln( argc, argv, &options ); strcpy( fli.filename, options.filename ); /* open flic and get header information */ if( !open_fli( &fli, &options ) ) { puts( "cannot open fli file" ); exit( 1 ); } /* show header */ if( options.verbose==1 ) { describe_fli( &fli ); } /* optionally set delays */ if( options.speed>=0 ) { fli.h.speed = options.speed; } /* optionally reduce delays to 0 (this overrides the set delay option) */ if( options.fast==1 ) { fli.h.speed = 0; } /* silly but might happen */ if( options.playrep<=0 ) { exit( 0 ); } /* determine graphics mode to use */ /* set mode */ context=aa_autoinit(&aa_defparams); if(context==NULL) { printf("Failed to initialize aalib\n"); exit(1); } aa_hidecursor(context); aa_autoinitkbd(context,0); aa_resizehandler(context,resize); params=aa_getrenderparams(); fli.scr_width = fli.h.width; fli.scr_height = fli.h.height; graph_mem=malloc(fli.scr_width*fli.scr_height); fli.frame = (struct FLI_FRAMECHUNK *)malloc( (fli.h.frames+1) * sizeof( struct FLI_FRAMECHUNK ) ); if( fli.frame==NULL ) { puts( "cannot allocate enough memory to store frame information\n" ); exit( 1 ); } fli.current = 0; /* preloading */ /* first the 1st frame which is a full screen frame */ scan_to_frame( &fli ); if( !options.blank ) { getframechunkdata( &fli ); processframechunk( &fli ); releaseframechunkdata( &fli ); playstartframe = 1; } else { getframechunkdata( &fli ); playstartframe = 0; if( options.release ) { releaseframechunkdata( &fli ); } } fli.current++; first=1; while( fli.current<=fli.h.frames && !quit ) { scan_to_frame( &fli ); getframechunkdata( &fli ); if( options.firstp ) { fli.time = gettime(); processframechunk( &fli ); } if( options.release ) { releaseframechunkdata( &fli ); } if(first||options.firstp) { fastscale(graph_mem,context->imagebuffer, fli.scr_width,aa_imgwidth(context), fli.scr_height,aa_imgheight(context), fli.scr_width,aa_imgwidth(context)); aa_renderpalette(context,pal,params,0,0,aa_imgwidth(context),aa_imgheight(context)); if(first&&!options.firstp) aa_puts(context,0,0,AA_SPECIAL,"Preloading..."); aa_flush(context); } fli.current++; first=0; if( f_getkey()=='q' ) quit = 1; if(options.firstp&&!quit) quit = await(); } if(!quit) { if( options.firstp ) { options.playrep--; if( options.playrep==0 ) { quit = 1; } } fli.current = playstartframe; while( !quit ) { fli.time = gettime(); getframechunkdata( &fli ); processframechunk( &fli ); if( options.release ) { releaseframechunkdata( &fli ); } fli.current++; fastscale(graph_mem,context->imagebuffer, fli.scr_width,aa_imgwidth(context), fli.scr_height,aa_imgheight(context), fli.scr_width,aa_imgwidth(context)); aa_renderpalette(context,pal,params,0,0,aa_imgwidth(context),aa_imgheight(context)); aa_flush(context); if( fli.current>fli.h.frames ) { fli.current = 1; options.playrep--; if( options.playrep==0 ) { quit = 1; break; } } quit = await(); } } /* restore textmode */ aa_close(context); /* close flic */ if( !options.stdinp ) { fclose( fli.f ); } fli.f = NULL; return 0; };