diff --git a/build/systemMenu_RED/sharedFont/Makefile b/build/systemMenu_RED/sharedFont/Makefile index 5daa1c9f..3417d6b0 100644 --- a/build/systemMenu_RED/sharedFont/Makefile +++ b/build/systemMenu_RED/sharedFont/Makefile @@ -18,26 +18,32 @@ TARGET_FIRM = SYSTEMMENU +SUBDIRS = compBLZ_modified \ + ntrcomp + include $(TWL_IPL_RED_ROOT)/build/buildtools/commondefs -FONT_TIMESTAMP = 08071200 +FONT_TIMESTAMP = 08061300 FONT_DIR = WW FONTS = TBF1_l.NFTR \ TBF1_m.NFTR \ TBF1_s.NFTR FONT_RSC = $(addprefix $(FONT_DIR)/, $(FONTS)) +FONT_ORG_DIR = $(TWL_IPL_RED_ROOT)/build/systemMenu_RED/sharedFont/WW +FONT_ORG_RSC = $(addprefix $(FONT_ORG_DIR)/, $(FONTS)) + ifneq ($(TWL_IPL_RED_PRIVATE_ROOT),) FONT_TABLE = TWLFontTable.dat endif -GEN_FONT_TABLE = $(SYSMENU_TOOLSDIR)/bin/genFontTable.plx +GEN_FONT_TABLE = ./genFontTable.plx #---------------------------------------------------------------------------- INSTALL_TARGETS = $(FONT_TABLE) INSTALL_DIR = $(TWL_IPL_RED_ROOT)/build/systemMenu_tools/NandInitializerRed/data -LDIRT_CLEAN = +LDIRT_CLEAN = $(FONT_TABLE) $(FONT_RSC) #---------------------------------------------------------------------------- @@ -45,7 +51,12 @@ include $(TWL_IPL_RED_ROOT)/build/buildtools/modulerules do-build : $(FONT_TABLE) -$(FONT_TABLE): $(FONT_RSC) ./Makefile +# step1 : copy sharedFonts into local directory for work +$(FONT_RSC): + cp $(FONT_ORG_RSC) $(FONT_DIR) + +# step2 : compress sharedFonts and them in local directory +$(FONT_TABLE): $(FONT_RSC) $(GEN_FONT_TABLE) $(FONT_TIMESTAMP) $(FONT_RSC) #===== End of Makefile ===== diff --git a/build/systemMenu_RED/sharedFont/compBLZ_modified/Makefile b/build/systemMenu_RED/sharedFont/compBLZ_modified/Makefile new file mode 100644 index 00000000..89101f6e --- /dev/null +++ b/build/systemMenu_RED/sharedFont/compBLZ_modified/Makefile @@ -0,0 +1,73 @@ +#! make -f +#--------------------------------------------------------------------------- +# Project: TwlSDK - tools - compBLZ +# File: Makefile +# +# Copyright 2008 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Date:: $ +# $Rev$ +# $Author$ +#--------------------------------------------------------------------------- + +TARGET_PLATFORM = TWL NITRO + +include $(TWLSDK_ROOT)/build/buildtools/commondefs + +#--------------------------------------------------------------------------- + +TARGET_BIN = compBLZ.exe +TARGETS = $(BINDIR)/$(TARGET_BIN) + +SRCS = main.c \ + compress.c \ + file.c \ + version.c + +HEADERS = common.h \ + compress.h \ + file.h + +INCDIR += ../../../include +OBJDIR = obj +BINDIR = bin + +OBJS = $(addprefix $(OBJDIR)/,$(SRCS:.c=.o)) + +MACROS += -DSDK_TWL $(addprefix -I,$(INCDIR)) +NEWDIRS = $(OBJDIR) $(BINDIR) +LDIRT_CLEAN += $(NEWDIRS) version.c + +#INSTALL_DIR = $(TWL_INSTALL_TOOLSDIR)/bin +#INSTALL_TARGETS = $(TARGETS) + +#--------------------------------------------------------------------------- + +include $(TWLSDK_ROOT)/build/buildtools/modulerules.x86 + +do-build: $(TARGETS) + +$(TARGETS): $(OBJS) $(LIBDGT) $(MAKEFILE) + $(CC_X86) $(OBJS) $(LIBDGT) -o $@ + +$(OBJS):%.o: + $(COMPILE_C) + +$(OBJDIR)/main.o: main.c file.h version.c +$(OBJDIR)/compress.o: compress.c compress.h +$(OBJDIR)/file.o: file.c file.h +$(OBJDIR)/version.o: version.c + +version.c: $(filter-out version.c,$(SRCS)) $(HEADERS) $(MAKEFILE) + @for i in $^ ; \ + do \ + date -r $$i +'const unsigned long SDK_DATE_OF_LATEST_FILE=%Y%m%dUL;'; \ + done | sort | tail -1 > $@ + +#===== End of Makefile ===== diff --git a/build/systemMenu_RED/sharedFont/compBLZ_modified/_ b/build/systemMenu_RED/sharedFont/compBLZ_modified/_ new file mode 100644 index 00000000..95665859 --- /dev/null +++ b/build/systemMenu_RED/sharedFont/compBLZ_modified/_ @@ -0,0 +1 @@ +SDK_CONFIDENTIAL diff --git a/build/systemMenu_RED/sharedFont/compBLZ_modified/common.h b/build/systemMenu_RED/sharedFont/compBLZ_modified/common.h new file mode 100644 index 00000000..9f60ce54 --- /dev/null +++ b/build/systemMenu_RED/sharedFont/compBLZ_modified/common.h @@ -0,0 +1,42 @@ +/*---------------------------------------------------------------------------* + Project: TwlSDK - tools - compstatic + File: common.h + + Copyright 2003 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: $ + $Rev$ + $Author$ + *---------------------------------------------------------------------------*/ +#ifndef COMMON_H__ +#define COMMON_H__ + +typedef enum +{ + TRUE = 1, + FALSE = 0 +} +BOOL; + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned long u32; +typedef signed char s8; +typedef signed short s16; +typedef signed long s32; + +// macro +#define MIN(a,b) ((a)<(b)?(a):(b)) +#define ROUNDUP4(x) (((x)+3)&~3) +#define LE(a) ((((a)<<24)&0xff000000)|(((a)<<8)&0x00ff0000)|\ + (((a)>>8)&0x0000ff00)|(((a)>>24)&0x000000ff)) +#define FREE(x) do { if (x){ free(x); x = NULL; } } while(0) + + +#endif //COMMON_H__ diff --git a/build/systemMenu_RED/sharedFont/compBLZ_modified/compress.c b/build/systemMenu_RED/sharedFont/compBLZ_modified/compress.c new file mode 100644 index 00000000..57f4b5bc --- /dev/null +++ b/build/systemMenu_RED/sharedFont/compBLZ_modified/compress.c @@ -0,0 +1,374 @@ +/*---------------------------------------------------------------------------* + Project: TwlSDK - tools - compstatic + File: compress.c + + Copyright 2003 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: $ + $Rev$ + $Author$ + *---------------------------------------------------------------------------*/ +#include +#include +#include "file.h" +#include "compress.h" + +static int LZCompressRV(u8 *src_buffer, int src_size, u8 *dst_buffer, int dst_size); +static int FindMatched(u8 *src_buffer, int src_size, u8 *dic_buffer, int dic_size, int *index); +static int HowManyMatched(u8 *src_buffer, u8 *dic_buffer, int max_len); +static int CheckOverwrite(int orig_size, u8 *cmprs_buffer, int cmprs_buffer_size, + int *orig_safe, int *cmprs_safe); + + +/*---------------------------------------------------------------------------* + Name: Compress + + Description: Buffer の逆順の圧縮を行なう. + ただし圧縮データと展開データがメモリ空間を共有できるように + 調整する + + buffer : 被圧縮データ + buffer_size : 被圧縮データサイズ + + Return: >=0: 圧縮後のサイズ + < 0: 失敗 + *---------------------------------------------------------------------------*/ +int Compress(u8 *buffer_original, int buffer_original_size) +{ + u8 *buffer; + int buffer_size; + int buffer_start; + u8 *temp_buffer_original; + u8 *temp_buffer; + int temp_buffer_size; + int temp_buffer_start; + int compressed_size; + int aligned_size; + int total_size; + int reduced; + int i; + CompFooter *footer; + + // 前準備 + if (NULL == (temp_buffer_original = (u8 *)malloc(buffer_original_size))) + { + ErrorPrintf("Cannot allocate memory size=%d\n", buffer_original_size); + return COMPRESS_FATAL_ERROR; + } + + if ((u32)buffer_original % 4 != 0) + { + ErrorPrintf("Top of buffer is not aligned by 4.\n"); + return COMPRESS_FATAL_ERROR; + } + + buffer = buffer_original; + buffer_size = buffer_original_size; + temp_buffer = temp_buffer_original; + temp_buffer_size = buffer_original_size; + + // 圧縮を行なう + reduced = LZCompressRV(buffer, buffer_size, temp_buffer, temp_buffer_size); + if (reduced < 0) + { + DebugPrintf("Compressed buffer size exceeds original data size.\n"); + free(temp_buffer_original); + return COMPRESS_LARGER_ORIGINAL; + } + + temp_buffer_size -= reduced; + temp_buffer += reduced; + + DebugPrintf("1: source size = %d compressed = %d\n", buffer_size, temp_buffer_size); + + // 展開不能な上書きが発生するか確認 + if (!CheckOverwrite + (buffer_size, temp_buffer, temp_buffer_size, &buffer_start, &temp_buffer_start)) + { + // 上書きが発生するなら圧縮範囲を変更する + buffer += buffer_start; + buffer_size -= buffer_start; + temp_buffer += temp_buffer_start; + temp_buffer_size -= temp_buffer_start; + + DebugPrintf(" !! Shrink back Compressed region to avoid overwriting.\n" + " !! Expand non-compressed region = +%d\n" + "2: source size = %d compressed = %d\n", + buffer_start, buffer_size, temp_buffer_size); + } + + // PADDING とパラメータ領域を加えても超えないかどうか判定 + compressed_size = buffer_start + temp_buffer_size; // header+body + aligned_size = ROUNDUP4(compressed_size); // +padding + total_size = aligned_size + sizeof(CompFooter); // +footer + + if (buffer_original_size <= total_size) + { + DebugPrintf("Compressed buffer size exceeds or equals original data size.\n"); + free(temp_buffer_original); + return COMPRESS_LARGER_ORIGINAL; + } + + // データをテンポラリバッファから元データへ上書きする + CopyBuffer(temp_buffer, buffer, temp_buffer_size); + free(temp_buffer_original); + + // サイズが 4 の倍数になるように PADDING + // LZ の実装上圧縮領域の最初のバイト値は 0xff にならない(最初は圧縮 + // フラグであり、最初のデータは圧縮なしで格納されるから)ので 0xff で + // 埋める + for (i = compressed_size; i < aligned_size; i++) + { + buffer_original[i] = 0xff; + } + + // サイズ設定 + // compressBottom は sizeof(PAD)+sizeof(footer) なので 1バイトで十分 + footer = (CompFooter *) (buffer_original + aligned_size); + footer->bufferTop = total_size - buffer_start; // 正の値 + footer->compressBottom = total_size - compressed_size; // 正の値 + footer->originalBottom = buffer_original_size - total_size; // 正の値 + + return total_size; +} + + +/*---------------------------------------------------------------------------* + Name: LZCompressRV + + Description: LZ 圧縮を行なう.ただしデータの後方から圧縮開始する + 圧縮結果も後ろ詰めになる + + Returns: 圧縮データの先頭 index + 圧縮データは dst_buffer+index から dst_buffer+dst_size-1 まで + -1: 圧縮失敗(圧縮した結果の方が大きい場合) + *---------------------------------------------------------------------------*/ +static int LZCompressRV(u8 *src_buffer, int src_size, u8 *dst_buffer, int dst_size) +{ + int src_index = src_size; + int dst_index = dst_size; + int compflag; + int compflag_index; + int i; + + while (src_index > 0) + { + if (dst_index < 1) + return -1; // Buffer Overflow + + // 8bit の圧縮フラグの挿入位置を予約 + compflag = 0x00; + compflag_index = --dst_index; + + // フラグ系列が8ビットデータとして格納されるため、8回ループ + for (i = 0; i < 8; i++) + { + compflag <<= 1; + + if (src_index > 0) // src が残っているか判定 + { + u8 *dic_buffer; + int dic_size; + u8 *ref_buffer; + int ref_size; + int index; + int len; + + dic_buffer = src_buffer + src_index; + dic_size = src_size - src_index; + ref_size = MIN(src_index, LZ_MAX_COPY); + ref_buffer = dic_buffer - ref_size; + + len = FindMatched(ref_buffer, ref_size, + dic_buffer, MIN(dic_size, LZ_MAX_DIC_LENGTH), &index); + + if (len >= LZ_MIN_COPY) + { + u16 half; + + // Offset/Len の記録が可能かどうか確認 + if (dst_index < 2) + return -1; // Buffer Overflow + + // src index 進める + src_index -= len; + + // len >= LZ_MIN_COPY なのでその分減算し値域を節約する + index -= (LZ_MIN_COPY - 1); + len -= (LZ_MIN_COPY - 0); + + // 16bit データとしてたたむ + half = (u16)((index & (LZ_MAX_INDEX - 1)) | (len << LZ_BIT_INDEX)); + dst_buffer[--dst_index] = (half >> 8) & 0xff; + dst_buffer[--dst_index] = (half >> 0) & 0xff; + + // flag セット + compflag |= 0x01; + } + else + { + // 値そのままを記録する & src index 進める + if (dst_index < 1) + return -1; // Buffer Overflow + dst_buffer[--dst_index] = src_buffer[--src_index]; + } + } + } + // 圧縮フラグの保存 + dst_buffer[compflag_index] = compflag; + } + return dst_index; +} + + +/*---------------------------------------------------------------------------* + Name: FindMatched + + Description: 一致するパターンの検索を行なう.ただしデータの後方から前方への + 検索 + + src_buffer[0...src_size-1] のパターンを src_buffer の後方から + dic_buffer[0...dic_size-1] のパターンと最大一致する部分を + 検索する. + + Returns: 一致したサイズ + *index 一致した位置 + *---------------------------------------------------------------------------*/ +static int FindMatched(u8 *src_buffer, int src_size, u8 *dic_buffer, int dic_size, int *index) +{ + u8 *src_bottom = src_buffer + src_size - 1; + u8 char_src_bottom = *src_bottom; + int n, len, max_len; + + // 返値初期化 + max_len = 0; + + for (n = 0; n < dic_size; n++) + { + // 高速化のためのキャッシュ + if (char_src_bottom == dic_buffer[n]) + { + len = HowManyMatched(src_bottom, dic_buffer + n, MIN(n + 1, src_size)); + if (max_len < len) + { + max_len = len; + *index = n; + } + } + } + + // 最小サイズ以上なら成功 + return max_len; +} + + +/*---------------------------------------------------------------------------* + Name: HowManyMatched + + Description: 2つのパターンが逆順にどこまで一致しているかを調査する + + src_buffer, dic_buffer 比較パターンのアドレス + (このアドレスから逆方向へ検索する) + max_len 最大調査する長さ + + Returns: 一致した長さ + *---------------------------------------------------------------------------*/ +static int HowManyMatched(u8 *src_buffer, u8 *dic_buffer, int max_len) +{ + int i; + + // パターン一致検索(逆順) + for (i = 0; i < max_len; i++) + { + if (*src_buffer != *dic_buffer) + { + break; + } + src_buffer--; + dic_buffer--; + } + return i; +} + + +/*---------------------------------------------------------------------------* + Name: CheckOverwrite + + Description: LZ 展開で展開先と展開元を同じアドレスに置いた場合に、どこまで + 正常に展開が可能かをチェックする + + Returns: 最後まで展開可能なら TRUE 途中までなら FALSE + *---------------------------------------------------------------------------*/ +static int CheckOverwrite(int orig_size, u8 *cmprs_buffer, int cmprs_buffer_size, + int *orig_safe, int *cmprs_safe) +{ + int src = cmprs_buffer_size; + int dst = orig_size; + int flag; + int i; + +//#define DETAIL + while (dst > 0) + { + flag = cmprs_buffer[--src]; // 圧縮非圧縮フラグ 8 ループ分 + +#ifdef DETAIL + DebugPrintf("%08x %08x FLG=0x%02x\n", src, dst, flag); +#endif + for (i = 0; i < 8; i++) + { + if (dst > 0) + { + if (flag & 0x80) // 圧縮データか? + { + u16 half; + int len; + + // 展開長を計算 + src -= 2; + half = (u16)(cmprs_buffer[src] | (cmprs_buffer[src + 1] << 8)); + len = ((half >> LZ_BIT_INDEX) & (LZ_MAX_LENGTH - 1)) + LZ_MIN_COPY; +#ifdef DETAIL + DebugPrintf("%08x %08x-%08x LEN=%d\n", src, dst - 1, dst - len, len); +#endif + // ソースデータを上書きしてしまうかチェック + dst -= len; + + if (dst < 0) + { + ErrorPrintf("System error in CheckOverwrite???\n"); + exit(-1); // Panic!! + } + + if (dst < src) + { + // 上書きしてしまうなら圧縮は現在のところまでで止める + *orig_safe = dst; + *cmprs_safe = src; + return FALSE; + } + } + else + { + // 非圧縮データならそのままコピーなので + // 破壊を伴なう上書きは起こらない + src--; + dst--; +#ifdef DETAIL + DebugPrintf("%08x %08x CHR=0x%02x\n", src, dst, cmprs_buffer[src]); +#endif + } + flag <<= 1; + } + } + } + *orig_safe = 0; + *cmprs_safe = 0; + return TRUE; +} diff --git a/build/systemMenu_RED/sharedFont/compBLZ_modified/compress.h b/build/systemMenu_RED/sharedFont/compBLZ_modified/compress.h new file mode 100644 index 00000000..9b141d6c --- /dev/null +++ b/build/systemMenu_RED/sharedFont/compBLZ_modified/compress.h @@ -0,0 +1,55 @@ +/*---------------------------------------------------------------------------* + Project: TwlSDK - tools - compstatic + File: compress.h + + Copyright 2003 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: $ + $Rev$ + $Author$ + *---------------------------------------------------------------------------*/ +#ifndef COMPRESS_H__ +#define COMPRESS_H__ +#include "common.h" + +//--------------------------------------------------------- +typedef struct +{ + u32 bufferTop:24; // 圧縮領域終端 - 先頭 + u32 compressBottom:8; // 圧縮領域終端 - データ終端 + u32 originalBottom; // 展開領域終端 - 圧縮領域終端 +} +CompFooter; + +//--------------------------------------------------------- +int Compress(u8 *buffer, int buffer_size); + +#define COMPRESS_LARGER_ORIGINAL (-1) +#define COMPRESS_FATAL_ERROR (-2) + + +// loader area +#define LOADER_SIZE_ARM9 (16*1024) +#define LOADER_SIZE_ARM7 ( 1*1024) + +// LZ compress parameters +#define LZ_BIT_INDEX 12 // 12bit offset +#define LZ_BIT_LENGTH 4 // 4bit length +#define LZ_MAX_INDEX (1 << LZ_BIT_INDEX) +#define LZ_MAX_LENGTH (1 << LZ_BIT_LENGTH) + +#define LZ_MIN_COPY 3 +#define LZ_MAX_COPY (LZ_MIN_COPY+LZ_MAX_LENGTH-1) +#define LZ_MAX_DIC_LENGTH (LZ_MIN_COPY+LZ_MAX_INDEX-1) + +// macro +#define MIN(a,b) ((a)<(b)?(a):(b)) +#define ROUNDUP4(x) (((x)+3)&~3) + +#endif diff --git a/build/systemMenu_RED/sharedFont/compBLZ_modified/file.c b/build/systemMenu_RED/sharedFont/compBLZ_modified/file.c new file mode 100644 index 00000000..871c6e46 --- /dev/null +++ b/build/systemMenu_RED/sharedFont/compBLZ_modified/file.c @@ -0,0 +1,314 @@ +/*---------------------------------------------------------------------------* + Project: TwlSDK - tools - compstatic + File: file.c + + Copyright 2003 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: $ + $Rev$ + $Author$ + *---------------------------------------------------------------------------*/ +#include +#include // calloc() +#include // free(), exit() +#include // stat() +#include // strlen/strdup/strcpy +#include // va_start(),va_end() +#include // unlink() +#include "file.h" + + +/*---------------------------------------------------------------------------* + Name: ReadFile + + Description: ファイルの読み込み バッファの確保 + *---------------------------------------------------------------------------*/ +int ReadFile(const char *filename, u8 **buffer) +{ + FILE *fp; + struct stat filestat; + int filesize; + + fp = NULL; + *buffer = NULL; + + if (filename == NULL) + { + ErrorPrintf("Not specified filename\n"); + goto error; + } + + /* Open file */ + if (stat(filename, &filestat) || !S_ISREG(filestat.st_mode) || + NULL == (fp = fopen(filename, "rb"))) + { + ErrorPrintf("Cannot open file '%s'\n", filename); + goto error; + } + + /* Read file */ + filesize = filestat.st_size; + if (NULL == (*buffer = malloc(filesize))) + { + ErrorPrintf("Cannot allocate memory size=%d\n", filesize); + goto error; + } + + if (filesize != fread(*buffer, sizeof(u8), filesize, fp)) + { + ErrorPrintf("Cannot read file '%s'\n", filename); + goto error; + } + + DebugPrintf("%p %8d bytes ReadFile \'%s\'\n", *buffer, filesize, filename); + + /* Close file */ + fclose(fp); + return filesize; + + error: + if (*buffer) + free(*buffer); + if (fp) + fclose(fp); + return -1; +} + + +/*---------------------------------------------------------------------------* + Name: WriteFile + + Description: ファイルの作成書き込み + *---------------------------------------------------------------------------*/ +int WriteFile(const char *filename, u8 *buffer, int size) +{ + FILE *fp; + + DebugPrintf("%p %8d bytes WriteFile \'%s\'\n", buffer, size, filename); + + /* Open file */ + if (NULL == (fp = fopen(filename, "wb"))) + { + ErrorPrintf("Cannot open file '%s'\n", filename); + return -1; + } + + /* Write file */ + if (size != fwrite(buffer, sizeof(u8), size, fp)) + { + ErrorPrintf("Cannot write file '%s'\n", filename); + (void)fclose(fp); + (void)unlink(filename); + return -1; + } + + /* Close file */ + if (0 > fclose(fp)) + { + ErrorPrintf("Cannot close file '%s'\n", filename); + (void)unlink(filename); + return -1; + } + return size; +} + +/*---------------------------------------------------------------------------* + Name: CopyBuffer + + Description: バッファのコピー + *---------------------------------------------------------------------------*/ +void CopyBuffer(const u8 *src, u8 *dst, int size) +{ + int i; + + if ((unsigned int)src > (unsigned int)dst) + { + for (i = 0; i < size; i++) + { + dst[i] = src[i]; + } + } + else + { + for (i = size - 1; i >= 0; i--) + { + dst[i] = src[i]; + } + } + return; +} + +/*---------------------------------------------------------------------------* + Name: GetDirName + + Description: ファイル名のディレクトリ名を取得する + *---------------------------------------------------------------------------*/ +char *GetDirName(const char *path) +{ + int i; + char *new_path; + + for (i = strlen(path) - 1; i >= 0; i--) + { + if (path[i] == '/' || path[i] == '\\') + { + if (NULL != (new_path = strdup(path))) + { + new_path[i] = '\0'; + } + return new_path; + } + if (path[i] == ':') + { + if (NULL != (new_path = malloc(i + 3))) + { + strncpy(new_path, path, i); + strcpy(new_path + i, ":."); + } + return new_path; + } + } + + return strdup("."); +} + +/*---------------------------------------------------------------------------* + Name: DebugPrintf + + Description: Debug 出力用 Printf + *---------------------------------------------------------------------------*/ +BOOL bDebugMode = FALSE; + +void DebugPrintf(const char *fmt, ...) +{ + va_list va; + + if (bDebugMode) + { + va_start(va, fmt); + vfprintf(stderr, fmt, va); + va_end(va); + } +} + +/*---------------------------------------------------------------------------* + Name: ConsolePrintf + + Description: 通常コンソール出力用 Printf + *---------------------------------------------------------------------------*/ +void ConsolePrintf(const char *fmt, ...) +{ + va_list va; + + va_start(va, fmt); + vfprintf(stdout, fmt, va); + va_end(va); +} + +/*---------------------------------------------------------------------------* + Name: ErrorPrintf + + Description: Error 出力用 Printf + *---------------------------------------------------------------------------*/ +void ErrorPrintf(const char *fmt, ...) +{ + va_list va; + + fprintf(stderr, "Error: "); + va_start(va, fmt); + vfprintf(stderr, fmt, va); + va_end(va); +} + + +/*---------------------------------------------------------------------------* + Name: StrDup + + Description: エラーハンドリング&古い値の free 付き strdup 関数 + *---------------------------------------------------------------------------*/ +char *StrDup(char *old, char *new) +{ + if (old) + { + free(old); + } + if (new && NULL == (new = strdup(new))) + { + ErrorPrintf("Cannot allocate memory\n"); + exit(1); + } + return new; +} + + +/*---------------------------------------------------------------------------* + Name: StrCat + + Description: 文字列の連結 + *---------------------------------------------------------------------------*/ +char *StrCat(int num, ...) +{ + va_list va; + int i; + int size; + char *result; + + va_start(va, num); + size = 0; + for (i = 0; i < num; i++) + { + size += strlen(va_arg(va, char *)); + } + va_end(va); + + if (NULL == (result = malloc(size + 1))) + { + ErrorPrintf("Cannot allocate memory\n"); + exit(1); + } + + va_start(va, num); + result[0] = '\0'; + for (i = 0; i < num; i++) + { + (void)strcat(result, va_arg(va, char *)); + } + va_end(va); + + return result; +} + +//--------------------------------------------------------------------------- +// パス文字列からファイル名部分のポインタを取得し、拡張子を削る +// @param path パス +// @return ファイル名のポインタ +//--------------------------------------------------------------------------- +char *StrCutFname(char *path) +{ + char *search_tmp; + + if (path == NULL) + { + return NULL; + } + + if ((search_tmp = strrchr(path, '/')) != NULL) + { + path = (search_tmp + 1); + } + if ((search_tmp = strrchr(path, '\\')) != NULL) + { + path = (search_tmp + 1); + } + if ((search_tmp = strrchr(path, '.')) != NULL) + { + *search_tmp = '\0'; + } + return path; +} diff --git a/build/systemMenu_RED/sharedFont/compBLZ_modified/file.h b/build/systemMenu_RED/sharedFont/compBLZ_modified/file.h new file mode 100644 index 00000000..b266b0ff --- /dev/null +++ b/build/systemMenu_RED/sharedFont/compBLZ_modified/file.h @@ -0,0 +1,35 @@ +/*---------------------------------------------------------------------------* + Project: TwlSDK - tools - compstatic + File: file.h + + Copyright 2003 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: $ + $Rev$ + $Author$ + *---------------------------------------------------------------------------*/ +#ifndef FILE_H__ +#define FILE_H__ + +#include "common.h" + +int ReadFile(const char *filename, u8 **buffer); +int WriteFile(const char *filename, u8 *buffer, int size); +void CopyBuffer(const u8 *src, u8 *dst, int size); +char *GetDirName(const char *filename); +void DebugPrintf(const char *fmt, ...); +void ErrorPrintf(const char *fmt, ...); +void ConsolePrintf(const char *fmt, ...); +char *StrDup(char *old, char *new); +char *StrCat(int num, ...); +char *StrCutFname(char *path); + +extern BOOL bDebugMode; + +#endif //FILE_H__ diff --git a/build/systemMenu_RED/sharedFont/compBLZ_modified/main.c b/build/systemMenu_RED/sharedFont/compBLZ_modified/main.c new file mode 100644 index 00000000..f7ec6980 --- /dev/null +++ b/build/systemMenu_RED/sharedFont/compBLZ_modified/main.c @@ -0,0 +1,171 @@ +/*---------------------------------------------------------------------------* + Project: TwlSDK - tools - compBLZ + File: main.c + + Copyright 2008 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: $ + $Rev$ + $Author$ + *---------------------------------------------------------------------------*/ +#include +#include +#include +#include // getopt() +#include "file.h" +#include "compress.h" + +extern const unsigned long SDK_DATE_OF_LATEST_FILE; + +static void usage(void); + +/*---------------------------------------------------------------------------* + Name: main + + Description: + *---------------------------------------------------------------------------*/ +int main(int argc, char *argv[]) +{ + int n; + int result; + int iptfilesize; + + char* optfilename; + char* suffix; + u8* filebuf; + + BOOL optfname_flag = FALSE; // if optfilename input, this flag is true. + + optfilename = suffix = NULL; + + while ((n = getopt(argc, argv, "o:e:dhv")) != -1) + { + switch (n) + { + case 'o': // output file name + optfilename = optarg; + optfname_flag = TRUE; + break; + + case 'e': + suffix = optarg; + break; + + case 'd': // Show debug message + bDebugMode = TRUE; + break; + + case 'h': + case 'v': + default: + usage(); // Never returns + break; + } + + DebugPrintf("option -%c: %s\n", n, optarg ? optarg : "No ARG"); + } + + argc -= optind; + argv += optind; + + if (bDebugMode) + { + int i; + + DebugPrintf("argc=%d optind=%d\n", argc, optind); + for (i = 0; i < argc; i++) + { + DebugPrintf("argv[%d] = [%s]\n", i, argv[i]); + } + } + + if (argc == 1) + { + if (optfilename == NULL) + { + optfilename = argv[0]; + } + iptfilesize = ReadFile(argv[0], &filebuf); + } + else + { + usage(); // Never returns + } + + if (iptfilesize < 0) + { + ConsolePrintf("exit...\n"); + return 1; + } + + if ((result = Compress(filebuf, iptfilesize)) < 0) + { + switch (result) + { + case COMPRESS_LARGER_ORIGINAL: + ConsolePrintf("Inputdata ..... Not compressed (enlarged or same size as before)\n"); + break; + case COMPRESS_FATAL_ERROR: + ConsolePrintf("Fatal error occured\n"); + break; + } + ConsolePrintf("exit...\n"); + return 1; + } + + // cut file path and suffix of input file + if (!optfname_flag) + { + optfilename = StrCutFname(optfilename); + } + + // create output filename + if (suffix == NULL) + { + optfilename = StrCat(2, optfilename, "_BLZ.bin"); + } + else + { + optfilename = StrCat(2, optfilename, suffix); + } + + // output file + if (WriteFile(optfilename, filebuf, result) < 0) + { + ConsolePrintf("exit...\n\n"); + return 1; + } + + ConsolePrintf("Inputdata ..... Compressed ... %9d -> %9d\n", iptfilesize, result); + + return 0; +} + +/*---------------------------------------------------------------------------* + Name: usage + + Description: + *---------------------------------------------------------------------------*/ +static void usage(void) +{ + fprintf(stderr, + "TWL-SDK Development Tool - compBLZ - Compress data\n" + "Build %lu\n" + "\n" + "Usage: compBLZ [-d] [-o outputFile] [-e suffix] inputFile\n" + "\n" + " Compress data (backward LZ)\n" + "\n" + " -o outputFile FILENAME for output file (default:input filename)\n" + " -e suffix SUFFIX for output file (default:\"_BLZ\")\n" + " -d Show debug messages (for test purpose)\n" + " -h Show this message\n" "\n", SDK_DATE_OF_LATEST_FILE); + + exit(1); +} diff --git a/build/systemMenu_RED/sharedFont/makeboot.bat b/build/systemMenu_RED/sharedFont/makeboot.bat new file mode 100644 index 00000000..2ec4818b --- /dev/null +++ b/build/systemMenu_RED/sharedFont/makeboot.bat @@ -0,0 +1,18 @@ +rem +rem ***** SD起動ファイル作成ツール ***** +rem +rem sdmc_launcher_writer.gcd が起動ファイルとして menu.srl しか受け付けないので +rem 引数として与えたファイルを menu.srl としてコピーする +rem + +if "%1" equ "" ( + echo 失敗しました。 + echo ディレクトリごとドラッグアンドドロップしてください。 + echo: + goto end +) + +set progdir=%~dp0 + +del %progdir%\menu.srl +copy %1 %progdir%\menu.srl diff --git a/build/systemMenu_RED/sharedFont/ntrcomp/@ b/build/systemMenu_RED/sharedFont/ntrcomp/@ new file mode 100644 index 00000000..ae91d2ff --- /dev/null +++ b/build/systemMenu_RED/sharedFont/ntrcomp/@ @@ -0,0 +1 @@ +NINTENDO PRIVATE diff --git a/build/systemMenu_RED/sharedFont/ntrcomp/Makefile b/build/systemMenu_RED/sharedFont/ntrcomp/Makefile new file mode 100644 index 00000000..cb219807 --- /dev/null +++ b/build/systemMenu_RED/sharedFont/ntrcomp/Makefile @@ -0,0 +1,27 @@ +#! make -f +#---------------------------------------------------------------------------- +# Project: TwlSDK - build - tools - ntrcomp +# File: Makefile +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Date:: $ +# $Rev$ +# $Author$ +#---------------------------------------------------------------------------- +TARGET_PLATFORM = NITRO + +export TWLSDK_PLATFORM = NITRO + +include $(TWLSDK_ROOT)/build/buildtools/commondefs + +SUBDIRS += gcc + +include $(TWLSDK_ROOT)/build/buildtools/modulerules.x86 + diff --git a/build/systemMenu_RED/sharedFont/ntrcomp/gcc/Makefile b/build/systemMenu_RED/sharedFont/ntrcomp/gcc/Makefile new file mode 100644 index 00000000..981c2ad2 --- /dev/null +++ b/build/systemMenu_RED/sharedFont/ntrcomp/gcc/Makefile @@ -0,0 +1,59 @@ +#! make -f +#---------------------------------------------------------------------------- +# Project: TwlSDK - build - tools - ntrcomp - gcc +# File: Makefile +# +# Copyright 2007 Nintendo. All rights reserved. +# +# These coded instructions, statements, and computer programs contain +# proprietary information of Nintendo of America Inc. and/or Nintendo +# Company Ltd., and are protected by Federal copyright law. They may +# not be disclosed to third parties or copied or duplicated in any form, +# in whole or in part, without the prior written consent of Nintendo. +# +# $Date:: $ +# $Rev$ +# $Author$ +#---------------------------------------------------------------------------- +TARGET_PLATFORM = NITRO + +include $(TWLSDK_ROOT)/build/buildtools/commondefs + +OBJECTS = ntcompress_main.o ntcompress_test.o nitroCompLib.o multipleCompLib.o rangeCoder.o + +WIN32 ?= 1 + +ifneq ($(WIN32),0) +TARGETS = ntrcomp.exe +CC_X86 := gcc -mno-cygwin +else +TARGETS = ntrcomp +CC_X86 := gcc +endif + +LDIRT_CLEAN = $(OBJECTS) $(TARGETS) + +#INSTALL_DIR = $(NITRO_INSTALL_TOOLSDIR)/bin +#INSTALL_TARGETS = $(TARGETS) + +#---------------------------------------------------------------------------- +# build +#---------------------------------------------------------------------------- +do-build: $(TARGETS) + +$(TARGETS): $(OBJECTS) + $(CC_X86) $+ -o $@ + +ntcompress_main.o: ../src/ntcompress_main.c ../src/nitroCompLib.h + $(CC_X86) -c $< -o $@ +ntcompress_test.o: ../src/ntcompress_test.c ../src/nitroCompLib.h + $(CC_X86) -c $< -o $@ +nitroCompLib.o: ../src/nitroCompLib.c ../src/nitroCompLib.h + $(CC_X86) -c $< -o $@ +multipleCompLib.o: ../src/multipleCompLib.c ../src/multipleCompLib.h + $(CC_X86) -c $< -o $@ +rangeCoder.o: ../src/rangeCoder.c ../src/rangeCoder.h + $(CC_X86) -c $< -o $@ + +include $(TWLSDK_ROOT)/build/buildtools/modulerules.x86 + diff --git a/build/systemMenu_RED/sharedFont/ntrcomp/src/multipleCompLib.c b/build/systemMenu_RED/sharedFont/ntrcomp/src/multipleCompLib.c new file mode 100644 index 00000000..fcb44e89 --- /dev/null +++ b/build/systemMenu_RED/sharedFont/ntrcomp/src/multipleCompLib.c @@ -0,0 +1,2494 @@ +/*---------------------------------------------------------------------------* + Project: NinTendo Compress tool + File: multipleCompLib.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: $ + $Rev$ + $Author$ + *---------------------------------------------------------------------------*/ + +#include "multipleCompLib.h" + +#undef _DEBUG +#ifdef _DEBUG + +#endif + +#define LH_CODE_HEADER (0x40) +#define LRC_CODE_HEADER (0x50) + +#define BLEND_COMP_FLAG 1 + +#define LH_ENC_OFFSET_WIDTH /* この設定が有効な場合にはoffset値全体ではなくoffsetのbit長を符号化する */ + +#if !defined(LH_ENC_OFFSET_WIDTH) + #define LH_OFFSET_BITS 12 + #define LH_OFFSET_TABLE_BITS LH_OFFSET_BITS +#else // if defined(LH_ENC_OFFSET_WIDTH) + #define LH_OFFSET_BITS 15 + #define LH_OFFSET_TABLE_BITS 5 +#endif + +#define LENGTH_BITS (8 + BLEND_COMP_FLAG) +#define OFFSET_SIZE_MAX (1 << 15) + +#define REVERSE_SEARCH + +typedef struct +{ + u16 WindowPos; // 現在のスライド辞書の先頭位置 + u16 WindowLen; // 現在のスライド辞書のサイズ + + s16 LZOffsetTable[ OFFSET_SIZE_MAX ]; // オフセットデータのテーブル + #ifdef REVERSE_SEARCH + s16 LZRevOffsetTable[ OFFSET_SIZE_MAX ]; // オフセットデータの逆順テーブル + #endif + s16 LZByteTable[ 256 ]; // データの先頭テーブル + s16 LZEndTable [ 256 ]; // データの終端テーブル + u8 OffsetBits; // オフセットを表現する為のビット数 +} LZCompressInfo; + +static LZCompressInfo gLZWork; + +INLINE u32 +RoundUp( u32 value, u32 base ) +{ + return (value + (base - 1)) & ~(base - 1); +} + +/*---------------------------------------------------------------------------* + Name: LZInitTable + Description: + Arguments: work + Returns: None. + *---------------------------------------------------------------------------*/ +static void +LZInitTable( LZCompressInfo* info ) +{ + u16 i; + + for ( i = 0; i < 256; i++ ) + { + info->LZByteTable[i] = -1; + info->LZEndTable[i] = -1; + } + info->WindowPos = 0; + info->WindowLen = 0; +} + +/*---------------------------------------------------------------------------* + Name: SlideByte + Description: 辞書を1バイトスライド + Arguments: *srcp + work + Returns: None. + *---------------------------------------------------------------------------*/ +static void +SlideByte( LZCompressInfo* info, const u8 *srcp ) +{ + s16 offset; + u8 in_data = *srcp; + u16 insert_offset; + +#if defined( REVERSE_SEARCH ) // 探索順を逆にする(新しいデータを優先) + s16 *const LZByteTable = info->LZEndTable; + s16 *const LZEndTable = info->LZByteTable; + s16 *const LZOffsetTable = info->LZRevOffsetTable; + s16 *const LZRevOffsetTable = info->LZOffsetTable; +#else + s16 *const LZByteTable = info->LZByteTable; + s16 *const LZEndTable = info->LZEndTable; + s16 *const LZOffsetTable = info->LZOffsetTable; +#endif + const u16 windowPos = info->WindowPos; + const u16 windowLen = info->WindowLen; + const u32 OFFSET_SIZE = (1 << info->OffsetBits); + + if ( windowLen == OFFSET_SIZE ) + { + u8 out_data = *(srcp - OFFSET_SIZE); + if ((LZByteTable[out_data] = LZOffsetTable[LZByteTable[out_data]]) == -1) + { + LZEndTable[out_data] = -1; + } + else + { + #if defined( REVERSE_SEARCH ) + LZRevOffsetTable[LZByteTable[out_data]] = -1; + #endif + } + insert_offset = windowPos; + } + else + { + insert_offset = windowLen; + } + + offset = LZEndTable[in_data]; + if (offset == -1) + { + LZByteTable[in_data] = insert_offset; + #if defined( REVERSE_SEARCH ) + LZRevOffsetTable[insert_offset] = -1; + #endif + } + else + { + #if defined( REVERSE_SEARCH ) + LZRevOffsetTable[insert_offset] = offset; + #endif + LZOffsetTable[offset] = insert_offset; + } + LZEndTable[in_data] = insert_offset; + LZOffsetTable[insert_offset] = -1; + + if (windowLen == OFFSET_SIZE) + { + info->WindowPos = (u16)((windowPos + 1) % OFFSET_SIZE); + } + else + { + info->WindowLen++; + } +} + +/*---------------------------------------------------------------------------* + Name: LZSlide + Description: + Arguments: *srcp + n + Returns: None. + *---------------------------------------------------------------------------*/ +static void +LZSlide( LZCompressInfo* info, const u8 *srcp, u32 n ) +{ + u32 i; + + for (i = 0; i < n; i++) + { + SlideByte(info, srcp++); + } +} + +//-------------------------------------------------------- +// LZ77圧縮でスライド窓の中から最長一致列を検索します。 +// Arguments: startp データの開始位置を示すポインタ +// nextp 検索を開始するデータのポインタ +// remainSize 残りデータサイズ +// offset 一致したオフセットを格納する領域へのポインタ +// Return : 一致列が見つかった場合は TRUE +// 見つからなかった場合は FALSE +//-------------------------------------------------------- +static u16 +SearchLZ( const LZCompressInfo* info, const u8 *nextp, u32 remainSize, u16 *offset, u16 minOffset, u32 maxLength ) +{ + const u8 *searchp; + const u8 *headp, *searchHeadp; + u16 currOffset; + u16 currLength = 2; + u16 tmpLength; + s32 w_offset; + const s16 * const LZOffsetTable = info->LZOffsetTable; + const u16 windowPos = info->WindowPos; + const u16 windowLen = info->WindowLen; + + if (remainSize < 3) + { + return 0; + } + + w_offset = info->LZByteTable[ *nextp ]; + + while (w_offset != -1) + { + if (w_offset < windowPos) + { + searchp = nextp - windowPos + w_offset; + } + else + { + searchp = nextp - windowLen - windowPos + w_offset; + } + + /* 無くても良いが、僅かに高速化する */ + if (*(searchp + 1) != *(nextp + 1) || *(searchp + 2) != *(nextp + 2)) + { + w_offset = LZOffsetTable[ w_offset ]; + continue; + } + + if (nextp - searchp < minOffset) + { + // VRAMは2バイトアクセスなので (VRAMからデータを読み出す場合があるため)、 + // 検索対象データは2バイト前からのデータにしなければならない。 + // + // オフセットは12ビットで格納されるため、4096以下 + #if defined( REVERSE_SEARCH ) + w_offset = LZOffsetTable[ w_offset ]; + continue; + #else + break; + #endif + } + tmpLength = 3; + searchHeadp = searchp + 3; + headp = nextp + 3; + + while (((u32)(headp - nextp) < remainSize) && (*headp == *searchHeadp)) + { + headp++; + searchHeadp++; + tmpLength++; + + // データ長は8ビットで格納されるため、258以下 (3の下駄をはかせる) + if (tmpLength == maxLength) + { + break; + } + } + if (tmpLength > currLength) + { + // 最大長オフセットを更新 + currLength = tmpLength; + currOffset = (u16)((u32)nextp - (u32)searchp); + if (currLength == maxLength) + { + // 一致長が最大なので、検索を終了する。 + break; + } + } + w_offset = LZOffsetTable[w_offset]; + } + + if (currLength < 3) + { + return 0; + } + *offset = currOffset; + return currLength; +} + +/*---------------------------------------------------------------------------* + Name: LZCompWrite + + Description: + + Arguments: *srcp + size + *dstp + lzSearchOffset + + Returns: + *---------------------------------------------------------------------------*/ +static u32 +LZCompWrite_( const u8 *srcp, s32 size, u8 *dstp, u8 lzSearchOffset, u8 offsetBits ) +{ + u32 LZDstCount = 0; // 圧縮データのバイト数 + u8 LZCompFlags; // 圧縮の有無を示すフラグ系列 + u8 *LZCompFlagsp; // LZCompFlags を格納するメモリ領域をポイント + u16 lastOffset; // 一致データまでのオフセット (その時点での最長一致データ) + u16 lastLength; // 一致データ長 (その時点での最長一致データ) + u8 i; + const u32 MAX_LENGTH = 0xFF + 3; + + LZInitTable( &gLZWork ); + gLZWork.OffsetBits = offsetBits; + + while ( size > 0 ) + { + LZCompFlags = 0; + LZCompFlagsp = dstp++; // フラグ系列の格納先 + LZDstCount++; + + // フラグ系列が8ビットデータとして格納されるため、8回ループ + for ( i = 0; i < 8; i++ ) + { + LZCompFlags <<= 1; // 初回 (i=0) は特に意味はない + if (size <= 0) + { + // 終端に来た場合はフラグを最後までシフトさせてから終了 + continue; + } + + if ( (lastLength = SearchLZ(&gLZWork, srcp, size, &lastOffset, lzSearchOffset, MAX_LENGTH)) != 0 ) + { + // 圧縮可能な場合はフラグを立てる + LZCompFlags |= 0x1; + + // オフセットは上位4ビットと下位8ビットに分けて格納 + *dstp++ = (u8)(lastLength - 3); + *dstp++ = (u8)((lastOffset - 1) & 0xff); // リトルエンディアン + *dstp++ = (u8)((lastOffset - 1) >> 8); + LZDstCount += 3; + LZSlide( &gLZWork, srcp, lastLength ); + srcp += lastLength; + size -= lastLength; + } + else + { + // 圧縮なし + LZSlide( &gLZWork, srcp, 1 ); + *dstp++ = *srcp++; + size--; + LZDstCount++; + } + } // 8回ループ終了 + *LZCompFlagsp = LZCompFlags; // フラグ系列を格納 + } + + // 4バイト境界アラインメント + // アラインメント用データ0 はデータサイズに含めない + i = 0; + while ((LZDstCount + i) & 0x3) + { + *dstp++ = 0; + i++; + } + + return LZDstCount; +} + + +typedef struct +{ + u16 No; // データNo + s16 PaNo; // 親No + u32 Freq; // 出現頻度 + s16 ChNo[2]; // 子No (0: 左側, 1: 右側) + u16 PaDepth; // 親ノードの深さ + u16 LeafDepth; // 葉までの深さ + u32 HuffCode; // ハフマン符号 + u16 Bit; // ノードのビットデータ + u16 HWord; // 各中間節点において、その節点をルートとする部分木を HuffTree 格納に必要なメモリ量 +} +HuffData; + +typedef struct +{ + u8 leftOffsetNeed; // 左の子節点へのオフセットが必要なら1 + u8 rightOffsetNeed; // 右の子節点へのオフセットが必要なら1 + u16 leftNodeNo; // 左の子節点No + u16 rightNodeNo; // 右の子節点No +} +HuffTreeCtrlData; + +// ハフマンワークバッファ構成 +typedef struct +{ + HuffData* huffTable; // huffTable[ 512 ]; 12288B + u16* huffTree; // huffTree[ 256 * 2 ]; 512B + HuffTreeCtrlData* huffTreeCtrl; // huffTreeCtrl[ 256 ]; 1536B + u16 huffTreeTop; // + u8 bitSize; // + u8 padding_[1]; // +} +HuffInfo; // 計 14340B + +static void HuffMakeHuffTree ( HuffInfo* info, u16 rootNo ); +static void HuffMakeSubsetHuffTree ( HuffInfo* info, u16 huffTreeNo, BOOL rightNodeFlag ); +static BOOL HuffRemainingNodeCanSetOffset( HuffInfo* info, u16 costHWord ); +static void HuffSetOneNodeOffset ( HuffInfo* info, u16 huffTreeNo, BOOL rightNodeFlag ); +static u16 HuffMakeNode ( HuffData* table, u8 bitSize ); + +static void HuffAddParentDepthToTable( HuffData *table, u16 leftNo, u16 rightNo ); +static void HuffAddCodeToTable ( HuffData* table, u16 nodeNo, u32 paHuffCode ); +static u16 HuffAddCountHWordToTable ( HuffData *table, u16 nodeNo ); + + +// ビットストリーム +typedef struct +{ + u8* dstp; // 出力先ポインタ + u32 cnt; // 出力サイズ + u32 stream; // カレントストリームデータ + u32 stream_len; // ストリームの長さ +} +BitStream; + +static void +BitStream_Init( BitStream* context, u8* dstp ) +{ + context->dstp = dstp; + context->cnt = 0; + context->stream = 0; + context->stream_len = 0; +} + +static void +BitStream_Write( BitStream* context, u32 data, u32 width ) +{ + u32 i; + u32 stream = context->stream; + u32 cnt = context->cnt; + u32 stream_len = context->stream_len; + u32 mask = (1 << width) - 1; + + if ( width == 0 ) + { + return; + } + + stream = (stream << width) | ( data & mask ); + stream_len += width; + + for ( i = 0; i < stream_len / 8; i++ ) + { + context->dstp[ cnt++ ] = (u8)( stream >> ( stream_len - ( i + 1 ) * 8 ) ); + } + stream_len %= 8; + + context->stream = stream; + context->cnt = cnt; + context->stream_len = stream_len; +} + +static void +BitStream_Terminate( BitStream* context, u32 align ) +{ + u32 stream = context->stream; + u32 cnt = context->cnt; + u32 stream_len = context->stream_len; + + if ( stream_len > 0 ) + { + stream <<= 8 - stream_len; + + if ( context->stream_len != 0 ) + { + context->dstp[ cnt++ ] = (u8)( stream ); + } + } + + while ( cnt % align ) + { + context->dstp[ cnt++ ] = 0; + } + context->cnt = cnt; + context->stream_len = 0; +} + + + + +/*---------------------------------------------------------------------------* + Name: HuffInitTable + Description: + Arguments: info + bitSize + Returns: None. + *---------------------------------------------------------------------------*/ +static void +HuffInitTable( HuffInfo* info, u8 bitSize ) +{ + u32 tableSize = (1 << bitSize); + u32 i; + + info->huffTable = (HuffData*)malloc( sizeof(HuffData) * tableSize * 2 ); + info->huffTree = (u16*)malloc( sizeof(u16) * tableSize * 2 ); + info->huffTreeCtrl = (HuffTreeCtrlData*)malloc( sizeof(HuffTreeCtrlData) * tableSize ); + + info->huffTreeTop = 1; + info->bitSize = bitSize; + + // huffTableを初期化 + { + HuffData* table = info->huffTable; + const HuffData HUFF_TABLE_INIT_DATA = { 0, 0, 0, {-1, -1}, 0, 0, 0, 0, 0 }; + for ( i = 0; i < tableSize * 2; i++ ) + { + table[ i ] = HUFF_TABLE_INIT_DATA; + table[ i ].No = (u16)i; + } + } + + // huffTree, huffTreeCtrlを初期化 + { + const HuffTreeCtrlData HUFF_TREE_CTRL_INIT_DATA = { 1, 1, 0, 0 }; + u16* huffTree = info->huffTree; + HuffTreeCtrlData* huffTreeCtrl = info->huffTreeCtrl; + + for ( i = 0; i < tableSize; i++ ) + { + huffTree[ i * 2 ] = 0; + huffTree[ i * 2 + 1 ] = 0; + huffTreeCtrl[ i ] = HUFF_TREE_CTRL_INIT_DATA; + } + } +} + + +/*---------------------------------------------------------------------------* + Name: LZCountHuffData + Description: + Arguments: srcp + srcSize + info8 + info16 + Returns: None. + *---------------------------------------------------------------------------*/ +static void +LZCountHuffData( const u8* srcp, u32 srcSize, HuffInfo* info8, HuffInfo* info16 ) +{ + u32 srcCnt = 0; + u32 i; + + while ( srcCnt < srcSize ) + { + u8 compFlags = srcp[ srcCnt++ ]; // 圧縮の有無を示すフラグ列 + for ( i = 0; i < 8; i++ ) + { + if ( compFlags & 0x80 ) // 圧縮されている、length:8, offset:16 + { + u8 length = srcp[ srcCnt++ ]; + u16 offset = srcp[ srcCnt++ ]; // リトルエンディアン + offset |= (srcp[ srcCnt++ ] << 8); + + #if BLEND_COMP_FLAG + info8->huffTable[ length | 0x100 ].Freq++; + #else + info8->huffTable[ length ].Freq++; + #endif + #if !defined(LH_ENC_OFFSET_WIDTH) + info16->huffTable[ offset ].Freq++; + #else + { + u32 offset_bit = 0; + while ( offset != 0 ) + { + ++offset_bit; + offset >>= 1; + } + info16->huffTable[ offset_bit ].Freq++; + } + #endif + } + else + { + u8 data = srcp[ srcCnt++ ]; + info8->huffTable[ data ].Freq++; + } + compFlags <<= 1; + if ( srcCnt >= srcSize ) + { + break; + } + } + } +} + + +/*---------------------------------------------------------------------------* + Name: ConstructHuffTree + Description: + Arguments: info + bitSize + Returns: None. + *---------------------------------------------------------------------------*/ +static void +ConstructHuffTree( HuffInfo* info, u8 bitSize ) +{ + HuffData* table = info->huffTable; + u16 rootNo; + + // 出現頻度からノードを構築 + rootNo = HuffMakeNode( table, bitSize ); + + // ハフマンコード生成 (table[i].HuffCode に) + HuffAddCodeToTable( table, rootNo, 0x00 ); // PaDepthのビット数だけ、HuffCode の下位ビットをマスクしたものがハフマンコード + + // 各中間節点において、その節点をルートとする部分木を huffTree 格納に必要なメモリ量の計算 + HuffAddCountHWordToTable( table, rootNo ); + + HuffMakeHuffTree( info, rootNo ); + info->huffTreeTop--; +} + +//----------------------------------------------------------------------- +// ハフマンコード表作成 +//----------------------------------------------------------------------- +static void +HuffMakeHuffTree( HuffInfo* info, u16 rootNo ) +{ + s16 i; + s16 costHWord, tmpCostHWord; // 部分木のコード表を作成しなかった時のコスト 最大値の節点の部分木コード表を作る + s16 costOffsetNeed, tmpCostOffsetNeed; + s16 costMaxKey; // コスト最小の節点を huffTreeBuf.huffTree から特定するための情報 + BOOL costMaxRightFlag; + u16 offsetNeedNum; + BOOL tmpRightFlag; + const u32 MAX_COST = 1 << (info->bitSize - 2); + + info->huffTreeTop = 1; + costOffsetNeed = 0; + + info->huffTreeCtrl[0].leftOffsetNeed = 0; // 使用しない (テーブルサイズとして使用) + info->huffTreeCtrl[0].rightNodeNo = rootNo; + + while ( 1 ) // return するまで + { + // オフセットを設定する必要のあるノード数の計算 + offsetNeedNum = 0; + for ( i = 0; i < info->huffTreeTop; i++ ) + { + if ( info->huffTreeCtrl[ i ].leftOffsetNeed ) + { + offsetNeedNum++; + } + if ( info->huffTreeCtrl[ i ].rightOffsetNeed ) + { + offsetNeedNum++; + } + } + + // 最大コストの節点を検索 + costHWord = -1; + costMaxKey = -1; + tmpRightFlag = 0; + + for ( i = 0; i < info->huffTreeTop; i++ ) + { + tmpCostOffsetNeed = (u16)( info->huffTreeTop - i ); + + // 左の子節点のコスト評価 + if ( info->huffTreeCtrl[i].leftOffsetNeed ) + { + tmpCostHWord = (s16)info->huffTable[ info->huffTreeCtrl[i].leftNodeNo ].HWord; + + if ( (u32)(tmpCostHWord + offsetNeedNum) > MAX_COST ) + { + goto leftCostEvaluationEnd; + } + if ( ! HuffRemainingNodeCanSetOffset( info, (u16)tmpCostHWord ) ) + { + goto leftCostEvaluationEnd; + } + if ( tmpCostHWord > costHWord ) + { + costMaxKey = i; + costMaxRightFlag = 0; + } + else if ( (tmpCostHWord == costHWord) && (tmpCostOffsetNeed > costOffsetNeed) ) + { + costMaxKey = i; + costMaxRightFlag = 0; + } + } +leftCostEvaluationEnd:{} + + if ( info->huffTreeCtrl[i].rightOffsetNeed ) + { + tmpCostHWord = (s16)info->huffTable[ info->huffTreeCtrl[i].rightNodeNo ].HWord; + + if ( (u32)(tmpCostHWord + offsetNeedNum) > MAX_COST ) + { + goto rightCostEvaluationEnd; + } + if ( ! HuffRemainingNodeCanSetOffset( info, (u16)tmpCostHWord ) ) + { + goto rightCostEvaluationEnd; + } + if ( tmpCostHWord > costHWord ) + { + costMaxKey = i; + costMaxRightFlag = 1; + } + else if ( (tmpCostHWord == costHWord) && (tmpCostOffsetNeed > costOffsetNeed) ) + { + costMaxKey = i; + costMaxRightFlag = 1; + } + } +rightCostEvaluationEnd:{} + } + + // 部分木をまるまる huffTree に格納 + if ( costMaxKey >= 0 ) + { + HuffMakeSubsetHuffTree( info, (u16)costMaxKey, costMaxRightFlag); + goto nextTreeMaking; + } + else + { + // 必要オフセット最大のノードを検索 + for ( i = 0; i < info->huffTreeTop; i++ ) + { + u16 tmp = 0; + tmpRightFlag = 0; + if ( info->huffTreeCtrl[i].leftOffsetNeed ) + { + tmp = info->huffTable[ info->huffTreeCtrl[i].leftNodeNo ].HWord; + } + if ( info->huffTreeCtrl[i].rightOffsetNeed ) + { + if ( info->huffTable[ info->huffTreeCtrl[i].rightNodeNo ].HWord > tmp ) + { + tmpRightFlag = 1; + } + } + if ( (tmp != 0) || (tmpRightFlag) ) + { + HuffSetOneNodeOffset( info, (u16)i, tmpRightFlag ); + goto nextTreeMaking; + } + } + } + return; +nextTreeMaking:{} + } +} + +//----------------------------------------------------------------------- +// 部分木をまるまる huffTree に格納 +//----------------------------------------------------------------------- +static void +HuffMakeSubsetHuffTree( HuffInfo* info, u16 huffTreeNo, BOOL rightNodeFlag ) +{ + u16 i; + + i = info->huffTreeTop; + HuffSetOneNodeOffset( info, huffTreeNo, rightNodeFlag ); + + if ( rightNodeFlag ) + { + info->huffTreeCtrl[ huffTreeNo ].rightOffsetNeed = 0; + } + else + { + info->huffTreeCtrl[ huffTreeNo ].leftOffsetNeed = 0; + } + + while ( i < info->huffTreeTop ) + { + if ( info->huffTreeCtrl[ i ].leftOffsetNeed ) + { + HuffSetOneNodeOffset( info, i, 0 ); + info->huffTreeCtrl[ i ].leftOffsetNeed = 0; + } + if ( info->huffTreeCtrl[ i ].rightOffsetNeed ) + { + HuffSetOneNodeOffset( info, i, 1 ); + info->huffTreeCtrl[ i ].rightOffsetNeed = 0; + } + i++; + } +} + +//----------------------------------------------------------------------- +// 与えられたデータ量の部分木を展開しても huffTree 構築に支障がないか調べる +//----------------------------------------------------------------------- +static BOOL +HuffRemainingNodeCanSetOffset( HuffInfo* info, u16 costHWord ) +{ + u16 i; + s16 capacity; + const u32 MAX_COST = 1 << (info->bitSize - 2); + + capacity = (s16)( MAX_COST - costHWord ); + + // オフセット数は i が小さいほど大きいので、ソートせず、i = 0 -> huffTreeTop で計算すればよい + for ( i = 0; i < info->huffTreeTop; i++ ) + { + if ( info->huffTreeCtrl[i].leftOffsetNeed ) + { + if ( (info->huffTreeTop - i) <= capacity ) + { + capacity--; + } + else + { + return 0; + } + } + if ( info->huffTreeCtrl[i].rightOffsetNeed ) + { + if ( (info->huffTreeTop - i) <= capacity ) + { + capacity--; + } + else + { + return 0; + } + } + } + + return 1; +} + + +/*---------------------------------------------------------------------------* + Name: HuffSetOneNodeOffset + Description: 1節点分、ハフマンコード表を作成 + Arguments: *table ハフマンテーブル + huffTreeNo + rightNodeFlag 右側のノードであるかどうかのフラグ + Returns: None. + *---------------------------------------------------------------------------*/ +static void +HuffSetOneNodeOffset( HuffInfo* info, u16 huffTreeNo, BOOL rightNodeFlag) +{ + u16 nodeNo; + u16 offsetData = 0; + + HuffData* huffTable = info->huffTable; + u16* huffTree = info->huffTree; + HuffTreeCtrlData* huffTreeCtrl = info->huffTreeCtrl; + u16 huffTreeTop = info->huffTreeTop; + + if (rightNodeFlag) + { + nodeNo = huffTreeCtrl[ huffTreeNo ].rightNodeNo; + huffTreeCtrl[ huffTreeNo ].rightOffsetNeed = 0; + } + else + { + nodeNo = huffTreeCtrl[ huffTreeNo ].leftNodeNo; + huffTreeCtrl [huffTreeNo ].leftOffsetNeed = 0; + } + + // 左の子節点 + if ( huffTable[ huffTable[nodeNo].ChNo[0] ].LeafDepth == 0) + { + offsetData |= 0x8000; + huffTree[ huffTreeTop * 2 + 0 ] = (u16)huffTable[ nodeNo ].ChNo[0]; + huffTreeCtrl[ huffTreeTop ].leftNodeNo = (u16)huffTable[ nodeNo ].ChNo[0]; + huffTreeCtrl[ huffTreeTop ].leftOffsetNeed = 0; // オフセットは必要なくなる + } + else + { + huffTreeCtrl[ huffTreeTop ].leftNodeNo = (u16)huffTable[ nodeNo ].ChNo[0]; // オフセットは必要 + } + + // 右の子節点 + if ( huffTable[ huffTable[ nodeNo ].ChNo[1] ].LeafDepth == 0 ) + { + offsetData |= 0x4000; + huffTree[ huffTreeTop * 2 + 1 ] = (u16)huffTable[nodeNo].ChNo[1]; + huffTreeCtrl[ huffTreeTop ].rightNodeNo = (u16)huffTable[ nodeNo ].ChNo[1]; + huffTreeCtrl[ huffTreeTop ].rightOffsetNeed = 0; // オフセットは必要なくなる + } + else + { + huffTreeCtrl[ huffTreeTop ].rightNodeNo = (u16)huffTable[ nodeNo ].ChNo[1]; // オフセットは必要 + } + + offsetData |= (u16)( huffTreeTop - huffTreeNo - 1 ); + huffTree[ huffTreeNo * 2 + (rightNodeFlag? 1 : 0) ] = offsetData; + + info->huffTreeTop++; +} + +/*---------------------------------------------------------------------------* + Name: HuffMakeNode + Description: 出現頻度からノードデータを構築 + Arguments: table + Returns: None. + *---------------------------------------------------------------------------*/ +static u16 +HuffMakeNode( HuffData* table, u8 bitSize ) +{ + u16 dataNum = ( 1 << bitSize ); + u16 tableTop = (u16)dataNum; // テーブル作成時の、テーブルトップNo + + u32 i; + s32 leftNo, rightNo; // 2分木作成時のノードNo + u16 rootNo; // 二分木のルートNo + + leftNo = -1; + rightNo = -1; + while ( 1 ) + { + // Freqの小さい部分木頂点を2つ探す 1つは必ず見つかるはず + // 子頂点(左)の探索 + for ( i = 0; i < tableTop; i++ ) + { + if ( ( table[i].Freq == 0 ) || + ( table[i].PaNo != 0 ) ) + { + continue; + } + + if ( leftNo < 0 ) + { + leftNo = i; + } + else if ( table[i].Freq < table[ leftNo ].Freq ) + { + leftNo = i; + } + } + + // 子頂点(右)の探索 + for ( i = 0; i < tableTop; i++ ) + { + if ( ( table[i].Freq == 0 ) || + ( table[i].PaNo != 0 ) || + ( i == leftNo ) ) + { + continue; + } + + if ( rightNo < 0 ) + { + rightNo = i; + } + else if ( table[i].Freq < table[ rightNo ].Freq ) + { + rightNo = i; + } + } + + // 1つしかなかったら、テーブル作成終了 + if ( rightNo < 0 ) + { + // 値が一種類しかない存在しない場合には01どちらも同じ値となるノードを1つ作成する + if ( tableTop == dataNum ) + { + table[ tableTop ].Freq = table[ leftNo ].Freq; + table[ tableTop ].ChNo[0] = (s16)leftNo; + table[ tableTop ].ChNo[1] = (s16)leftNo; + table[ tableTop ].LeafDepth = 1; + table[ leftNo ].PaNo = (s16)tableTop; + table[ leftNo ].Bit = 0; + table[ leftNo ].PaDepth = 1; + } + else + { + tableTop--; + } + rootNo = tableTop; + return rootNo; + } + + // 左部分木と右部分木を統合する頂点作成 + table[ tableTop ].Freq = table[ leftNo ].Freq + table[ rightNo ].Freq; + table[ tableTop ].ChNo[0] = (s16)leftNo; + table[ tableTop ].ChNo[1] = (s16)rightNo; + if ( table[ leftNo ].LeafDepth > table[ rightNo ].LeafDepth ) + { + table[ tableTop ].LeafDepth = (u16)( table[ leftNo ].LeafDepth + 1 ); + } + else + { + table[ tableTop ].LeafDepth = (u16)( table[ rightNo ].LeafDepth + 1 ); + } + + table[ leftNo ].PaNo = table[ rightNo ].PaNo = (s16)( tableTop ); + table[ leftNo ].Bit = 0; + table[ rightNo ].Bit = 1; + + HuffAddParentDepthToTable( table, (u16)leftNo, (u16)rightNo ); + + tableTop++; + leftNo = rightNo = -1; + } +} + + +//----------------------------------------------------------------------- +// 2文木作成時に、部分木を統合したときに、部分木の各構成ノードの深さを+1する +//----------------------------------------------------------------------- +static void +HuffAddParentDepthToTable( HuffData *table, u16 leftNo, u16 rightNo ) +{ + table[ leftNo ].PaDepth++; + table[ rightNo ].PaDepth++; + + if ( table[ leftNo ].LeafDepth != 0 ) + { + HuffAddParentDepthToTable( table, (u16)table[ leftNo ].ChNo[0], (u16)table[ leftNo ].ChNo[1] ); + } + if ( table[ rightNo ].LeafDepth != 0 ) + { + HuffAddParentDepthToTable( table, (u16)table[ rightNo ].ChNo[0], (u16)table[ rightNo ].ChNo[1] ); + } +} + +//----------------------------------------------------------------------- +// ハフマンコード生成 +//----------------------------------------------------------------------- +static void +HuffAddCodeToTable( HuffData* table, u16 nodeNo, u32 paHuffCode ) +{ + table[ nodeNo ].HuffCode = (paHuffCode << 1) | table[ nodeNo ].Bit; + + if ( table[ nodeNo ].LeafDepth != 0 ) + { + HuffAddCodeToTable( table, (u16)table[ nodeNo ].ChNo[0], table[ nodeNo ].HuffCode ); + HuffAddCodeToTable( table, (u16)table[ nodeNo ].ChNo[1], table[ nodeNo ].HuffCode ); + } +} + + +//----------------------------------------------------------------------- +// 中間ノードが huffTree 作成に必要とするデータ量 +//----------------------------------------------------------------------- +static u16 +HuffAddCountHWordToTable( HuffData *table, u16 nodeNo) +{ + u16 leftHWord, rightHWord; + + switch ( table[ nodeNo ].LeafDepth ) + { + case 0: + return 0; + case 1: + leftHWord = rightHWord = 0; + break; + default: + leftHWord = HuffAddCountHWordToTable( table, (u16)table[nodeNo].ChNo[0] ); + rightHWord = HuffAddCountHWordToTable( table, (u16)table[nodeNo].ChNo[1] ); + break; + } + + table[ nodeNo ].HWord = (u16)( leftHWord + rightHWord + 1 ); + return (u16)( leftHWord + rightHWord + 1 ); +} + + + +/*---------------------------------------------------------------------------* + Name: LZMakeHuffTree + Description: + Arguments: srcp + tree8 + tree16 + Returns: None. + *---------------------------------------------------------------------------*/ +static void +LZMakeHuffTree( const u8* srcp, u32 srcSize, HuffInfo* info8, HuffInfo* info16 ) +{ + HuffInitTable( info8, LENGTH_BITS ); + HuffInitTable( info16, LH_OFFSET_TABLE_BITS ); + + LZCountHuffData( srcp, srcSize, info8, info16 ); + + ConstructHuffTree( info8, LENGTH_BITS ); + ConstructHuffTree( info16, LH_OFFSET_TABLE_BITS ); +} + + +/*---------------------------------------------------------------------------* + Name: ExportHuffTree + Description: + Arguments: dstp + info + bitSize + Returns: + *---------------------------------------------------------------------------*/ +static u32 +ExportHuffTree( u8* dstp, HuffInfo* info, u8 bitSize ) +{ + BitStream stream; + u32 i; + u8* pSize; + u32 tblSize; + + BitStream_Init( &stream, dstp ); + + pSize = dstp; + BitStream_Write( &stream, 0, RoundUp( bitSize, 8 ) ); + + for ( i = 1; i < (u16)( (info->huffTreeTop + 1) * 2); i++ ) + { + u16 flags = (u16)( info->huffTree[ i ] & 0xC000 ); + u32 data = info->huffTree[ i ] | (flags >> (16 - bitSize)); + BitStream_Write( &stream, data, bitSize ); + } + BitStream_Terminate( &stream, 4 ); + + // テーブルサイズの1/4をサイズ領域へ保存 + tblSize = (stream.cnt / 4) - 1; + if ( RoundUp( bitSize, 8 ) == 8 ) + { + if ( tblSize >= 0x100 ) + { + fprintf(stderr, "table size is over!\n"); + } + *pSize = (u8)( tblSize ); + } + else // RoundUp( bitSize, 8 ) == 16 ) + { + if ( tblSize >= 0x10000 ) + { + fprintf(stderr, "table size is over!\n"); + } + *(u16*)pSize = (u16)( tblSize ); + } + return stream.cnt; +} + + +/*---------------------------------------------------------------------------* + Name: ConvertHuff + Description: + Arguments: info + data + stream + Returns: None. + *---------------------------------------------------------------------------*/ +static void +ConvertHuff( HuffInfo* info, u16 data, BitStream* stream ) +{ + u16 width = info->huffTable[ data ].PaDepth; + u32 code = info->huffTable[ data ].HuffCode; + + BitStream_Write( stream, code, width ); +} + + +/*---------------------------------------------------------------------------* + Name: LZConvertHuffData + Description: + Arguments: srcp + tmpSize + dstp + info8 + info16 + Returns: + *---------------------------------------------------------------------------*/ +static u32 +LZConvertHuffData( const u8* srcp, u32 srcSize, u8* dstp, HuffInfo* info8, HuffInfo* info16 ) +{ + u32 srcCnt = 0; + u32 dstCnt = 0; + + BitStream stream; + + BitStream_Init( &stream, dstp ); + + while ( srcCnt < srcSize ) + { + u32 i; + u8 compFlags = srcp[ srcCnt++ ]; // 圧縮の有無を示すフラグ列 + #if BLEND_COMP_FLAG + #else + BitStream_Write( &stream, compFlags, 8 ); + #endif + + for ( i = 0; i < 8; i++ ) + { + if ( compFlags & 0x80 ) // 圧縮されている、length:8, offset:16 + { + u8 length = srcp[ srcCnt++ ]; + u16 offset = srcp[ srcCnt++ ]; // リトルエンディアン + offset |= srcp[ srcCnt++ ] << 8; + + #if BLEND_COMP_FLAG + ConvertHuff( info8, length | 0x100, &stream ); + #else + ConvertHuff( info8, length, &stream ); + #endif + #if ! defined(LH_ENC_OFFSET_WIDTH) + ConvertHuff( info16, offset, &stream ); + #else + { + u16 offset_bit = 0; + u16 offset_tmp = offset; + while ( offset_tmp > 0 ) + { + offset_tmp >>= 1; + ++offset_bit; + } + ConvertHuff( info16, offset_bit, &stream ); + // offsetが0であることはないので、最上位のビットは省略する + BitStream_Write( &stream, offset & ~(1 << (offset_bit - 1)), offset_bit - 1 ); + } + #endif + } + else + { + u8 data = srcp[ srcCnt++ ]; + + ConvertHuff( info8, data, &stream ); + } + compFlags <<= 1; + if ( srcCnt >= srcSize ) + { + break; + } + } + } + + BitStream_Terminate( &stream, 4 ); + return stream.cnt; +} + +/*---------------------------------------------------------------------------* + Name: LHCompWrite + Description: + Arguments: *srcp + size + *dstp + Returns: + *---------------------------------------------------------------------------*/ +u32 +LHCompWrite( const u8 *srcp, s32 srcSize, u8 *dstp ) +{ + static HuffInfo sTree8; + static HuffInfo sTree16; + + u32 tmpSize; + u32 dstSize; + u8* tmpBuf = (u8*)malloc( srcSize * 3 ); + // まずはsrcpを普通にLZ圧縮 + tmpSize = LZCompWrite_( srcp, srcSize, tmpBuf, 2, LH_OFFSET_BITS ); + + // offsetとlengthの集計 + LZMakeHuffTree( tmpBuf, tmpSize, &sTree8, &sTree16 ); + + dstSize = 0; + + // ヘッダの書き込み + if ( srcSize < 0x1000000 && srcSize > 0 ) + { + *(u32*)dstp = LH_CODE_HEADER | ( srcSize << 8 ); + dstSize = 4; + } + else + { + *(u32*)dstp = LH_CODE_HEADER; + *(u32*)&dstp[4] = srcSize; + dstSize = 8; + } + // ハフマンテーブルを出力 + dstSize += ExportHuffTree( &dstp[ dstSize ], &sTree8, LENGTH_BITS ); + dstSize += ExportHuffTree( &dstp[ dstSize ], &sTree16, LH_OFFSET_TABLE_BITS ); + + // 圧縮結果をハフマン符号化しながら出力 + dstSize += LZConvertHuffData( tmpBuf, tmpSize, &dstp[ dstSize ], &sTree8, &sTree16 ); + + return dstSize; +} + + + +typedef struct +{ + u16 huffTable9 [ (1 << LENGTH_BITS) * 2 ]; + u16 huffTable12[ OFFSET_SIZE_MAX * 2 ]; +} +LHContext; + + +/*---------------------------------------------------------------------------* + Name: HuffImportTree + Description: + Arguments: pTable + srcp + bitSize + srcRemainSize + Returns: + *---------------------------------------------------------------------------*/ +static u32 +HuffImportTree( u16* pTable, const u8* srcp, u8 bitSize, u32 srcRemainSize ) +{ + u32 tableSize; + u32 idx = 1; + u32 data = 0; + u32 bitNum = 0; + u32 bitMask = (1 << bitSize) - 1; + u32 srcCnt = 0; + + if ( bitSize > 8 ) + { + tableSize = *(u16*)srcp; + srcp += 2; + srcCnt += 2; + } + else + { + tableSize = *srcp; + srcp += 1; + srcCnt += 1; + } + tableSize = (tableSize + 1) * 4; + if ( srcRemainSize < tableSize ) + { + return tableSize; + } + + while ( srcCnt < tableSize ) + { + while ( bitNum < bitSize ) + { + data <<= 8; + data |= *srcp++; + ++srcCnt; + bitNum += 8; + } + if ( idx < (u32)((1 << bitSize) * 2) ) + { + pTable[ idx++ ] = (u16)( ( data >> (bitNum - bitSize) ) & bitMask ); + } + bitNum -= bitSize; + } + + pTable[ 0 ] = (u16)idx; + return tableSize; +} + + +/*---------------------------------------------------------------------------* + Name: HuffVerifyTable + + Description: ハフマンテーブルの整合性をチェック + + Arguments: pTable ハフマンテーブルへのポインタ + bit ハフマン符号のビット数 + + Returns: 正常なテーブルの場合には TRUE + 不正なテーブルの場合には FALSE + *---------------------------------------------------------------------------*/ +static BOOL +HuffVerifyTable( const void* pTable, u8 bit ) +{ +#if !defined(LH_ENC_OFFSET_WIDTH) + enum { FLAGS_ARRAY_NUM = 8192 / 8 }; /* 1024Byte */ + static u8 end_flags[ FLAGS_ARRAY_NUM ]; +#else + enum { FLAGS_ARRAY_NUM = 1024 / 8 }; /* 128Byte */ + u8 end_flags[ FLAGS_ARRAY_NUM ]; +#endif + u16* treep = (u16*)pTable; + u16* treeStartp = treep + 1; + u32 treeSize = *treep; + u16* treeEndp = (u16*)pTable + treeSize; + u32 i; + u32 idx; + const u16 ofs_mask = (u16)( (1 << (bit - 2)) - 1 ); + const u16 l_mask = (u16)( 1 << (bit - 1) ); + const u16 r_mask = (u16)( 1 << (bit - 2) ); + + for ( i = 0; i < FLAGS_ARRAY_NUM; i++ ) + { + end_flags[ i ] = 0; + } + + if ( treeSize > (1U << (bit + 1)) ) + { + return FALSE; + } + + idx = 1; + treep = treeStartp; + while ( treep < treeEndp ) + { + if ( (end_flags[ idx / 8 ] & (1 << (idx % 8) )) == 0 ) + { + u32 offset = (u32)( ( (*treep & ofs_mask) + 1 ) << 1 ); + u16* nodep = (u16*)((u32)treep & ~0x3) + offset; + + // 終端のアライメント用データは読み飛ばす + if ( *treep == 0 && idx >= treeSize - 4 ) + { + goto next; + } + if ( nodep >= treeEndp ) + { + return FALSE; + } + if ( *treep & l_mask ) + { + u32 left = (idx & ~0x1) + offset; + end_flags[ left / 8 ] |= (u8)( 1 << (left % 8) ); + } + if ( *treep & r_mask ) + { + u32 right = (idx & ~0x1) + offset + 1; + end_flags[ right / 8 ] |= (u8)( 1 << (right % 8) ); + } + } + next: + ++idx; + ++treep; + } + return TRUE; +} + + +typedef struct +{ + const u8* srcp; + u32 cnt; + u32 srcSize; + u32 stream; + u32 stream_len; +} +BitReader; + +static INLINE void +BitReader_Init( BitReader* context, const u8* srcp, u32 srcSize ) +{ + context->srcp = srcp; + context->cnt = 0; + context->stream = 0; + context->stream_len = 0; + context->srcSize = srcSize; +} + +static INLINE s8 +BitReader_Read( BitReader* context ) +{ + s8 bit; + if ( context->stream_len == 0 ) + { + if ( context->cnt > context->srcSize ) + { + return -1; + } + context->stream = context->srcp[context->cnt++]; + context->stream_len = 8; + } + bit = (s8)( (context->stream >> (context->stream_len - 1)) & 0x1 ); + context->stream_len--; + return bit; +} + +static s32 +BitReader_ReadEx( BitReader* context, u8 width ) +{ + s32 data; + + ASSERT( width <= 24 ); + + if ( width == 0 ) + { + return 0; + } + + while ( context->stream_len < width ) + { + if ( context->cnt > context->srcSize ) + { + return -1; + } + context->stream <<= 8; + context->stream |= context->srcp[context->cnt++]; + context->stream_len += 8; + } + data = (s32)( (context->stream >> (context->stream_len - width)) & ((1 << width) - 1) ); + context->stream_len -= width; + return data; +} + + +/*---------------------------------------------------------------------------* + Name: LHCompRead + Description: + Arguments: srcp + size + *dstp + Returns: + *---------------------------------------------------------------------------*/ +s32 +LHCompRead( const u8* srcp, u32 srcSize, u8* dstp ) +{ + static LHContext sContext; + u32 dstSize; + u32 srcCnt = 0; + u32 dstCnt = 0; + BitReader stream; + + if ( srcSize < 4 ) + { + return -1; + } + + // ヘッダの読み込み + dstSize = *(u32*)srcp >> 8; + srcCnt = 4; + if ( dstSize == 0 ) + { + if ( srcSize < 8 ) + { + return -1; + } + dstSize = *(u32*)(srcp + 4); + srcCnt += 4; + } + + // ハフマンテーブルを読む + srcCnt += HuffImportTree( sContext.huffTable9, &srcp[srcCnt], LENGTH_BITS, srcSize - srcCnt ); + if ( srcCnt >= srcSize || (!HuffVerifyTable( sContext.huffTable9, LENGTH_BITS )) ) + { + return -1; + } + + srcCnt += HuffImportTree( sContext.huffTable12, &srcp[srcCnt], LH_OFFSET_TABLE_BITS, srcSize - srcCnt ); + + if ( srcCnt >= srcSize || (!HuffVerifyTable( sContext.huffTable12, LH_OFFSET_TABLE_BITS )) ) + { + return -1; + } + + BitReader_Init( &stream, &srcp[srcCnt], srcSize - srcCnt ); + + while ( dstCnt < dstSize ) + { + u16* nodep = sContext.huffTable9 + 1; + u16 val; + do + { + s8 bit = BitReader_Read( &stream ); + u32 offset = (((*nodep & 0x7F) + 1) << 1) + bit; + + if ( bit < 0 ) + { + return -1; + } + + if ( *nodep & (0x100 >> bit) ) + { + nodep = (u16*)((u32)nodep & ~0x3); + val = *(nodep + offset); + break; + } + else + { + nodep = (u16*)((u32)nodep & ~0x3); + nodep += offset; + } + } while ( 1 ); + + if ( val < 0x100 ) + // 非圧縮データ + { + dstp[dstCnt++] = (u8)val; + } + else + // 圧縮データ + { + #if !defined(LH_ENC_OFFSET_WIDTH) + #define OFFSET_MASK 0x3FF + #define LEAF_FLAG 0x800 + #else + #define OFFSET_MASK 0x07 + #define LEAF_FLAG 0x10 + u16 offset_bit; + #endif + u16 length = (val & 0xFF) + 3; + u16* nodep = sContext.huffTable12 + 1; + do + { + s8 bit = BitReader_Read( &stream ); + u32 offset = (((*nodep & OFFSET_MASK) + 1) << 1) + bit; + + if ( bit < 0 ) + { + return -1; + } + + if ( *nodep & (LEAF_FLAG >> bit) ) + { + nodep = (u16*)((u32)nodep & ~0x3); + val = *(nodep + offset); + break; + } + else + { + nodep = (u16*)((u32)nodep & ~0x3); + nodep += offset; + } + } while ( 1 ); + + #if defined(LH_ENC_OFFSET_WIDTH) + offset_bit = val; + val = 0; + if ( offset_bit > 0 ) + { + val = 1; + while ( --offset_bit > 0 ) + { + val <<= 1; + val |= BitReader_Read( &stream ); + } + } + #endif + val += 1; + + // バッファオーバーランをチェック + if ( dstCnt + length > dstSize ) + { + return -1; + } + if ( dstCnt < val ) + { + return -1; + } + if ( srcCnt + stream.cnt > srcSize ) + { + return -1; + } + + while ( length-- > 0 ) + { + dstp[dstCnt] = dstp[dstCnt - val]; + ++dstCnt; + } + #undef OFFSET_MASK + #undef LEAF_FLAG + } + } + return dstCnt; + + +} + + +//============================================================================== +// +// LRC圧縮/展開 +// +//============================================================================== + +#define LRC_ADAPTIVE // 適応型レンジコーダを使用するかどうか +#define RC_MAX_RANGE 0x80000000 +#define RC_UNIT_BITS 8 // 1バイト単位で出力 +// #define LRC_ENC_OFFSET_WIDTH // NOTE: このオプションでは正常に動作できない。 + // 正確には、展開時に生のbitデータがレンジコードとして先に + // 読まれてしまうので、復元後には次のデータが取り出せない。 + +#if defined( LRC_ADAPTIVE ) + #define TABLE8_ADAPTIVE TRUE +#else + #define TABLE8_ADAPTIVE FALSE +#endif + +#if defined( LRC_ENC_OFFSET_WIDTH ) + #define TABLE16_ADAPTIVE FALSE // bitLenを使用する場合には、境界テーブルが小さいのでofsは静的RCでOK + #define LRC_OFFSET_BITS 15 + #define LRC_OFFSET_TABLE_BITS 5 +#else + #define TABLE16_ADAPTIVE TRUE // bitLenを使用しない場合には、境界テーブルが大きくなるのでofsは動的RCを使用 + #define LRC_OFFSET_BITS 12 + #define LRC_OFFSET_TABLE_BITS LRC_OFFSET_BITS +#endif + +// レンジコーダ用構造体 +typedef struct +{ + u32 *freq; // 出現頻度テーブル (1 << bitSize) * sizeof(u32) Byte + u32 *low_cnt; // LOW境界値テーブル (1 << bitSize) * sizeof(u32) Byte + u32 total; // トータル 4 Byte + u8 bitSize; // ビットサイズ 1 Byte + u8 padding_[1]; // +} +RCCompressionInfo; + +// レンジコーダ状態構造体 +typedef struct +{ + u32 low; + u32 range; + u32 code; // 展開時のみ使用 + u8 carry; // 圧縮時のみ使用 + u32 carry_cnt; // 圧縮時のみ使用 +} +RCState; + +/*---------------------------------------------------------------------------* + Name: RCInitState_ + + Description: RC状態の初期化をおこないます。 + + Arguments: state + + Returns: None. + *---------------------------------------------------------------------------*/ +static void +RCInitState_( RCState* state ) +{ + // 開始Rangeが0x80000000なので、初回いきなり桁上げが発生することはない + state->low = 0; + state->range = RC_MAX_RANGE; + state->code = 0; + state->carry = 0; + state->carry_cnt = 0; +} + + +/*---------------------------------------------------------------------------* + Name: RCInitInfo_ + + Description: 静的レンジコーダのテーブル初期化 + すべての出現頻度を0で初期化します。 + + Arguments: info + + Returns: None. + *---------------------------------------------------------------------------*/ +static void +RCInitInfo_( RCCompressionInfo* info, u8 bitSize ) +{ + u32 tableSize = (1 << bitSize); + u32 i; + + info->bitSize = bitSize; + info->freq = (u32*)malloc( sizeof(u32) * tableSize ); + info->low_cnt = (u32*)malloc( sizeof(u32) * tableSize ); + + for ( i = 0; i < tableSize; i++ ) + { + info->freq[ i ] = 0; + info->low_cnt[ i ] = 0; + } + info->total = 0; +} + +/*---------------------------------------------------------------------------* + Name: RCExportTable_ + + Description: 静的RCテーブルを出力します。 + + Arguments: dstp + info + + Returns: 出力データサイズ + *---------------------------------------------------------------------------*/ +static u32 +RCExportTable_( u8* dstp, RCCompressionInfo* info ) +{ + u32 tableSize = (1 << info->bitSize); + u32 cnt = 0; + u32 i; + + // 頻度テーブルの出力(16bitリトルエンディアン) + for ( i = 0; i < tableSize; i++ ) + { + dstp[ cnt++ ] = (u8)( info->freq[ i ] ); + dstp[ cnt++ ] = (u8)( info->freq[ i ] >> 8 ); + } + return cnt; +} + +/*---------------------------------------------------------------------------* + Name: RCImportTable_ + + Description: + + Arguments: info + srcp + srcRemainSize + + Returns: + *---------------------------------------------------------------------------*/ +static u32 +RCImportTable_( RCCompressionInfo* info, const u8* srcp, u32 srcRemainSize ) +{ + u32 tableSize = (1 << info->bitSize); + u32 cnt = 0; + u32 i; + + if ( srcRemainSize < tableSize * sizeof(u16) ) + { + return srcRemainSize; + } + + // 頻度テーブルのインポート(16bitリトルエンディアン) + for ( i = 0; i < tableSize; i++ ) + { + info->freq[ i ] = srcp[ cnt ] | ( srcp[ cnt + 1 ] << 8 ); + cnt += 2; + } + info->low_cnt[ 0 ] = 0; + info->total = info->freq[ 0 ]; + for ( i = 1; i < tableSize; i++ ) + { + info->low_cnt[ i ] = info->low_cnt[ i - 1 ] + info->freq[ i - 1 ]; + info->total += info->freq[ i ]; + } + return cnt; +} + +/*---------------------------------------------------------------------------* + Name: RCNormalizeTable_ + + Description: RCテーブルの正規化 + + Arguments: info + + Returns: None. + *---------------------------------------------------------------------------*/ +static void +RCNormalizeTable_( RCCompressionInfo* info ) +{ + u32 tableSize = (1 << info->bitSize); + u32 i; + + // トータルのカウント + info->total = 0; + for ( i = 0; i < tableSize; i++ ) + { + info->total += info->freq[ i ]; + } + + // 正規化 + // 0x10000へ正規化 + #define NORMAL_FREQ 0x10000 + { + f32 rate = (f32)NORMAL_FREQ / info->total; + u32 max_i = 0; + u32 max_freq = 0; + + info->total = 0; + for ( i = 0; i < tableSize; i++ ) + { + u32 orig = info->freq[ i ]; + info->freq[ i ] = (u32)(rate * info->freq[ i ] + 0.5f); + if ( orig != 0 && info->freq[ i ] == 0 ) + { + info->freq[ i ] = 1; + } + + info->total += info->freq[ i ]; + if ( info->freq[ i ] >= max_freq ) + { + max_i = i; + max_freq = info->freq[ i ]; + } + } + // 最も出現頻度の高い値を誤差調整に利用 + if ( info->total > NORMAL_FREQ ) + { + info->freq[ max_i ] -= (info->total - NORMAL_FREQ); + } + else + { + info->freq[ max_i ] += (NORMAL_FREQ - info->total); + } + info->total = NORMAL_FREQ; + } + #undef NORMAL_FREQ + + // low_cntの計算 + info->low_cnt[ 0 ] = 0; + for ( i = 1; i < tableSize; i++ ) + { + info->low_cnt[ i ] = info->low_cnt[ i - 1 ] + info->freq[ i - 1 ]; + } +} + + +/*---------------------------------------------------------------------------* + Name: RCCountData_ + + Description: LZ圧縮したデータから静的レンジコーダ用のテーブルを作成します。 + + Arguments: srcp + srcSize + info8 + info16 + + Returns: + *---------------------------------------------------------------------------*/ +static void +RCCountData_( const u8* srcp, u32 srcSize, RCCompressionInfo* info8, RCCompressionInfo* info16 ) +{ + u32 srcCnt = 0; + u32 i; + + while ( srcCnt < srcSize ) + { + u8 compFlags = srcp[ srcCnt++ ]; // 圧縮の有無を示すフラグ列 + for ( i = 0; i < 8; i++ ) + { + if ( compFlags & 0x80 ) // 圧縮されている、length:8, offset:16 + { + u8 length = srcp[ srcCnt++ ]; + u16 offset = srcp[ srcCnt++ ]; // リトルエンディアン + offset |= (srcp[ srcCnt++ ] << 8); + + #if BLEND_COMP_FLAG + info8->freq[ length | 0x100 ]++; + #else + info8->freq[ length ]++; + #endif + #if !defined( LRC_ENC_OFFSET_WIDTH ) + info16->freq[ offset ]++; + #else + { + u32 offset_bit = 0; + while ( offset != 0 ) + { + ++offset_bit; + offset >>= 1; + } + info16->freq[ offset_bit ]++; + } + #endif + } + else + { + u8 data = srcp[ srcCnt++ ]; + info8->freq[ data ]++; + } + compFlags <<= 1; + if ( srcCnt >= srcSize ) + { + break; + } + } + } + + RCNormalizeTable_( info8 ); + RCNormalizeTable_( info16 ); +} + + + +/*---------------------------------------------------------------------------* + Name: RCAInitInfo_ + + Description: 適応型レンジコーダのテーブル初期化 + すべての出現頻度を1で初期化します。 + + Arguments: info + + Returns: None. + *---------------------------------------------------------------------------*/ +static void +RCAInitInfo_( RCCompressionInfo* info, u8 bitSize ) +{ + u32 tableSize = (1 << bitSize); + u32 i; + + info->bitSize = bitSize; + info->freq = (u32*)malloc( sizeof(u32) * tableSize ); + info->low_cnt = (u32*)malloc( sizeof(u32) * tableSize ); + + for ( i = 0; i < tableSize; i++ ) + { + info->freq[ i ] = 1; + info->low_cnt[ i ] = i; + } + info->total = tableSize; +} + + +/*---------------------------------------------------------------------------* + Name: RCAAddCount_ + + Description: 適応型レンジコーダの頻度テーブルを更新します。 + + Arguments: info + val + + Returns: None. + *---------------------------------------------------------------------------*/ +static void +RCAAddCount_( RCCompressionInfo* info, u16 val ) +{ + u32 i; + u32 tableSize = (1 << info->bitSize); + + info->freq[ val ]++; + info->total++; + for ( i = val + 1; i < tableSize; i++ ) + { + info->low_cnt[ i ]++; + } + + // トータルが最大値を越えた場合には、再構成する。 + if ( info->total >= 0x00010000 ) + { + if ( info->freq[ 0 ] > 1 ) + { + info->freq[ 0 ] = info->freq[ 0 ] / 2; + } + info->low_cnt[ 0 ] = 0; + info->total = info->freq[ 0 ]; + + for ( i = 1; i < tableSize; i++ ) + { + if ( info->freq[ i ] > 1 ) + { + info->freq[ i ] >>= 1; + } + info->low_cnt[ i ] = info->low_cnt[ i - 1 ] + info->freq[ i - 1 ]; + info->total += info->freq[ i ]; + } + } +} + + +/*---------------------------------------------------------------------------* + Name: ConvertRC + Description: + Arguments: info + data + stream + Returns: None. + *---------------------------------------------------------------------------*/ +static void +ConvertRC( RCCompressionInfo* info, u16 data, BitStream* stream, RCState* state, BOOL adaptive ) +{ +#define MIN_RANGE 0x01000000 + u32 temp = state->range / info->total; + u32 prevLow = state->low; + + state->low += info->low_cnt[ data ] * temp; + state->range = info->freq[ data ] * temp; + + if ( adaptive ) + { + // 出現頻度テーブルを更新 + RCAAddCount_( info, data ); + } + + // 桁上がりが発生する場合の処理 + if ( prevLow > state->low ) + { + // キャリーを1くり上げ + state->carry++; + // キャリーと(キャリーカウンタ - 1)個の0x00を出力します。 + if ( state->carry_cnt > 1 ) + { + BitStream_Write( stream, state->carry, RC_UNIT_BITS ); + state->carry_cnt--; + state->carry = 0x00; + } + while ( state->carry_cnt > 1 ) + { + BitStream_Write( stream, 0x00, RC_UNIT_BITS ); + state->carry_cnt--; + } + } + + // Rangeの上位1バイトが空になったら桁上げ + while ( state->range < MIN_RANGE ) + { + u8 candidate = (u8)( state->low >> 24 ); + // 次のキャリーが0xFFの場合は更に桁上げがあり得るのでcarryを出力せずにcarry_cntだけ増やす + if ( candidate == 0xFF ) + { + state->carry_cnt++; + } + else + // 次のキャリーが0xFFではない場合はcarryを出力する + { + // carryと(carry_cnt - 1)分の0xFFを出力する + if ( state->carry_cnt > 0 ) + { + BitStream_Write( stream, state->carry, RC_UNIT_BITS ); + state->carry_cnt--; + } + while ( state->carry_cnt > 0 ) + { + BitStream_Write( stream, 0xFF, RC_UNIT_BITS ); + state->carry_cnt--; + } + // 新しいcarryに置き換え + state->carry = candidate; + state->carry_cnt = 1; + } + state->low <<= 8; + state->range <<= 8; + } +#undef MIN_RANGE +} + +/*---------------------------------------------------------------------------* + Name: FinalizeRC_ + Description: + Arguments: srcp + tmpSize + dstp + info8 + info16 + Returns: + *---------------------------------------------------------------------------*/ +static void +FinalizeRC_( BitStream* stream, RCState* state ) +{ + // carryと(carry_cnt - 1)分の0xFFを出力する + if ( state->carry_cnt > 0 ) + { + BitStream_Write( stream, state->carry, RC_UNIT_BITS ); + state->carry_cnt--; + } + while ( state->carry_cnt > 0 ) + { + BitStream_Write( stream, 0xFF, RC_UNIT_BITS ); + state->carry_cnt--; + } + // lowに残ったデータを書き出す + BitStream_Write( stream, state->low >> 24, RC_UNIT_BITS ); + BitStream_Write( stream, state->low >> 16, RC_UNIT_BITS ); + BitStream_Write( stream, state->low >> 8, RC_UNIT_BITS ); + BitStream_Write( stream, state->low , RC_UNIT_BITS ); +} + + +/*---------------------------------------------------------------------------* + Name: LZConvertDataRC + Description: + Arguments: srcp + tmpSize + dstp + info8 + info16 + Returns: + *---------------------------------------------------------------------------*/ +static u32 +LZConvertDataRC( const u8* srcp, u32 srcSize, u8* dstp, RCCompressionInfo* info8, RCCompressionInfo* info16 ) +{ + u32 srcCnt = 0; + u32 dstCnt = 0; + + BitStream stream; + RCState rcState; + + RCInitState_( &rcState ); + + BitStream_Init( &stream, dstp ); + + while ( srcCnt < srcSize ) + { + u32 i; + u8 compFlags = srcp[ srcCnt++ ]; // 圧縮の有無を示すフラグ列 + + for ( i = 0; i < 8; i++ ) + { + if ( compFlags & 0x80 ) // 圧縮されている、length:8, offset:16 + { + u8 length = srcp[ srcCnt++ ]; + u16 offset = srcp[ srcCnt++ ]; // リトルエンディアン + offset |= srcp[ srcCnt++ ] << 8; + + // length | 0x100をレンジコーダに掛ける + ConvertRC( info8, length | 0x100, &stream, &rcState, TABLE8_ADAPTIVE ); + + #if !defined( LRC_ENC_OFFSET_WIDTH ) // テーブルサイズが大きいので強制的に適応型 + ConvertRC( info16, offset, &stream, &rcState, TABLE16_ADAPTIVE ); + #else + { + u16 offset_bit = 0; + u16 offset_tmp = offset; + while ( offset_tmp > 0 ) + { + offset_tmp >>= 1; + ++offset_bit; + } + ConvertRC( info16, offset_bit, &stream, &rcState, TABLE16_ADAPTIVE ); + // offsetが0であることはないので、最上位のビットは省略する + BitStream_Write( &stream, offset & ~(1 << (offset_bit - 1)), offset_bit - 1 ); + } + #endif + } + else + { + u8 data = srcp[ srcCnt++ ]; + + ConvertRC( info8, data, &stream, &rcState, TABLE8_ADAPTIVE ); + } + compFlags <<= 1; + if ( srcCnt >= srcSize ) + { + break; + } + } + } + // carryとlowに残ったデータを吐き出す + FinalizeRC_( &stream, &rcState ); + + BitStream_Terminate( &stream, 4 ); + return stream.cnt; +} + + +/*---------------------------------------------------------------------------* + Name: LRCCompWrite + + Description: + + Arguments: srcp + size + dstp + + Returns: + *---------------------------------------------------------------------------*/ +u32 LRCCompWrite( const u8* srcp, u32 srcSize, u8* dstp ) +{ + RCCompressionInfo sRCInfo8; + RCCompressionInfo sRCInfo16; + + u32 tmpSize; + u32 dstSize; + u8* tmpBuf = (u8*)malloc( srcSize * 3 ); + // まずはsrcpを普通にLZ圧縮 + tmpSize = LZCompWrite_( srcp, srcSize, tmpBuf, 2, LRC_OFFSET_BITS ); + + // テーブル初期化 + RCInitInfo_( &sRCInfo8, LENGTH_BITS ); + RCInitInfo_( &sRCInfo16, LRC_OFFSET_TABLE_BITS ); + + dstSize = 0; + + // ヘッダの書き込み + if ( srcSize < 0x1000000 && srcSize > 0 ) + { + *(u32*)dstp = LRC_CODE_HEADER | ( srcSize << 8 ); + dstSize = 4; + } + else + { + *(u32*)dstp = LRC_CODE_HEADER; + *(u32*)&dstp[4] = srcSize; + dstSize = 8; + } + + // 静的レンジコーダの頻度表を作成だけしておく(使用するかどうかはオプション次第) + RCCountData_( tmpBuf, tmpSize, &sRCInfo8, &sRCInfo16 ); + +#if TABLE8_ADAPTIVE + RCAInitInfo_( &sRCInfo8, LENGTH_BITS ); +#else // if (! TABLE8_ADAPTIVE ) + dstSize += RCExportTable_( &dstp[ dstSize ], &sRCInfo8 ); +#endif + +#if TABLE16_ADAPTIVE + RCAInitInfo_( &sRCInfo16, LRC_OFFSET_TABLE_BITS ); +#else // if (! TABLE16_ADAPTIVE ) + dstSize += RCExportTable_( &dstp[ dstSize ], &sRCInfo16 ); +#endif + + // 圧縮結果をレンジコーダ符号化しながら出力 + dstSize += LZConvertDataRC( tmpBuf, tmpSize, &dstp[ dstSize ], &sRCInfo8, &sRCInfo16 ); + + return dstSize; +} + + + +/*---------------------------------------------------------------------------* + Name: SearchRC_ + + Description: + + Arguments: info + code + range + low + + Returns: + *---------------------------------------------------------------------------*/ +static u16 +SearchRC_( RCCompressionInfo* info, u32 code, u32 range, u32 low ) +{ + u32 tableSize = (1 << info->bitSize); + u32 codeVal = code - low; + u32 i; + u32 temp = range / info->total; + u32 tempVal = codeVal / temp; + +#if 0 + // TODO: とりあえず線形探索、二分探索にするべき + for ( i = 0; i < tableSize - 1; i++ ) + { + if ( info->low_cnt[ i + 1 ] > tempVal ) + { + while ( info->freq[ i ] == 0 ) + { + --i; + } + return (u16)i; + } + } + return (u16)( (1 << info->bitSize) - 1 ); +#else + // 二分探索 + u32 left = 0; + u32 right = tableSize - 1; + + while ( left < right ) + { + i = (left + right) / 2; + + if ( info->low_cnt[ i ] > tempVal ) + { + right = i; + } + else + { + left = i + 1; + } + } + + i = left; + while ( info->low_cnt[ i ] > tempVal ) + { + --i; + } + return (u16)i; + +#endif +} + + + +static u16 +RCGetDate_( BitReader* stream, RCCompressionInfo* info, RCState* state, BOOL adaptive ) +{ +#define MIN_RANGE 0x01000000 + u16 val = SearchRC_( info, state->code, state->range, state->low ); + + { + u32 tmp; + tmp = state->range / info->total; + state->low += info->low_cnt[ val ] * tmp; + state->range = info->freq[ val ] * tmp; + } + + // 出現頻度テーブルを更新 + if ( adaptive ) + { + RCAAddCount_( info, val ); + } + while ( state->range < MIN_RANGE ) + { + state->code <<= 8; + state->code += BitReader_ReadEx( stream, 8 ); + state->range <<= 8; + state->low <<= 8; + } + return val; +#undef MIN_RANGE +} + + +/*---------------------------------------------------------------------------* + Name: LRCCompRead + + Description: + + Arguments: srcp + size + dstp + + Returns: + *---------------------------------------------------------------------------*/ +s32 LRCCompRead( const u8* srcp, u32 srcSize, u8* dstp ) +{ + RCCompressionInfo sRCInfo8; + RCCompressionInfo sRCInfo16; + RCState rcState; + + u32 dstSize; + u32 srcCnt = 0; + u32 dstCnt = 0; + BitReader stream; + + if ( srcSize < 4 ) + { + return -1; + } + + // ヘッダの読み込み + dstSize = *(u32*)srcp >> 8; + srcCnt = 4; + if ( dstSize == 0 ) + { + if ( srcSize < 8 ) + { + return -1; + } + dstSize = *(u32*)(srcp + 4); + srcCnt += 4; + } + + // RC頻度テーブルを読む +#if TABLE8_ADAPTIVE + RCAInitInfo_( &sRCInfo8, LENGTH_BITS ); +#else + RCInitInfo_( &sRCInfo8, LENGTH_BITS ); + srcCnt += RCImportTable_( &sRCInfo8, &srcp[ srcCnt ], srcSize - srcCnt ); +#endif +#if TABLE16_ADAPTIVE + RCAInitInfo_( &sRCInfo16, LRC_OFFSET_TABLE_BITS ); +#else + RCInitInfo_( &sRCInfo16, LRC_OFFSET_TABLE_BITS ); + srcCnt += RCImportTable_( &sRCInfo16, &srcp[ srcCnt ], srcSize - srcCnt ); +#endif + + BitReader_Init( &stream, &srcp[ srcCnt ], srcSize - srcCnt ); + + if ( srcSize - srcCnt <= 4 ) + { + // 最低でも初回のコード分の4Byteは必要 + return -1; + } + + RCInitState_( &rcState ); + + rcState.code = (u32)( (BitReader_ReadEx( &stream, 8 ) << 24) | + (BitReader_ReadEx( &stream, 8 ) << 16) | + (BitReader_ReadEx( &stream, 8 ) << 8) | + (BitReader_ReadEx( &stream, 8 ) ) ); + + while ( dstCnt < dstSize ) + { + u16 val = (u16)( RCGetDate_( &stream, &sRCInfo8, &rcState, TABLE8_ADAPTIVE ) ); + + if ( val < 0x100 ) + // 非圧縮データ + { + dstp[ dstCnt++ ] = (u8)val; + } + else + // 圧縮データ + { + u16 length = (val & 0xFF) + 3; + val = (u16)( RCGetDate_( &stream, &sRCInfo16, &rcState, TABLE16_ADAPTIVE ) ); + + #if defined( LRC_ENC_OFFSET_WIDTH ) + { + u16 offset_bit = val; + val = 0; + if ( offset_bit > 0 ) + { + val = 1; + while ( --offset_bit > 0 ) + { + val <<= 1; + val |= BitReader_Read( &stream ); + } + } + } + #endif + val += 1; + + // バッファオーバーランをチェック + if ( dstCnt + length > dstSize ) + { + return -1; + } + if ( dstCnt < val ) + { + return -1; + } + if ( srcCnt + stream.cnt > srcSize ) + { + return -1; + } + + while ( length-- > 0 ) + { + dstp[ dstCnt ] = dstp[ dstCnt - val ]; + ++dstCnt; + } + } + } + + return dstCnt; +} + + diff --git a/build/systemMenu_RED/sharedFont/ntrcomp/src/multipleCompLib.h b/build/systemMenu_RED/sharedFont/ntrcomp/src/multipleCompLib.h new file mode 100644 index 00000000..72dc2e5b --- /dev/null +++ b/build/systemMenu_RED/sharedFont/ntrcomp/src/multipleCompLib.h @@ -0,0 +1,55 @@ +/*---------------------------------------------------------------------------* + Project: NinTendo Compress tool + File: multipleCompLib.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: $ + $Rev$ + $Author$ + *---------------------------------------------------------------------------*/ + +// nitroCompLib.h : nitroCompLib.DLL のメイン ヘッダー ファイル +// + +#ifndef __MULTIPLE_COMPLIB_H__ +#define __MULTIPLE_COMPLIB_H__ + +//=========================================================================================== +// インクルード +//=========================================================================================== +#include +#include +#include +#include +#include "types.h" + +//=========================================================================================== +// プロトタイプ宣言 +//=========================================================================================== +// C++用 +#ifdef __cplusplus +extern "C" +{ +#endif + +s32 LHCompRead( const u8* srcp, u32 srcSize, u8* dstp ); + +u32 LHCompWrite( const u8* srcp, s32 size, u8* dstp ); + +s32 LRCCompRead( const u8* srcp, u32 srcSize, u8* dstp ); + +u32 LRCCompWrite( const u8* srcp, u32 size, u8* dstp ); + + +#ifdef __cplusplus +} +#endif + +#endif // __MULTIPLEX_COMPLIB_H__ diff --git a/build/systemMenu_RED/sharedFont/ntrcomp/src/nitroCompLib.c b/build/systemMenu_RED/sharedFont/ntrcomp/src/nitroCompLib.c new file mode 100644 index 00000000..135f7a45 --- /dev/null +++ b/build/systemMenu_RED/sharedFont/ntrcomp/src/nitroCompLib.c @@ -0,0 +1,2282 @@ +/*---------------------------------------------------------------------------* + Project: NinTendo Compress tool + File: nitroCompLib.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: $ + $Rev$ + $Author$ + *---------------------------------------------------------------------------*/ + +#include "nitroCompLib.h" + +#undef _DEBUG +#ifdef _DEBUG +#define new DEBUG_NEW +#define DEBUG_PRINT +// #define DEBUG_PRINT_DIFFFILT +#define DEBUG_PRINT_RL +// #define DEBUG_PRINT_HUFF +// #define DEBUG_PRINT_LZ +// #define DEBUG_PRINT_DATAMATCH +#endif + + +#ifdef __cplusplus +#define EXTERN extern "C" +#define STATIC +#else +#define EXTERN +#define STATIC static +#endif + + +#ifdef DEBUG_PRINT +#define dbg_printf fprintf +#else +#define dbg_printf dummy +#endif + +#ifdef DEBUG_PRINT_DIFFFILT +#define dbg_printf_dif fprintf +#else +#define dbg_printf_dif dummy +#endif + +#ifdef DEBUG_PRINT_RL +#define dbg_printf_rl fprintf +#else +#define dbg_printf_rl dummy +#endif + +#ifdef DEBGU_PRINT_HUFF +#define dbg_printf_huff fprintf +#else +#define dbg_printf_huff dummy +#endif + +#ifdef DEBGU_PRINT_LZ +#define dbg_printf_lz fprintf +#else +#define dbg_printf_lz dummy +#endif + +#ifdef DEBUG_PRINT_DATAMATCH +#define dbg_printf_match fprintf +#else +#define dbg_printf_match dummy +#endif + +void dummy(void *fp, ...) +{ +} + + +//================================================================================== +// グローバル変数宣言 +//================================================================================== +static u8 *pCompBuf[2]; // 圧縮処理中に用いるダブルバッファ +static u8 compBufNo = 1; // 有効なダブルバッファを示す + +//================================================================================== +// プロトタイプ宣言 +//================================================================================== +static u32 RawWrite(u8 *srcp, u32 size, u8 *dstp); +static u32 DiffFiltWrite(u8 *srcp, u32 size, u8 *dstp, u8 diffBitSize); +static u32 RLCompWrite(u8 *srcp, u32 size, u8 *dstp); +static u32 LZCompWriteEx(u8 *srcp, u32 size, u8 *dstp, u8 lzSearchOffset, BOOL ex_available); +static u32 HuffCompWrite(u8 *srcp, u32 size, u8 *dstp, u8 huffBitSize); + +static s32 RawRead ( const u8 *srcp, u32 srcSize, u8 *dstp, u32 dstSize ); +static s32 DiffFiltRead( const u8 *srcp, u32 srcSize, u8 *dstp, u32 dstSize, u8 diffBitSize ); +static s32 RLCompRead ( const u8 *srcp, u32 srcSize, u8 *dstp, u32 dstSize ); +static s32 LZCompReadEx( const u8 *srcp, u32 srcSize, u8 *dstp, u32 dstSize, BOOL ex_available ); +static s32 HuffCompRead( const u8 *srcp, u32 srcSize, u8 *dstp, u32 dstSize, u8 huffBitSize ); + +/* +//================================================================================== +// DLL用関数 +//================================================================================== +EXTERN BOOL WINAPI DllMain( HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved) +{ + return TRUE; +} +*/ + +//---------------------------------------------------------------------------------- +// 圧縮後のデータを置くためのメモリ領域を確保 +// 圧縮前のデータの2倍の領域を確保する +//---------------------------------------------------------------------------------- +EXTERN u8 * STDCALL nitroCompMalloc(u32 size) +{ + return (u8 *)malloc(size * 3 + 512); +} + +//---------------------------------------------------------------------------------- +// 圧縮後のデータを置いていたメモリ領域を解放 +//---------------------------------------------------------------------------------- +EXTERN void STDCALL nitroCompFree(u8 *p) +{ + if (p != NULL) + { + free((void *)p); + p = NULL; + } +} + +//------------------------------------------------------------ +// データの圧縮 +//------------------------------------------------------------ +EXTERN u32 STDCALL nitroCompress( const u8 *srcp, u32 srcSize, u8 *dstp, char *compList, u8 rawHeaderFlag ) +{ + char *pCompList; // compListの現在の参照ポイント + u32 dataSize, nextDataSize; // 圧縮データのサイズ(バイト単位) + u8 *pReadBuf; // 圧縮データの先頭番地を指すポインタ + u8 bitSize; // 差分フィルタ,ハフマン符号化の適用単位 + char str[16]; + u16 i, j; + u8 lzSearchOffset; + +// pCompBuf[0] = (u8 *)malloc(srcSize*2 + 4 + 256*2); // 最悪のハフマンが size*2 + 4 + 256*2 なので、 +// pCompBuf[1] = (u8 *)malloc(srcSize*2 + 4 + 256*2); // その他の圧縮やデータヘッダ追加で、不足する可能性あり + pCompBuf[0] = (u8 *)malloc(srcSize * 3 + 256 * 2); + pCompBuf[1] = (u8 *)malloc(srcSize * 3 + 256 * 2); + pReadBuf = pCompBuf[0]; + compBufNo = 1; // 重要!! これをしないと2回目にnitroCompressを呼び出したときにおかしくなる + + // malloc チェック + if (pCompBuf[0] == NULL || pCompBuf[1] == NULL) + { + fprintf(stderr, "Error: Memory is not enough.\n"); + exit(1); + } + + dataSize = srcSize; + + // NULLヘッダ(圧縮前のデータ用の、擬似ヘッダ) の追加処理 + if (rawHeaderFlag) + { + dataSize += 4; + *(u32 *)pReadBuf = srcSize << 8 | 0; // データ・ヘッダ + memcpy(&pReadBuf[4], srcp, srcSize); + } + else + { + memcpy(pReadBuf, srcp, srcSize); + } + + pCompList = compList; // 圧縮順序を格納した配列をポイント + + // 圧縮順序を格納した配列に要素がある限り、ループ + while (1) + { + switch (*pCompList) + { + case 'd': + { + pCompList++; // 'd' の次には、8 か 16 + str[0] = *pCompList; + if (*pCompList == '1') + { + pCompList++; + str[1] = *pCompList; + str[2] = '\n'; + } + bitSize = atoi(str); // 差分フィルタの適用単位を格納 + str[0] = str[1] = '\n'; + + dbg_printf(stderr, "nitroCompress Diff %d\n", bitSize); + + if ((bitSize == 16) && (dataSize & 0x01)) + { + fprintf(stderr, "16-bit differencial filter must be 2-byte allignment.\n"); + exit(1); + } + nextDataSize = DiffFiltWrite(pReadBuf, dataSize, pCompBuf[compBufNo], bitSize); + } + break; + + case 'r': + { + dbg_printf(stderr, "nitroCompress RL\n"); + + nextDataSize = RLCompWrite(pReadBuf, dataSize, pCompBuf[compBufNo]); + } + break; + + case 'l': + case 'L': + { + BOOL ex_format = (*pCompList == 'L')? TRUE : FALSE; + + pCompList++; + i = 0; + while (isdigit(*pCompList)) + { + str[i] = *pCompList; + pCompList++; + i++; + if (i == 15) + { + break; + } + } + str[i] = '\n'; + pCompList--; + lzSearchOffset = (u8)atoi(str); // 大きな値は切り捨てて丸め + for (j = 0; j < i; j++) + { + str[j] = '\n'; + } + dbg_printf(stderr, "nitroCompress L %d\n", lzSearchOffset); + + nextDataSize = LZCompWriteEx(pReadBuf, dataSize, pCompBuf[compBufNo], lzSearchOffset, ex_format); + } + break; + + case 'h': + { + pCompList++; // 'h' の次には、4 か 8 + str[0] = *pCompList; + str[1] = '\n'; + bitSize = atoi(str); // 4 or 8 + str[0] = '\n'; + + dbg_printf(stderr, "nitroCompress Huff %d\n", bitSize); + + nextDataSize = HuffCompWrite(pReadBuf, dataSize, pCompBuf[compBufNo], bitSize); + } + break; + //----------------------------------------- + // 圧縮終了 (*CompTypeBufp が NULL) + default: + { + dbg_printf(stderr, "nitroCompress raw\n"); + + RawWrite(pReadBuf, dataSize, dstp); + if (pCompBuf[0] != NULL) + { + free((void *)pCompBuf[0]); + pCompBuf[0] = NULL; + } + if (pCompBuf[1] != NULL) + { + free(pCompBuf[1]); + pCompBuf[1] = NULL; + } + return dataSize; + } + } + // もう一周 + pReadBuf = pCompBuf[compBufNo]; + compBufNo ^= 0x01; + dataSize = nextDataSize; + pCompList++; + } +} + + +//=========================================================================== +// 圧縮データのコピー +//=========================================================================== +static u32 RawWrite(u8 *srcp, u32 size, u8 *dstp) +{ + u32 i; + + dbg_printf(stderr, "RawWrite\tsize=%d\n\n", size); + + size = (size + 0x3) & ~0x3; + for (i = 0; i < size - 1; i++) + { + *dstp = *srcp; + dstp++; + srcp++; + } + *dstp = *srcp; + + return size; +} + + +//=========================================================================== +// 差分フィルタ +//=========================================================================== +static u32 DiffFiltWrite(u8 *srcp, u32 size, u8 *dstp, u8 diffBitSize) +{ + u32 DiffCount; // 圧縮データのバイト数 + u32 i; + + u16 *src16p = (u16 *)srcp; + u16 *dst16p = (u16 *)dstp; + + dbg_printf_dif(stderr, "DiffFiltWrite\tsize=%d\tdiffBitSize=%d\n", size, diffBitSize); + + if ( size < 0x1000000 && size > 0 ) + { + *(u32 *)dstp = size << 8 | (DIFF_CODE_HEADER | diffBitSize / 8); // データ・ヘッダ + DiffCount = 4; + } + else + { + *(u32 *)dstp = (DIFF_CODE_HEADER | diffBitSize / 8); // データ・ヘッダ + *(u32 *)(dstp + 4) = size; + DiffCount = 8; + } + + if (diffBitSize == 8) + { +#ifdef DEBUG_PRINT_DIFFFILT + for (i = 0; i < 16; i++) + { + dbg_printf_dif(stderr, "srcp[%d] = %x\n", i, srcp[i]); + } +#endif + dstp[DiffCount] = srcp[0]; // 先頭データのみ差分無し + DiffCount++; + for (i = 1; i < size; i++, DiffCount++) + { + dbg_printf_dif(stderr, "dstp[%x] = srcp[%d]-srcp[%d] = %x - %x = %x\n", + DiffCount, i, i - 1, srcp[i], srcp[i - 1], srcp[i] - srcp[i - 1]); + + dstp[DiffCount] = srcp[i] - srcp[i - 1]; // 差分データ格納 + } + } + else // 16ビットサイズ + { + dst16p[DiffCount / 2] = src16p[0]; + DiffCount += 2; + for (i = 1; i < size / 2; i++, DiffCount += 2) + { + dst16p[DiffCount / 2] = src16p[i] - src16p[i - 1]; + } + } + + // 4バイト境界アラインメント + // アラインメント用データ0 はデータサイズに含めない + i = 0; + while ((DiffCount + i) & 0x3) + { + dstp[DiffCount + i] = 0; + i++; + } + + return DiffCount; +} + + +//=========================================================================== +// ランレングス符号化 (バイト単位) +//=========================================================================== +static u32 RLCompWrite(u8 *srcp, u32 size, u8 *dstp) +{ + u32 RLDstCount; // 圧縮データのバイト数 + u32 RLSrcCount; // 圧縮対象データの処理済データ量(バイト単位) + u8 RLCompFlag; // ランレングス符号化を行う場合1 + u8 runLength; // ランレングス + u8 rawDataLength; // ランになっていないデータのレングス + u32 i; + + u8 *startp; // 一回の処理ループにおける、圧縮対象データの先頭をポイント + + dbg_printf_rl(stderr, "RLCompWrite\tsize=%d\n", size); + + // データヘッダ (サイズは展開後のもの) + if ( size < 0x1000000 && size > 0 ) + { + *(u32 *)dstp = size << 8 | RL_CODE_HEADER; // データ・ヘッダ + RLDstCount = 4; + } + else + // サイズが24bitに収まらない場合には拡張形式のヘッダとなる + { + *(u32 *)dstp = RL_CODE_HEADER; + *(u32 *)(dstp + 4) = size; + RLDstCount = 8; + } + + RLSrcCount = 0; + rawDataLength = 0; + RLCompFlag = 0; + + while (RLSrcCount < size) + { + startp = &srcp[RLSrcCount]; // 圧縮対象データの設定 + + for (i = 0; i < 128; i++) // 7ビットで表現できるデータ量が 0~127 + { + // 圧縮対象データの末尾に到達 + if (RLSrcCount + rawDataLength >= size) + { + rawDataLength = (u8)(size - RLSrcCount); + break; + } + + if (RLSrcCount + rawDataLength + 2 < size) + { + if (startp[i] == startp[i + 1] && startp[i] == startp[i + 2]) + { + RLCompFlag = 1; + break; + } + } + rawDataLength++; + } + + // 符号化しないデータを格納 + // データ長格納バイトの8ビット目が0なら、符号化しないデータ系列 + // データ長は -1 した数になるので、0-127 が 1-128 となる + if (rawDataLength) + { + dstp[RLDstCount++] = rawDataLength - 1; // "データ長-1" 格納(7ビット) + for (i = 0; i < rawDataLength; i++) + { + dstp[RLDstCount++] = srcp[RLSrcCount++]; + } + rawDataLength = 0; + } + + // ランレングス符号化 + if (RLCompFlag) + { + runLength = 3; + for (i = 3; i < 128 + 2; i++) + { + // 圧縮用データの末尾に到達 + if (RLSrcCount + runLength >= size) + { + runLength = (u8)(size - RLSrcCount); + break; + } + + // ランが途切れた場合 + if (srcp[RLSrcCount] != srcp[RLSrcCount + runLength]) + { + break; + } + // ラン継続中 + runLength++; + } + + // データ長格納バイトの8ビット目が1なら、符号化したデータ系列 + dstp[RLDstCount++] = 0x80 | (runLength - 3); // 3の下駄をはかせて、3~130を格納 + dstp[RLDstCount++] = srcp[RLSrcCount]; + RLSrcCount += runLength; + RLCompFlag = 0; + } + } + + // 4バイト境界アラインメント + // アラインメント用データ0 はデータサイズに含めない + i = 0; + while ((RLDstCount + i) & 0x3) + { + dstp[RLDstCount + i] = 0; + i++; + } + + return RLDstCount; +} + + +//=========================================================================== +// LZ77圧縮 +//=========================================================================== + +#define LZ_OFFSET_BITS 12 +#define LZ_OFFSET_SIZE (1<LZByteTable[i] = -1; + info->LZEndTable[i] = -1; + } + info->WindowPos = 0; + info->WindowLen = 0; +} + +/*---------------------------------------------------------------------------* + Name: SlideByte + Description: 辞書を1バイトスライド + Arguments: *srcp + work + Returns: None. + *---------------------------------------------------------------------------*/ +static void SlideByte(LZCompressInfo * info, const u8 *srcp) +{ + s16 offset; + u8 in_data = *srcp; + u16 insert_offset; + + s16 *const LZByteTable = info->LZByteTable; + s16 *const LZEndTable = info->LZEndTable; + s16 *const LZOffsetTable = info->LZOffsetTable; + const u16 windowPos = info->WindowPos; + const u16 windowLen = info->WindowLen; + + if (windowLen == LZ_OFFSET_SIZE) + { + u8 out_data = *(srcp - LZ_OFFSET_SIZE); + if ((LZByteTable[out_data] = LZOffsetTable[LZByteTable[out_data]]) == -1) + { + LZEndTable[out_data] = -1; + } + insert_offset = windowPos; + } + else + { + insert_offset = windowLen; + } + + offset = LZEndTable[in_data]; + if (offset == -1) + { + LZByteTable[in_data] = insert_offset; + } + else + { + LZOffsetTable[offset] = insert_offset; + } + LZEndTable[in_data] = insert_offset; + LZOffsetTable[insert_offset] = -1; + + if (windowLen == LZ_OFFSET_SIZE) + { + info->WindowPos = (u16)((windowPos + 1) % LZ_OFFSET_SIZE); + } + else + { + info->WindowLen++; + } +} + +static void LZSlide(LZCompressInfo * info, const u8 *srcp, u32 n) +{ + u32 i; + + for (i = 0; i < n; i++) + { + SlideByte(info, srcp++); + } +} + + +/*---------------------------------------------------------------------------* + Name: LZCompWriteEx + + Description: LZ77圧縮を行なう関数(最大lengthの拡張) + + Arguments: srcp 圧縮元データへのポインタ + size 圧縮元データサイズ + dstp 圧縮先データへのポインタ + 圧縮元データよりも大きいサイズのバッファが必要です。 + + Returns: 圧縮後のデータサイズ。 + 圧縮後のデータが圧縮前よりも大きくなる場合には圧縮を中断し0を返します。 + *---------------------------------------------------------------------------*/ +static u32 LZCompWriteEx(u8 *srcp, u32 size, u8 *dstp, u8 lzSearchOffset, BOOL ex_available ) +{ + static LZCompressInfo sLZInfo; + + u32 LZDstCount; // 圧縮データのバイト数 + u8 LZCompFlags; // 圧縮の有無を示すフラグ系列 + u8 *LZCompFlagsp; // LZCompFlags を格納するメモリ領域をポイント + u16 lastOffset; // 一致データまでのオフセット (その時点での最長一致データ) + u32 lastLength; // 一致データ長 (その時点での最長一致データ) + u8 i; + const u32 MAX_LENGTH = (ex_available)? (0xFFFF + 0xFF + 0xF + 3) : (0xF + 3); + + if ( size < 0x1000000 && size > 0 ) + { + *(u32 *)dstp = size << 8 | LZ_CODE_HEADER | (ex_available? 1 : 0 ); // データ・ヘッダ + dstp += 4; + LZDstCount = 4; + } + else + { + *(u32 *)dstp = LZ_CODE_HEADER | (ex_available? 1 : 0); + *(u32 *)(dstp + 4) = size; + dstp += 8; + LZDstCount = 8; + } + LZInitTable( &sLZInfo ); + + while (size > 0) + { + LZCompFlags = 0; + LZCompFlagsp = dstp++; // フラグ系列の格納先 + LZDstCount++; + + // フラグ系列が8ビットデータとして格納されるため、8回ループ + for (i = 0; i < 8; i++) + { + LZCompFlags <<= 1; // 初回 (i=0) は特に意味はない + if (size <= 0) + { + // 終端に来た場合はフラグを最後までシフトさせてから終了 + continue; + } + + if ( (lastLength = SearchLZ(&sLZInfo, srcp, size, &lastOffset, lzSearchOffset, MAX_LENGTH)) != 0) + { + u32 length; + // 圧縮可能な場合はフラグを立てる + LZCompFlags |= 0x1; + + if ( ex_available ) + { + if ( lastLength >= 0xFF + 0xF + 3 ) + { + length = lastLength - 0xFF - 0xF - 3; + *dstp++ = (u8)( 0x10 | (length >> 12) ); + *dstp++ = (u8)( length >> 4 ); + LZDstCount += 2; + } + else if ( lastLength >= 0xF + 2 ) + { + length = lastLength - 0xF - 2; + *dstp++ = (u8)( length >> 4 ); + LZDstCount += 1; + } + else + { + length = lastLength - 1; + } + } + else + { + length = lastLength - 3; + } + // オフセットは上位4ビットと下位8ビットに分けて格納 + *dstp++ = (u8)( length << 4 | (lastOffset - 1) >> 8 ); + *dstp++ = (u8)( (lastOffset - 1) & 0xFF ); + LZDstCount += 2; + LZSlide( &sLZInfo, srcp, lastLength); + srcp += lastLength; + size -= lastLength; + } + else + { + // 圧縮なし + LZSlide(&sLZInfo, srcp, 1); + *dstp++ = *srcp++; + size--; + LZDstCount++; + } + } // 8回ループ終了 + *LZCompFlagsp = LZCompFlags; // フラグ系列を格納 + } + + // 4バイト境界アラインメント + // アラインメント用データ0 はデータサイズに含めない + i = 0; + while ((LZDstCount + i) & 0x3) + { + *dstp++ = 0; + i++; + } + + return LZDstCount; +} + + +//-------------------------------------------------------- +// LZ77圧縮でスライド窓の中から最長一致列を検索します。 +// Arguments: startp データの開始位置を示すポインタ +// nextp 検索を開始するデータのポインタ +// remainSize 残りデータサイズ +// offset 一致したオフセットを格納する領域へのポインタ +// Return : 一致列が見つかった場合は TRUE +// 見つからなかった場合は FALSE +//-------------------------------------------------------- +static u32 SearchLZ( const LZCompressInfo * info, const u8 *nextp, u32 remainSize, u16 *offset, u16 minOffset, u32 maxLength ) +{ + const u8 *searchp; + const u8 *headp, *searchHeadp; + u16 currOffset; + u32 currLength = 2; + u32 tmpLength; + s32 w_offset; + const s16 * const LZOffsetTable = info->LZOffsetTable; + const u16 windowPos = info->WindowPos; + const u16 windowLen = info->WindowLen; + + if (remainSize < 3) + { + return 0; + } + + w_offset = info->LZByteTable[*nextp]; + + while (w_offset != -1) + { + if (w_offset < windowPos) + { + searchp = nextp - windowPos + w_offset; + } + else + { + searchp = nextp - windowLen - windowPos + w_offset; + } + + /* 無くても良いが、僅かに高速化する */ + if (*(searchp + 1) != *(nextp + 1) || *(searchp + 2) != *(nextp + 2)) + { + w_offset = LZOffsetTable[w_offset]; + continue; + } + + if (nextp - searchp < minOffset) + { + // VRAMは2バイトアクセスなので (VRAMからデータを読み出す場合があるため)、 + // 検索対象データは2バイト前からのデータにしなければならない。 + // + // オフセットは12ビットで格納されるため、4096以下 + break; + } + tmpLength = 3; + searchHeadp = searchp + 3; + headp = nextp + 3; + + while (((u32)(headp - nextp) < remainSize) && (*headp == *searchHeadp)) + { + headp++; + searchHeadp++; + tmpLength++; + + // 一致長が最大なので、検索を終了する + if (tmpLength == maxLength) + { + break; + } + } + if (tmpLength > currLength) + { + // 最大長オフセットを更新 + currLength = tmpLength; + currOffset = (u16)(nextp - searchp); + if (currLength == maxLength) + { + // 一致長が最大なので、検索を終了する。 + break; + } + } + w_offset = LZOffsetTable[w_offset]; + } + + if (currLength < 3) + { + return 0; + } + *offset = currOffset; + return currLength; +} + + +//=========================================================================== +// ハフマン符号化 +//=========================================================================== +#define HUFF_END_L 0x80 +#define HUFF_END_R 0x40 + +typedef struct +{ + u16 No; // データNo + s16 PaNo; // 親No + u32 Freq; // 出現頻度 + s16 ChNo[2]; // 子No (0: 左側, 1: 右側) + u16 PaDepth; // 親ノードの深さ + u16 LeafDepth; // 葉までの深さ + u32 HuffCode; // ハフマン符号 + u8 Bit; // ノードのビットデータ + u16 HWord; // 各中間節点において、その節点をルートとする部分木を HuffTree 格納に必要なメモリ量 +} +HuffData; + +typedef struct +{ + u8 leftOffsetNeed; // 左の子節点へのオフセットが必要なら1 + u8 rightOffsetNeed; // 右の子節点へのオフセットが必要なら1 + u16 leftNodeNo; // 左の子節点No + u16 rightNodeNo; // 右の子節点No +} +HuffTreeCtrlData; + +// ハフマンワークバッファ構成 +typedef struct +{ + HuffData huffTable[ 512 ]; // huffTable[ 512 ]; 12288B + u8 huffTree [ 256 * 2 ]; // huffTree[ 256 * 2 ]; 512B + HuffTreeCtrlData huffTreeCtrl[ 256 ]; // huffTreeCtrl[ 256 ]; 1536B + u16 huffTreeTop; // + u8 bitSize; // + u8 padding_[1]; // +} +HuffCompressionInfo; // 計 14340B + +static void HuffInitTable( HuffCompressionInfo* info, u8 bitSize ); +static void HuffCountData( HuffData* table, const u8 *srcp, u32 size, u8 bitSize ); +static void HuffConstructTree( HuffCompressionInfo *info, u8 bitSize ); +static u32 HuffExportTree( u8* dstp, HuffCompressionInfo* info ); +static u32 HuffConvertData( const HuffData *table, const u8* srcp, u8* dstp, u32 srcSize, u8 bitSize ); + +static void HuffAddParentDepthToTable( HuffData *table, u16 leftNo, u16 rightNo ); +static void HuffAddCodeToTable ( HuffData *table, u16 nodeNo, u32 paHuffCode ); +static u16 HuffAddCountHWordToTable ( HuffData *table, u16 nodeNo ); + +static u16 HuffMakeNode ( HuffData* table, u8 bitSize ); +static void HuffMakeHuffTree ( HuffCompressionInfo* info, u16 rootNo ); +static void HuffMakeSubsetHuffTree ( HuffCompressionInfo* info, u16 huffTreeNo, BOOL rightNodeFlag ); +static BOOL HuffRemainingNodeCanSetOffset( HuffCompressionInfo* info, u16 costHWord ); +static void HuffSetOneNodeOffset ( HuffCompressionInfo* info, u16 huffTreeNo, BOOL rightNodeFlag ); + +HuffCompressionInfo sHuffCompressionInfo; + +/*---------------------------------------------------------------------------* + Name: HuffCompWrite + Description: ハフマン圧縮 + Arguments: *srcp + size + *dstp + huffBitSize + Returns: None. + *---------------------------------------------------------------------------*/ +static u32 HuffCompWrite(u8 *srcp, u32 size, u8 *dstp, u8 huffBitSize) +{ + u32 huffDstCount; // 圧縮データのバイト数 + u32 offset; + HuffCompressionInfo* info = &sHuffCompressionInfo; + + u16 huffDataNum = 1 << huffBitSize; // 8->256, 4->16 + + // テーブル初期化 + HuffInitTable( info, huffBitSize ); + + // 出現頻度チェック + HuffCountData( info->huffTable, srcp, size, huffBitSize ); + + // ハフマン符号テーブル作成 + HuffConstructTree( info, huffBitSize ); + + // データ・ヘッダ + if ( size < 0x1000000 && size > 0 ) + { + *(u32 *)dstp = size << 8 | HUFF_CODE_HEADER | huffBitSize; + offset = 4; + } + else + { + *(u32 *)dstp = HUFF_CODE_HEADER | huffBitSize; + *(u32 *)(dstp + 4) = size; + offset = 8; + } + huffDstCount = offset; + + // ハフマンテーブルをバイナリ出力 + huffDstCount += HuffExportTree( &dstp[ huffDstCount ], info ); + + // ハフマンテーブルによるデータ変換 + huffDstCount += HuffConvertData( info->huffTable, srcp, &dstp[ huffDstCount ], size, huffBitSize ); + + return huffDstCount; +} + + + + +/*---------------------------------------------------------------------------* + Name: HuffInitTable + Description: ハフマンテーブルの初期化 + Arguments: table + size + Returns: None. + *---------------------------------------------------------------------------*/ +static void HuffInitTable( HuffCompressionInfo* info, u8 bitSize ) +{ + u32 tableSize = (1 << bitSize); + u32 i; + + info->huffTreeTop = 1; + info->bitSize = bitSize; + + // huffTableを初期化 + { + HuffData* table = info->huffTable; + + const HuffData HUFF_TABLE_INIT_DATA = { 0, 0, 0, {-1, -1}, 0, 0, 0, 0, 0 }; + for ( i = 0; i < tableSize * 2U; i++ ) + { + table[ i ] = HUFF_TABLE_INIT_DATA; + table[ i ].No = (u16)i; + } + } + + // huffTree, huffTreeCtrlを初期化 + { + const HuffTreeCtrlData HUFF_TREE_CTRL_INIT_DATA = { 1, 1, 0, 0 }; + u8* huffTree = info->huffTree; + HuffTreeCtrlData* huffTreeCtrl = info->huffTreeCtrl; + + for ( i = 0; i < 256; i++ ) + { + huffTree[ i * 2 ] = 0; + huffTree[ i * 2 + 1 ] = 0; + huffTreeCtrl[ i ] = HUFF_TREE_CTRL_INIT_DATA; + } + } +} + + +/*---------------------------------------------------------------------------* + Name: HuffCountData + Description: 出現頻度のカウント + Arguments: table + *srcp + size + bitSize + Returns: None. + *---------------------------------------------------------------------------*/ +static void HuffCountData( HuffData* table, const u8 *srcp, u32 size, u8 bitSize ) +{ + u32 i; + u8 tmp; + + if ( bitSize == 8 ) + { + for ( i = 0; i < size; i++ ) + { + table[ srcp[ i ] ].Freq++; // 8ビット符号化 + } + } + else + { + for ( i = 0; i < size; i++ ) // 4ビット符号化 + { + tmp = (srcp[ i ] & 0xf0) >> 4; + table[ tmp ].Freq++; // 上位4ビットから先に格納// どっちでもいい + tmp = srcp[ i ] & 0x0f; + table[ tmp ].Freq++; // 問題は符号化のとこ + } + } +} + + +/*---------------------------------------------------------------------------* + Name: HuffConstructTree + Description: ハフマンツリーを構築 + Arguments: *table + dataNum + Returns: None. + *---------------------------------------------------------------------------*/ +static void HuffConstructTree( HuffCompressionInfo *info, u8 bitSize ) +{ + HuffData* table = info->huffTable; + u16 rootNo; // 二分木のルートNo + + // 出現頻度からノードを構築 + rootNo = HuffMakeNode( table, bitSize ); + + // ハフマンコード生成 (table[i].HuffCode に) + HuffAddCodeToTable( table, rootNo, 0x00 ); // PaDepthのビット数だけ、HuffCode の下位ビットをマスクしたものがハフマンコード + + // 各中間節点において、その節点をルートとする部分木を huffTree 格納に必要なメモリ量の計算 + HuffAddCountHWordToTable( table, rootNo ); + + // sHuffTreeBuf.huffTree 作成 + HuffMakeHuffTree( info, rootNo ); + + info->huffTree[0] = (u8)( --info->huffTreeTop ); +} + + +/*---------------------------------------------------------------------------* + Name: HuffMakeNode + Description: 出現頻度からノードデータを構築 + Arguments: table + Returns: None. + *---------------------------------------------------------------------------*/ +static u16 +HuffMakeNode( HuffData* table, u8 bitSize ) +{ + u16 dataNum = ( 1 << bitSize ); + u16 tableTop = (u16)dataNum; // テーブル作成時の、テーブルトップNo + + u32 i; + s32 leftNo, rightNo; // 2分木作成時のノードNo + u16 rootNo; // 二分木のルートNo + + leftNo = -1; + rightNo = -1; + while ( 1 ) + { + // Freqの小さい部分木頂点を2つ探す 1つは必ず見つかるはず + // 子頂点(左)の探索 + for ( i = 0; i < tableTop; i++ ) + { + if ( ( table[i].Freq == 0 ) || + ( table[i].PaNo != 0 ) ) + { + continue; + } + + if ( leftNo < 0 ) + { + leftNo = i; + } + else if ( table[i].Freq < table[ leftNo ].Freq ) + { + leftNo = i; + } + } + + // 子頂点(右)の探索 + for ( i = 0; i < tableTop; i++ ) + { + if ( ( table[i].Freq == 0 ) || + ( table[i].PaNo != 0 ) || + ( i == leftNo ) ) + { + continue; + } + + if ( rightNo < 0 ) + { + rightNo = i; + } + else if ( table[i].Freq < table[ rightNo ].Freq ) + { + rightNo = i; + } + } + + // 1つしかなかったら、テーブル作成終了 + if ( rightNo < 0 ) + { + // 値が一種類しかない存在しない場合には01どちらも同じ値となるノードを1つ作成する + if ( tableTop == dataNum ) + { + if ( leftNo < 0 ) + { + leftNo = 0; + } + table[ tableTop ].Freq = table[ leftNo ].Freq; + table[ tableTop ].ChNo[0] = (s16)leftNo; + table[ tableTop ].ChNo[1] = (s16)leftNo; + table[ tableTop ].LeafDepth = 1; + table[ leftNo ].PaNo = (s16)tableTop; + table[ leftNo ].Bit = 0; + table[ leftNo ].PaDepth = 1; + } + else + { + tableTop--; + } + rootNo = tableTop; + return rootNo; + } + + // 左部分木と右部分木を統合する頂点作成 + table[ tableTop ].Freq = table[ leftNo ].Freq + table[ rightNo ].Freq; + table[ tableTop ].ChNo[0] = (s16)leftNo; + table[ tableTop ].ChNo[1] = (s16)rightNo; + if ( table[ leftNo ].LeafDepth > table[ rightNo ].LeafDepth ) + { + table[ tableTop ].LeafDepth = (u16)( table[ leftNo ].LeafDepth + 1 ); + } + else + { + table[ tableTop ].LeafDepth = (u16)( table[ rightNo ].LeafDepth + 1 ); + } + + table[ leftNo ].PaNo = table[ rightNo ].PaNo = (s16)( tableTop ); + table[ leftNo ].Bit = 0; + table[ rightNo ].Bit = 1; + + HuffAddParentDepthToTable( table, (u16)leftNo, (u16)rightNo ); + + tableTop++; + leftNo = rightNo = -1; + } +} + + +//----------------------------------------------------------------------- +// 2文木作成時に、部分木を統合したときに、部分木の各構成ノードの深さを+1する +//----------------------------------------------------------------------- +static void HuffAddParentDepthToTable( HuffData *table, u16 leftNo, u16 rightNo ) +{ + table[ leftNo ].PaDepth++; + table[ rightNo ].PaDepth++; + + if ( table[ leftNo ].LeafDepth != 0 ) + { + HuffAddParentDepthToTable( table, (u16)table[ leftNo ].ChNo[0], (u16)table[ leftNo ].ChNo[1] ); + } + if ( table[ rightNo ].LeafDepth != 0 ) + { + HuffAddParentDepthToTable( table, (u16)table[ rightNo ].ChNo[0], (u16)table[ rightNo ].ChNo[1] ); + } +} + +//----------------------------------------------------------------------- +// ハフマンコード生成 +//----------------------------------------------------------------------- +static void HuffAddCodeToTable( HuffData* table, u16 nodeNo, u32 paHuffCode ) +{ + table[ nodeNo ].HuffCode = (paHuffCode << 1) | table[ nodeNo ].Bit; + + if ( table[ nodeNo ].LeafDepth != 0 ) + { + HuffAddCodeToTable( table, (u16)table[ nodeNo ].ChNo[0], table[ nodeNo ].HuffCode ); + HuffAddCodeToTable( table, (u16)table[ nodeNo ].ChNo[1], table[ nodeNo ].HuffCode ); + } +} + + +//----------------------------------------------------------------------- +// 中間ノードが huffTree 作成に必要とするデータ量 +//----------------------------------------------------------------------- +static u16 HuffAddCountHWordToTable( HuffData *table, u16 nodeNo) +{ + u16 leftHWord, rightHWord; + + switch ( table[ nodeNo ].LeafDepth ) + { + case 0: + return 0; + case 1: + leftHWord = rightHWord = 0; + break; + default: + leftHWord = HuffAddCountHWordToTable( table, (u16)table[nodeNo].ChNo[0] ); + rightHWord = HuffAddCountHWordToTable( table, (u16)table[nodeNo].ChNo[1] ); + break; + } + + table[ nodeNo ].HWord = (u16)( leftHWord + rightHWord + 1 ); + return (u16)( leftHWord + rightHWord + 1 ); +} + + +//----------------------------------------------------------------------- +// ハフマンコード表作成 +//----------------------------------------------------------------------- +static void HuffMakeHuffTree( HuffCompressionInfo* info, u16 rootNo ) +{ + s16 i; + s16 costHWord, tmpCostHWord; // 部分木のコード表を作成しなかった時のコスト 最大値の節点の部分木コード表を作る + s16 costOffsetNeed, tmpCostOffsetNeed; + s16 costMaxKey; // コスト最小の節点を huffTreeBuf.huffTree から特定するための情報 + BOOL costMaxRightFlag; + u16 offsetNeedNum; + BOOL tmpRightFlag; + const u32 MAX_COST = 64; + + info->huffTreeTop = 1; + costOffsetNeed = 0; + + info->huffTreeCtrl[0].leftOffsetNeed = 0; // 使用しない (テーブルサイズとして使用) + info->huffTreeCtrl[0].rightNodeNo = rootNo; + + + while ( 1 ) // return するまで + { + // オフセットを設定する必要のあるノード数の計算 + offsetNeedNum = 0; + for ( i = 0; i < info->huffTreeTop; i++ ) + { + if ( info->huffTreeCtrl[ i ].leftOffsetNeed ) + { + offsetNeedNum++; + } + if ( info->huffTreeCtrl[ i ].rightOffsetNeed ) + { + offsetNeedNum++; + } + } + + // 最大コストの節点を検索 + costHWord = -1; + costMaxKey = -1; + tmpRightFlag = 0; + + for ( i = 0; i < info->huffTreeTop; i++ ) + { + tmpCostOffsetNeed = (u16)( info->huffTreeTop - i ); + + // 左の子節点のコスト評価 + if ( info->huffTreeCtrl[i].leftOffsetNeed ) + { + tmpCostHWord = (s16)info->huffTable[ info->huffTreeCtrl[i].leftNodeNo ].HWord; + + if ( (u32)(tmpCostHWord + offsetNeedNum) > MAX_COST ) + { + goto leftCostEvaluationEnd; + } + if ( ! HuffRemainingNodeCanSetOffset( info, (u16)tmpCostHWord ) ) + { + goto leftCostEvaluationEnd; + } + if ( tmpCostHWord > costHWord ) + { + costMaxKey = i; + costMaxRightFlag = 0; + } + else if ( (tmpCostHWord == costHWord) && (tmpCostOffsetNeed > costOffsetNeed) ) + { + costMaxKey = i; + costMaxRightFlag = 0; + } + } +leftCostEvaluationEnd:{} + + if ( info->huffTreeCtrl[i].rightOffsetNeed) + { + tmpCostHWord = (s16)info->huffTable[info->huffTreeCtrl[i].rightNodeNo].HWord; + + if ( (u32)(tmpCostHWord + offsetNeedNum) > MAX_COST ) + { + goto rightCostEvaluationEnd; + } + if ( ! HuffRemainingNodeCanSetOffset( info, (u16)tmpCostHWord ) ) + { + goto rightCostEvaluationEnd; + } + if ( tmpCostHWord > costHWord ) + { + costMaxKey = i; + costMaxRightFlag = 1; + } + else if ( (tmpCostHWord == costHWord) && (tmpCostOffsetNeed > costOffsetNeed) ) + { + costMaxKey = i; + costMaxRightFlag = 1; + } + } +rightCostEvaluationEnd:{} + } + + // 部分木をまるまる huffTree に格納 + if ( costMaxKey >= 0 ) + { + HuffMakeSubsetHuffTree( info, (u16)costMaxKey, costMaxRightFlag); + goto nextTreeMaking; + } + else + { + // 必要オフセット最大のノードを検索 + for ( i = 0; i < info->huffTreeTop; i++ ) + { + u16 tmp = 0; + tmpRightFlag = 0; + if (info->huffTreeCtrl[i].leftOffsetNeed) + { + tmp = info->huffTable[ info->huffTreeCtrl[i].leftNodeNo ].HWord; + } + if (info->huffTreeCtrl[i].rightOffsetNeed) + { + if ( info->huffTable[info->huffTreeCtrl[i].rightNodeNo ].HWord > tmp ) + { + tmpRightFlag = 1; + } + } + if ( (tmp != 0) || (tmpRightFlag) ) + { + HuffSetOneNodeOffset( info, (u16)i, tmpRightFlag); + goto nextTreeMaking; + } + } + } + return; +nextTreeMaking:{} + } +} + +//----------------------------------------------------------------------- +// 部分木をまるまる huffTree に格納 +//----------------------------------------------------------------------- +static void HuffMakeSubsetHuffTree( HuffCompressionInfo* info, u16 huffTreeNo, BOOL rightNodeFlag ) +{ + u16 i; + + i = info->huffTreeTop; + HuffSetOneNodeOffset( info, huffTreeNo, rightNodeFlag ); + + if ( rightNodeFlag ) + { + info->huffTreeCtrl[ huffTreeNo ].rightOffsetNeed = 0; + } + else + { + info->huffTreeCtrl[ huffTreeNo ].leftOffsetNeed = 0; + } + + while ( i < info->huffTreeTop ) + { + if ( info->huffTreeCtrl[ i ].leftOffsetNeed ) + { + HuffSetOneNodeOffset( info, i, 0); + info->huffTreeCtrl[ i ].leftOffsetNeed = 0; + } + if ( info->huffTreeCtrl[ i ].rightOffsetNeed ) + { + HuffSetOneNodeOffset( info, i, 1); + info->huffTreeCtrl[ i ].rightOffsetNeed = 0; + } + i++; + } +} + +//----------------------------------------------------------------------- +// 与えられたデータ量の部分木を展開しても huffTree 構築に支障がないか調べる +//----------------------------------------------------------------------- +static BOOL HuffRemainingNodeCanSetOffset( HuffCompressionInfo* info, u16 costHWord ) +{ + u16 i; + s16 capacity; + const u32 MAX_COST = 64; + + capacity = (s16)( MAX_COST - costHWord ); + + // オフセット数は i が小さいほど大きいので、ソートせず、i = 0 -> huffTreeTop で計算すればよい + for ( i = 0; i < info->huffTreeTop; i++ ) + { + if ( info->huffTreeCtrl[i].leftOffsetNeed ) + { + if ( (info->huffTreeTop - i) <= capacity ) + { + capacity--; + } + else + { + return 0; + } + } + if ( info->huffTreeCtrl[i].rightOffsetNeed ) + { + if ( (info->huffTreeTop - i) <= capacity ) + { + capacity--; + } + else + { + return 0; + } + } + } + + return 1; +} + + +/*---------------------------------------------------------------------------* + Name: HuffSetOneNodeOffset + Description: 1節点分、ハフマンコード表を作成 + Arguments: *table ハフマンテーブル + huffTreeNo + rightNodeFlag 右側のノードであるかどうかのフラグ + Returns: None. + *---------------------------------------------------------------------------*/ +static void HuffSetOneNodeOffset( HuffCompressionInfo* info, u16 huffTreeNo, BOOL rightNodeFlag) +{ + u16 nodeNo; + u16 offsetData = 0; + + HuffData* huffTable = info->huffTable; + u8* huffTree = info->huffTree; + HuffTreeCtrlData* huffTreeCtrl = info->huffTreeCtrl; + u16 huffTreeTop = info->huffTreeTop; + + if (rightNodeFlag) + { + nodeNo = huffTreeCtrl[ huffTreeNo ].rightNodeNo; + huffTreeCtrl[ huffTreeNo ].rightOffsetNeed = 0; + } + else + { + nodeNo = huffTreeCtrl[ huffTreeNo ].leftNodeNo; + huffTreeCtrl [huffTreeNo ].leftOffsetNeed = 0; + } + + // 左の子節点 + if ( huffTable[ huffTable[nodeNo].ChNo[0] ].LeafDepth == 0) + { + offsetData |= 0x80; + huffTree[ huffTreeTop * 2 + 0 ] = (u8)huffTable[ nodeNo ].ChNo[0]; + huffTreeCtrl[ huffTreeTop ].leftNodeNo = (u8)huffTable[ nodeNo ].ChNo[0]; + huffTreeCtrl[ huffTreeTop ].leftOffsetNeed = 0; // オフセットは必要なくなる + } + else + { + huffTreeCtrl[ huffTreeTop ].leftNodeNo = (u16)huffTable[ nodeNo ].ChNo[0]; // オフセットは必要 + } + + // 右の子節点 + if ( huffTable[ huffTable[ nodeNo ].ChNo[1] ].LeafDepth == 0 ) + { + offsetData |= 0x40; + huffTree[ huffTreeTop * 2 + 1 ] = (u8)huffTable[nodeNo].ChNo[1]; + huffTreeCtrl[ huffTreeTop ].rightNodeNo = (u8)huffTable[ nodeNo ].ChNo[1]; + huffTreeCtrl[ huffTreeTop ].rightOffsetNeed = 0; // オフセットは必要なくなる + } + else + { + huffTreeCtrl[ huffTreeTop ].rightNodeNo = (u16)huffTable[ nodeNo ].ChNo[1]; // オフセットは必要 + } + + offsetData |= (u16)( huffTreeTop - huffTreeNo - 1 ); + huffTree[ huffTreeNo * 2 + (rightNodeFlag? 1 : 0) ] = (u8)offsetData; + + info->huffTreeTop++; +} + + +/*---------------------------------------------------------------------------* + Name: HuffExportTree + + Description: ハフマンテーブルをバイナリ出力 + + Arguments: dstp + info + bitSize + + Returns: + *---------------------------------------------------------------------------*/ +static u32 HuffExportTree( u8* dstp, HuffCompressionInfo* info ) +{ + u32 cnt = 0; + s32 i; + + for ( i = 0; i < (info->huffTreeTop + 1) * 2; i++ ) // ツリーテーブル + { + dstp[ cnt++ ] = ((u8*)info->huffTree)[ i ]; + } + + // 4バイト境界アラインメント + // アラインメント用データ0 はデータサイズに含める (デコーダのアルゴリズムによる) + while ( cnt & 0x3 ) + { + if ( cnt & 0x1 ) + { + info->huffTreeTop++; + dstp[ 0 ] = dstp[ 0 ] + 1; + } + dstp[ cnt++ ] = 0; + } + return cnt; +} + + +/*---------------------------------------------------------------------------* + Name: HuffConvertData + Description: ハフマンテーブルを元にデータ変換 + Arguments: *table + srcp + dstp + srcSize + bitSize + Returns: None. + *---------------------------------------------------------------------------*/ +static u32 HuffConvertData( const HuffData *table, const u8* srcp, u8* dstp, u32 srcSize, u8 bitSize ) +{ + u32 i, ii, iii; + u8 srcTmp; + u32 bitStream = 0; + u32 streamLength = 0; + u32 dstSize = 0; + + // ハフマン符号化 + for ( i = 0; i < srcSize; i++ ) + { // データ圧縮 + u8 val = srcp[ i ]; + if ( bitSize == 8 ) + { // 8ビットハフマン + bitStream = (bitStream << table[ val ].PaDepth) | table[ val ].HuffCode; + streamLength += table[ val ].PaDepth; + for ( ii = 0; ii < streamLength / 8; ii++ ) + { + dstp[ dstSize++ ] = (u8)(bitStream >> (streamLength - (ii + 1) * 8)); + } + streamLength %= 8; + } + else // 4ビットハフマン + { + for ( ii = 0; ii < 2; ii++ ) + { + if ( ii ) + { + srcTmp = val >> 4; // 上位4ビットが後 + } + else + { + srcTmp = val & 0x0F; // 下位4ビットが先( デコーダがLittleEndianでアクセスする関係 ) + } + bitStream = (bitStream << table[ srcTmp ].PaDepth) | table[ srcTmp ].HuffCode; + streamLength += table[srcTmp].PaDepth; + for ( iii = 0; iii < streamLength / 8; iii++ ) + { + dstp[ dstSize++ ] = (u8)(bitStream >> (streamLength - (iii + 1) * 8)); + } + streamLength %= 8; + } + } + } + if ( streamLength != 0 ) + { + dstp[ dstSize++ ] = (u8)(bitStream << (8 - streamLength)); + } + + // 4バイト境界アラインメント + // アラインメント用データ0 はデータサイズに含め「る」 + // ハフマン符号化だけ特別! リトルエンディアン変換するため、アラインメント境界データより後にデータが格納される + while ( dstSize & 0x3 ) + { + dstp[ dstSize++ ] = 0; + } + + // リトルエンディアン変換 + for ( i = 0; i < dstSize / 4; i++ ) + { + u8 tmp; + tmp = dstp[i * 4 + 0]; + dstp[i * 4 + 0] = dstp[i * 4 + 3]; + dstp[i * 4 + 3] = tmp; // スワップ + tmp = dstp[i * 4 + 1]; + dstp[i * 4 + 1] = dstp[i * 4 + 2]; + dstp[i * 4 + 2] = tmp; // スワップ + } + return dstSize; +} + + + +/*---------------------------------------------------------------------------* + Name: HuffVerifyTable + + Description: ハフマンテーブルの整合性をチェック + + Arguments: ハフマンテーブルへのポインタ + + Returns: 正常なテーブルの場合には TRUE + 不正なテーブルの場合には FALSE + *---------------------------------------------------------------------------*/ +static BOOL +HuffVerifyTable( const void* pTable, u8 bit ) +{ + enum { FLAGS_ARRAY_NUM = 512 / 8 }; /* 64Byte */ + u8* treep = (u8*)pTable; + u8* treeStartp = treep + 1; + u32 treeSize = *treep; + u8* treeEndp = (u8*)pTable + (treeSize + 1) * 2; + u32 i; + u8 end_flags[ FLAGS_ARRAY_NUM ]; + u32 idx; + + for ( i = 0; i < FLAGS_ARRAY_NUM; i++ ) + { + end_flags[ i ] = 0; + } + + if ( bit == 4 ) + { + if ( treeSize >= 0x10 ) + { + return FALSE; + } + } + + idx = 1; + treep = treeStartp; + while ( treep < treeEndp ) + { + if ( (end_flags[ idx / 8 ] & (1 << (idx % 8) )) == 0 ) + { + u32 offset = (u32)( ( (*treep & 0x3F) + 1 ) << 1); + u8* nodep = (u8*)( (((u32)treep >> 1) << 1) + offset ); + + // 終端のアライメント用データは読み飛ばす + if ( *treep == 0 && idx >= (treeSize * 2) ) + { + goto next; + } + if ( nodep >= treeEndp ) + { + return FALSE; + } + if ( *treep & 0x80 ) + { + u32 left = (idx & ~0x1) + offset; + end_flags[ left / 8 ] |= (u8)( 1 << (left % 8) ); + } + if ( *treep & 0x40 ) + { + u32 right = (idx & ~0x1) + offset + 1; + end_flags[ right / 8 ] |= (u8)( 1 << (right % 8) ); + } + } + next: + ++idx; + ++treep; + } + return TRUE; +} + + + + +//############################################################################################## +//############################################################################################## +// 展開関連の関数は以下 +//############################################################################################## +//############################################################################################## + +//================================================================================== +// Rawデータ展開 +//================================================================================== +static s32 RawRead( const u8 *srcp, u32 srcSize, u8 *dstp, u32 dstSize ) +{ +// memcpy(dstp, srcp, size); + u32 i; + + if ( srcSize < dstSize ) + { + return -1; + } + + for (i = 0; i < dstSize; i++) + { + *dstp = *srcp; + dstp++; + srcp++; + } + + return dstSize; +} + +//================================================================================== +// 差分圧縮データ展開 +//================================================================================== +static s32 DiffFiltRead( const u8 *srcp, u32 srcSize, u8 *dstp, u32 dstSize, u8 diffBitSize ) +{ + s32 DiffCount = 0; // 展開データのバイト数 + u32 i; + + u16 *src16p = (u16 *)srcp; + u16 *dst16p = (u16 *)dstp; + + // ソースのバッファオーバーフローチェック + if ( dstSize > srcSize ) + { + return -1; + } + + if (diffBitSize == 8) + { +#ifdef DEBUG_PRINT_DIFFFILT + for (i = 0; i < 16; i++) + { + dbg_printf_dif(stderr, "srcp[%d] = %x\n", i, srcp[i]); + } +#endif + dstp[DiffCount] = srcp[0]; // 先頭データのみ差分無し + DiffCount++; + for (i = 1; i < dstSize; i++, DiffCount++) + { + dbg_printf_dif(stderr, "dstp[%x] = srcp[%d]+dstp[%d] = %x + %x = %x\n", + DiffCount, i, i - 1, srcp[i], dstp[i - 1], srcp[i] - dstp[i - 1]); + dstp[DiffCount] = srcp[i] + dstp[i - 1]; // 差分データ格納 + } + } + else // 16ビットサイズ + { + dst16p[DiffCount / 2] = src16p[0]; + DiffCount += 2; + for (i = 1; i < dstSize / 2; i++, DiffCount += 2) + { + dst16p[DiffCount / 2] = src16p[i] + dst16p[i - 1]; + } + } + + // 4バイト境界アラインメント + // アラインメント用データ0 はデータサイズに含めない + i = 0; + while ((DiffCount + i) & 0x3) + { + dstp[DiffCount + i] = 0; + i++; + } + + return DiffCount; +} + +//================================================================================== +// ランレングス圧縮データ展開 +//================================================================================== +static s32 RLCompRead( const u8 *srcp, u32 srcSize, u8 *dstp, u32 dstSize ) +{ + u32 RLDstCount; // 展開データのバイト数 + u32 RLSrcCount; // 展開対象データの処理済データ量(バイト単位) + u32 i; + + RLSrcCount = 0; + RLDstCount = 0; + while ( RLDstCount < dstSize ) + { + if ( srcp[ RLSrcCount ] & 0x80 ) // 復号処理(ランレングス符号化されている) + { + u8 length = (srcp[ RLSrcCount++ ] & 0x7f) + 3; // データ長を格納(3の下駄を履いているので、実際は+3して考える) + // バッファオーバーランチェック + if ( RLSrcCount >= srcSize ) + { + return -1; + } + if ( RLDstCount + length > dstSize ) + { + return -1; + } + for ( i = 0; i < length; i++ ) + { + dstp[ RLDstCount++ ] = srcp[ RLSrcCount ]; + } + RLSrcCount++; + } + else // 生データをコピー(ランレングス符号化されていない) + { + u8 length = srcp[ RLSrcCount++ ] + 1; // (srcp[RLSrcCount] & 0x7f と同じ) + // バッファオーバーランチェック + if ( RLSrcCount + length > srcSize ) + { + return -1; + } + if ( RLDstCount + length > dstSize ) + { + return -1; + } + + for ( i = 0; i < length; i++ ) + { // データ長は -1 されて格納されているため +1 + dstp[ RLDstCount++ ] = srcp[ RLSrcCount++ ]; + } + } + } + + // 4バイト境界アラインメント + // アラインメント用データ0 はデータサイズに含めない + i = 0; + while ((RLDstCount + i) & 0x3) + { + dstp[RLDstCount + i] = 0; + i++; + } + + return RLDstCount; +} + +//================================================================================== +// LZ77圧縮データ展開 +//================================================================================== +static s32 LZCompReadEx( const u8 *srcp, u32 srcSize, u8 *dstp, u32 dstSize, BOOL ex_available) +{ + u32 LZDstCount; // 展開データのバイト数 + u32 LZSrcCount; // 展開対象データの処理済データ量(バイト単位) + u32 i; + + LZSrcCount = 0; + LZDstCount = 0; + + while ( LZDstCount < dstSize ) + { + u8 compFlags = srcp[LZSrcCount++]; // 圧縮の有無を示すフラグ列 + if ( LZSrcCount > srcSize ) + { + return -1; + } + + for ( i = 0; i < 8; i++ ) + { + if (compFlags & 0x80) // 圧縮されている + { + u32 length; // 対象データ長 + u16 offset; // 一致データオフセット - 1 (常に2以上)(下位4ビット,offsetでは11-8ビット目) + + length = srcp[ LZSrcCount ] >> 4; + + if ( ex_available ) + { + if ( length == 1 ) + { + length = (srcp[ LZSrcCount ] & 0x0F) << 12; + LZSrcCount++; + length |= srcp[ LZSrcCount ] << 4; + LZSrcCount++; + length |= srcp[ LZSrcCount ] >> 4; + length += 0xFF + 0xF + 3; + } + else if ( length == 0 ) + { + length = (srcp[ LZSrcCount ] & 0x0F) << 4; + LZSrcCount++; + length |= srcp[ LZSrcCount ] >> 4; + length += 0xF + 2; + } + else + { + length += 1; + } + } + else + { + length += 3; + } + offset = (srcp[LZSrcCount] & 0x0F) << 8; + LZSrcCount++; + offset |= srcp[LZSrcCount]; + offset++; + LZSrcCount++; + + // バッファオーバーランをチェック + if ( LZSrcCount > srcSize ) + { + return -1; + } + if ( LZDstCount + length > dstSize ) + { + return -1; + } + if ( LZDstCount < offset ) + { + return -1; + } + + // 展開処理 + do + { + dstp[ LZDstCount++ ] = dstp[ LZDstCount - offset ]; + } while ( --length > 0 ); + } + else // 圧縮無し + { + dstp[ LZDstCount++ ] = srcp[ LZSrcCount++ ]; + if ( LZSrcCount > srcSize ) + { + return -1; + } + } + // サイズに達したら終了 + if ( LZDstCount >= dstSize ) + { + break; + } + compFlags <<= 1; + } + } + + // 4バイト境界アラインメント + // アラインメント用データ0 はデータサイズに含めない + i = 0; + while ( (LZDstCount + i) & 0x3 ) + { + dstp[ LZDstCount + i ] = 0; + i++; + } + return LZDstCount; +} + + +//================================================================================== +// ハフマン符号化データ展開 +//================================================================================== +static s32 HuffCompRead( const u8 *srcp, u32 srcSize, u8 *dstp, u32 dstSize, u8 huffBitSize ) +{ + u16 treeSize; // huffTreeのサイズ * 2 + u32 HuffSrcCount; // 展開対象データの処理済データ量(バイト単位) + u32 HuffDstCount; // 展開データ + u32 currentBitStream; + u8 currentBit; + u16 treeAddr; + u8 treeData; + u8 preTreeData; + u8 isUpper4bits = 0; + + treeSize = ((*srcp) + 1) * 2; + HuffSrcCount = treeSize; // データの先頭を取得 + HuffDstCount = 0; + treeAddr = 1; + preTreeData = srcp[ 1 ]; + + dbg_printf_huff(stderr, "HuffSrcCount = %d\n", HuffSrcCount); + + // ハフマンテーブルの整合性チェック + if ( ! HuffVerifyTable( srcp, huffBitSize ) ) + { + return -1; + } + if ( srcSize < treeSize ) + { + return -1; + } + + // 展開処理 + while ( HuffDstCount < dstSize ) // return まで + { + u16 i; + + currentBitStream = srcp[HuffSrcCount++]; + currentBitStream |= srcp[HuffSrcCount++] << 8; + currentBitStream |= srcp[HuffSrcCount++] << 16; + currentBitStream |= srcp[HuffSrcCount++] << 24; + + if ( HuffSrcCount > srcSize ) + { + return -1; + } + + for ( i = 0; i < 32; i++ ) + { + currentBit = (u8)(currentBitStream >> 31); + currentBitStream <<= 1; + + if (((currentBit == 0) && (preTreeData & 0x80)) || + ((currentBit == 1) && (preTreeData & 0x40))) + { + if (huffBitSize == 8) + { + treeData = srcp[(treeAddr * 2) + currentBit]; // 符号データ + dstp[HuffDstCount++] = treeData; + } + else if (isUpper4bits) + { + treeData |= (srcp[(treeAddr * 2) + currentBit]) << 4; + dstp[HuffDstCount++] = treeData; + isUpper4bits = 0; + } + else + { + treeData = srcp[(treeAddr * 2) + currentBit]; + isUpper4bits = 1; + } + + if (HuffDstCount >= dstSize) + { + return HuffDstCount; + } + + treeAddr = 1; + preTreeData = srcp[ 1 ]; + } + else + { + preTreeData = srcp[(treeAddr * 2) + currentBit]; // オフセット・データ + treeAddr += (preTreeData & 0x3f) + 1; + } + } + } + return HuffDstCount; +} + + +//================================================================================== +// 圧縮ファイルの元ファイルサイズ取得 +//================================================================================== +EXTERN u32 STDCALL nitroGetDecompFileSize( const void* srcp ) +{ + const u32* p = (const u32*)srcp; + + u32 size = *p >> 8; + if ( size == 0 ) + { + size = *(p + 1); + } + return size; +} + + +//================================================================================== +// データ展開制御関数 (自動展開のため、最後にrawデータ展開用ヘッダがないと動作しない) +//================================================================================== +EXTERN s32 STDCALL nitroDecompress( const u8 *srcp, u32 srcSize, u8 *dstp, s8 depth ) +{ + // rawData // データ・ヘッダ + // *(u32 *)pReadBuf = size << 8 | 0; + // [i+3] [i+2] [i+1](サイズ) | [0000 0000] + // DiffFilt + // *(u32 *)dstp = size << 8 | 0x80 | diffBitSize/8; + // [i+3] [i+2] [i+1](サイズ) | [1000 00XX] + // RL + // *(u32 *)dstp = size << 8 | 0x30; + // [i+3] [i+2] [i+1](サイズ) | [0011 0000] + // LZ77 + // *(u32 *)dstp = size << 8 | 0x10; + // [i+3] [i+2] [i+1](サイズ) | [0001 0000] + // Huffman + // *(u32 *)dstp = size << 8 | 0x20 | huffBitSize; + // [i+3] [i+2] [i+1](サイズ) | [0010 XX00] + u32 header; + s32 dstSize; + u32 memSize = srcSize * 3 + 256 * 2; + u8 *pReadBuf; // 圧縮データの先頭番地を指すポインタ + u8 offset; + s8 curDepth = 0; + s8 targetDepth; + + pCompBuf[0] = (u8 *)malloc(memSize); + pCompBuf[1] = (u8 *)malloc(memSize); + pReadBuf = pCompBuf[0]; + + // malloc チェック + if (pCompBuf[0] == NULL || pCompBuf[1] == NULL) + { + fprintf(stderr, "Error: Memory is not enough.\n"); + exit(1); + } + + compBufNo = 1; + memcpy(pReadBuf, srcp, srcSize); + + if (depth < 1) + { + targetDepth = -1; + } + else + { + targetDepth = depth; + } + dbg_printf(stderr, "nitroCompress \t(Compressed size is 0x%x)\n", srcSize); + + while (1) + { + // targetDepth指定の際の終了条件 + if (curDepth == targetDepth) + { + dbg_printf(stderr, "nitroDecompress Raw \t(Decompressed size will be 0x%x)\n", + dstSize); + dstSize = RawRead(pReadBuf, dstSize, dstp, dstSize); + + if (pCompBuf[0] != NULL) + { + free(pCompBuf[0]); + pCompBuf[0] = NULL; + } + if (pCompBuf[1] != NULL) + { + free(pCompBuf[1]); + pCompBuf[1] = NULL; + } + return dstSize; + } + + header = *(u32 *)pReadBuf; + dstSize = header >> 8; // ヘッダを含まないサイズ, 展開関数にもヘッダを含めずに渡す + offset = 4; + + if ( dstSize == 0 ) + { + dstSize = *(u32 *)(pReadBuf + 4); + offset = 8; + } + + if ( memSize < (u32)dstSize ) + { + memSize = dstSize * 3 + 256 * 2; + pCompBuf[0] = (u8 *)realloc(pCompBuf[0], memSize); + pCompBuf[1] = (u8 *)realloc(pCompBuf[1], memSize); + pReadBuf = pCompBuf[compBufNo ^ 0x1]; + + } + + switch (header & CODE_HEADER_MASK) + { + case DIFF_CODE_HEADER: + { + dbg_printf(stderr, "nitroDecompress Diff %d \t(Decompressed size will be 0x%x)\n", + ((u8)header & 0x03) * 8, dstSize ); + dstSize = + DiffFiltRead(&pReadBuf[offset], srcSize - offset, pCompBuf[compBufNo], dstSize, + ((u8)header & 0x03) * 8); + } + break; + case HUFF_CODE_HEADER: + { + dbg_printf(stderr, "nitroDecompress Huff %d \t(Decompressed size will be 0x%x)\n", + ((u8)header & 0x0f), dstSize); + dstSize = + HuffCompRead(&pReadBuf[offset], srcSize - offset, pCompBuf[compBufNo], dstSize, (u8)header & 0x0f); + } + break; + case LZ_CODE_HEADER: + { + BOOL ex_format = ((header & 0xF) == 0)? FALSE : TRUE; + + dbg_printf(stderr, "nitroDecompress LZ \t(Decompressed size will be 0x%x)\n", + dstSize); + dstSize = LZCompReadEx(&pReadBuf[offset], srcSize - offset, pCompBuf[compBufNo], dstSize, ex_format); + } + break; + case RL_CODE_HEADER: + { + dbg_printf(stderr, "nitroDecompress RL \t(Decompressed size will be 0x%x)\n", + dstSize); + dstSize = RLCompRead(&pReadBuf[offset], srcSize - offset, pCompBuf[compBufNo], dstSize); + } + break; + default: + { + dbg_printf(stderr, "nitroDecompress Raw \t(Decompressed size will be 0x%x)\n", + dstSize); + + dstSize = RawRead(&pReadBuf[offset], srcSize - offset, dstp, dstSize); + if (pCompBuf[0] != NULL) + { + free(pCompBuf[0]); + pCompBuf[0] = NULL; + } + if (pCompBuf[1] != NULL) + { + free(pCompBuf[1]); + pCompBuf[1] = NULL; + } + return dstSize; + } + } + + if ( dstSize < 0 ) + // 不正なファイルで解凍に失敗 + { + dbg_printf(stderr, "decompress fail\n"); + return -1; + } + + // もう一周 + pReadBuf = pCompBuf[compBufNo]; + compBufNo ^= 0x01; + srcSize = dstSize; + curDepth++; + } +} + +//================================================================================== +// メモリ内容を16進で出力 +//================================================================================== +EXTERN void STDCALL debugMemPrint(FILE * fp, u8 *str, u32 size) +{ + u32 i = 0; + + while (str) + { + fprintf(fp, "%4lx:\t0x%2x\n", i, *str); + str++; + i++; + if (i >= size) + { + break; + } + } +} + +//================================================================================== +// メモリ内容を2進で出力 +//================================================================================== +EXTERN void STDCALL debugMemBitPrint(FILE * fp, u8 *str, u32 size) +{ + u32 i = 0; + u8 j; + + while (str) + { + if (i >= size) + { + break; + } + + fprintf(fp, "%4lx:\t0x%2x\t(binary\t", i, *str); + for (j = 0; j < 8; j++) + { + fprintf(fp, "%d", *str >> (7 - j) & 0x01); + } + fprintf(fp, " )\n"); + str++; + i++; + } +} + +//================================================================================== +// 圧縮前と展開後のデータの比較(正しく展開できていれば、"DATA match"と出力される) +//================================================================================== +EXTERN int STDCALL matchingCheck(u8 *srcp, u32 srcSize, u8 *dstp, u32 dstSize) +{ + u32 minSize, i; + u8 dataMatchFlag = 1; + u8 sizeMatchFlag; + + sizeMatchFlag = (srcSize == dstSize); + if (srcSize < dstSize) + { + minSize = srcSize; + } + else + { + minSize = dstSize; + } + + for (i = 0; i < minSize; i++) + { + dbg_printf_match(stderr, "src[%3x], dst[%3x] = %2x , %2x", i, i, srcp[i], dstp[i]); + if (srcp[i] != dstp[i]) + { + dataMatchFlag = 0; + dbg_printf_match(stderr, "\t; mismatch here!"); + } + dbg_printf_match(stderr, "\n"); + } + + if (sizeMatchFlag) + { + fprintf(stderr, "\nSIZE match.\n"); + } + else + { + fprintf(stderr, "\nSIZE mismatch!\n"); + } + + if (dataMatchFlag) + { + fprintf(stderr, "DATA match.\n"); + } + else + { + fprintf(stderr, "DATA mismatch!\n"); + } + + if (dataMatchFlag && sizeMatchFlag) + { + return 1; + } + else + { + return 0; + } +} + + diff --git a/build/systemMenu_RED/sharedFont/ntrcomp/src/nitroCompLib.h b/build/systemMenu_RED/sharedFont/ntrcomp/src/nitroCompLib.h new file mode 100644 index 00000000..7c96d8db --- /dev/null +++ b/build/systemMenu_RED/sharedFont/ntrcomp/src/nitroCompLib.h @@ -0,0 +1,127 @@ +/*---------------------------------------------------------------------------* + Project: NinTendo Compress tool + File: nitroCompLib.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: $ + $Rev$ + $Author$ + *---------------------------------------------------------------------------*/ + +// nitroCompLib.h : nitroCompLib.DLL のメイン ヘッダー ファイル +// + +#ifndef __NITROCOMPLIB_H__ +#define __NITROCOMPLIB_H__ + +//=========================================================================================== +// インクルード +//=========================================================================================== +#include +#include +#include +#include +#include "types.h" + +#ifdef WIN32 +#define STDCALL __stdcall +#else +#define STDCALL +#endif + +//=========================================================================================== +// プロトタイプ宣言 +//=========================================================================================== +// C++用 +#ifdef __cplusplus + extern "C" + { +#endif + +//BOOL WINAPI DllMain( HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved); +u8* STDCALL nitroCompMalloc( u32 size ); +void STDCALL nitroCompFree( u8 *p ); +u32 STDCALL nitroCompress ( const u8 *srcp, u32 srcSize, u8 *dstp, char *compList, u8 rawHeader ); +s32 STDCALL nitroDecompress( const u8 *srcp, u32 srcSize, u8 *dstp, s8 depth ); +void STDCALL debugMemPrint ( FILE * fp, u8 *str, u32 size ); +void STDCALL debugMemBitPrint( FILE * fp, u8 *str, u32 size ); +int STDCALL matchingCheck( u8 *srcp, u32 srcSize, u8 *dstp, u32 dstSize ); +u32 STDCALL nitroGetDecompFileSize( const void* srcp ); + +#ifdef __cplusplus + } +#endif + +//=========================================================================================== +// 関数の使用法 +//=========================================================================================== +//---------------------------------------------------------------------------------- +// 圧縮後のデータを置くためのメモリ領域を確保 +// 圧縮前のデータの2倍の領域を確保する +// 引数 +// u32 size 圧縮前のデータサイズ +// u32 は符号無し整数型でたいていは、 +// unsigned int (処理系依存) +// 返り値 +// u8 * 圧縮後のデータ領域を指すポインタ +// (汎用ポインタなので、好きな型にキャスト) +// ではない +//---------------------------------------------------------------------------------- +//u8 *nitroCompMalloc(u32 size); + +//---------------------------------------------------------------------------------- +// 圧縮後のデータを置いていたメモリ領域を解放 +// 引数 +// u8 *p 圧縮後のデータ領域を指すポインタ +//---------------------------------------------------------------------------------- +//void nitroCompFree(u8 *p); + +//---------------------------------------------------------------------------------- +// データを圧縮する +// 圧縮方式、圧縮順序は引数compListを用いて指示する +// 引数 +// u8 *srcp 圧縮対象データを指すポインタ +// u32 size 圧縮対象データのサイズ(単位はバイト) +// u8 *dstp 圧縮後のデータを保持するデータ領域を指すポインタ +// 十分な領域を確保しておく必要あり +// nitroCompMallocの返り値でよい +// char *compList 圧縮方式、圧縮順序を格納したリスト (C言語のヌル文字列) +// d8 : 8ビット差分フィルタ +// d16 : 16ビット差分フィルタ +// r : ランレングス符号化 +// lx : LZ77.xには同一データ検索の開始点を示すオフセット +// : 2以上でなければならない.上限は255. +// h4 : 4ビット・ハフマン圧縮 +// h8 : 8ビット・ハフマン圧縮 +// u8 rawHeaderFlag 圧縮前のデータであることを示すヘッダ情報を付加するかどうかを +// 指示するフラグ.0であれば付加せず、1であれば、展開しきった +// データにもヘッダが付加される. +// 返り値 +// 圧縮後のデータサイズ +//---------------------------------------------------------------------------------- +//u32 nitroCompress(u8 *srcp, u32 size, u8 *dstp, char *compList, u8 rawHeader); + +//---------------------------------------------------------------------------------- +// データを展開する +// 展開方式、展開順序はデータのヘッダを見て判断する +// 引数 +// u8 *srcp 展開対象データを指すポインタ +// u32 size 展開対象データのサイズ(単位はバイト) +// u8 *dstp 展開後のデータを保持するデータ領域を指すポインタ +// 十分な領域を確保しておく必要あり +// nitroCompMallocの返り値でよい +// u8 depth 展開する深さ(回数) +// 0未満の場合は、圧縮前データを示すヘッダ情報が得られるまで展開し続ける +// 返り値 +// 展開後のデータサイズ +//---------------------------------------------------------------------------------- +//u32 nitroDecompress(u8 *srcp, u32 size, u8 *dstp, s8 depth); + +#endif // __NITROCOMPLIB_H__ diff --git a/build/systemMenu_RED/sharedFont/ntrcomp/src/ntcompress_main.c b/build/systemMenu_RED/sharedFont/ntrcomp/src/ntcompress_main.c new file mode 100644 index 00000000..ce37efb2 --- /dev/null +++ b/build/systemMenu_RED/sharedFont/ntrcomp/src/ntcompress_main.c @@ -0,0 +1,1164 @@ +/*---------------------------------------------------------------------------* + Project: NinTendo Compress tool + File: ntcompress_main.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: $ + $Rev$ + $Author$ + *---------------------------------------------------------------------------*/ + +#include +#include +#include +#include +#include "nitroCompLib.h" +#include "ntcompress_test.h" +#include "multipleCompLib.h" +#include "rangeCoder.h" + +#define FOR_NITRO + +#if defined( FOR_NITRO ) + #define INCLUDE_TARGET "nitro" + #define TARGET_NAME "ntrcomp" + #define NTCOMPRESS_VERSION "1.1.0" +#else // for revolution + #define SUPPORT_LH_LRC + #define INCLUDE_TARGET "revolution" + #define TARGET_NAME "ntcompress" + #define NTCOMPRESS_VERSION "2.0.0" +#endif + + +#define uint unsigned int +#define ushort unsigned short +#define ulong unsigned long +#define uchar unsigned char + + +#define ROUNDUP(x,n) (((x) + ((n) - 1)) & (~((n)-1))) +#define ROUNDUP4(x) ROUNDUP((x),4) + +#define TEXT_BIG_ENDIAN_FLAG 0x10 +#define TEXT_WIDTH_MASK 0x07 + +//--------------------------------------------------------------------------- +// 型定義 +//--------------------------------------------------------------------------- +// 圧縮タイプ +typedef enum +{ + CONV_TYPE_NON, // 変換なし + CONV_TYPE_DIFF, // 差分フィルタ + CONV_TYPE_RUNLENGTH, // ランレングス符号化 + CONV_TYPE_LZ77, // LZ77圧縮 + CONV_TYPE_HUFFMAN, // ハフマン圧縮 + CONV_TYPE_UNCOMPRESS, // 解凍 + CONV_TYPE_LZ77EX, // 拡張LZ77圧縮 + CONV_TYPE_LH, // LZ77-ハフマン複合圧縮 + CONV_TYPE_LRC, // LZ77-RangeCoder複合圧縮 + CONV_TYPE_RANGECODER // レンジコーダ +} +t_conv_type; + +// 圧縮データ情報構造体 +typedef struct +{ + t_conv_type type; // 圧縮タイプ + uint opt; // 圧縮パラメータ + ulong src_size; // 圧縮前データサイズ + ulong dest_size; // 圧縮後データサイズ + uchar *src_buf; // 圧縮前データバッファ + uchar *dest_buf; // 圧縮後データバッファ + uchar textout_width; // テキスト出力するかどうかのフラグ兼サイズ + uchar alignment; // 出力ファイルサイズのアライメント + uchar header_flg :1; // ヘッダ情報を付加するかどうかのフラグ + uchar silent_flg :1; // 標準出力へメッセージを出力するかどうかのフラグ + uchar verify_flg :1; // 圧縮ファイルを展開する際のべりファイをおこなうかどうかのフラグ +} +t_conv_data; + +typedef enum +{ + OPT_NONE, // 無効なオプション + OPT_VERSION, // バージョン表示(-v) + OPT_SILENT, // 標準出力抑止(-s) + OPT_LOWER_TEXT, // 小文字でのテキスト出力(-t) + OPT_UPPER_TEXT, // 大文字でのテキスト出力(-T) + OPT_ALIGN, // アライメント指定(-A4|8|16|32) + OPT_HEADER, // ヘッダ付加(-H) + OPT_OUTPUT, // 出力ファイル指定(-o) + OPT_EXTRACT, // 解凍(-x) + OPT_DIFF_FILTER, // 差分フィルタ(-d) + OPT_RUN_LENGTH, // ランレングス圧縮(-r) + OPT_LZ77, // 旧LZ77圧縮(-l) + OPT_LZ77EX, // LZ77圧縮(-lex) + OPT_LH, // LH圧縮(-lh) + OPT_LRC, // LRC圧縮(-lrc) + OPT_HUFFMAN, // ハフマン圧縮(-h4|8) + + OPT_RANGECODER, // レンジコーダ圧縮(デバッグ版のみ) (-R) + OPT_VERIFY, // ベリファイ (デバッグ版のみ) (-D) + OPT_TEST, // テスト (デバッグ版のみ) (--TEST) +} +t_opt; + + + + +//--------------------------------------------------------------------------- +// プロトタイプ宣言 +//--------------------------------------------------------------------------- +static void usage( const char *cmd_string ); +static void version_info( void ); +static int parse_opt( int argc, char *argv[], t_conv_data * cnv_dat, char **in_fname, + char **out_fname ); +static int load_file ( const char *fname, t_conv_data * cnv_dat ); +static int save_file ( const char *fname, const t_conv_data * cnv_dat ); +static int convert_data( t_conv_data * cnv_dat ); +static int fwrite_text ( const uchar * buf, ulong size, uint width, FILE * fp, const char *name ); +static char* cut_fname ( char *path ); +static char* make_output_fname( const char *input_fname, const t_conv_data * cnv_dat ); +static int verify_data ( t_conv_data * cnv_dat ); + + +//--------------------------------------------------------------------------- +// エンディアン変換 +//--------------------------------------------------------------------------- +static ushort +reverse_endian16( ushort x ) +{ + return (ushort)( ( (x >> 8) & 0x00FF ) | ( (x << 8) & 0xFF00 ) ); +} + +static ulong +reverse_endian32( ulong x ) +{ + return (ulong)( ( (x >> 24) & 0x000000FF ) | + ( (x >> 8) & 0x0000FF00 ) | + ( (x << 8) & 0x00FF0000 ) | + ( (x << 24) & 0xFF000000 ) ); +} + + + +//--------------------------------------------------------------------------- +// メイン関数 +//--------------------------------------------------------------------------- +int main(int argc, char *argv[]) +{ + t_conv_data cnv_dat; + char *in_fname, *out_fname; + +#ifdef _DEBUG + if ( argc >= 2 && strcmp(argv[1], "-TEST") == 0 ) + { + ntcompress_test(); + return 0; + } +#endif + + // データの初期化 + memset(&cnv_dat, 0, sizeof(cnv_dat)); + + // コマンドオプションの解析 + if (parse_opt(argc, argv, &cnv_dat, &in_fname, &out_fname) != 0) + { + exit(1); + } + + // 入力ファイルをバッファへ読み込み + if (load_file(in_fname, &cnv_dat) != 0) + { + exit(1); + } + + // ファイルの圧縮 + if (convert_data(&cnv_dat) != 0) + { + exit(1); + } + + // 圧縮データのベリファイ + if (verify_data( &cnv_dat ) != 0) + { + exit(1); + } + + // バッファから出力ファイルへの書き込み + if (save_file(out_fname, &cnv_dat) != 0) + { + exit(1); + } + + if (!cnv_dat.silent_flg) + { + fprintf(stdout, "convert %s(%ldbyte) to %s(%ldbyte)\n", in_fname, cnv_dat.src_size, + out_fname, cnv_dat.dest_size); + } + + free(cnv_dat.src_buf); + if (cnv_dat.dest_buf != cnv_dat.src_buf) + { + free(cnv_dat.dest_buf); + } + + return 0; +} + +//--------------------------------------------------------------------------- +// USAGE: +//--------------------------------------------------------------------------- +static void usage(const char *cmd_string) +{ + fprintf(stderr, "\n"); +#if defined( SUPPORT_LH_LRC ) + fprintf(stderr, + "Usage: %s <-d(4|8)|r|l|lex|h(8|16)|lh|lrc> [-o outputFile] [-A(4|8|16|32)] [-[width]] [-s] [-H] [-v] \n" + " %s -x [-o outputFile] [-s] \n", + cmd_string, cmd_string); +#else + fprintf(stderr, + "Usage: %s <-d(4|8)|r|l|lex|h(8|16)> [-o outputFile] [-A(4|8|16|32)] [-[width]] [-s] [-H] [-v] \n" + " %s -x [-o outputFile] [-s] \n", + cmd_string, cmd_string); +#endif + fprintf(stderr, "\t-v Show version\n"); + fprintf(stderr, "\t-r Runlength encode.\n"); + fprintf(stderr, "\t-l LZ77 encode(compatible with previous LZ77).\n"); + fprintf(stderr, "\t-lex LZ77 encode.\n"); + fprintf(stderr, "\t-h BitSize(4|8) Huffman encode.\n"); + fprintf(stderr, "\t-d BitSize(8|16) Differential filter.\n"); +#if defined( SUPPORT_LH_LRC ) + fprintf(stderr, "\t-lh LZ and Huffman encode\n"); + fprintf(stderr, "\t-lrc LZ and RangeCoder encode\n"); +#endif + fprintf(stderr, "\n"); + fprintf(stderr, "\t-A(4|8|16|32) Align n byte for compressed filesize\n"); + fprintf(stderr, "\t-o outputFile Specify the output file name.\n"); + fprintf(stderr, "\t-t[TypeWidth(1|2|4)] output C format text(little endian).\n"); + fprintf(stderr, "\t We can specify the type(1=u8,2=u16,4=u32).\n"); + fprintf(stderr, "\t-T[TypeWidth(1|2|4)] output C format text(big endian).\n"); + fprintf(stderr, "\t We can specify the type(1=u8,2=u16,4=u32).\n"); + fprintf(stderr, "\t-s Print no message if you've been successful in the conversion.\n"); + fprintf(stderr, "\t-H Raw data header\n"); + fprintf(stderr, "\t-x Extract compressed file.\n"); +} + + +static void version_info( void ) +{ + fprintf(stderr, "%s version %s\n", TARGET_NAME, NTCOMPRESS_VERSION); +} + +//--------------------------------------------------------------------------- +// パス文字列からファイル名部分のポインタを取得し、拡張子を削る +// @param path パス +// @return ファイル名のポインタ +//--------------------------------------------------------------------------- +static char *cut_fname(char *path) +{ + char *search_tmp; + + if (path == NULL) + { + return NULL; + } + + if ((search_tmp = strrchr(path, '/')) != NULL) + { + path = (search_tmp + 1); + } + if ((search_tmp = strrchr(path, '\\')) != NULL) + { + path = (search_tmp + 1); + } + if ((search_tmp = strrchr(path, '.')) != NULL) + { + *search_tmp = '\0'; + } + return path; +} + +//--------------------------------------------------------------------------- +// 入力ファイル名から出力ファイル名を生成する関数 +// @param input_fname 入力ファイル名 +// @param cnv_dat 変換情報 +// @return 出力ファイル名へのポインタ +//--------------------------------------------------------------------------- +static char *make_output_fname(const char *input_fname, const t_conv_data * cnv_dat) +{ + char *out_fname, *edit_tmp; + + if (input_fname == NULL) + { + return NULL; + } + + // メモリ領域の確保 + if ((out_fname = (char *)malloc(strlen(input_fname) + 0x10)) == NULL) + { + fprintf(stderr, "internal err: malloc fail!\n"); + exit(1); + } + + strcpy(out_fname, input_fname); + edit_tmp = cut_fname(out_fname); + + // 圧縮方式をファイル名に付加 + switch ( cnv_dat->type ) + { + case CONV_TYPE_NON: + strcat(edit_tmp, "_NONE"); + break; + case CONV_TYPE_DIFF: + strcat(edit_tmp, "_DIFF"); + break; + case CONV_TYPE_RUNLENGTH: + strcat(edit_tmp, "_RL"); + break; + case CONV_TYPE_LZ77: + case CONV_TYPE_LZ77EX: + strcat(edit_tmp, "_LZ"); + break; + case CONV_TYPE_HUFFMAN: + strcat(edit_tmp, "_HUFF"); + break; + case CONV_TYPE_LH: + strcat(edit_tmp, "_LH"); + break; + case CONV_TYPE_LRC: + strcat(edit_tmp, "_LRC"); + break; + case CONV_TYPE_RANGECODER: + strcat(edit_tmp, "_RC"); + break; + case CONV_TYPE_UNCOMPRESS: + strcat(edit_tmp, "_DECOMP"); + } + // 拡張子を付加 + if (cnv_dat->textout_width != 0) + { + strcat(edit_tmp, ".c"); + } + else + { + strcat(edit_tmp, ".bin"); + } + return out_fname; +} + + +//--------------------------------------------------------------------------- +// 次のオプションのタイプ判別 +// @param opt 判別したいオプション文字列 +// @return オプションのタイプ +//--------------------------------------------------------------------------- +t_opt +get_opt_type( const char* opt ) +{ + if ( strncmp( opt, "--", 2 ) == 0 ) + { + } + else + { + // オプション解析 + switch ( opt[1] ) + { + case 'v': + return OPT_VERSION; + case 's': + return OPT_SILENT; + case 'D': + return OPT_VERIFY; + case 't': + return OPT_LOWER_TEXT; + case 'T': + return OPT_UPPER_TEXT; + case 'A': + return OPT_ALIGN; + case 'H': + return OPT_HEADER; + case 'o': + return OPT_OUTPUT; + case 'x': + return OPT_EXTRACT; + case 'd': + return OPT_DIFF_FILTER; + case 'r': + return OPT_RUN_LENGTH; + case 'R': + return OPT_RANGECODER; + case 'l': + if ( strcmp(opt, "-lex" ) == 0 ) + { + return OPT_LZ77EX; + } +#if defined( SUPPORT_LH_LRC ) + else if ( strcmp(opt, "-lh") == 0 ) + { + return OPT_LH; + } + else if ( strcmp(opt, "-lrc") == 0 ) + { + return OPT_LRC; + } +#endif + else + { + return OPT_LZ77; + } + case 'h': + return OPT_HUFFMAN; + } + } + return OPT_NONE; +} + + +//--------------------------------------------------------------------------- +// コマンドパラメータ解析 +// @param argc パラメータ数 +// @param argv パラメータ配列 +// @retval cnv_dat 圧縮情報データ +// @retval in_fname 入力ファイル名 +// @retval out_fname 出力ファイル名 +// @return 0 ファイルの書き込み成功 +// -1 ファイルの書き込みエラー +//--------------------------------------------------------------------------- + +static int parse_opt(int argc, char *argv[], t_conv_data * cnv_dat, + char **in_fname, char **out_fname) +{ + int i = 1; + t_conv_type type; + int opt_num; + char *cmd_string; +#define SYNTAX_CHECK(exp) do { if (!(exp)) { usage(cmd_string); return -1; } } while(0) + + // コマンド名取得 + cmd_string = cut_fname(argv[0]); + + // 状態初期化 + cnv_dat->type = CONV_TYPE_NON; + cnv_dat->header_flg = 0; + cnv_dat->textout_width = 0; + cnv_dat->silent_flg = 0; + cnv_dat->alignment = 0; + cnv_dat->verify_flg = 0; + *out_fname = NULL; + *in_fname = NULL; + + // オプション解析 + while (i < argc) + { + t_opt opt; + + if (argv[i][0] != '-') + { + SYNTAX_CHECK(*in_fname == NULL); // 2重読みチェック + + // 入力ファイル名取得 + *in_fname = argv[i]; + i++; + continue; + } + + opt = get_opt_type( argv[i] ); + + // ハイフン付オプション解析 + switch ( opt ) + { + //-------------------- + // バージョン表示オプション'v'(引数0) + case OPT_VERSION: + SYNTAX_CHECK(argv[i][2] == '\0'); // 文字数チェック + + version_info(); + return -1; + + //-------------------- + // 標準出力無しオプション's' (引数0) + case OPT_SILENT: + SYNTAX_CHECK(argv[i][2] == '\0'); // 文字数チェック + SYNTAX_CHECK(!cnv_dat->silent_flg); // 2重読みチェック + + cnv_dat->silent_flg = 1; + break; + +#ifdef _DEBUG + //-------------------- + // ベリファイオプション'D' (引数0) + case OPT_VERIFY: + SYNTAX_CHECK(argv[i][2] == '\0'); // 文字数チェック + SYNTAX_CHECK(!cnv_dat->verify_flg); // 2重読みチェック + cnv_dat->verify_flg = 1; + break; +#endif + + //-------------------- + // C言語テキスト出力オプション't' (引数0-1) (リトルエンディアン) + case OPT_LOWER_TEXT: + SYNTAX_CHECK(cnv_dat->textout_width == 0); // 2重読みチェック + + if (strlen(argv[i]) == 2) // パラメータが無い場合はバイト単位で書き込む + { + cnv_dat->textout_width = 1; + } + else + { + cnv_dat->textout_width = atoi(&argv[i][2]); + SYNTAX_CHECK(cnv_dat->textout_width == 1 || cnv_dat->textout_width == 2 + || cnv_dat->textout_width == 4); + } + break; + + //-------------------- + // C言語テキスト出力オプション'T' (引数0-1) (ビッグエンディアン) + case OPT_UPPER_TEXT: + SYNTAX_CHECK(cnv_dat->textout_width == 0); // 2重読みチェック + + if (strlen(argv[i]) == 2) // パラメータが無い場合はバイト単位で書き込む + { + cnv_dat->textout_width = 1; + } + else + { + cnv_dat->textout_width = atoi(&argv[i][2]); + SYNTAX_CHECK(cnv_dat->textout_width == 1 || cnv_dat->textout_width == 2 + || cnv_dat->textout_width == 4); + } + cnv_dat->textout_width |= TEXT_BIG_ENDIAN_FLAG; + break; + + //-------------------- + // 出力ファイルサイズアライン指定'A4|8|16|32' (引数0) + case OPT_ALIGN: + SYNTAX_CHECK(strlen(argv[i]) <= 4 ); // 文字数チェック + SYNTAX_CHECK( cnv_dat->alignment == 0 ); // 2重読みチェック + + cnv_dat->alignment = atoi(&argv[i][2]); + SYNTAX_CHECK( cnv_dat->alignment == 4 || cnv_dat->alignment == 8 || cnv_dat->alignment == 16 || cnv_dat->alignment == 32 ); + + break; + + //-------------------- + // 展開後データの先頭にデータサイズを付加するオプション'H' (引数0) + case OPT_HEADER: + SYNTAX_CHECK(strlen(argv[i]) == 2); // 文字数チェック + SYNTAX_CHECK(!cnv_dat->header_flg); // 2重読みチェック + + cnv_dat->header_flg = 1; + break; + + //-------------------- + // 出力ファイル指定オプション 'o' (引数1) + case OPT_OUTPUT: + SYNTAX_CHECK(strlen(argv[i]) == 2); // 文字数チェック + SYNTAX_CHECK(*out_fname == NULL); // 2重読みチェック + SYNTAX_CHECK(i + 1 < argc); // パラメータチェック + + *out_fname = argv[++i]; + break; + + //-------------------- + // 解凍指定 'x'(引数0) + case OPT_EXTRACT: + SYNTAX_CHECK(strlen(argv[i]) == 2); // 文字数チェック + + type = CONV_TYPE_UNCOMPRESS; + opt_num = 0; + goto common; + + //-------------------- + // 差分フィルタ指定 'd'(引数1) + case OPT_DIFF_FILTER: + type = CONV_TYPE_DIFF; + goto common_opt1; + + //-------------------- + // ランレングス圧縮指定'r'(引数0) + case OPT_RUN_LENGTH: + SYNTAX_CHECK(strlen(argv[i]) == 2); // 文字数チェック + + type = CONV_TYPE_RUNLENGTH; + opt_num = 0; + goto common; + +#ifdef _DEBUG + //-------------------- + // レンジコーダ圧縮指定'R'(引数0) + case OPT_RANGECODER: + SYNTAX_CHECK(strlen(argv[i]) == 2); // 文字数チェック + + type = CONV_TYPE_RANGECODER; + opt_num = 0; + goto common; +#endif + //-------------------- + // 旧LZ77圧縮"-l"(引数0) + case OPT_LZ77: + SYNTAX_CHECK( (argv[i][2] == '\0') || isdigit(argv[i][2]) ); + type = CONV_TYPE_LZ77; + opt_num = 0; + cnv_dat->opt = 2; + + #if defined( FOR_NITRO ) + // 次にパラメータがあり、入力ファイル名でなければ読み飛ばす + if ( i + 2 < argc && argv[i + 1][0] != '-' && + argv[i + 1][1] == '\0' && atoi(&argv[i + 1][0]) != 0 ) + { + i++; + } + #endif + goto common; + + //-------------------- + // LZ77圧縮"-lex"(引数0) + case OPT_LZ77EX: + type = CONV_TYPE_LZ77EX; + opt_num = 0; + cnv_dat->opt = 2; // 検索開始位置はオプションで指定できない事にする + goto common; + + //-------------------- + // LH圧縮"-lh"(引数0) + case OPT_LH: + type = CONV_TYPE_LH; + opt_num = 0; + goto common; + + //-------------------- + // LRC圧縮"-lrc"(引数0) + case OPT_LRC: + type = CONV_TYPE_LRC; + opt_num = 0; + goto common; + + //-------------------- + // ハフマン圧縮指定'h'(引数1) + case OPT_HUFFMAN: + type = CONV_TYPE_HUFFMAN; + goto common_opt1; + + //-------------------- + // 圧縮方式指定部分の共通処理 + common_opt1: + if (strlen(argv[i]) == 2) + // 文字列が終了していれば次の引数がパラメータとなる + { + opt_num = 1; + } + else + // 文字列が続いていればその値がパラメータとなる + { + cnv_dat->opt = atoi(&argv[i][2]); + SYNTAX_CHECK(cnv_dat->opt != 0); // パラメータが1以上の数字であるかのチェック + opt_num = 0; + } + // don't break; + common: + SYNTAX_CHECK(cnv_dat->type == CONV_TYPE_NON); // 2重読みチェック + + cnv_dat->type = type; + + if (opt_num == 0) + { + break; + } + + // 引数がある場合 + SYNTAX_CHECK(i + 1 < argc); // パラメータチェック + i++; + SYNTAX_CHECK(argv[i][0] != '-'); // パラメータにハイフンで始まらないかのチェック + + cnv_dat->opt = atoi(argv[i]); + + SYNTAX_CHECK(cnv_dat->opt != 0); // パラメータが1以上の数字であるかのチェック + + break; + + //-------------------- + default: + usage(cmd_string); + return -1; + } + i++; + } + + SYNTAX_CHECK(*in_fname != NULL); // 入力ファイルが指定されたかどうかのチェック + + // 出力ファイル名の自動生成 + if (*out_fname == NULL) + { + *out_fname = make_output_fname(*in_fname, cnv_dat); + } + + if (strcmp(*in_fname, *out_fname) == 0) + { + fprintf(stderr, "ERR: output file %s is same as input file\n", *out_fname); + return -1; + } + + if ( cnv_dat->type == CONV_TYPE_UNCOMPRESS ) + // 解凍オプションが指定された場合のオプションチェック + { + SYNTAX_CHECK(cnv_dat->alignment == 0); // アライメントの指定は不能です + SYNTAX_CHECK(cnv_dat->header_flg == 0); // ヘッダの追加オプションは不能です + } + + return 0; +#undef SYNTAX_CHECK +} + + + +//--------------------------------------------------------------------------- +// 入力ファイルの読み込み関数 +// +// @param fname 入力ファイル名 +// @retval 圧縮情報構造体へのポインタ +// @return 0 ファイルの読み込み成功 +// -1 ファイルの読み込みエラー +//--------------------------------------------------------------------------- + +static int load_file(const char *fname, t_conv_data * cnv_dat) +{ + FILE *in_file; + + // ファイルのオープン + if ((in_file = fopen(fname, "rb")) == NULL) + { + fprintf(stderr, "ERR: could not open file: %s\n", fname); + return -1; + } + + // ファイルのサイズ取得 + fseek(in_file, 0, SEEK_END); + if ((cnv_dat->src_size = ftell(in_file)) < 0) + { + fprintf(stderr, "ERR: file error\n"); + return -1; + } + + // 入力ファイルをバッファへ読み込み + if ((cnv_dat->src_buf = (uchar *) malloc(ROUNDUP4(cnv_dat->src_size))) == NULL) + { + fprintf(stderr, "ERR: memory exhausted\n"); + return -1; + } + + fseek(in_file, 0, SEEK_SET); + if (fread(cnv_dat->src_buf, 1, cnv_dat->src_size, in_file) != cnv_dat->src_size) + { + fprintf(stderr, "ERR: read error\n"); + return -1; + } + + fclose(in_file); + + // 4バイトアラインまでを0で埋める + { + ulong i; + for (i = 0; (cnv_dat->src_size + i) & 3; i++) + { + cnv_dat->src_buf[cnv_dat->src_size + i] = 0; + } + } + + return 0; +} + + +//--------------------------------------------------------------------------- +// 出力ファイルへの書き込み関数 +// +// @param fname 出力ファイル名 +// @retval 圧縮情報構造体へのポインタ +// @return 0 ファイルの書き込み成功 +// -1 ファイルの書き込みエラー +//--------------------------------------------------------------------------- + +static int save_file(const char *fname, const t_conv_data * cnv_dat) +{ + FILE *out_file; + + // 書き込みデータのチェック + if (cnv_dat->dest_buf == NULL || cnv_dat->dest_size < 0) + { + fprintf(stderr, "ERR: convert error\n"); + return -1; + } + + // ファイルのオープン + if ((out_file = fopen(fname, "wb")) == NULL) + { + fprintf(stderr, "ERR: could not open file: %s\n", fname); + return -1; + } + + // バッファを出力ファイルへ書き込み + if (cnv_dat->textout_width != 0) + { + // テキスト出力 + if ( fwrite_text(cnv_dat->dest_buf, cnv_dat->dest_size, cnv_dat->textout_width, out_file, fname) + != cnv_dat->dest_size ) + { + fprintf(stderr, "ERR: write error\n"); + return -1; + } + } + else + { + // バイナリ出力 + if ( fwrite(cnv_dat->dest_buf, 1, cnv_dat->dest_size, out_file) != cnv_dat->dest_size ) + { + fprintf(stderr, "ERR: write error\n"); + return -1; + } + } + + fclose(out_file); + + return 0; +} + +//--------------------------------------------------------------------------- +// C言語フォーマットでのテキスト出力 +// @param buf データバッファ +// @param size データサイズ +// @param fp 出力ファイルポインタ +// @param name 定数ラベル名 +// @return 0 ファイルの書き込み成功 +// -1 ファイルの書き込みエラー +//--------------------------------------------------------------------------- + +static int fwrite_text(const uchar * buf, ulong size, uint width, FILE * fp, const char *name) +{ + char *const_name_buf; + char *const_name_ptr; + ulong i; + ulong line_num; + uint big_endian = width & TEXT_BIG_ENDIAN_FLAG; + + width &= TEXT_WIDTH_MASK; + + if (fp == NULL) + { + return -1; + } + if (buf == NULL) + { + return -1; + } + if (name == NULL) + { + return -1; + } + + // 定数名の決定 + if ((const_name_buf = (char *)malloc( strlen(name) + 1 )) == NULL) + { + fprintf(stderr, "internal err: malloc fail!\n"); + return -1; + } + + strcpy(const_name_buf, name); + const_name_ptr = cut_fname(const_name_buf); + size = (size + width - 1) / width; + + // データの書き込み + if ( fprintf(fp, "#include <%s/types.h>\n\n", INCLUDE_TARGET) < 0 ) + { + return -1; + } + + if ( fprintf(fp, "const u%d %s[0x%lX] = {", width * 8, const_name_ptr, size) < 0 ) + { + return -1; + } + + line_num = (width >= 2) ? 2 : 1; + + for ( i = 0; i < size; i++ ) + { + if ((i * line_num) % 0x10 == 0) + { + if (fprintf(fp, "\n") < 0) + { + return -1; + } + } + switch ( width ) + { + case 1: + { + if ( fprintf(fp, "0x%02X,", *buf) < 0 ) + { + return -1; + } + buf++; + } + break; + case 2: + { + ushort val = *(ushort*)buf; + if ( big_endian ) + { + val = reverse_endian16( val ); + } + + if ( fprintf(fp, "0x%04X,", val) < 0 ) + { + return -1; + } + buf += 2; + } + break; + case 4: + { + ulong val = *(ulong*)buf; + if ( big_endian ) + { + val = reverse_endian32( val ); + } + + if ( fprintf(fp, "0x%08lX,", val) < 0 ) + { + return -1; + } + buf += 4; + } + break; + default: + return -1; + } + } + + if (fprintf(fp, "\n};\n") < 0) + { + return -1; + } + + free(const_name_buf); + + return i * width; +} + + +//--------------------------------------------------------------------------- +// データの変換 +// @retval cnv_dat 変換情報データ +// @return 0 変換成功 +// -1 変換失敗 +//--------------------------------------------------------------------------- + +static int convert_data(t_conv_data * cnv_dat) +{ + char cmd_str[16]; + + // コマンドストリングの生成 + switch (cnv_dat->type) + { + //--------------------- + // 差分フィルタ + case CONV_TYPE_DIFF: + if (cnv_dat->opt != 8 && cnv_dat->opt != 16) + { + fprintf(stderr, "ERR: invalid DIFF parameter %d\n", cnv_dat->opt); + return -1; + }; + sprintf(cmd_str, "d%d", cnv_dat->opt); + break; + + //--------------------- + // ランレングス圧縮 + case CONV_TYPE_RUNLENGTH: + // ランレングス符号化 + strcpy(cmd_str, "r"); + break; + + //--------------------- + // LZ77圧縮 + case CONV_TYPE_LZ77: + if (cnv_dat->opt < 2 || cnv_dat->opt > 255) + { + fprintf(stderr, "ERR: invalid LZ77 parameter %d\n", cnv_dat->opt); + return -1; + }; + sprintf(cmd_str, "l%d", cnv_dat->opt); + break; + + //--------------------- + // LZ77拡張圧縮 + case CONV_TYPE_LZ77EX: + if (cnv_dat->opt < 2 || cnv_dat->opt > 255) + { + fprintf(stderr, "ERR: invalid LZ77 parameter %d\n", cnv_dat->opt); + return -1; + }; + sprintf(cmd_str, "L%d", cnv_dat->opt); + break; + + //--------------------- + // ハフマン圧縮 + case CONV_TYPE_HUFFMAN: + if (cnv_dat->opt != 4 && cnv_dat->opt != 8) + { + fprintf(stderr, "ERR: invalid HUFFMAN parameter %d\n", cnv_dat->opt); + return -1; + }; + sprintf(cmd_str, "h%d", cnv_dat->opt); + break; + + //--------------------- + // レンジコーダ圧縮 + case CONV_TYPE_RANGECODER: + cnv_dat->dest_buf = nitroCompMalloc( cnv_dat->src_size ); + cnv_dat->dest_size = RCACompWrite( cnv_dat->src_buf, cnv_dat->src_size, cnv_dat->dest_buf ); + goto finish; + + //--------------------- + // LZ-ハフマン複合圧縮 + case CONV_TYPE_LH: + cnv_dat->dest_buf = nitroCompMalloc( cnv_dat->src_size ); + cnv_dat->dest_size = LHCompWrite( cnv_dat->src_buf, cnv_dat->src_size, cnv_dat->dest_buf ); + goto finish; + + //--------------------- + // LZ-RangeCoder複合圧縮 + case CONV_TYPE_LRC: + cnv_dat->dest_buf = nitroCompMalloc( cnv_dat->src_size ); + cnv_dat->dest_size = LRCCompWrite( cnv_dat->src_buf, cnv_dat->src_size, cnv_dat->dest_buf ); + goto finish; + + //--------------------- + // 無圧縮 + case CONV_TYPE_NON: + { + cnv_dat->dest_size = cnv_dat->src_size; + cnv_dat->dest_buf = cnv_dat->src_buf; + + // 4バイトアラインチェック + if (cnv_dat->alignment) + { + cnv_dat->dest_size = ROUNDUP(cnv_dat->src_size, cnv_dat->alignment); + } + else if (cnv_dat->textout_width >= 2) + { + cnv_dat->dest_size = ROUNDUP( cnv_dat->src_size, cnv_dat->textout_width & TEXT_WIDTH_MASK ); + } + } + return 0; + + //--------------------- + // 解凍 + case CONV_TYPE_UNCOMPRESS: + { + s32 ret; + if ( cnv_dat->src_size == 4 ) + { + cnv_dat->dest_size = 0; + cnv_dat->dest_buf = NULL; + goto finish; + } + cnv_dat->dest_size = nitroGetDecompFileSize( cnv_dat->src_buf ); + cnv_dat->dest_buf = (void*)malloc( cnv_dat->dest_size + 0x20 ); // アライメント分余分に確保 + memset(cnv_dat->dest_buf, 0, cnv_dat->dest_size + 0x20); + + switch ( *cnv_dat->src_buf & 0xF0 ) + { + case LH_CODE_HEADER: + ret = LHCompRead( cnv_dat->src_buf, cnv_dat->src_size, cnv_dat->dest_buf ); + break; + case LRC_CODE_HEADER: + ret = LRCCompRead( cnv_dat->src_buf, cnv_dat->src_size, cnv_dat->dest_buf ); + break; + case DIFF_CODE_HEADER: + case LZ_CODE_HEADER: + case HUFF_CODE_HEADER: + case RL_CODE_HEADER: + ret = nitroDecompress( cnv_dat->src_buf, cnv_dat->src_size, cnv_dat->dest_buf, 1 ); + break; + default: + ret = -1; + } + if ( ret < 0 ) + { + fprintf(stderr, "ERR: Can't extract this file.\n"); + return -1; + } + } + goto finish; + + default: + fprintf(stderr, "ERR: invalid convert type\n"); + return -1; + } + + // 圧縮後ファイルのバッファ領域確保 + cnv_dat->dest_buf = nitroCompMalloc( cnv_dat->src_size ); + + // データ圧縮 + cnv_dat->dest_size = nitroCompress(cnv_dat->src_buf, + cnv_dat->src_size, + cnv_dat->dest_buf, cmd_str, cnv_dat->header_flg); + // 4バイトアラインチェック + if (cnv_dat->alignment) + { + cnv_dat->dest_size = ROUNDUP(cnv_dat->dest_size, cnv_dat->alignment); + } + +finish: + // テキスト形式出力の型によるアラインチェック + if (cnv_dat->textout_width >= 2) + { + cnv_dat->dest_size = ROUNDUP( cnv_dat->dest_size, cnv_dat->textout_width & TEXT_WIDTH_MASK ); + } + + return 0; +} + + +/*---------------------------------------------------------------------------* + Name: verify_data + + Description: 圧縮データの展開をおこない、元データと一致するかどうか + 確認する。 + + Arguments: cnv_dat 変換情報データ + + Returns: 0 ベリファイOK + -1 ベリファイNG + *---------------------------------------------------------------------------*/ +static int verify_data( t_conv_data * cnv_dat ) +{ + // 圧縮データの展開チェック + char *decomp_buf; + ulong decomp_size; + + if ( ! cnv_dat->verify_flg ) + { + return 0; + } + + decomp_buf = nitroCompMalloc( cnv_dat->src_size ); + decomp_size = nitroDecompress( cnv_dat->dest_buf, cnv_dat->dest_size, decomp_buf, 1 ); + + if ( matchingCheck(cnv_dat->src_buf, cnv_dat->src_size, decomp_buf, decomp_size) == 0 ) + { + fprintf(stderr, "ERR: data convert error\n"); + return -1; + } + return 0; +} + + diff --git a/build/systemMenu_RED/sharedFont/ntrcomp/src/ntcompress_test.c b/build/systemMenu_RED/sharedFont/ntrcomp/src/ntcompress_test.c new file mode 100644 index 00000000..588590d9 --- /dev/null +++ b/build/systemMenu_RED/sharedFont/ntrcomp/src/ntcompress_test.c @@ -0,0 +1,661 @@ +/*---------------------------------------------------------------------------* + Project: NinTendo Compress tool + File: ntcompress_test.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: $ + $Rev$ + $Author$ + *---------------------------------------------------------------------------*/ + +#if defined( _DEBUG ) + +#include +#include +#include +#include +#include +#include "nitroCompLib.h" +#include "multipleCompLib.h" + +#define MAX_DATA_SIZE_EX 0x02000000 // 32MB +#define MAX_DATA_SIZE 0x00100000 // 1MB +//#define MAX_DATA_SIZE 0x100 // 256B + +#define LOOP_CNT 100 +#define LOOP_CNT_EX 4 + +#if 0 +#define TestReport(...) (void)0 +#else +#define TestReport printf +#endif + +#define ASSERT assert + +static u8* s_original; + +static int TestInit( void ); +static void TestLZ8( void ); +static void TestRL8( void ); +static void TestHuffman( void ); + +static struct +{ + u64 x; // 乱数値 + u64 mul; // 乗数 + u64 add; // 加算する数 +} +sRandContext; + +static void InitRand32_(u64 seed) +{ + sRandContext.x = seed; + sRandContext.mul = (1566083941LL << 32) + 1812433253LL; + sRandContext.add = 2531011; +} + +static u32 Rand32_(u32 max) +{ + sRandContext.x = sRandContext.mul * sRandContext.x + sRandContext.add; + + // 引数maxが定数ならばコンパイラにより最適化される。 + if (max == 0) + { + return (u32)(sRandContext.x >> 32); + } + else + { + return (u32)((((sRandContext.x >> 32) * (max >> 1)) >> 32) + (max >> 1)); + } +} + + + +static int compare( const u8 a[], const u8 b[], int size ) +{ + int i; + int equivalent_flag = 1; + for ( i = 0; i < size; ++i ) + { + if ( a[i] != b[i] ) + { + equivalent_flag = 0; + break; + } + } + return equivalent_flag; +} + + +//-------------------------------------------------- +// 元データ生成用関数 +//-------------------------------------------------- + +// LZ,Huffmanで圧縮されやすい元データを作成する。 +static u32 makeOriginalDataForLZHuff( u32 seed ) +{ + u32 fsize; + u32 i; + + TestReport("seed: %u\n", seed); + InitRand32_( seed ); + (void)Rand32_( 0 ); + fsize = Rand32_( MAX_DATA_SIZE ); + if ( fsize <= 4 ) + { + fsize += 5; + } + + for ( i = 0; i < fsize; ++i ) + { + s_original[ i ] = (u8)(Rand32_(0x100) & 0xF0); + } + return fsize; +} + + +// RL圧縮されやすい元データを作成する。 +static u32 makeOriginalDataForRL( u32 seed ) +{ + u8 val; + u32 i; + u32 fsize; + + TestReport("seed: %u\n", seed); + InitRand32_( seed ); + (void)Rand32_( 0 ); + fsize = Rand32_( MAX_DATA_SIZE ); + if ( fsize <= 4 ) + { + fsize += 5; + } + + for ( i = 0; i < fsize; ++i ) + { + if ( (i % 4) == 0 ) + { + val = (u8)Rand32_(0x100); + } + s_original[ i ] = val; + } + return fsize; +} + +// 圧縮されにくい元データを作成する。 +static u32 makeOriginalDataRand( u32 seed ) +{ + u32 i; + u32 fsize; + + TestReport("seed: %u\n", seed); + InitRand32_( seed ); + (void)Rand32_( 0 ); + fsize = Rand32_( MAX_DATA_SIZE ); + if ( fsize <= 4 ) + { + fsize += 5; + } + + for ( i = 0; i < fsize; ++i ) + { + s_original[ i ] = (u8)Rand32_(0x100); + } + return fsize; +} + + +// すべてが単一値の元データを作成する。 +static u32 makeOriginalDataMonoValue( u32 seed ) +{ + u32 i; + u32 fsize; + u8 val; + + TestReport("seed: %u\n", seed); + InitRand32_( seed ); + (void)Rand32_( 0 ); + fsize = Rand32_( MAX_DATA_SIZE ); + if ( fsize <= 4 ) + { + fsize += 5; + } + + val = (u8)Rand32_(0x100); + for ( i = 0; i < fsize; ++i ) + { + s_original[ i ] = val; + } + return fsize; +} + +// LZ,Huffmanで圧縮されやすい元データを作成する。 +static u32 makeOriginalDataForLZHuffEx( u32 seed ) +{ + u32 i; + u32 fsize; + + TestReport("seed: %u\n", seed); + InitRand32_( seed ); + fsize = Rand32_(MAX_DATA_SIZE_EX / 2) + (MAX_DATA_SIZE_EX / 2); + if ( fsize <= 4 ) + { + fsize += 5; + } + + for ( i = 0; i < fsize; ++i ) + { + s_original[ i ] = (u8)(Rand32_(0x100) & 0xF0); + } + return fsize; +} + + +// RL圧縮されやすい元データを作成する。 +static u32 makeOriginalDataForRLEx( u32 seed ) +{ + u32 i; + u32 fsize; + u8 val; + + TestReport("seed: %u\n", seed); + InitRand32_( seed ); + fsize = Rand32_(MAX_DATA_SIZE_EX / 2) + (MAX_DATA_SIZE_EX / 2); + if ( fsize <= 4 ) + { + fsize += 5; + } + + val; + for ( i = 0; i < fsize; ++i ) + { + if ( (i % 4) == 0 ) + { + val = (u8)Rand32_(0x100); + } + s_original[ i ] = val; + } + return fsize; +} + +// 圧縮されにくい元データを作成する。 +static u32 makeOriginalDataRandEx( u32 seed ) +{ + u32 fsize; + u32 i; + + TestReport("seed: %u\n", seed); + InitRand32_( seed ); + fsize = Rand32_(MAX_DATA_SIZE_EX / 2) + (MAX_DATA_SIZE_EX / 2); + if ( fsize <= 4 ) + { + fsize += 5; + } + + for ( i = 0; i < fsize; ++i ) + { + s_original[ i ] = (u8)Rand32_(0x100); + } + return fsize; +} + + +// すべてが単一値の元データを作成する。 +static u32 makeOriginalDataMonoValueEx( u32 seed ) +{ + u32 fsize; + u32 i; + u8 val; + + TestReport("seed: %u\n", seed); + InitRand32_( seed ); + fsize = Rand32_(MAX_DATA_SIZE_EX / 2) + (MAX_DATA_SIZE_EX / 2); + if ( fsize <= 4 ) + { + fsize += 5; + } + + val = (u8)Rand32_(0x100); + for ( i = 0; i < fsize; ++i ) + { + s_original[ i ] = val; + } + return fsize; +} + + + +typedef u32 (*MakeDataFunc)( u32 seed ); +#define WATER_MARK 0xDeadBeaf + + +static void TestLZ8_template( MakeDataFunc makeDataFunc, u32 exFmt, u32 loop_cnt ) +{ + u32 i; + for ( i = 0; i < loop_cnt; ++i ) + { + u32 fsize = makeDataFunc( i ); + + u8* s_compressed = nitroCompMalloc( fsize ); + u8* s_uncompressed = (u8*)malloc( fsize + 4 ); + u32 sz; + + TestReport("size = %d\n", fsize); + if ( ! exFmt ) + { + sz = nitroCompress( s_original, fsize, s_compressed, "l2", 0 ); + } + else + { + sz = nitroCompress( s_original, fsize, s_compressed, "L2", 0 ); + } + + if ( sz == 0 ) + { + TestReport( "compress LZ fail size %d\n", fsize ); + } + else + { + TestReport("compress LZ %d -> %d\n", fsize, sz ); + *(u32*)(&s_uncompressed[ fsize ]) = WATER_MARK; + nitroDecompress( s_compressed, sz, s_uncompressed, 1 ); + ASSERT( compare( s_original, s_uncompressed, (int)fsize ) ); + // 展開時のオーバーアクセスをチェック + ASSERT( *(u32*)(&s_uncompressed[ fsize ]) == WATER_MARK ); + } + + nitroCompFree( s_compressed ); + free( s_uncompressed ); + } +} + +static void TestRL8_template( MakeDataFunc makeDataFunc, u32 loop_cnt ) +{ + u32 i; + for ( i = 0; i < loop_cnt; ++i ) + { + u32 fsize = makeDataFunc( i ); + + u8* s_compressed = nitroCompMalloc( fsize ); + u8* s_uncompressed = (u8*)malloc( fsize + 4 ); + u32 sz; + + TestReport("size = %d\n", fsize); + sz = nitroCompress( s_original, fsize, s_compressed, "r", 0 ); + + if ( sz == 0 ) + { + TestReport( "compress RL fail size %d\n", fsize ); + } + else + { + TestReport("compress RL %d -> %d\n", fsize, sz ); + *(u32*)(&s_uncompressed[ fsize ]) = WATER_MARK; + nitroDecompress( s_compressed, sz, s_uncompressed, 1 ); + ASSERT( compare(s_original, s_uncompressed, (int)fsize) ); + // 展開時のオーバーアクセスをチェック + ASSERT( *(u32*)(&s_uncompressed[ fsize ]) == WATER_MARK ); + } + + nitroCompFree( s_compressed ); + free( s_uncompressed ); + } +} + +static void TestHuffman_template( MakeDataFunc makeDataFunc, u8 encBit, u32 loop_cnt ) +{ + u32 i; + for (i = 0; i < loop_cnt; ++i ) + { + u32 fsize = makeDataFunc( i ); + u8* s_compressed = nitroCompMalloc( fsize ); + u8* s_uncompressed = (u8*)malloc( ((fsize + 0x3) & ~0x3) + 4 ); + u32 sz; + + TestReport("size = %d\n", fsize); + sz = nitroCompress( s_original, fsize, s_compressed, (encBit == 4 )? "h4" : "h8", 0 ); + + if ( sz == 0 ) + { + TestReport( "compress Huff fail size %d\n", fsize ); + } + else + { + TestReport("compress Huff %d -> %d\n", fsize, sz ); + // ハフマン展開では末尾の4バイトアラインまではオーバーアクセスされ得る + // コードになっている + *(u32*)(&s_uncompressed[ (fsize + 0x3) & ~0x3 ]) = WATER_MARK; + nitroDecompress( s_compressed, sz, s_uncompressed, 1 ); + ASSERT( compare(s_original, s_uncompressed, (int)fsize) ); + // 展開時のオーバーアクセスをチェック + ASSERT( *(u32*)(&s_uncompressed[ (fsize + 0x3) & ~0x3 ]) == WATER_MARK ); + } + nitroCompFree( s_compressed ); + free( s_uncompressed ); + } +} + + +static void TestLH8_template( MakeDataFunc makeDataFunc, u32 exFmt, u32 loop_cnt ) +{ + u32 i; + for ( i = 0; i < loop_cnt; ++i ) + { + u32 fsize = makeDataFunc( i ); + + u8* s_compressed = nitroCompMalloc( fsize ); + u8* s_uncompressed = (u8*)malloc( fsize + 4 ); + u32 sz; + + TestReport("size = %d\n", fsize); + sz = LHCompWrite( s_original, fsize, s_compressed ); + + if ( sz == 0 ) + { + TestReport( "compress LH fail size %d\n", fsize ); + } + else + { + TestReport("compress LH %d -> %d\n", fsize, sz ); + *(u32*)(&s_uncompressed[ fsize ]) = WATER_MARK; + LHCompRead( s_compressed, sz, s_uncompressed ); + ASSERT( compare(s_original, s_uncompressed, (int)fsize) ); + // 展開時のオーバーアクセスをチェック + ASSERT( *(u32*)(&s_uncompressed[ fsize ]) == WATER_MARK ); + } + + nitroCompFree( s_compressed ); + free( s_uncompressed ); + } +} + + +static void TestLRC8_template( MakeDataFunc makeDataFunc, u32 exFmt, u32 loop_cnt ) +{ + u32 i; + for ( i = 0; i < loop_cnt; ++i ) + { + u32 fsize = makeDataFunc( i ); + + u8* s_compressed = nitroCompMalloc( fsize ); + u8* s_uncompressed = (u8*)malloc( fsize + 4 ); + u32 sz; + + TestReport("size = %d\n", fsize); + sz = LRCCompWrite( s_original, fsize, s_compressed ); + + if ( sz == 0 ) + { + TestReport( "compress LRC fail size %d\n", fsize ); + } + else + { + TestReport("compress LRC %d -> %d\n", fsize, sz ); + *(u32*)(&s_uncompressed[ fsize ]) = WATER_MARK; + LRCCompRead( s_compressed, sz, s_uncompressed ); + ASSERT( compare(s_original, s_uncompressed, (int)fsize) ); + // 展開時のオーバーアクセスをチェック + ASSERT( *(u32*)(&s_uncompressed[ fsize ]) == WATER_MARK ); + } + + nitroCompFree( s_compressed ); + free( s_uncompressed ); + } +} + + +//-------------------------------------------------- +// テストメイン +//-------------------------------------------------- + +static int TestInit( void ) +{ + time_t t; + s_original = (u8*)malloc( MAX_DATA_SIZE_EX + 4 ); + makeOriginalDataRand( (u32)time( &t ) ); + + return 0; +} + + +static void TestLZ8_normal( void ) +{ + TestLZ8_template( makeOriginalDataForLZHuff, 0, LOOP_CNT ); +} +static void TestLZ8_random( void ) +{ + TestLZ8_template( makeOriginalDataRand, 0, LOOP_CNT ); +} +static void TestLZ8_mono( void ) +{ + TestLZ8_template( makeOriginalDataMonoValue, 0, LOOP_CNT ); +} + +static void TestLZ8EX_normal( void ) +{ + TestLZ8_template( makeOriginalDataForLZHuff, 1, LOOP_CNT ); +} +static void TestLZ8EX_random( void ) +{ + TestLZ8_template( makeOriginalDataRand, 1, LOOP_CNT ); +} +static void TestLZ8EX_mono( void ) +{ + TestLZ8_template( makeOriginalDataMonoValue, 1, LOOP_CNT ); +} + + +static void TestRL8_normal( void ) +{ + TestRL8_template( makeOriginalDataForRL, LOOP_CNT ); +} +static void TestRL8_random( void ) +{ + TestRL8_template( makeOriginalDataRand, LOOP_CNT ); +} +static void TestRL8_mono( void ) +{ + TestRL8_template( makeOriginalDataMonoValue, LOOP_CNT ); +} + +static void TestHuffman8_normal( void ) +{ + TestHuffman_template( makeOriginalDataForLZHuff, 8, LOOP_CNT ); +} +static void TestHuffman8_random( void ) +{ + TestHuffman_template( makeOriginalDataRand, 8, LOOP_CNT ); +} +static void TestHuffman8_mono( void ) +{ + TestHuffman_template( makeOriginalDataMonoValue, 8, LOOP_CNT ); +} + +static void TestHuffman4_normal( void ) +{ + TestHuffman_template( makeOriginalDataForLZHuff, 4, LOOP_CNT ); +} +static void TestHuffman4_random( void ) +{ + TestHuffman_template( makeOriginalDataRand, 4, LOOP_CNT ); +} +static void TestHuffman4_mono( void ) +{ + TestHuffman_template( makeOriginalDataMonoValue, 4, LOOP_CNT ); +} + +static void TestLZ_ex(void) +{ + TestLZ8_template( makeOriginalDataForLZHuffEx, 0, LOOP_CNT_EX ); +} + +static void TestLZEX_ex(void) +{ + TestLZ8_template( makeOriginalDataForLZHuffEx, 1, LOOP_CNT_EX ); +} + +static void TestRL_ex(void) +{ + TestRL8_template( makeOriginalDataForRLEx, LOOP_CNT_EX ); +} + +static void TestHuffman8_ex(void) +{ + TestHuffman_template( makeOriginalDataForLZHuffEx, 8, LOOP_CNT_EX ); +} + +static void TestHuffman4_ex(void) +{ + TestHuffman_template( makeOriginalDataForLZHuffEx, 4, LOOP_CNT_EX ); +} + +static void TestLH_normal(void) +{ + TestLH8_template( makeOriginalDataForLZHuff, 0, LOOP_CNT ); +} + +static void TestLH_random(void) +{ + TestLH8_template( makeOriginalDataRand, 0, LOOP_CNT ); +} + +static void TestLH_mono(void) +{ + TestLH8_template( makeOriginalDataMonoValue, 0, LOOP_CNT ); +} + +static void TestLRC_normal(void) +{ + TestLRC8_template( makeOriginalDataForLZHuff, 0, LOOP_CNT ); +} + +static void TestLRC_random(void) +{ + TestLRC8_template( makeOriginalDataRand, 0, LOOP_CNT ); +} + +static void TestLRC_mono(void) +{ + TestLRC8_template( makeOriginalDataMonoValue, 0, LOOP_CNT ); +} + +#endif /* defined( _DEBUG ) */ + + + + + +/*---------------------------------------------------------------------------* + Name: ntcompress_test + + Description: テストメイン + + Arguments: None. + + Returns: None. + *---------------------------------------------------------------------------*/ +void ntcompress_test( void ) +{ +#if defined( _DEBUG ) + TestInit(); + + TestLH_normal(); + TestLH_random(); + TestLH_mono (); + TestLRC_normal(); + TestLRC_random(); + TestLRC_mono (); + + TestLZ8EX_normal(); + TestLZ8EX_random(); + TestLZ8EX_mono (); + TestLZ8_normal(); + TestLZ8_random(); + TestLZ8_mono (); + TestRL8_normal(); + TestRL8_random(); + TestRL8_mono (); + TestHuffman8_normal(); + TestHuffman8_random(); + TestHuffman8_mono (); + TestHuffman4_normal(); + TestHuffman4_random(); + TestHuffman4_mono (); + TestLZ_ex(); + TestLZEX_ex(); + TestRL_ex(); + TestHuffman8_ex(); + TestHuffman4_ex(); + +#endif +} + + + diff --git a/build/systemMenu_RED/sharedFont/ntrcomp/src/ntcompress_test.h b/build/systemMenu_RED/sharedFont/ntrcomp/src/ntcompress_test.h new file mode 100644 index 00000000..1167a58d --- /dev/null +++ b/build/systemMenu_RED/sharedFont/ntrcomp/src/ntcompress_test.h @@ -0,0 +1,23 @@ +/*---------------------------------------------------------------------------* + Project: NinTendo Compress tool + File: ntcompress_test.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: $ + $Rev$ + $Author$ + *---------------------------------------------------------------------------*/ +#ifndef NTCOMPRESS_TEST_H__ +#define NTCOMPRESS_TEST_H__ + +void ntcompress_test( void ); + +#endif // NTCOMPRESS_TEST_H__ + diff --git a/build/systemMenu_RED/sharedFont/ntrcomp/src/rangeCoder.c b/build/systemMenu_RED/sharedFont/ntrcomp/src/rangeCoder.c new file mode 100644 index 00000000..193baa9c --- /dev/null +++ b/build/systemMenu_RED/sharedFont/ntrcomp/src/rangeCoder.c @@ -0,0 +1,539 @@ +/*---------------------------------------------------------------------------* + Project: NinTendo Compress tool + File: rangeCoder.c + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: $ + $Rev$ + $Author$ + *---------------------------------------------------------------------------*/ + +#include "rangeCoder.h" + +typedef struct +{ + u32 freq[ 0x100 ]; + u32 low_cnt[ 0x100 ]; + u32 total; +} +RCCompressionInfo; + +// レンジコーダ状態構造体 +typedef struct +{ + u32 low; + u32 range; + u32 code; // 展開時のみ使用 + u8 carry; // 圧縮時のみ使用 + u32 carry_cnt; // 圧縮時のみ使用 +} +RCState; + + +#define MAX_RANGE 0x80000000 + +/*---------------------------------------------------------------------------* + Name: RCInitInfo_ + Description: + Arguments: info + Returns: None. + *---------------------------------------------------------------------------*/ +static void +RCInitInfo_( RCCompressionInfo* info, BOOL adaptive ) +{ + u32 i; + + if ( adaptive ) + // 適応型レンジコーダ + { + for ( i = 0; i < 0x100; i++ ) + { + info->freq[ i ] = 1; + info->low_cnt[ i ] = i; + } + info->total = 0x100; + } + else + // 静的レンジコーダ + { + for ( i = 0; i < 0x100; i++ ) + { + info->freq[ i ] = 0; + info->low_cnt[ i ] = 0; + } + info->total = 0; + } +} + + +/*---------------------------------------------------------------------------* + Name: RCCount_ + Description: + Arguments: info + srcp + size + Returns: None. + *---------------------------------------------------------------------------*/ +static void +RCCount_( RCCompressionInfo* info, const u8* srcp, u32 size ) +{ + u32 srcCnt = 0; + u32 i; + + while ( srcCnt < size ) + { + info->freq[ srcp[ srcCnt ] ]++; + info->total++; + srcCnt++; + } + + // 0x10000へ正規化 + #define NORMAL_FREQ 0x10000 + { + f32 rate = (f32)NORMAL_FREQ / info->total; + u32 max_i = 0; + u32 max_freq = 0; + + info->total = 0; + for ( i = 0; i < 0x100; i++ ) + { + u32 orig = info->freq[ i ]; + info->freq[ i ] = (u32)(rate * info->freq[ i ] + 0.5f); + if ( orig != 0 && info->freq[ i ] == 0 ) + { + info->freq[ i ] = 1; + } + + info->total += info->freq[ i ]; + if ( info->freq[ i ] >= max_freq ) + { + max_i = i; + max_freq = info->freq[ i ]; + } + } + if ( info->total > NORMAL_FREQ ) + { + info->freq[ max_i ] -= (info->total - NORMAL_FREQ); + } + else + { + info->freq[ max_i ] += (NORMAL_FREQ - info->total); + } + info->total = NORMAL_FREQ; + } + #undef NORMAL_FREQ + + info->low_cnt[ 0 ] = 0; + + for ( i = 1; i < 0x100; i++ ) + { + info->low_cnt[ i ] = info->low_cnt[ i - 1 ] + info->freq[ i - 1 ]; + } +} + + + +/*---------------------------------------------------------------------------* + Name: RCAAddCount_ + + Description: 適応型レンジコーダの頻度テーブルを更新します。 + + Arguments: info + val + + Returns: None. + *---------------------------------------------------------------------------*/ +static void +RCAAddCount_( RCCompressionInfo* info, u8 val ) +{ + u32 i; + + info->freq[ val ]++; + info->total++; + for ( i = val + 1; i < 0x100; i++ ) + { + info->low_cnt[ i ]++; + } + + // トータルが最大値を越えた場合には、再構成する。 + if ( info->total >= 0x00010000 ) + { + if ( info->freq[ 0 ] > 1 ) + { + info->freq[ 0 ] = info->freq[ 0 ] / 2; + } + info->low_cnt[ 0 ] = 0; + info->total = info->freq[ 0 ]; + + for ( i = 1; i < 0x100; i++ ) + { + if ( info->freq[ i ] > 1 ) + { + info->freq[ i ] >>= 1; + } + info->low_cnt[ i ] = info->low_cnt[ i - 1 ] + info->freq[ i - 1 ]; + info->total += info->freq[ i ]; + } + } +} + + +/*---------------------------------------------------------------------------* + Name: RCCompWrite + + Description: レンジコーダの圧縮(テーブル型) + + Arguments: srcp + size + dstp + + Returns: + *---------------------------------------------------------------------------*/ +s32 +RCCompWrite_( const u8* srcp, u32 size, u8* dstp, BOOL adaptive ) +{ +#define MIN_RANGE 0x01000000 + static RCCompressionInfo sInfo; + u32 srcCnt = 0; + u32 dstCnt = 0; + u32 low, range; + u8 carry; + u32 carry_cnt; + u32 i; + + // ワークの初期化 + RCInitInfo_( &sInfo, adaptive ); + + if ( ! adaptive ) + { + // 出現頻度を計算 + RCCount_( &sInfo, srcp, size ); + } + + // 開始Rangeが0x80000000なので、初回いきなり桁上げが発生することはない + low = 0; + range = MAX_RANGE; + carry = 0; + carry_cnt = 0; + + // ヘッダ出力 + dstp[ dstCnt++ ] = LRC_CODE_HEADER; + if ( size > 0x1000000 ) + { + dstp[ dstCnt++ ] = 0; + dstp[ dstCnt++ ] = 0; + dstp[ dstCnt++ ] = 0; + } + dstp[ dstCnt++ ] = (u8)( size ); + dstp[ dstCnt++ ] = (u8)( size >> 8 ); + dstp[ dstCnt++ ] = (u8)( size >> 16 ); + if ( size > 0x1000000 ) + { + dstp[ dstCnt++ ] = (u8)( size >> 24 ); + } + + if ( ! adaptive ) + { + // 頻度テーブルの出力(16bitリトルエンディアン) + for ( i = 0; i < 0x100; i++ ) + { + dstp[ dstCnt++ ] = (u8)( sInfo.freq[ i ] ); + dstp[ dstCnt++ ] = (u8)( sInfo.freq[ i ] >> 8 ); + } + } + + // コードの出力 + while ( srcCnt < size ) + { + u8 val = srcp[ srcCnt++ ]; + u32 temp = range / sInfo.total; + u32 prevLow = low; + low = low + sInfo.low_cnt[ val ] * temp; + range = sInfo.freq[ val ] * temp; + + if ( adaptive ) + { + // 出現頻度テーブルを更新 + RCAAddCount_( &sInfo, val ); + } + + // 桁上がりが発生する場合の処理 + if ( prevLow > low ) + { + // キャリーを1繰上げ + ++carry; + // キャリーと(キャリーカウンタ - 1)分の0x00を出力します。 + if ( carry_cnt > 1 ) + { + dstp[ dstCnt++ ] = carry; + --carry_cnt; + carry = 0x00; + } + while ( carry_cnt > 1 ) + { + dstp[ dstCnt++ ] = 0x00; + --carry_cnt; + } + } + + // Rangeの上位1バイトが空になったら桁上げ + while ( range < MIN_RANGE ) + { + u8 candidate = (u8)( low >> 24 ); + + // 次のキャリーが0xFFの場合は更に桁上げがあり得るのでcarryを出力せずにcarry_cntだけ増やす + if ( candidate == 0xFF ) + { + ++carry_cnt; + } + else + // 次のキャリーが0xFFではない場合はcarryを出力する + { + // carryと(carry_cnt - 1)分の0xFFを出力する + if ( carry_cnt > 0 ) + { + dstp[ dstCnt++ ] = carry; + --carry_cnt; + } + while ( carry_cnt > 0 ) + { + dstp[ dstCnt++ ] = 0xFF; + --carry_cnt; + } + // 新しいcarryに置き換え + carry = candidate; + carry_cnt = 1; + } + low <<= 8; + range <<= 8; + } + } + + if ( carry_cnt > 0 ) + { + dstp[ dstCnt++ ] = carry; + --carry_cnt; + } + while ( carry_cnt > 0 ) + { + dstp[ dstCnt++ ] = 0xFF; + --carry_cnt; + } + dstp[ dstCnt++ ] = (u8)( (low >> 24) & 0xFF ); + dstp[ dstCnt++ ] = (u8)( (low >> 16) & 0xFF ); + dstp[ dstCnt++ ] = (u8)( (low >> 8 ) & 0xFF ); + dstp[ dstCnt++ ] = (u8)( (low >> 0 ) & 0xFF ); + + return dstCnt; +#undef MIN_RANGE +} + + +/*---------------------------------------------------------------------------* + Name: RCACompWrite + + Description: レンジコーダの圧縮(適応型) + + Arguments: srcp + size + dstp + + Returns: + *---------------------------------------------------------------------------*/ +s32 +RCCompWrite( const u8* srcp, u32 size, u8* dstp ) +{ + return RCCompWrite_( srcp, size, dstp, FALSE ); +} + +s32 +RCACompWrite( const u8* srcp, u32 size, u8* dstp ) +{ + return RCCompWrite_( srcp, size, dstp, TRUE ); +} + +static u8 +SearchRC_( RCCompressionInfo* info, u32 code, u32 range, u32 low ) +{ +#define TABLE_SIZE 0x100 + u32 codeVal = code - low; + u32 i; + u32 temp = range / info->total; + u32 tempVal = codeVal / temp; + +#if 0 + // TODO: とりあえず線形探索、二分探索にするべき + for ( i = 0; i < TABLE_SIZE - 1; i++ ) + { + if ( info->low_cnt[ i + 1 ] > tempVal ) + { + while ( info->freq[ i ] == 0 ) + { + --i; + } + return (u8)i; + } + } + return TABLE_SIZE - 1; +#else + // 二分探索 + u32 left = 0; + u32 right = TABLE_SIZE - 1; + + while ( left < right ) + { + i = (left + right) / 2; + + if ( info->low_cnt[ i ] > tempVal ) + { + right = i; + } + else + { + left = i + 1; + } + } + + i = left; + while ( info->low_cnt[ i ] > tempVal ) + { + --i; + } + return (u8)i; +#endif +} + +/*---------------------------------------------------------------------------* + Name: RCCompRead + + Description: レンジコーダ展開(テーブル型) + + Arguments: srcp + size + dstp + + Returns: + *---------------------------------------------------------------------------*/ +s32 +RCCompRead_( const u8* srcp, u32 srcSize, u8* dstp, BOOL adaptive ) +{ +#define MIN_RANGE 0x01000000 + static RCCompressionInfo sInfo; + + u32 dstSize = *(u32*)srcp >> 8; + u32 dstCnt = 0; + u32 srcCnt = 0; + u32 i; + u32 code, range, low; + + if ( srcSize < 4 ) + { + return -1; + } + + srcCnt = 4; + if ( dstSize == 0 ) + { + dstSize = *(u32*)&srcp[ srcCnt ]; + srcCnt += 4; + if ( srcSize < 8 ) + { + return -1; + } + } + + if ( srcSize < sizeof(u16) * 0x100 + srcCnt ) + { + return -1; + } + + // ワークの初期化 + if ( adaptive ) + { + RCInitInfo_( &sInfo, TRUE ); + } + else + { + sInfo.total = 0; + for ( i = 0; i < 0x100; i++ ) + { + sInfo.freq[ i ] = *(u16*)&srcp[ srcCnt ]; + srcCnt += 2; + sInfo.total += sInfo.freq[ i ]; + } + sInfo.low_cnt[ 0 ] = 0; + for ( i = 1; i < 0x100; i++ ) + { + sInfo.low_cnt[ i ] = sInfo.low_cnt[ i - 1 ] + sInfo.freq[ i - 1 ]; + } + } + + code = (u32)((srcp[ srcCnt ] << 24) | (srcp[ srcCnt + 1 ] << 16) | + (srcp[ srcCnt + 2 ] << 8) | (srcp[ srcCnt + 3 ])); + range = MAX_RANGE; + low = 0; + srcCnt += 4; + + while ( dstCnt < dstSize ) + { + u8 val = SearchRC_( &sInfo, code, range, low ); + + dstp[ dstCnt++ ] = val; + + { + u32 tmp; + tmp = range / sInfo.total; + low = low + sInfo.low_cnt[ val ] * tmp; + range = sInfo.freq[ val ] * tmp; + } + + if ( adaptive ) + { + // 出現頻度テーブルを更新 + RCAAddCount_( &sInfo, val ); + } + + while ( range < MIN_RANGE ) + { + code <<= 8; + code += srcp[ srcCnt++ ]; + range <<= 8; + low <<= 8; + } + } + + return dstSize; +#undef MIN_RANGE +} + + +/*---------------------------------------------------------------------------* + Name: RCACompRead + + Description: レンジコーダ展開(適応型) + + Arguments: srcp + size + dstp + + Returns: + *---------------------------------------------------------------------------*/ +s32 +RCCompRead( const u8* srcp, u32 srcSize, u8* dstp ) +{ + return RCCompRead_( srcp, srcSize, dstp, FALSE ); +} + +s32 +RCACompRead( const u8* srcp, u32 srcSize, u8* dstp ) +{ + return RCCompRead_( srcp, srcSize, dstp, TRUE ); +} + diff --git a/build/systemMenu_RED/sharedFont/ntrcomp/src/rangeCoder.h b/build/systemMenu_RED/sharedFont/ntrcomp/src/rangeCoder.h new file mode 100644 index 00000000..53808e5a --- /dev/null +++ b/build/systemMenu_RED/sharedFont/ntrcomp/src/rangeCoder.h @@ -0,0 +1,46 @@ +/*---------------------------------------------------------------------------* + Project: NinTendo Compress tool + File: rangeCoder.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: $ + $Rev$ + $Author$ + *---------------------------------------------------------------------------*/ + +#include "types.h" + +#ifndef __NTCOMPRESS_RANGE_CODER_H__ +#define __NTCOMPRESS_RANGE_CODER_H__ + +/*---------------------------------------------------------------------------* + Name: RCCompWrite + + Description: レンジコーダの圧縮 + + Arguments: srcp + size + dstp + + Returns: + *---------------------------------------------------------------------------*/ +s32 +RCCompWrite( const u8* srcp, u32 size, u8* dstp ); + +s32 +RCCompRead( const u8* srcp, u32 srcSize, u8* dstp ); + +s32 +RCACompWrite( const u8* srcp, u32 size, u8* dstp ); + +s32 +RCACompRead( const u8* srcp, u32 srcSize, u8* dstp ); + +#endif // __NTCOMPRESS_RANGE_CODER_H__ diff --git a/build/systemMenu_RED/sharedFont/ntrcomp/src/types.h b/build/systemMenu_RED/sharedFont/ntrcomp/src/types.h new file mode 100644 index 00000000..7d76786b --- /dev/null +++ b/build/systemMenu_RED/sharedFont/ntrcomp/src/types.h @@ -0,0 +1,57 @@ +/*---------------------------------------------------------------------------* + Project: NinTendo Compress tool + File: types.h + + Copyright 2007 Nintendo. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Date:: $ + $Rev$ + $Author$ + *---------------------------------------------------------------------------*/ + +#ifndef __NTCOMPRESS_TYPES_H__ +#define __NTCOMPRESS_TYPES_H__ + +//=========================================================================================== +// 型定義 +//=========================================================================================== +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned long u32; +typedef signed char s8; +typedef signed short s16; +typedef signed long s32; +typedef float f32; +typedef unsigned long BOOL; +#define FALSE 0 +#define TRUE 1 + +#define DIFF_CODE_HEADER (0x80) +#define LZ_CODE_HEADER (0x10) +#define HUFF_CODE_HEADER (0x20) +#define RL_CODE_HEADER (0x30) +#define LH_CODE_HEADER (0x40) +#define LRC_CODE_HEADER (0x50) +#define CODE_HEADER_MASK (0xF0) + + +#if defined( __GNUC__ ) + typedef unsigned long long int u64; + typedef signed long long int s64; + #define INLINE inline + #define ASSERT(x) (void)0 +#else + #include + #define INLINE __inline + #define ASSERT assert + typedef unsigned __int64 u64; + typedef signed __int64 s64; +#endif + +#endif // __NTCOMPRESS_TYPES_H__ diff --git a/build/systemMenu_RED/sharedFont/ntrcomp/vc++/ntcompress.sln b/build/systemMenu_RED/sharedFont/ntrcomp/vc++/ntcompress.sln new file mode 100644 index 00000000..541afd75 --- /dev/null +++ b/build/systemMenu_RED/sharedFont/ntrcomp/vc++/ntcompress.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ntcompress", "ntcompress.vcproj", "{FA042C1E-4CB1-4B8A-87A6-B46B4222605E}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {FA042C1E-4CB1-4B8A-87A6-B46B4222605E}.Debug.ActiveCfg = Debug|Win32 + {FA042C1E-4CB1-4B8A-87A6-B46B4222605E}.Debug.Build.0 = Debug|Win32 + {FA042C1E-4CB1-4B8A-87A6-B46B4222605E}.Release.ActiveCfg = Release|Win32 + {FA042C1E-4CB1-4B8A-87A6-B46B4222605E}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/build/systemMenu_RED/sharedFont/ntrcomp/vc++/ntcompress.vcproj b/build/systemMenu_RED/sharedFont/ntrcomp/vc++/ntcompress.vcproj new file mode 100644 index 00000000..050e7b60 --- /dev/null +++ b/build/systemMenu_RED/sharedFont/ntrcomp/vc++/ntcompress.vcproj @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/bin/genFontTable.plx b/tools/bin/genFontTable.plx index 7db4a83c..9bd7efbb 100644 --- a/tools/bin/genFontTable.plx +++ b/tools/bin/genFontTable.plx @@ -1,9 +1,9 @@ #!/usr/bin/perl ###################################################################### -# genFontTable.pl +# genFontTable.pl # -# generate Secure Shared Font Data Table +# generate Secure Shared Font Data Table # # [[ HEADER FORMAT ]] # security code (128 bytes) : RSA signature of Header @@ -16,7 +16,7 @@ # # Font info table ( 64 bytes * number) # fileName ( 32 bytes) : font file name -# padding ( 4 bytes) : +# comp_length ( 4 bytes) : length of the compression file # file offset ( 4 bytes) : file offset of Font data # length ( 4 bytes) : length of file (bytes) # digest ( 20 bytes) : SHA1 digest of Font data @@ -39,16 +39,28 @@ my $headerFile = "header.bin"; my $digestFile = "sha1.bin"; my $signFile = "sign.bin"; my $tempFile = "temp.bin"; +my $compprog = "./compBLZ_modified/bin/compBLZ.exe"; # 圧縮プログラム +my $compoption = "-e \"\""; +#my $compprog = "./ntrcomp/gcc/ntrcomp.exe"; +#my $compoption = "-h8 -A32 -s"; # 後始末 sub deleteTemp { - system ("rm -f $infoFile"); - system ("rm -f $headerFile"); - system ("rm -f $digestFile"); - system ("rm -f $signFile"); - system ("rm -f $tempFile"); + system ("rm -f $infoFile"); + system ("rm -f $headerFile"); + system ("rm -f $digestFile"); + system ("rm -f $signFile"); + system ("rm -f $tempFile"); } +# 環境変数からSDKのルートをサーチして(ntrcompの場所特定に必要) +#foreach ( sort keys ( %ENV ) ){ +# if ($_ =~ m/TWLSDK_ROOT/s) { +# $SDKROOT = $ENV{$_}; +# } +#} +#printf "TWLSDK_ROOT is ${SDKROOT}\n"; + my $signSize = 0x80; my $headerSize = 0x20; my @files; @@ -56,113 +68,127 @@ my @files; # 要素数算出 my $num = 0; foreach ( @ARGV ) { - next if( $_ eq $ARGV[0] ); - $files[ $num ] = $_; - $num++; + next if( $_ eq $ARGV[0] ); + $files[ $num ] = $_; + $num++; } # 情報テーブルの出力 { - my $elementSize = 0x40; - my $fileNameMax = 0x20; - my $padLen = 0x04; - # offset length = 0x04; - # file length = 0x04; - my $sha1Len = 0x14; - - open INFO, ">$infoFile" or die; - binmode INFO; - - # オフセット算出 - my $offset = $signSize + $headerSize + $num * $elementSize; - if( ( $offset % 32 ) > 0 ) { $offset += 32 - ( $offset % 32 ); } - - foreach ( @files ) { - # NULL指定時は、NULL出力 - if( "NULL" eq basename( $_ ) ) { - syswrite( INFO, pack( "x$elementSize") ); - next; - } - - # ファイルネームの出力 - if( !( -e $_ ) ) { - close( INFO ); - deleteTemp(); - die "file not exist. : $_\n"; - } - my $name = basename( $_ ); - if( length $name >= $fileNameMax ) { - close( INFO ); - deleteTemp(); - die "file name length must be smaller than $fileNameMax. : $_\n"; - } - my $data = pack( "a$fileNameMax", $name ); - syswrite( INFO, $data, $fileNameMax ); - - # パディングの出力 - syswrite( INFO, pack( "x$padLen") ); - - # ファイルオフセットの出力 - $data = pack( "L", $offset ); - syswrite( INFO, $data, 4 ); - - # ファイル長の出力 - $data = pack( "L", -s $_ ); - syswrite( INFO, $data, 4 ); - - # ファイルのSHA1ハッシュの出力 - { - my $digest; - system ("openssl dgst -sha1 -binary -out $digestFile $_"); - open DIGEST, $digestFile or die; - binmode DIGEST; - sysread( DIGEST, $digest, $sha1Len ); - close DIGEST; - syswrite( INFO, $digest, $sha1Len ); - } - - printf "%s\t0x%08x\t0x%08x\n", $_, $offset, -s $_; - - # オフセット加算 - $offset += -s $_; - if( ( $offset % 32 ) > 0 ) { $offset += 32 - ( $offset % 32 ); } - } - close INFO; + my $elementSize = 0x40; + my $fileNameMax = 0x20; + my $padLen = 0x04; + # offset length = 0x04; + # file length = 0x04; + my $sha1Len = 0x14; + + open INFO, ">$infoFile" or die; + binmode INFO; + + # オフセット算出 + my $offset = $signSize + $headerSize + $num * $elementSize; + if( ( $offset % 32 ) > 0 ) { $offset += 32 - ( $offset % 32 ); } + + printf "---------------------------------------------------------------\n"; + printf "filename\toffset \torig_size\tcompressed_size\n"; + foreach ( @files ) { + # NULL指定時は、NULL出力 + if( "NULL" eq basename( $_ ) ) { + syswrite( INFO, pack( "x$elementSize") ); + next; + } + + # ファイルネームの出力 + if( !( -e $_ ) ) { + close( INFO ); + deleteTemp(); + die "file not exist. : $_\n"; + } + my $name = basename( $_ ); + if( length $name >= $fileNameMax ) { + close( INFO ); + deleteTemp(); + die "file name length must be smaller than $fileNameMax. : $_\n"; + } + my $data = pack( "a$fileNameMax", $name ); + syswrite( INFO, $data, $fileNameMax ); + + # 圧縮 + # SDKのcompBLZ.exeは引数バグがあるためローカルに修正版を入れておく + my $compfile = "$_.comp"; + system ("${compprog} $compoption $_ -o $compfile"); + + # パディングの出力 + #syswrite( INFO, pack( "x$padLen") ); + + # 圧縮ファイル長を出力(もともとはpadding) + $data = pack( "L", -s $compfile ); + syswrite( INFO, $data, 4 ); + + # ファイルオフセットの出力 + $data = pack( "L", $offset ); + syswrite( INFO, $data, 4 ); + + # ファイル長の出力 + $data = pack( "L", -s $_ ); + syswrite( INFO, $data, 4 ); + + # ファイルのSHA1ハッシュの出力 + { + my $digest; + #system ("openssl dgst -sha1 -binary -out $digestFile $_"); + system ("openssl dgst -sha1 -binary -out $digestFile $compfile"); # 圧縮後のファイルにハッシュをつける + open DIGEST, $digestFile or die; + binmode DIGEST; + sysread( DIGEST, $digest, $sha1Len ); + close DIGEST; + syswrite( INFO, $digest, $sha1Len ); + } + + printf "%s\t0x%08x\t0x%08x\t0x%08x\n", $_, $offset, -s $_, -s $compfile; + + # オフセット加算 + #$offset += -s $_; + $offset += -s $compfile; + if( ( $offset % 32 ) > 0 ) { $offset += 32 - ( $offset % 32 ); } + } + close INFO; + printf "---------------------------------------------------------------\n"; } # ヘッダの出力 { - # timestampLen = 0x08; - # elementNumLen = 0x02; - my $padLen = 0x06; - my $sha1Len = 0x14; - - open HEADER, ">$headerFile" or die; - binmode HEADER; - - # タイムスタンプの出力 -# my $timestamp = strftime "%y%m%d%H", localtime; - my $timestamp = $ARGV[ 0 ]; - printf "timestamp = %s\n", $timestamp; - syswrite( HEADER, pack( "N", unpack( "L", pack( "H8", $timestamp ) ) ) ); - - # 要素数の出力 - syswrite( HEADER, pack( "S", $num ) ); - - # パディングの出力 - syswrite( HEADER, pack( "x$padLen") ); - - # 情報テーブルのSHA1ハッシュの出力 - { - my $digest; - system ("openssl dgst -sha1 -binary -out $digestFile $infoFile"); - open DIGEST, $digestFile or die; - binmode DIGEST; - sysread( DIGEST, $digest, $sha1Len ); - close DIGEST; - syswrite( HEADER, $digest, $sha1Len ); - } - close HEADER; + # timestampLen = 0x08; + # elementNumLen = 0x02; + my $padLen = 0x06; + my $sha1Len = 0x14; + + open HEADER, ">$headerFile" or die; + binmode HEADER; + + # タイムスタンプの出力 +# my $timestamp = strftime "%y%m%d%H", localtime; + my $timestamp = $ARGV[ 0 ]; + printf "timestamp = %s\n", $timestamp; + syswrite( HEADER, pack( "N", unpack( "L", pack( "H8", $timestamp ) ) ) ); + + # 要素数の出力 + syswrite( HEADER, pack( "S", $num ) ); + + # パディングの出力 + syswrite( HEADER, pack( "x$padLen") ); + + # 情報テーブルのSHA1ハッシュの出力 + { + my $digest; + system ("openssl dgst -sha1 -binary -out $digestFile $infoFile"); + open DIGEST, $digestFile or die; + binmode DIGEST; + sysread( DIGEST, $digest, $sha1Len ); + close DIGEST; + syswrite( HEADER, $digest, $sha1Len ); + } + close HEADER; } # 環境変数サーチ @@ -172,51 +198,57 @@ foreach ( sort keys ( %ENV ) ){ } } if (!$KEYROOT) { - deleteTemp(); - die "No TWL_IPL_RED_PRIVATE_ROOT is found.\n"; + deleteTemp(); + die "No TWL_IPL_RED_PRIVATE_ROOT is found.\n"; } # ヘッダへの署名付加 { system ( "openssl dgst -sha1 -binary -out $digestFile $headerFile" ); system ( "openssl rsautl -sign -in $digestFile -inkey $KEYROOT/keys/rsa/private_sharedFont.der -keyform DER -out $signFile" ); - system ( "cat $signFile $headerFile >$tempFile" ); - system ( "cat $tempFile $infoFile >$outFile" ); - deleteTemp(); + system ( "cat $signFile $headerFile >$tempFile" ); + system ( "cat $tempFile $infoFile >$outFile" ); + deleteTemp(); } # フォントの出力 { - open FONTTABLE, ">>$outFile" or die; - binmode FONTTABLE; - - { - # パディング出力 - my $fileLen = -s $outFile; - my $padNum = ( $fileLen % 32 ) ? ( 32 - ( $fileLen % 32 ) ) : 0; - my $padding = pack( "x$padNum" ); - syswrite( FONTTABLE, $padding, $padNum ); - } + open FONTTABLE, ">>$outFile" or die; + binmode FONTTABLE; + + { + # パディング出力 + my $fileLen = -s $outFile; + my $padNum = ( $fileLen % 32 ) ? ( 32 - ( $fileLen % 32 ) ) : 0; + my $padding = pack( "x$padNum" ); + syswrite( FONTTABLE, $padding, $padNum ); + } - foreach ( @files ) { - # NULL指定時はスキップ - if( "NULL" eq basename( $_ ) ) { - next; - } - - # フォント出力 - my $fileLen = -s $_; - open TEST, $_ or die; - binmode TEST; - sysread ( TEST, $buffer, $fileLen ); - close TEST; - syswrite( FONTTABLE, $buffer, $fileLen ); - - # パディング出力 - my $padNum = ( $fileLen % 32 ) ? ( 32 - ( $fileLen % 32 ) ) : 0; - my $padding = pack( "x$padNum" ); - syswrite( FONTTABLE, $padding, $padNum ); - } - close FONTTABLE; + foreach ( @files ) { + # NULL指定時はスキップ + if( "NULL" eq basename( $_ ) ) { + next; + } + + # フォント出力 + #my $fileLen = -s $_; + #open TEST, $_ or die; + my $compfile = "$_.comp"; # 圧縮ファイルを出力 + $fileLen = -s $compfile; + open TEST, $compfile or die; + binmode TEST; + sysread ( TEST, $buffer, $fileLen ); + close TEST; + syswrite( FONTTABLE, $buffer, $fileLen ); + + # パディング出力 + my $padNum = ( $fileLen % 32 ) ? ( 32 - ( $fileLen % 32 ) ) : 0; + my $padding = pack( "x$padNum" ); + syswrite( FONTTABLE, $padding, $padNum ); + + # 圧縮ファイルを削除 + system ("rm -rf $compfile"); + } + close FONTTABLE; }