BlinkenArea - GitList
Repositories
Blog
Wiki
BlinkenLib
Code
Commits
Branches
Tags
Search
Tree:
962674e
Branches
Tags
master
v0.1
v0.2
v0.3
v0.3.1
v0.4
v0.4.1
v0.5
v0.5.1
v0.5.2
v0.5.3
v0.5.4
v0.5.5
v0.6.0
v0.6.1
v0.6.2
v0.6.3
v0.6.4
v0.6.5
v0.6.6
v0.6.7
v0.6.8
v0.6.9
v0.7.0
v0.7.1
v0.7.10
v0.7.2
v0.7.3
v0.7.4
v0.7.5
v0.7.6
v0.7.7
v0.7.8
v0.7.9
v0.8.0
v0.8.1
BlinkenLib
BlinkenMovie.c
BlinkenLib v.0.1.1 (2005-01-23)
Christian Heimke
commited
962674e
at 2011-07-15 09:00:54
BlinkenMovie.c
Blame
History
Raw
/* BlinkenLib * version 0.1 date 2004-11-25 * Copyright (C) 2004: Stefan Schuermans <1stein@schuermans.info> * Copyleft: GNU public license - http://www.gnu.org/copyleft/gpl.html * a blinkenarea.org project * powered by eventphone.de */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include "BlinkenConstants.h" #include "BlinkenFrame.h" #include "BlinkenMovie.h" #include "Tools.h" struct sBlinkenMovie { int height; int width; int channels; int maxval; int infoCnt; char * * * pppInfos; int frameCnt; stBlinkenFrame * * ppFrames; }; stBlinkenMovie * BlinkenMovieNew( int height, int width, int channels, int maxval ) { stBlinkenMovie * pMovie; if( height < BlinkenHeightMin ) height = BlinkenHeightMin; if( height > BlinkenHeightMax ) height = BlinkenHeightMax; if( width < BlinkenWidthMin ) width = BlinkenWidthMin; if( width > BlinkenWidthMax ) width = BlinkenWidthMax; if( channels < BlinkenChannelsMin ) channels = BlinkenChannelsMin; if( channels > BlinkenChannelsMax ) channels = BlinkenMaxvalMax; if( maxval < BlinkenMaxvalMin ) maxval = BlinkenMaxvalMin; if( maxval > BlinkenMaxvalMax ) maxval = BlinkenMaxvalMax; pMovie = (stBlinkenMovie *)malloc( sizeof( stBlinkenMovie ) ); if( pMovie == NULL ) return NULL; pMovie->height = height; pMovie->width = width; pMovie->channels = channels; pMovie->maxval = maxval; pMovie->infoCnt = 0; pMovie->pppInfos = (char * * *)malloc2D( 0, 2, sizeof( char * ) ); if( pMovie->pppInfos == NULL ) { free( pMovie ); return NULL; } pMovie->frameCnt = 0; pMovie->ppFrames = (stBlinkenFrame * *)malloc1D( 0, sizeof( stBlinkenFrame * ) ); if( pMovie->ppFrames == NULL ) { free( pMovie->pppInfos ); free( pMovie ); return NULL; } return pMovie; } stBlinkenMovie * BlinkenMovieClone( stBlinkenMovie * pSrcMovie ) { stBlinkenMovie * pMovie; int i; pMovie = BlinkenMovieNew( pSrcMovie->height, pSrcMovie->width, pSrcMovie->channels, pSrcMovie->maxval ); if( pMovie == NULL ) return NULL; for( i = 0; i < pSrcMovie->infoCnt; i++ ) BlinkenMovieAppendInfo( pMovie, pSrcMovie->pppInfos[i][0], pSrcMovie->pppInfos[i][1] ); for( i = 0; i < pSrcMovie->frameCnt; i++ ) BlinkenMovieAppendFrame( pMovie, BlinkenFrameClone( pSrcMovie->ppFrames[i] ) ); return pMovie; } void BlinkenMovieFree( stBlinkenMovie * pMovie ) { int i; if( pMovie == NULL ) return; for( i = 0; i < pMovie->infoCnt; i++ ) { free( pMovie->pppInfos[i][0] ); free( pMovie->pppInfos[i][1] ); } free( pMovie->pppInfos ); for( i = 0; i < pMovie->frameCnt; i++ ) BlinkenFrameFree( pMovie->ppFrames[i] ); free( pMovie->ppFrames ); free( pMovie ); } int BlinkenMovieGetHeight( stBlinkenMovie * pMovie ) { if( pMovie == NULL ) return 0; return pMovie->height; } int BlinkenMovieGetWidth( stBlinkenMovie * pMovie ) { if( pMovie == NULL ) return 0; return pMovie->width; } int BlinkenMovieGetChannels( stBlinkenMovie * pMovie ) { if( pMovie == NULL ) return 0; return pMovie->channels; } int BlinkenMovieGetMaxval( stBlinkenMovie * pMovie ) { if( pMovie == NULL ) return 0; return pMovie->maxval; } int BlinkenMovieGetDuration( stBlinkenMovie * pMovie ) { int i, duration; if( pMovie == NULL ) return 0; duration = 0; for( i = 0; i < pMovie->frameCnt; i++ ) duration += BlinkenFrameGetDuration( pMovie->ppFrames[i] ); return duration; } int BlinkenMovieGetInfoCnt( stBlinkenMovie * pMovie ) { if( pMovie == NULL ) return 0; return pMovie->infoCnt; } char * BlinkenMovieGetInfoType( stBlinkenMovie * pMovie, int infoNo ) { if( pMovie == NULL || pMovie->infoCnt < 1 ) return ""; if( infoNo < 0 ) infoNo = 0; if( infoNo >= pMovie->infoCnt ) infoNo = pMovie->infoCnt - 1; return pMovie->pppInfos[infoNo][0]; } char * BlinkenMovieGetInfoData( stBlinkenMovie * pMovie, int infoNo ) { if( pMovie == NULL || pMovie->infoCnt < 1 ) return ""; if( infoNo < 0 ) infoNo = 0; if( infoNo >= pMovie->infoCnt ) infoNo = pMovie->infoCnt - 1; return pMovie->pppInfos[infoNo][1]; } void BlinkenMovieSetInfo( stBlinkenMovie * pMovie, int infoNo, char * pInfoType, char * pInfoData ) { char * pType, * pData; if( pMovie == NULL || infoNo < 0 || infoNo >= pMovie->infoCnt ) return; pType = strdup( pInfoType ); if( pType == NULL ) return; pData = strdup( pInfoData ); if( pData == NULL ) { free( pType ); return; } free( pMovie->pppInfos[infoNo][0] ); pMovie->pppInfos[infoNo][0] = pType; free( pMovie->pppInfos[infoNo][1] ); pMovie->pppInfos[infoNo][1] = pData; } void BlinkenMovieInsertInfo( stBlinkenMovie * pMovie, int infoNo, char * pInfoType, char * pInfoData ) { char * * * pppNewInfos, * pType, * pData; int i; if( pMovie == NULL || infoNo < 0 || infoNo > pMovie->infoCnt ) return; pppNewInfos = (char * * *)malloc2D( pMovie->infoCnt + 1, 2, sizeof( char * ) ); if( pppNewInfos == NULL ) return; pType = strdup( pInfoType ); if( pType == NULL ) { free( pppNewInfos ); return; } pData = strdup( pInfoData ); if( pData == NULL ) { free( pppNewInfos ); free( pType ); return; } for( i = 0; i < infoNo; i++ ) { pppNewInfos[i][0] = pMovie->pppInfos[i][0]; pppNewInfos[i][1] = pMovie->pppInfos[i][1]; } pppNewInfos[infoNo][0] = pType; pppNewInfos[infoNo][1] = pData; for( i = infoNo; i < pMovie->infoCnt; i++ ) { pppNewInfos[i+1][0] = pMovie->pppInfos[i][0]; pppNewInfos[i+1][1] = pMovie->pppInfos[i][1]; } free( pMovie->pppInfos ); pMovie->pppInfos = pppNewInfos; pMovie->infoCnt++; } void BlinkenMovieAppendInfo( stBlinkenMovie * pMovie, char * pInfoType, char * pInfoData ) { if( pMovie == NULL ) return; BlinkenMovieInsertInfo( pMovie, pMovie->infoCnt, pInfoType, pInfoData ); } void BlinkenMovieDeleteInfo( stBlinkenMovie * pMovie, int infoNo ) { char * * * pppNewInfos; int i; if( pMovie == NULL || infoNo < 0 || infoNo >= pMovie->infoCnt ) return; pppNewInfos = (char * * *)malloc2D( pMovie->infoCnt - 1, 2, sizeof( char * ) ); if( pppNewInfos == NULL ) return; for( i = 0; i < infoNo; i++ ) { pppNewInfos[i][0] = pMovie->pppInfos[i][0]; pppNewInfos[i][1] = pMovie->pppInfos[i][1]; } free( pMovie->pppInfos[infoNo][0] ); free( pMovie->pppInfos[infoNo][1] ); for( i = infoNo; i < pMovie->infoCnt - 1; i++ ) { pppNewInfos[i][0] = pMovie->pppInfos[i+1][0]; pppNewInfos[i][1] = pMovie->pppInfos[i+1][1]; } free( pMovie->pppInfos ); pMovie->pppInfos = pppNewInfos; pMovie->infoCnt--; } void BlinkenMovieDeleteInfos( stBlinkenMovie * pMovie ) { char * * * pppNewInfos; int i; if( pMovie == NULL ) return; pppNewInfos = (char * * *)malloc2D( 0, 2, sizeof( char * ) ); if( pppNewInfos == NULL ) return; for( i = 0; i < pMovie->infoCnt; i++ ) { free( pMovie->pppInfos[i][0] ); free( pMovie->pppInfos[i][1] ); } free( pMovie->pppInfos ); pMovie->pppInfos = pppNewInfos; pMovie->infoCnt = 0; } int BlinkenMovieGetFrameCnt( stBlinkenMovie * pMovie ) { if( pMovie == NULL ) return 0; return pMovie->frameCnt; } stBlinkenFrame * BlinkenMovieGetFrame( stBlinkenMovie * pMovie, int frameNo ) { if( pMovie == NULL || pMovie->frameCnt < 1 ) return NULL; if( frameNo < 0 ) frameNo = 0; if( frameNo >= pMovie->frameCnt ) frameNo = pMovie->frameCnt - 1; return pMovie->ppFrames[frameNo]; } void BlinkenMovieSetFrame( stBlinkenMovie * pMovie, int frameNo, stBlinkenFrame * pFrame ) { if( pMovie == NULL || frameNo < 0 || frameNo >= pMovie->frameCnt ) return; BlinkenFrameResize( pFrame, pMovie->height, pMovie->width, pMovie->channels, pMovie->maxval ); pMovie->ppFrames[frameNo] = pFrame; } void BlinkenMovieInsertFrame( stBlinkenMovie * pMovie, int frameNo, stBlinkenFrame * pFrame ) { stBlinkenFrame * * ppNewFrames; int i; if( pMovie == NULL || frameNo < 0 || frameNo > pMovie->frameCnt ) return; ppNewFrames = (stBlinkenFrame * *)malloc1D( pMovie->frameCnt + 1, sizeof( stBlinkenFrame * ) ); if( ppNewFrames == NULL ) return; for( i = 0; i < frameNo; i++ ) ppNewFrames[i] = pMovie->ppFrames[i]; BlinkenFrameResize( pFrame, pMovie->height, pMovie->width, pMovie->channels, pMovie->maxval ); ppNewFrames[frameNo] = pFrame; for( i = frameNo; i < pMovie->frameCnt; i++ ) ppNewFrames[i+1] = pMovie->ppFrames[i]; free( pMovie->ppFrames ); pMovie->ppFrames = ppNewFrames; pMovie->frameCnt++; } void BlinkenMovieAppendFrame( stBlinkenMovie * pMovie, stBlinkenFrame * pFrame ) { if( pMovie == NULL ) return; BlinkenMovieInsertFrame( pMovie, pMovie->frameCnt, pFrame ); } void BlinkenMovieDeleteFrame( stBlinkenMovie * pMovie, int frameNo ) { stBlinkenFrame * * ppNewFrames; int i; if( pMovie == NULL || frameNo < 0 || frameNo >= pMovie->frameCnt ) return; ppNewFrames = (stBlinkenFrame * *)malloc1D( pMovie->frameCnt - 1, sizeof( stBlinkenFrame * ) ); if( ppNewFrames == NULL ) return; for( i = 0; i < frameNo; i++ ) ppNewFrames[i] = pMovie->ppFrames[i]; BlinkenFrameFree( pMovie->ppFrames[frameNo] ); for( i = frameNo; i < pMovie->frameCnt - 1; i++ ) ppNewFrames[i] = pMovie->ppFrames[i+1]; free( pMovie->ppFrames ); pMovie->ppFrames = ppNewFrames; pMovie->frameCnt--; } void BlinkenMovieDeleteFrames( stBlinkenMovie * pMovie ) { stBlinkenFrame * * ppNewFrames; int i; if( pMovie == NULL ) return; ppNewFrames = (stBlinkenFrame * *)malloc1D( 0, sizeof( stBlinkenFrame * ) ); if( ppNewFrames == NULL ) return; for( i = 0; i < pMovie->frameCnt; i++ ) BlinkenFrameFree( pMovie->ppFrames[i] ); free( pMovie->ppFrames ); pMovie->ppFrames = ppNewFrames; pMovie->frameCnt = 0; } void BlinkenMovieResize( stBlinkenMovie * pMovie, int height, int width, int channels, int maxval ) { int i; if( pMovie == NULL ) return; if( height < BlinkenHeightMin ) height = BlinkenHeightMin; if( height > BlinkenHeightMax ) height = BlinkenHeightMax; if( width < BlinkenWidthMin ) width = BlinkenWidthMin; if( width > BlinkenWidthMax ) width = BlinkenWidthMax; if( channels < BlinkenChannelsMin ) channels = BlinkenChannelsMin; if( channels > BlinkenChannelsMax ) channels = BlinkenMaxvalMax; if( maxval < BlinkenMaxvalMin ) maxval = BlinkenMaxvalMin; if( maxval > BlinkenMaxvalMax ) maxval = BlinkenMaxvalMax; pMovie->height = height; pMovie->width = width; pMovie->channels = channels; pMovie->maxval = maxval; for( i = 0; i < pMovie->frameCnt; i++ ) BlinkenFrameResize( pMovie->ppFrames[i], height, width, channels, maxval ); } void BlinkenMovieScale( stBlinkenMovie * pMovie, int height, int width ) { int i; if( pMovie == NULL ) return; if( height < BlinkenHeightMin ) height = BlinkenHeightMin; if( height > BlinkenHeightMax ) height = BlinkenHeightMax; if( width < BlinkenWidthMin ) width = BlinkenWidthMin; if( width > BlinkenWidthMax ) width = BlinkenWidthMax; pMovie->height = height; pMovie->width = width; for( i = 0; i < pMovie->frameCnt; i++ ) BlinkenFrameScale( pMovie->ppFrames[i], height, width ); } char * BlinkenMovieToString( stBlinkenMovie * pMovie ) { char * * strs, * str, * ptr; int i, size; if( pMovie == NULL ) return NULL; strs = (char * *)malloc1D( pMovie->frameCnt, sizeof( char * ) ); if( strs == NULL ) return NULL; for( i = 0; i < pMovie->frameCnt; i++ ) { strs[i] = BlinkenFrameToString( pMovie->ppFrames[i] ); if( strs[i] == NULL ) { for( i--; i >= 0; i-- ) free( strs[i] ); free( strs ); return NULL; } } size = 128; for( i = 0; i < pMovie->infoCnt; i++ ) size += strlen( pMovie->pppInfos[i][0] ) + strlen( pMovie->pppInfos[i][1] ) + 8; for( i = 0; i < pMovie->frameCnt; i++ ) size += strlen( strs[i] ) + 32; str = (char *)malloc( size ); if( str == NULL ) { for( i = 0; i < pMovie->frameCnt; i++ ) free( strs[i] ); free( strs ); return NULL; } ptr = str; sprintf( ptr, "BlinkenMovie %ux%u-%u/%u\n", pMovie->width, pMovie->height, pMovie->channels, pMovie->maxval ); ptr += strlen( ptr ); for( i = 0; i < pMovie->infoCnt; i++ ) { sprintf( ptr, "%s = %s\n", pMovie->pppInfos[i][0], pMovie->pppInfos[i][1] ); ptr += strlen( ptr ); } for( i = 0; i < pMovie->frameCnt; i++ ) { sprintf( ptr, "frame %u\n%s", i, strs[i] ); ptr += strlen( ptr ); free( strs[i] ); } free( strs ); return str; } stBlinkenMovie * BlinkenMovieLoadBlm( char * pFilename ) { FILE * pFile; stBlinkenMovie * pMovie; stBlinkenFrame * pFrame; int width, height, y, x, chr, duration; char infoType[256], infoData[1024], pixel[2]; if( pFilename == NULL ) return NULL; //open file pFile = fopen( pFilename, "rt" ); //read magic and size if( fscanf( pFile, " # BlinkenLights Movie %ux%u", &width, &height ) != 2 ) { fclose( pFile ); return NULL; } //allocate a new movie pMovie = BlinkenMovieNew( height, width, 1, 1 ); if( pMovie == NULL ) { fclose( pFile ); return NULL; } //no frame yet pFrame = NULL; y = 0; //read frames while( ! feof( pFile ) ) { //skip rest of previous line (including newline) while( (chr = fgetc( pFile )) != '\n' && chr != EOF ); //info line if( fscanf( pFile, " # %255[A-Za-z0-9] %*[=:] %1023[^\n]", infoType, infoData ) == 2 ) { BlinkenMovieAppendInfo( pMovie, infoType, infoData ); } //start of frame else if( fscanf( pFile, " @ %u", &duration ) == 1 ) { //create new frame and append it to movie pFrame = BlinkenFrameNew( height, width, 1, 1, duration ); if( pFrame != NULL ) { BlinkenFrameClear( pFrame ); BlinkenMovieAppendFrame( pMovie, pFrame ); y = 0; } } //data line else if( fscanf( pFile, "%1[01]", pixel ) == 1 ) { if( pFrame != NULL ) { for( x = 0; ; x++ ) { BlinkenFrameSetPixel( pFrame, y, x, 0, pixel[0] == '1' ? 1 : 0 ); //set pixel if( fscanf( pFile, "%1[01]", pixel ) != 1 ) //read next pixel break; } y++; //next row } } } //while( ! feof( pFile ) ) //close file fclose( pFile ); return pMovie; } stBlinkenMovie * BlinkenMovieLoadBmm( char * pFilename ) { FILE * pFile; stBlinkenMovie * pMovie; stBlinkenFrame * pFrame; int width, height, y, x, chr, duration, val; char infoType[256], infoData[1024], pixel[8]; if( pFilename == NULL ) return NULL; //open file pFile = fopen( pFilename, "rt" ); //read magic and size if( fscanf( pFile, " # BlinkenMini Movie %ux%u", &width, &height ) != 2 ) { fclose( pFile ); return NULL; } //allocate a new movie pMovie = BlinkenMovieNew( height, width, 1, 255 ); if( pMovie == NULL ) { fclose( pFile ); return NULL; } //no frame yet pFrame = NULL; y = 0; //read frames while( ! feof( pFile ) ) { //skip rest of previous line (including newline) while( (chr = fgetc( pFile )) != '\n' && chr != EOF ); //info line if( fscanf( pFile, " # %255[A-Za-z0-9] %*[=:] %1023[^\n]", infoType, infoData ) == 2 ) { BlinkenMovieAppendInfo( pMovie, infoType, infoData ); } //start of frame else if( fscanf( pFile, " @ %u", &duration ) == 1 ) { //create new frame and append it to movie pFrame = BlinkenFrameNew( height, width, 1, 255, duration ); if( pFrame != NULL ) { BlinkenFrameClear( pFrame ); BlinkenMovieAppendFrame( pMovie, pFrame ); y = 0; } } //data line else if( fscanf( pFile, "%7[0-9A-FXa-fx]", pixel ) == 1 ) { if( pFrame != NULL ) { for( x = 0; ; x++ ) { if( sscanf( pixel, "%i", &val ) != 1 ) //convert pixel to number break; BlinkenFrameSetPixel( pFrame, y, x, 0, (unsigned char)val ); //set pixel fscanf( pFile, "%*[ \t]" ); //kill space if( fscanf( pFile, "%7[0-9A-FXa-fx]", pixel ) != 1 ) //read next pixel break; } y++; //next row } } } //while( ! feof( pFile ) ) //close file fclose( pFile ); return pMovie; } stBlinkenMovie * BlinkenMovieLoadBml( char * pFilename ) { FILE * pFile; stBlinkenMovie * pMovie; stBlinkenFrame * pFrame; int width, height, channels, bits, maxval, chrs, y, x, c, duration, val; char buffer[2048], infoType[256], infoData[1024], pixelFormat[16], pixel[8], * ptr, chr; if( pFilename == NULL ) return NULL; //open file pFile = fopen( pFilename, "rt" ); //no movie yet - blm tag not yet found pMovie = NULL; //no frame yet pFrame = NULL; y = 0; //read tags maxval = 0; while( ! feof( pFile ) ) { //skip to just before beginning of next tag fscanf( pFile, "%*[^<]" ); //skip beginning character of next tag if( fgetc( pFile ) != '<' ) //end loop (no more tags) break; //no blm tag yet if( pMovie == NULL ) { //blm tag if( fscanf( pFile, "blm%2047[^>]", buffer ) == 1 ) { //get attributes width = 0; height = 0; channels = 0; bits = 0; maxval = 0; if( (ptr = strstr( buffer, "height=\"" )) != NULL ) //height sscanf( ptr+8, "%u", &height ); if( (ptr = strstr( buffer, "width=\"" )) != NULL ) //width sscanf( ptr+7, "%u", &width ); if( (ptr = strstr( buffer, "channels=\"" )) != NULL ) //channels sscanf( ptr+10, "%u", &channels ); if( (ptr = strstr( buffer, "bits=\"" )) != NULL ) //bits sscanf( ptr+6, "%u", &bits ); maxval = (1 << bits) - 1; //maxval //allocate a new movie pMovie = BlinkenMovieNew( height, width, channels, maxval ); if( pMovie == NULL ) { fclose( pFile ); return NULL; } //get number of characters per channel chrs = (bits + 3) >> 2; //get fscanf formart string for reading a pixel sprintf( pixelFormat, "%%%d[0-9A-Fa-f]", chrs > 4 ? 5 : chrs ); //read max 5 chars (2 already in use by prefix) //initialize pixel buffer with hex prefix strcpy( pixel, "0x" ); } } //if( pMovie == NULL ) //blm tag was already found else //if( pMovie == NULL ) { //title tag if( fscanf( pFile, "title>%2047[^<]", buffer ) == 1 ) { //add info to movie BlinkenMovieAppendInfo( pMovie, "title", buffer ); } //description tag else if( fscanf( pFile, "description>%2047[^<]", buffer ) == 1 ) { //check if generic info if( sscanf( buffer, "%255[A-Za-z0-9] %*[=:] %1023[^\n]", infoType, infoData ) == 2 ) //add info to movie BlinkenMovieAppendInfo( pMovie, infoType, infoData ); else //add info to movie BlinkenMovieAppendInfo( pMovie, "description", buffer ); } //creator tag else if( fscanf( pFile, "creator>%2047[^<]", buffer ) == 1 ) { //add info to movie BlinkenMovieAppendInfo( pMovie, "creator", buffer ); } //author tag else if( fscanf( pFile, "author>%2047[^<]", buffer ) == 1 ) { //add info to movie BlinkenMovieAppendInfo( pMovie, "author", buffer ); } //email tag else if( fscanf( pFile, "email>%2047[^<]", buffer ) == 1 ) { //add info to movie BlinkenMovieAppendInfo( pMovie, "email", buffer ); } //url tag else if( fscanf( pFile, "url>%2047[^<]", buffer ) == 1 ) { //add info to movie BlinkenMovieAppendInfo( pMovie, "url", buffer ); } //frame tag else if( fscanf( pFile, "frame%2047[^>]", buffer ) == 1 ) { //get attributes duration = 0; if( (ptr = strstr( buffer, "duration=\"" )) != NULL ) //duration sscanf( ptr+10, "%u", &duration ); //create new frame and append it to movie pFrame = BlinkenFrameNew( height, width, channels, maxval, duration ); if( pFrame != NULL ) { BlinkenFrameClear( pFrame ); BlinkenMovieAppendFrame( pMovie, pFrame ); y = 0; } } //row tag else if( fscanf( pFile, "row%c", &chr ) == 1 && chr == '>' ) { if( pFrame != NULL ) { //parse row for( x = 0; x < width; x++ ) { for( c = 0; c < channels; c++ ) { //read next pixel (one channel of pixel) if( fscanf( pFile, pixelFormat, pixel+2 ) != 1 ) { x = width; //also terminate x loop break; } //convert pixel (one channel of pixel) to number if( sscanf( pixel, "%i", &val ) != 1 ) { x = width; //also terminate x loop break; } //set pixel (one channel of pixel) BlinkenFrameSetPixel( pFrame, y, x, c, (unsigned char)val ); } } y++; //next row } } } //if( pMovie == NULL ) ... else } //while( ! feof( pFile ) ) //close file fclose( pFile ); return pMovie; } /* public boolean loadBml( String filename ) { Pattern blmTag, blmHeight, blmWidth, blmChannels, blmBits; Pattern infoTitle, infoDescription, infoGeneric, infoCreator, infoAuthor, infoEmail, infoUrl; Pattern frameTag, frameDuration, rowTag, tag; BufferedReader file; String line, data, row; boolean blmTagFound; Matcher matcher, submatcher; int height, width, channels, bits, maxval, chrs, duration, y, x, c, len, i, val; BlinkenFrame frame; //initialize needed regexp patterns blmTag = Pattern.compile( "^[^<]*<blm([^>]*)>" ); blmHeight = Pattern.compile( "height=\"?([0-9]*)\"?" ); blmWidth = Pattern.compile( "width=\"?([0-9]*)\"?" ); blmChannels = Pattern.compile( "channels=\"?([0-9]*)\"?" ); blmBits = Pattern.compile( "bits=\"?([0-9]*)\"?" ); infoTitle = Pattern.compile( "^[^<]*<title>([^<]*)</title>" ); infoDescription = Pattern.compile( "[^<]*<description>([^<]*)</description>" ); infoGeneric = Pattern.compile( "^([A-Za-z0-9]+)(?: *= *|: *)(.*)" ); infoCreator = Pattern.compile( "^[^<]*<creator>([^<]*)</creator>" ); infoAuthor = Pattern.compile( "^[^<]*<author>([^<]*)</author>" ); infoEmail = Pattern.compile( "^[^<]*<email>([^<]*)</email>" ); infoUrl = Pattern.compile( "^[^<]*<url>([^<]*)</url>" ); frameTag = Pattern.compile( "^[^<]*<frame([^>]*)>" ); frameDuration = Pattern.compile( "duration=\"?([0-9]*)\"?" ); rowTag = Pattern.compile( "^[^<]*<row>([0-9A-Fa-f]*)</row>" ); tag = Pattern.compile( "^[^<]*<[^>]*>" ); //delete all frames deleteInfos( ); deleteFrames( ); resize( 0, 0, 0, 0 ); //try to read file try { //open file file = new BufferedReader( new FileReader( filename ) ); //create unused dummy frame for beginning frame = new BlinkenFrame( 0, 0, 0, 0, 0 ); y = 0; chrs = 1; //read file data = ""; blmTagFound = false; while( (line = file.readLine( )) != null ) { data += " " + line; //add new line to data //match tags while( true ) { //no blm tag yet if( ! blmTagFound ) { //blm tag if( (matcher = blmTag.matcher( data )).find( ) ) { //remove matched part data = data.substring( matcher.end( ) ); //get attributes width = 0; height = 0; channels = 0; bits = 0; maxval = 0; if( (submatcher = blmHeight.matcher( matcher.group( 1 ) )).find( ) ) //height height = Integer.parseInt( submatcher.group( 1 ) ); if( (submatcher = blmWidth.matcher( matcher.group( 1 ) )).find( ) ) //width width = Integer.parseInt( submatcher.group( 1 ) ); if( (submatcher = blmChannels.matcher( matcher.group( 1 ) )).find( ) ) //channels channels = Integer.parseInt( submatcher.group( 1 ) ); if( (submatcher = blmBits.matcher( matcher.group( 1 ) )).find( ) ) //bits maxval = (1 << (bits = Integer.parseInt( submatcher.group( 1 ) ))) - 1; //remember that blm tag was found blmTagFound = true; //set movie size resize( height, width, channels, maxval ); //get number of characters per channel chrs = (bits + 3) >> 2; } //unknown tag else if( (matcher = tag.matcher( data )).find( ) ) //remove matched part data = data.substring( matcher.end( ) ); //nothing matches else //end loop break; } //if( ! blmTagFound ) //blm tag was already found else { //title tag if( (matcher = infoTitle.matcher( data )).find( ) ) { //remove matched part data = data.substring( matcher.end( ) ); //add info to movie appendInfo( "title", matcher.group( 1 ) ); } //description tag else if( (matcher = infoDescription.matcher( data )).find( ) ) { //remove matched part data = data.substring( matcher.end( ) ); //check if generic info if( (submatcher = infoGeneric.matcher( matcher.group( 1 ) )).find( ) ) //add info to movie appendInfo( submatcher.group( 1 ), submatcher.group( 2 ) ); else //add info to movie appendInfo( "description", matcher.group( 1 ) ); } //creator tag else if( (matcher = infoCreator.matcher( data )).find( ) ) { //remove matched part data = data.substring( matcher.end( ) ); //add info to movie appendInfo( "creator", matcher.group( 1 ) ); } //author tag else if( (matcher = infoAuthor.matcher( data )).find( ) ) { //remove matched part data = data.substring( matcher.end( ) ); //add info to movie appendInfo( "author", matcher.group( 1 ) ); } //email tag else if( (matcher = infoEmail.matcher( data )).find( ) ) { //remove matched part data = data.substring( matcher.end( ) ); //add info to movie appendInfo( "email", matcher.group( 1 ) ); } //url tag else if( (matcher = infoUrl.matcher( data )).find( ) ) { //remove matched part data = data.substring( matcher.end( ) ); //add info to movie appendInfo( "url", matcher.group( 1 ) ); } //frame tag else if( (matcher = frameTag.matcher( data )).find( ) ) { //remove matched part data = data.substring( matcher.end( ) ); //get attributes duration = 0; if( (submatcher = frameDuration.matcher( matcher.group( 1 ) )).find( ) ) //duration duration = Integer.parseInt( submatcher.group( 1 ) ); //create new frame and append it to movie frame = new BlinkenFrame( this.height, this.width, this.channels, this.maxval, duration ); frame.clear( ); appendFrame( frame ); y = 0; } //row tag else if( (matcher = rowTag.matcher( data )).find( ) ) { //remove matched part data = data.substring( matcher.end( ) ); //parse row row = matcher.group( 1 ); len = row.length( ); i = 0; for( x = 0; x < this.width && i + chrs <= len; x++ ) { for( c = 0; c < this.channels && i + chrs <= len; c++, i += chrs ) { val = Integer.parseInt( row.substring( i, i + chrs ), 0x10 ); frame.setPixel( y, x, c, (byte)val ); //set pixel } } y++; //next row } //unknown tag else if( (matcher = tag.matcher( data )).find( ) ) //remove matched part data = data.substring( matcher.end( ) ); //nothing matches else //end loop break; } //if( ! blmTagFound ) ... else } //while( true ) } //while( (line = ... //close file file.close( ); //success return true; } catch( IOException e ) { } //some error return false; } */ stBlinkenMovie * BlinkenMovieLoadBbm( char * pFilename ) { FILE * pFile; stBlinkenMovie * pMovie; stBlinkenFrame * pFrame; unsigned char header[24], subHeader[6], frameStartMarker[4]; unsigned long headerMagic, headerFrameCnt, headerDuration, headerFramePtr; unsigned short headerHeight, headerWidth, headerChannels, headerMaxval; unsigned long subHeaderMagic, frameStartMarkerMagic; unsigned short subHeaderSize; unsigned char * pInfoHeader, * pInfoHeaderX, * pFrameData; int len, duration, y, x, c, i; if( pFilename == NULL ) return NULL; //open file pFile = fopen( pFilename, "rb" ); //read header if( fread( header, 1, 24, pFile ) != 24 ) { fclose( pFile ); return NULL; } headerMagic = (unsigned long)header[0] << 24 | (unsigned long)header[1] << 16 | (unsigned long)header[2] << 8 | (unsigned long)header[3]; headerHeight = (unsigned short)header[4] << 8 | (unsigned short)header[5]; headerWidth = (unsigned short)header[6] << 8 | (unsigned short)header[7]; headerChannels = (unsigned short)header[8] << 8 | (unsigned short)header[9]; headerMaxval = (unsigned short)header[10] << 8 | (unsigned short)header[11]; headerFrameCnt = (unsigned long)header[12] << 24 | (unsigned long)header[13] << 16 | (unsigned long)header[14] << 8 | (unsigned long)header[15]; headerDuration = (unsigned long)header[16] << 24 | (unsigned long)header[17] << 16 | (unsigned long)header[18] << 8 | (unsigned long)header[19]; headerFramePtr = (unsigned long)header[20] << 24 | (unsigned long)header[21] << 16 | (unsigned long)header[22] << 8 | (unsigned long)header[23]; //check magic if( headerMagic != 0x23542666 ) { fclose( pFile ); return NULL; } //allocate a new movie pMovie = BlinkenMovieNew( headerHeight, headerWidth, headerChannels, headerMaxval ); if( pMovie == NULL ) { fclose( pFile ); return NULL; } //read subheaders while( ftell( pFile ) + 6 <= (long)headerFramePtr ) { if( fread( subHeader, 1, 6, pFile ) != 6 ) { BlinkenMovieFree( pMovie ); fclose( pFile ); return NULL; } subHeaderMagic = (unsigned long)subHeader[0] << 24 | (unsigned long)subHeader[1] << 16 | (unsigned long)subHeader[2] << 8 | (unsigned long)subHeader[3]; subHeaderSize = (unsigned short)subHeader[4] << 8 | (unsigned short)subHeader[5]; //header fits into gap to frame start if( subHeaderSize >= 6 && ftell( pFile ) + subHeaderSize - 6 <= (long)headerFramePtr ) { //info header if( subHeaderMagic == 0x696E666F ) //'i' 'n' 'f' 'o' { //read rest of info header pInfoHeader = (unsigned char *)malloc( subHeaderSize - 6 ); if( pInfoHeader == NULL ) { BlinkenMovieFree( pMovie ); fclose( pFile ); return NULL; } if( fread( pInfoHeader, 1, subHeaderSize - 6, pFile ) != (unsigned short)(subHeaderSize - 6) ) { free( pInfoHeader ); BlinkenMovieFree( pMovie ); fclose( pFile ); return NULL; } //parse information if( (pInfoHeaderX = memchr( pInfoHeader, 0, subHeaderSize - 6 )) != NULL ) { pInfoHeaderX++; len = pInfoHeaderX - pInfoHeader; if( memchr( pInfoHeaderX, 0, subHeaderSize - 6 - len ) != NULL ) BlinkenMovieAppendInfo( pMovie, pInfoHeader, pInfoHeaderX ); } free( pInfoHeader ); } //unknown subHeader else //skip fseek( pFile, subHeaderSize - 6, SEEK_CUR ); } //if( ftell( pFile ) ... } //while( ftell( pFile ) ... //seek to start of frames fseek( pFile, headerFramePtr, SEEK_SET ); //read frame start marker if( fread( frameStartMarker, 1, 4, pFile ) != 4 ) { BlinkenMovieFree( pMovie ); fclose( pFile ); return NULL; } frameStartMarkerMagic = (unsigned long)frameStartMarker[0] << 24 | (unsigned long)frameStartMarker[1] << 16 | (unsigned long)frameStartMarker[2] << 8 | (unsigned long)frameStartMarker[3]; if( frameStartMarkerMagic != 0x66726D73 ) //'f' 'r' 'm' 's' { BlinkenMovieFree( pMovie ); fclose( pFile ); return NULL; } //allocate buffer for frame data len = 2 + headerHeight * headerWidth * headerChannels; pFrameData = (unsigned char *)malloc( len ); if( pFrameData == NULL ) { BlinkenMovieFree( pMovie ); fclose( pFile ); return NULL; } //read frames for( ; ; ) { //read frame if( fread( pFrameData, 1, len, pFile ) != (unsigned int)len ) break; duration = (unsigned short)pFrameData[0] << 8 | (unsigned short)pFrameData[1]; //build frame and append it to movie pFrame = BlinkenFrameNew( headerHeight, headerWidth, headerChannels, headerMaxval, duration ); if( pFrame == NULL ) break; i = 2; for( y = 0; y < headerHeight; y++ ) for( x = 0; x < headerWidth; x++ ) for( c = 0; c < headerChannels; c++, i++ ) BlinkenFrameSetPixel( pFrame, y, x, c, pFrameData[i] ); BlinkenMovieAppendFrame( pMovie, pFrame ); } //for( ; ; ) //free buffer for frame data free( pFrameData ); //close file fclose( pFile ); return pMovie; } stBlinkenMovie * BlinkenMovieLoad( char * pFilename ) { int len; if( pFilename == NULL ) return NULL; len = strlen( pFilename ); if( len > 4 && strcmp( pFilename + len - 4, ".blm" ) == 0 ) return BlinkenMovieLoadBlm( pFilename ); if( len > 4 && strcmp( pFilename + len - 4, ".bmm" ) == 0 ) return BlinkenMovieLoadBmm( pFilename ); if( len > 4 && strcmp( pFilename + len - 4, ".bml" ) == 0 ) return BlinkenMovieLoadBml( pFilename ); if( len > 4 && strcmp( pFilename + len - 4, ".bbm" ) == 0 ) return BlinkenMovieLoadBbm( pFilename ); return NULL; } int BlinkenMovieSaveBlm( stBlinkenMovie * pMovie, char * pFilename ) { stBlinkenMovie * pOutMovie; FILE * pFile; int i, y, x; if( pMovie == NULL || pFilename == NULL ) return -1; //convert movie to suitable format pOutMovie = BlinkenMovieClone( pMovie ); if( pOutMovie == NULL ) return -1; BlinkenMovieResize( pOutMovie, pOutMovie->height, pOutMovie->width, 1, 1 ); //open file pFile = fopen( pFilename, "wt" ); if( pFile == NULL ) { BlinkenMovieFree( pOutMovie ); return -1; } //write header line fprintf( pFile, "# BlinkenLights Movie %ux%u\n", pOutMovie->width, pOutMovie->height ); //write information lines for( i = 0; i < pOutMovie->infoCnt; i++ ) fprintf( pFile, "# %s = %s\n", pOutMovie->pppInfos[i][0], pOutMovie->pppInfos[i][1] ); //write frames for( i = 0; i < pOutMovie->frameCnt; i++ ) { fprintf( pFile, "\n@%u\n", BlinkenFrameGetDuration( pOutMovie->ppFrames[i] ) ); for( y = 0; y < pOutMovie->height; y++ ) { for( x = 0; x < pOutMovie->width; x++ ) { if( BlinkenFrameGetPixel( pOutMovie->ppFrames[i], y, x, 0 ) != 0 ) fprintf( pFile, "1" ); else fprintf( pFile, "0" ); } fprintf( pFile, "\n" ); } } //close file fclose( pFile ); //free copied movie BlinkenMovieFree( pOutMovie ); //success return 0; } int BlinkenMovieSaveBmm( stBlinkenMovie * pMovie, char * pFilename ) { stBlinkenMovie * pOutMovie; FILE * pFile; int i, y, x; if( pMovie == NULL || pFilename == NULL ) return -1; //convert movie to suitable format pOutMovie = BlinkenMovieClone( pMovie ); if( pOutMovie == NULL ) return -1; BlinkenMovieResize( pOutMovie, pOutMovie->height, pOutMovie->width, 1, 255 ); //open file pFile = fopen( pFilename, "wt" ); if( pFile == NULL ) { BlinkenMovieFree( pOutMovie ); return -1; } //write header line fprintf( pFile, "# BlinkenMini Movie %ux%u\n", pOutMovie->width, pOutMovie->height ); //write information lines for( i = 0; i < pOutMovie->infoCnt; i++ ) fprintf( pFile, "# %s = %s\n", pOutMovie->pppInfos[i][0], pOutMovie->pppInfos[i][1] ); //write frames for( i = 0; i < pOutMovie->frameCnt; i++ ) { fprintf( pFile, "\n@%u\n", BlinkenFrameGetDuration( pOutMovie->ppFrames[i] ) ); for( y = 0; y < pOutMovie->height; y++ ) { fprintf( pFile, "0x%02X", BlinkenFrameGetPixel( pOutMovie->ppFrames[i], y, 0, 0 ) ); for( x = 1; x < pOutMovie->width; x++ ) fprintf( pFile, " 0x%02X", BlinkenFrameGetPixel( pOutMovie->ppFrames[i], y, x, 0 ) ); fprintf( pFile, "\n" ); } } //close file fclose( pFile ); //free copied movie BlinkenMovieFree( pOutMovie ); //success return 0; } int BlinkenMovieSaveBml( stBlinkenMovie * pMovie, char * pFilename ) { stBlinkenMovie * pOutMovie; FILE * pFile; int bits, val, i, y, x, c; if( pMovie == NULL || pFilename == NULL ) return -1; //convert movie to suitable format pOutMovie = BlinkenMovieClone( pMovie ); if( pOutMovie == NULL ) return -1; val = pOutMovie->maxval; //get number of bits for( bits = 0; val != 0; val >>= 1, bits++ ); BlinkenMovieResize( pOutMovie, pOutMovie->height, pOutMovie->width, pOutMovie->channels, (1 << bits) - 1 ); //open file pFile = fopen( pFilename, "wt" ); if( pFile == NULL ) { BlinkenMovieFree( pOutMovie ); return -1; } //write header line fprintf( pFile, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" ); //write blm start tag fprintf( pFile, "<blm width=\"%u\" height=\"%u\" bits=\"%u\" channels=\"%u\">\n", pOutMovie->width, pOutMovie->height, bits, pOutMovie->channels ); //write information lines fprintf( pFile, "\t<header>\n" ); for( i = 0; i < pOutMovie->infoCnt; i++ ) { if( strcmp( pOutMovie->pppInfos[i][0], "title" ) == 0 ) fprintf( pFile, "\t\t<title>%s</title>\n", pOutMovie->pppInfos[i][1] ); else if( strcmp( pOutMovie->pppInfos[i][0], "description" ) == 0 ) fprintf( pFile, "\t\t<description>%s</description>\n", pOutMovie->pppInfos[i][1] ); else if( strcmp( pOutMovie->pppInfos[i][0], "creator" ) == 0 ) fprintf( pFile, "\t\t<creator>%s</creator>\n", pOutMovie->pppInfos[i][1] ); else if( strcmp( pOutMovie->pppInfos[i][0], "author" ) == 0) fprintf( pFile, "\t\t<author>%s</author>\n", pOutMovie->pppInfos[i][1] ); else if( strcmp( pOutMovie->pppInfos[i][0], "email" ) == 0) fprintf( pFile, "\t\t<email>%s</email>\n", pOutMovie->pppInfos[i][1] ); else if( strcmp( pOutMovie->pppInfos[i][0], "url" ) == 0) fprintf( pFile, "\t\t<url>%s</url>\n", pOutMovie->pppInfos[i][1] ); else fprintf( pFile, "\t\t<description>%s: %s</description>\n", pOutMovie->pppInfos[i][0], pOutMovie->pppInfos[i][1] ); } fprintf( pFile, "\t</header>\n" ); //write frames for( i = 0; i < pOutMovie->frameCnt; i++ ) { fprintf( pFile, "\n\t<frame duration=\"%u\">\n", BlinkenFrameGetDuration( pOutMovie->ppFrames[i] ) ); for( y = 0; y < pOutMovie->height; y++ ) { fprintf( pFile, "\t\t<row>" ); for( x = 0; x < pOutMovie->width; x++ ) for( c = 0; c < pOutMovie->channels; c++ ) fprintf( pFile, bits > 4 ? "%02X" : "%01X", BlinkenFrameGetPixel( pOutMovie->ppFrames[i], y, x, c ) ); fprintf( pFile, "</row>\n" ); } fprintf( pFile, "\t</frame>\n" ); } //write blm end tag fprintf( pFile, "</blm>\n" ); //close file fclose( pFile ); //free copied movie BlinkenMovieFree( pOutMovie ); //success return 0; } int BlinkenMovieSaveBbm( stBlinkenMovie * pMovie, char * pFilename ) { unsigned char * pFrameData; FILE * pFile; unsigned char header[24], infoHeader[6], framePointer[4], frameStartMarker[4]; int duration, len, len0, len1, i, j, y, x, c, val; long pos; if( pMovie == NULL || pFilename == NULL ) return -1; //allocate frame data buffer pFrameData = (unsigned char *)malloc( 2 + pMovie->height * pMovie->width * pMovie->channels ); if( pFrameData == NULL ) return -1; //open file pFile = fopen( pFilename, "wb" ); if( pFile == NULL ) { free( pFrameData ); return -1; } //write header header[0] = 0x23; //magic header[1] = 0x54; header[2] = 0x26; header[3] = 0x66; header[4] = (unsigned char)(pMovie->height >> 8); header[5] = (unsigned char)pMovie->height; header[6] = (unsigned char)(pMovie->width >> 8); header[7] = (unsigned char)pMovie->width; header[8] = (unsigned char)(pMovie->channels >> 8); header[9] = (unsigned char)pMovie->channels; header[10] = (unsigned char)(pMovie->maxval >> 8); header[11] = (unsigned char)pMovie->maxval; header[12] = (unsigned char)(pMovie->frameCnt >> 24); header[13] = (unsigned char)(pMovie->frameCnt >> 16); header[14] = (unsigned char)(pMovie->frameCnt >> 8); header[15] = (unsigned char)pMovie->frameCnt; duration = 0; for( i = 0; i < pMovie->frameCnt; i++ ) duration += BlinkenFrameGetDuration( pMovie->ppFrames[i] ); header[16] = (unsigned char)(duration >> 24); header[17] = (unsigned char)(duration >> 16); header[18] = (unsigned char)(duration >> 8); header[19] = (unsigned char)duration; header[20] = 0; //frame pointer is written later header[21] = 0; header[22] = 0; header[23] = 0; fwrite( header, 1, 24, pFile ); //write information for( i = 0; i < pMovie->infoCnt; i++ ) { len0 = strlen( pMovie->pppInfos[i][0] ); if( len0 > 32760 ) len0 = 32760; len1 = strlen( pMovie->pppInfos[i][1] ); if( len1 > 32760 ) len1 = 32760; len = 8 + len0 + len1; infoHeader[0] = 0x69; //'i' infoHeader[1] = 0x6E; //'n' infoHeader[2] = 0x66; //'f' infoHeader[3] = 0x6F; //'o' infoHeader[4] = (unsigned char)(len >> 8); infoHeader[5] = (unsigned char)len; fwrite( infoHeader, 1, 6, pFile ); fwrite( pMovie->pppInfos[i][0], 1, len0, pFile ); fwrite( "\0", 1, 1, pFile ); fwrite( pMovie->pppInfos[i][1], 1, len1, pFile ); fwrite( "\0", 1, 1, pFile ); } //write frame pointer pos = ftell( pFile ); framePointer[0] = (unsigned char)(pos >> 24); framePointer[1] = (unsigned char)(pos >> 16); framePointer[2] = (unsigned char)(pos >> 8); framePointer[3] = (unsigned char)pos; fseek( pFile, 20, SEEK_SET ); fwrite( framePointer, 1, 4, pFile ); fseek( pFile, pos, SEEK_SET ); //write frame start marker frameStartMarker[0] = 0x66; //'f' frameStartMarker[1] = 0x72; //'r' frameStartMarker[2] = 0x6D; //'m' frameStartMarker[3] = 0x73; //'s' fwrite( frameStartMarker, 1, 4, pFile ); //write frames for( i = 0; i < pMovie->frameCnt; i++ ) { val = BlinkenFrameGetDuration( pMovie->ppFrames[i] ); pFrameData[0] = (unsigned char)(val >> 8); pFrameData[1] = (unsigned char)val; for( j = 2, y = 0; y < pMovie->height; y++ ) for( x = 0; x < pMovie->width; x++ ) for( c = 0; c < pMovie->channels; c++, j++ ) pFrameData[j] = BlinkenFrameGetPixel( pMovie->ppFrames[i], y, x, c ); fwrite( pFrameData, 1, j, pFile ); } //free frame data buffer free( pFrameData ); //close file fclose( pFile ); //success return 0; } int BlinkenMovieSave( stBlinkenMovie * pMovie, char * pFilename ) { int len; if( pMovie == NULL || pFilename == NULL ) return -1; len = strlen( pFilename ); if( len > 4 && strcmp( pFilename + len - 4, ".blm" ) == 0 ) return BlinkenMovieSaveBlm( pMovie, pFilename ); if( len > 4 && strcmp( pFilename + len - 4, ".bmm" ) == 0 ) return BlinkenMovieSaveBmm( pMovie, pFilename ); if( len > 4 && strcmp( pFilename + len - 4, ".bml" ) == 0 ) return BlinkenMovieSaveBml( pMovie, pFilename ); if( len > 4 && strcmp( pFilename + len - 4, ".bbm" ) == 0 ) return BlinkenMovieSaveBbm( pMovie, pFilename ); return -1; }