// LZSS圧縮ソフト // include --------- #include #include #include // define ---------- // 各型とバイト数の関係( 環境によって変わりますが・・・ ) // int : 4バイト(0〜4,294,967,295) // short : 2バイト(0〜65,535) // char : 1バイト(0〜255) // data type ------- // 圧縮データの情報構造体 struct LZSS_ENCODE_INFO { int OriginalSize ; // 圧縮前のデータのサイズ(バイト数) int PressSize ; // 圧縮後のデータのサイズ(この構造体のサイズも含む) int EncodeCode ; // 圧縮情報の開始を示す数値 } ; // proto type ------ // データを圧縮する // // 戻り値:圧縮後のサイズ -1 はエラー Dest に NULL を入れると圧縮データ格納に必要なサイズが返る int LZSS_Encode( void *Src, int SrcSize, void *Dest ) ; // code ------------ // メイン関数 // argc:引数の数( 最初の1個は exe ファイルのパスなので0個になることはない ) // argv:引数の文字列配列へのポインタ( 最初の文字列は exe ファイルのパス文字列 ) int main( int argc, char **argv ) { void *buf, *press_buf ; int size, press_size ; // ソフトのタイトルを出力 printf( "LZSS Encode Soft\n\n" ) ; // ファイル名の指定がなかったらヘルプを表示 if( argc != 2 ) { printf( " LzssEncode.exe FilePath\n" ) ; return 0 ; } // ファイルを丸ごと読み込む { FILE *fp ; // ファイルを開く fp = fopen( argv[1], "rb" ) ; if( fp == NULL ) { printf( " File open Error!!\n" ) ; return 0 ; } // ファイルサイズを調べる { // ファイルポインタをファイルの末尾に fseek( fp, 0L, SEEK_END ) ; // ファイルの末尾で現在のファイルポインタアドレスを取得すればそれはファイルのサイズ size = ftell( fp ) ; // ファイルポインタをファイルの先頭に戻す fseek( fp, 0L, SEEK_SET ) ; } // ファイルを丸ごと読み込む為のメモリを確保する buf = malloc( size ) ; if( buf == NULL ) { printf( " Memory alloc Error!!\n" ) ; return 0 ; } // ファイルを丸ごと読み込む fread( buf, size, 1, fp ) ; // ファイルを閉じる fclose( fp ) ; } // 圧縮したデータを作成する { // 圧縮したデータのサイズを取得する press_size = LZSS_Encode( buf, size, NULL ) ; // 圧縮したデータを格納するためのメモリを確保する press_buf = malloc( press_size ) ; if( press_buf == NULL ) { printf( " Memory alloc Error!!\n" ) ; return 0 ; } // 圧縮したデータを確保したメモリ領域に保存する LZSS_Encode( buf, size, press_buf ) ; // 圧縮前のデータを解放する free( buf ) ; } // 圧縮したデータを保存する { FILE *fp ; char FileName[256] ; // 拡張子を『lzs』に変更して保存する { char *LastPeriod ; // 元のファイルのファイルパスをコピーする strcpy( FileName, argv[1] ) ; // 最後の『.』のアドレスを取得する LastPeriod = strrchr( FileName, '.' ) ; // ピリオドがなかった場合は拡張子が無いと判断して『.lzs』を末尾に追加する if( LastPeriod == NULL ) { strcat( FileName, ".lzs" ) ; } else { // あった場合はピリオドの後の文字列を『lzs』にしてしまう strcpy( LastPeriod + 1, "lzs" ) ; } } // 圧縮データを保存するファイルを開く fp = fopen( FileName, "wb" ) ; // 圧縮データを丸ごと書き込み fwrite( press_buf, press_size, 1, fp ) ; // ファイルを閉じる fclose( fp ) ; // 圧縮データを解放する free( press_buf ) ; } // 終了 return 0 ; } // データを圧縮する // // 戻り値:圧縮後のサイズ -1 はエラー Dest に NULL を入れると圧縮データ格納に必要なサイズが返る int LZSS_Encode( void *Src, int SrcSize, void *Dest ) { int i ; unsigned char *SrcPoint, *DestPoint ; int PressSizeCounter, SrcSizeCounter ; LZSS_ENCODE_INFO EncodeInfo ; int EncodeCode ; // void 型のポインタではアドレスの操作が出来ないので unsigned char 型のポインタにする SrcPoint = ( unsigned char * )Src ; DestPoint = ( unsigned char * )Dest ; // 圧縮元データの中で一番出現頻度が低い数値を算出する { int NumCounter[256] ; // 1バイトで表現できる数値は0〜255の256種類 int MinNum ; // カウンターを初期化 for( i = 0 ; i < 256 ; i ++ ) { NumCounter[i] = 0 ; } // 出現回数をカウント for( i = 0 ; i < SrcSize ; i ++ ) { NumCounter[ SrcPoint[i] ] ++ ; } // 一番カウンターの値が少ない数値を圧縮情報の開始を示す数値に任命 { // 最初は仮に0番を開始を示す数値としておく EncodeCode = 0 ; MinNum = NumCounter[0] ; for( i = 1 ; i < 256 ; i ++ ) { // より出現数が低い数値が見つかったら更新 if( MinNum > NumCounter[i] ) { MinNum = NumCounter[i] ; EncodeCode = i ; } } } } // 圧縮処理 { int Index, EqualNum, MaxEqualNum, MaxIndex ; unsigned char *PressData ; // 圧縮データを格納するアドレスをセット // (圧縮データ本体は元のサイズ、圧縮後のサイズ、圧縮情報の開始を示す数値を // 格納するデータ領域の後になる) PressData = DestPoint + sizeof( LZSS_ENCODE_INFO ) ; // 圧縮するデータの参照アドレスを初期化 SrcSizeCounter = 0 ; // 圧縮したデータの格納アドレスを初期化 PressSizeCounter = 0 ; // 全てのデータを圧縮するまで繰り返す while( SrcSizeCounter < SrcSize ) { // 今までに同じ数値の羅列がなかったか調べる { MaxEqualNum = -1 ; MaxIndex = -1 ; // 最高で254バイト前まで調べる for( Index = 1 ; Index <= 254 ; Index ++ ) { // データの先頭より前を調べようとしていたら抜ける if( SrcSizeCounter - Index < 0 ) break ; // 同じ数値が何回続いているか調べる for( EqualNum = 0 ; EqualNum < Index ; EqualNum ++ ) { // 圧縮元データの最後に到達したらループを抜ける if( EqualNum + SrcSizeCounter >= SrcSize ) break ; // 数値が違ったらループを抜ける if( SrcPoint[SrcSizeCounter + EqualNum] != SrcPoint[SrcSizeCounter - Index + EqualNum] ) break ; } // 同じだった回数が4以上(4未満だと逆にサイズが増える)で、且 // 今まで見つけた回数よりも多い場合に参照アドレスを更新する if( EqualNum >= 4 && MaxEqualNum < EqualNum ) { MaxEqualNum = EqualNum ; MaxIndex = Index ; } } } // 同じ数値の羅列が見つからなかったら普通に出力 if( MaxIndex == -1 ) { if( Dest != NULL ) PressData[PressSizeCounter] = SrcPoint[SrcSizeCounter] ; PressSizeCounter ++ ; // 圧縮情報の開始を示す数値と同じだった場合は2回連続で出力する if( SrcPoint[SrcSizeCounter] == EncodeCode ) { if( Dest != NULL ) PressData[PressSizeCounter] = SrcPoint[SrcSizeCounter] ; PressSizeCounter ++ ; } // 圧縮元データの参照アドレスを一つ進める SrcSizeCounter ++ ; } else { // 見つかった場合は見つけた位置と長さを出力する // 最初に圧縮情報の開始を示す数値を出力する if( Dest != NULL ) PressData[PressSizeCounter] = ( unsigned char )EncodeCode ; PressSizeCounter ++ ; // 次に『何バイト前からが同じか?』の数値を出力する { if( Dest != NULL ) PressData[PressSizeCounter] = ( unsigned char )MaxIndex ; // もし圧縮情報の開始を示す数値と同じ場合、展開時に // 『圧縮情報の開始を示す数値そのもの』と判断されて // しまうため、圧縮情報の開始を示す数値と同じかそれ以上の // 場合は数値を +1 するというルールを使う。(展開時は逆に -1 にする) if( Dest != NULL && PressData[PressSizeCounter] >= EncodeCode ) PressData[PressSizeCounter] ++ ; PressSizeCounter ++ ; } // 次に『何バイト同じか?』の数値を出力する if( Dest != NULL ) PressData[PressSizeCounter] = ( unsigned char )MaxEqualNum ; PressSizeCounter ++ ; // 同じだったバイト数分だけ圧縮元データの参照アドレスを進める SrcSizeCounter += MaxEqualNum ; } } } // 圧縮データの情報をセットする { // 圧縮前のデータのサイズをセット EncodeInfo.OriginalSize = SrcSize ; // 圧縮後のデータのサイズをセット EncodeInfo.PressSize = PressSizeCounter + sizeof( LZSS_ENCODE_INFO ) ; // 圧縮情報の開始を知らせる数値をセット EncodeInfo.EncodeCode = EncodeCode ; } // 圧縮データの情報を圧縮データにコピーする if( Dest != NULL ) memcpy( DestPoint, &EncodeInfo, sizeof( LZSS_ENCODE_INFO ) ) ; // 圧縮後のデータのサイズを返す return EncodeInfo.PressSize ; }