#define SNOWFLAKE \
" _ 8* @ \n"\
" . O __ _| |_ __ ' * ' \n"\
" * /'v /\\_ + _/\\ v`\\ o \n"\
" > x \\ _| |_ / x < \n"\
" .0 __`./\\ _^_ /\\.'__ @ .: ' \n"\
" _\\ \\__/ /\\ v /\\ \\__/ /_ \n"\
"' |_ + __ < > < > __ + _| 0. \n"\
" /_/ \\ \\/_^_\\/ / \\_\\ . \n"\
" . @ .'\\/ _ v _ \\/`. * 8 \n"\
" > x / _| |_ \\ x < @ .\n"\
" . : \\.^_\\/_ + _\\/_^./ Oo * \n"\
" ' |_| \n"\
/* this software is she12ware * .o '. @ o. */
#define PAYPLAN "!!!PLEASE DONATE TO SDF.ORG!!!"
#define DID_PAY 0
#define VERSION 1.0
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#ifdef __unix__
#include <sys/ioctl.h>
#define DECXY struct winsize win;
#define GETXY ioctl(STDIN_FILENO,TIOCGWINSZ,&win)!=-1
#define GETXV win.ws_col
#define GETYV win.ws_row
#define ENVXV "COLUMNS"
#define ENVYV "LINES"
#define SEEDR srandom(time(NULL))
#define GETRA 32+random()%(126-32+1)
#define GETRC(S) S[random()%strlen(S)]
#define GETRN(B,F) B+random()%F
#define CLEAR printf("\x1B[H\x1B[2J")
#define SLEEP(T) usleep(T)
#else
#include <windows.h>
#define DECXY CONSOLE_SCREEN_BUFFER_INFO win
#define GETXY GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),&win)
#define GETXV win.srWindow.Right-win.srWindow.Left+1
#define GETYV win.srWindow.Bottom-win.srWindow.Top+1
#define ENVXV "COLS"
#define ENVYV "ROWS"
#define SEEDR srand(time(NULL))
#define RANDM rand()
#define GETRA 32+rand()%(126-32+1)
#define GETRC(S) S[rand()%strlen(S)]
#define GETRN(B,F) B+rand()%F
#define CLEAR system("cls")
#define SLEEP(T) sleep(T/1000000)
#endif
char *init_string(char,long);
char **init_strings(char,long,long);
void read_strings(char*,char**,long,long);
int main(int argc,char **argv){
int c,s;
int cascade=0;
int debug=0;
int frame=0;
int increase=0;
int maximize=0;
int randomize=0;
int tail=0;
int upwards=0;
int wide=0;
long h,w,x;
long length;
long density=750;
long height=10;
long iterations=-1;
long swing=0;
long timeout=140000;
long width=53;
char bg=' ';
char *background=NULL;
char *charset=NULL;
char *frameset="--||+";
char *framebar_top;
char *framebar_bot;
char *intro="It'Snowtime!";
char *overlay=DID_PAY?NULL:PAYPLAN;
char *snow=".*'.'*:oOO008@****...@";
char **buf;
char **top;
char **bot;
while((c=getopt(argc,argv,"b:c:d:Df:Fh:i:Io:LRs:St:Tw:WUX"))!=-1)
switch(c){
case'b':if(strlen(optarg)>1)
background=optarg;
else bg=*optarg;break;
case'c':snow=optarg;break;
case'd':density=strtol(optarg,NULL,0);break;
case'f':frameset=optarg;break;
case'h':height=strtol(optarg,NULL,0);break;
case'i':iterations=strtol(optarg,NULL,0);break;
case'o':overlay=optarg;break;
case's':swing=strtol(optarg,NULL,0);break;
case't':timeout=strtol(optarg,NULL,0);break;
case'w':width=strtol(optarg,NULL,0);break;
case'D':debug=1;break;
case'F':frame=1;break;
case'I':increase=1;break;
case'L':cascade=1;break;
case'R':cascade=2;break;
case'S':randomize=1;break;
case'T':tail=1;break;
case'U':upwards=1;break;
case'W':wide=1;break;
case'X':maximize=1;break;
default:printf("\n%s\nusage: %s [b:c:d:Df:Fh:i:Io:LRs:St:Tw:WUX]\n"
"\t-b <c>[c*].....set background character, string or file\n"
"\t-c <s>.........set flake charset string\n"
"\t-d <ld>........set background density\n"
"\t-f <cccc>[c]...set framebar characters: <top><bottom><left><right>[corner]\n"
"\t-h <ld>........set height\n"
"\t-i <ld>........set iterations\n"
"\t-o <s>.........set overlay string or file\n"
"\t-s <ld>........set flake swing range\n"
"\t-t <ld>........set timeout in microseconds\n"
"\t-w <ld>........set width\n"
"\n"
"\t-D.............show debug information\n"
"\t-F.............draw frame\n"
"\t-I.............increase density each iteration\n"
"\t-L.............cascade to left\n"
"\t-R.............cascade to right\n"
"\t-S.............random parameters\n"
"\t-T.............flakes draw tails\n"
"\t-U.............flakes go upwards\n"
"\t-W.............full width flakes\n"
"\t-X.............maximize to current terminal size\n"
,SNOWFLAKE,*argv);
return 1;
}
SEEDR;
/* get terminal window size .8o .@ : o*. */
if(maximize){
DECXY;
if(GETXY){
width=GETXV;
height=GETYV;
height-=debug*(5+strlen(snow)/width)+1;
}else if(getenv(ENVXV)&&getenv(ENVYV)){
width=strtol(getenv(ENVXV),NULL,0);
height=strtol(getenv(ENVYV),NULL,0);
height-=debug*(5+strlen(snow)/width)+1;
}
}
height=height>0?height:1;
width=width>0?width:1;
/* randomize parameters .0 : .*:. .' O' 0 */
if(randomize){
height=GETRN(1,height);
width=GETRN(1,width);
randomize=GETRN(1,10*height);
bg=GETRA;
background="/dev/urandom";
density=GETRN(10,randomize+10*height);
swing=GETRN(0,height);
timeout=GETRN(35000,900000-35000);
increase=GETRN(0,2);
cascade=GETRN(0,3);
tail=GETRN(0,2);
upwards=GETRN(0,2);
wide=GETRN(0,2);
frame=GETRN(0,2);
frameset=init_string('\0',5);
if(frame&&!frameset)return 1;
for(w=0;w<5;w++)
frameset[w]=GETRA;
snow=init_string('\0',randomize);
if(!snow)return 1;
for(w=0;w<randomize;w++)
snow[w]=GETRA;
}
/* create framebars . * .@*@* * 8 'O */
if(frame){
if(height>2&&width>2){
framebar_top=init_string(frameset[0],width);
framebar_bot=init_string(frameset[1],width);
if(!framebar_top||!framebar_bot)return 1;
if(frameset[4])
framebar_top[0]
=framebar_bot[0]
=framebar_top[width-1]
=framebar_bot[width-1]
=frameset[4];
height-=2;
width-=2;
}else frame=0;
}
/* create background '*8 '. o . .* 0 */
bot=init_strings(bg,height,width);
if(!bot)return 1;
if(background)
read_strings(background,bot,height,width);
/* create snowerlay . * O.o. * .'* o */
top=init_strings(bg,height,width);
if(!top)return 1;
if(overlay)
read_strings(overlay,top,height,width);
/* create charset * ' * . O :. o0 8 */
charset=init_string('\0',density+strlen(snow)+2);
if(!charset)return 1;
memset(charset,bg,density);
strcat(charset,snow);
/* create buffer . 'o . *8 O * ' @. :*/
length=(cascade?width+height:width)+swing;
buf=init_strings(bg,height,length);
if(!buf)return 1;
if(debug){
x=length/2-strlen(intro)/2;
if(x>=0)
memcpy(buf[height/2]+x,intro,strlen(intro));
}
/* start of output . * * . . O. @' */
while(iterations>-1?iterations--:iterations!=0){
CLEAR;
if(debug){
if(frame)putchar(' ');
for(w=1;w<=width;w++)
putchar(w%5?',':'|');
putchar('\n');
}
if(frame)puts(framebar_top);
/* snow row by row ' 8 . . @ *o * .*/
for(c=h=0;h<height;h++){
if(frame)putchar(frameset[2]);
if(swing){
if(c==0||c==swing)
s=c?0:1;
s?c++:c--;
if(swing>height)
c=GETRN(1,height);
}
if(cascade)
x=cascade+upwards==2?height-h:h;
else x=0;
/* snow flake by flake . @8 @* . 8 */
for(w=c+x;w<width+c+x;w++)
if(top[h][w-c-x]!=bg)
putchar(top[h][w-c-x]);
else if(buf[h][wide?c+cascade:w]!=bg)
putchar(buf[h][wide?c+cascade:w]);
else putchar(bot[h][w-c-x]);
if(frame)putchar(frameset[3]);
putchar('\n');
}
if(frame)puts(framebar_bot);
if(debug)
printf("height: %ld width: %ld round: %ld timeout: %ld\n"
"cascade: %s swing: %d/%ld density: %ld increase: %d\n"
"direction: %s tail: %s wide: %s random: %s\n"
"background: '%c' frameset: '%s' charset: '%s'\n"
,height,width,iterations,timeout
,cascade?cascade<2?"left":"right":"no",c,swing,density,increase
,upwards?"up":"down",tail?"yes":"no",wide?"yes":"no",randomize?"yes":"no"
,bg,frameset,snow);
/* end of output *. @ .8o O :' . */
if(increase&&density){
x=100;
density=strlen(charset)-strlen(snow);
increase=density?density>x?density/x:1:0;
charset+=increase;
}
/* shift rows .O :'@ :. * 8 */
if(upwards){
for(h=0;h<height-1;h++)
strncpy(buf[h],buf[h+1],length);
c=h-1;
x=0;
}else{
for(--h;h;h--)
strncpy(buf[h],buf[h-1],length);
c=h+1;
x=height/2;
}
/* recreate last row *' 0 0 * . @ */
for(w=0;w<length;w++)
if((tail
&&height>1)
&&buf[c][w]!=bg
&&buf[c][w]!=buf[GETRN(x,height/2)][w])
buf[h][w]=buf[c][w];
else
if(w==0)
buf[h][w]=GETRC(charset);
else
for(buf[h][w]=GETRC(charset);
buf[h][w]==buf[h][w-1]
&&buf[h][w]!=bg&&density>1;
buf[h][w]=GETRC(charset))
;
SLEEP(timeout);
}
return 0;
}
/* allocate and fill char array * .0 * oO .*/
char *init_string(char bg,long width){
char *str=NULL;
str=malloc(width+1);
if(!str)return NULL;
memset(str,'\0',width+1);
memset(str,bg,width);
return str;
}
/* allocate and fill 2D char array .o '* .@ */
char **init_strings(char bg,long height,long width){
long h;
char **strs=NULL;
strs=malloc(height*sizeof(*strs));
if(!strs)return NULL;
for(h=0;h<height;h++){
strs[h]=malloc(width+1);
if(!strs[h])return NULL;
memset(strs[h],'\0',width+1);
memset(strs[h],bg,width);
}
return strs;
}
/* read file or string and fill 2D char array .o@*/
void read_strings(char *input,char **strs,long height,long width){
long h,w;
char c,*p;
FILE *in=NULL;
/* read input file . * 8o . :' ' @ */
if(strlen(input)==1&&*input=='-')
in=stdin;
else
in=fopen(input,"r");
if(in){
input=init_string(' ',height*width);
w=0;
while((c=fgetc(in))!=EOF&&w<height*width)
input[w++]=c;
if(in!=stdin)
fclose(in);
}
/* transfer ascii chars into 2D array *' 8 */
p=input;
for(h=0;h<height;h++)
for(w=0;w<width;w++)
switch(c=*p++){
case'\0':h=height;w=width;break;
case'\r':
case'\n':w=width;break;
case'\t':w+=3;break;
default:if(c<32||c>126)break;
strs[h][w]=c;
}
if(in)free(input);
}