# include <stdint.h>
# include <unistd.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <fcntl.h>
# include <stdio.h>
# include <stdlib.h>
# include <sys/mman.h>
# include <arpa/inet.h>
# include <string.h>
# include "espfsformat.h"
//Heatshrink
# include "heatshrink_common.h"
# include "heatshrink_config.h"
# include "heatshrink_encoder.h"
//Routines to convert host format to the endianness used in the xtensa
short htoxs ( short in ) {
char r [ 2 ] ;
r [ 0 ] = in ;
r [ 1 ] = in > > 8 ;
return * ( ( short * ) r ) ;
}
int htoxl ( int in ) {
unsigned char r [ 4 ] ;
r [ 0 ] = in ;
r [ 1 ] = in > > 8 ;
r [ 2 ] = in > > 16 ;
r [ 3 ] = in > > 24 ;
return * ( ( int * ) r ) ;
}
size_t compressHeatshrink ( char * in , int insize , char * out , int outsize , int level ) {
char * inp = in ;
char * outp = out ;
size_t len ;
int ws [ ] = { 5 , 6 , 8 , 11 , 13 } ;
int ls [ ] = { 3 , 3 , 4 , 4 , 4 } ;
HSE_poll_res pres ;
HSE_sink_res sres ;
size_t r ;
if ( level = = - 1 ) level = 8 ;
level = ( level - 1 ) / 2 ; //level is now 0, 1, 2, 3, 4
heatshrink_encoder * enc = heatshrink_encoder_alloc ( ws [ level ] , ls [ level ] ) ;
if ( enc = = NULL ) {
perror ( " allocating mem for heatshrink " ) ;
exit ( 1 ) ;
}
//Save encoder parms as first byte
* outp = ( ws [ level ] < < 4 ) | ls [ level ] ;
outp + + ; outsize - - ;
r = 1 ;
do {
if ( insize > 0 ) {
sres = heatshrink_encoder_sink ( enc , inp , insize , & len ) ;
if ( sres ! = HSER_SINK_OK ) break ;
inp + = len ; insize - = len ;
if ( insize = = 0 ) heatshrink_encoder_finish ( enc ) ;
}
do {
pres = heatshrink_encoder_poll ( enc , outp , outsize , & len ) ;
if ( pres ! = HSER_POLL_MORE & & pres ! = HSER_POLL_EMPTY ) break ;
outp + = len ; outsize - = len ;
r + = len ;
} while ( pres = = HSER_POLL_MORE ) ;
} while ( insize ! = 0 ) ;
if ( insize ! = 0 ) {
fprintf ( stderr , " Heatshrink: Bug? insize is still %d. sres=%d pres=%d \n " , insize , sres , pres ) ;
exit ( 1 ) ;
}
heatshrink_encoder_free ( enc ) ;
return r ;
}
int handleFile ( int f , char * name , int compression , int level ) {
char * fdat , * cdat ;
off_t size , csize ;
EspFsHeader h ;
int nameLen ;
size = lseek ( f , 0 , SEEK_END ) ;
fdat = mmap ( NULL , size , PROT_READ , MAP_SHARED , f , 0 ) ;
if ( fdat = = MAP_FAILED ) {
perror ( " mmap " ) ;
return 0 ;
}
if ( compression = = COMPRESS_NONE ) {
csize = size ;
cdat = fdat ;
} else if ( compression = = COMPRESS_HEATSHRINK ) {
cdat = malloc ( size * 2 ) ;
csize = compressHeatshrink ( fdat , size , cdat , size * 2 , level ) ;
} else {
fprintf ( stderr , " Unknown compression - %d \n " , compression ) ;
exit ( 1 ) ;
}
if ( csize > size ) {
//Compressing enbiggened this file. Revert to uncompressed store.
compression = COMPRESS_NONE ;
csize = size ;
cdat = fdat ;
}
//Fill header data
h . magic = ( ' E ' < < 0 ) + ( ' S ' < < 8 ) + ( ' f ' < < 16 ) + ( ' s ' < < 24 ) ;
h . flags = 0 ;
h . compression = compression ;
nameLen = strlen ( name ) + 1 ;
if ( nameLen & 3 ) nameLen + = 4 - ( nameLen & 3 ) ; //Round to next 32bit boundary
h . nameLen = htoxs ( nameLen ) ;
h . fileLenComp = htoxl ( csize ) ;
h . fileLenDecomp = htoxl ( size ) ;
write ( 1 , & h , sizeof ( EspFsHeader ) ) ;
write ( 1 , name , nameLen ) ; //ToDo: this can eat up a few bytes after the buffer.
write ( 1 , cdat , csize ) ;
//Pad out to 32bit boundary
while ( csize & 3 ) {
write ( 1 , " \000 " , 1 ) ;
csize + + ;
}
munmap ( fdat , size ) ;
return ( csize * 100 ) / size ;
}
//Write final dummy header with FLAG_LASTFILE set.
void finishArchive ( ) {
EspFsHeader h ;
h . magic = ( ' E ' < < 0 ) + ( ' S ' < < 8 ) + ( ' f ' < < 16 ) + ( ' s ' < < 24 ) ;
h . flags = FLAG_LASTFILE ;
h . compression = COMPRESS_NONE ;
h . nameLen = htoxs ( 0 ) ;
h . fileLenComp = htoxl ( 0 ) ;
h . fileLenDecomp = htoxl ( 0 ) ;
write ( 1 , & h , sizeof ( EspFsHeader ) ) ;
}
int main ( int argc , char * * argv ) {
int f , x ;
char fileName [ 1024 ] ;
char * realName ;
struct stat statBuf ;
int serr ;
int rate ;
int err = 0 ;
int compType = 1 ; //default compression type - heatshrink
int compLvl = - 1 ;
for ( x = 1 ; x < argc ; x + + ) {
if ( strcmp ( argv [ x ] , " -c " ) = = 0 & & argc > = x - 2 ) {
compType = atoi ( argv [ x = 1 ] ) ;
x + + ;
} else if ( strcmp ( argv [ x ] , " -l " ) = = 0 & & argc > = x - 2 ) {
compLvl = atoi ( argv [ x = 1 ] ) ;
if ( compLvl < 1 | | compLvl > 9 ) err = 1 ;
x + + ;
} else {
err = 1 ;
}
}
if ( err ) {
fprintf ( stderr , " %s - Program to create espfs images \n " , argv [ 0 ] ) ;
fprintf ( stderr , " Usage: \n find | %s [-c compressor] [-l compression_level] > out.espfs \n " , argv [ 0 ] ) ;
fprintf ( stderr , " Compressors: \n 0 - None \n 1 - Heatshrink(defautl \n " ) ;
fprintf ( stderr , " Compression level: 1 is worst but low RAM usage, higher is better compression \n but uses more ram on decompression. -1 = compressors default. \n " ) ;
exit ( 0 ) ;
}
while ( fgets ( fileName , sizeof ( fileName ) , stdin ) ) {
//Kill off '\n' at the end
fileName [ strlen ( fileName ) - 1 ] = 0 ;
//Only include files
serr = stat ( fileName , & statBuf ) ;
if ( ( serr = = 0 ) & & S_ISREG ( statBuf . st_mode ) ) {
//Strip off './' or '/' madness.
realName = fileName ;
if ( fileName [ 0 ] = = ' . ' ) realName + + ;
if ( realName [ 0 ] = = ' / ' ) realName + + ;
f = open ( fileName , O_RDONLY ) ;
if ( f > 0 ) {
rate = handleFile ( f , realName , compType , compLvl ) ;
fprintf ( stderr , " %s (%d%%) \n " , realName , rate ) ;
close ( f ) ;
} else {
perror ( fileName ) ;
}
} else {
if ( serr ! = 0 ) {
perror ( fileName ) ;
}
}
}
finishArchive ( ) ;
return 0 ;
}