#include <fcntl.h> // _O_WTEXT
#include <io.h> // _setmode
#include <stdio.h> // _fileno
#include <tchar.h> // TCHAR
#include <Windows.h>
#define dprintf(fmt, var) _tprintf(_T(#var)_T("=[")fmt##_T("]\n"), var)
#pragma pack(push, 1)
typedef struct {
BYTE rgbtRed;
BYTE rgbtGreen;
BYTE rgbtBlue;
} SRGBTriple;
typedef struct {
char acSignature[3];
char acVersion[3];
USHORT usLogicalScreenWidth;
USHORT usLogicalScreenHeight;
struct {
UCHAR SizeofGlobalColorTable: 3; // 00000???
UCHAR SortFlag: 1; // 0000?000
UCHAR ColorResolution: 3; // 0???0000
UCHAR GlobalColorTableFlag: 1; // ?0000000
} ucbf; // BitField
UCHAR ucBackgroundColorIndex;
UCHAR ucPixelAspectRatio;
} SGIFHeader;
typedef struct {
USHORT usImageLeftPosition;
USHORT usImageTopPosition;
USHORT usImageWidth;
USHORT usImageHeight;
struct {
UCHAR SizeofLocalColorTable: 3;
UCHAR Reserved: 2;
UCHAR SortFlag: 1;
UCHAR InterlaceFlag: 1;
UCHAR LocalColorTableFlag: 1;
} ucbf;
} SImageBlock;
typedef struct {
UCHAR ucBlockSize;
struct {
UCHAR TransparentColorFlag: 1;
UCHAR UserInputFlag: 1;
UCHAR DisposalMethod: 3;
UCHAR Reserved: 3;
} ucbf;
USHORT usDelayTime;
UCHAR ucTransparentColorIndex;
UCHAR ucBlockTerminator;
} SGraphicControlExtension;
typedef struct {
UCHAR ucBlockSize;
char acApplicationIdentifier[8];
char acApplicationAuthenticationCode[3];
} SApplicationExtension;
#pragma pack(pop)
// グローバル変数
static FILE* g_pFile; // ファイルポインタ
static UCHAR* g_pucImageData; // 画像データ
static int g_iImageData; // 画像データのサイズ
static int g_iImageIndex; // 画像データの指標
static int g_aiDicTable[0x1000]; // 辞書テーブル
static int g_iDicIndex; // 辞書テーブルの指標
static UCHAR* g_pucCodeData; // コード化データ
static int g_iCodeData; // コード化データのサイズ
static int g_iCodeIndex; // コード化データの指標
static UCHAR g_ucLZWMinimumCodeSize; // 最小コードビット数
static int g_iClearCode; // クリアコード
static int g_iEoICode; // 終了コード
static int g_iCodeSize; // コードのビット数
// 関数プロトタイプ宣言
void FileRead(void* pvBuffer, size_t sizeCount);
int HexDump(UCHAR* pucBuf, int iBuf);
int ParseBlockData(void);
int ParseColorTable(int iNum);
int ParseGIFHeader(void);
int ParseGraphicControlExtension(void);
int ParseCommentExtension(void);
int ParseApplicationExtension(void);
int ParseImageBlock(void);
int ParseImageData(void);
int LZWDecode(void);
int FetchCodeData(int iCodeSize);
int AddImageData(int iCode);
int AddDicTable(void);
int _tmain(int argc, TCHAR* argv[])
{
TCHAR* ptcFile; // ファイル名
UCHAR ucLabel; // ラベル
int iStatus; // 状態
_setmode(_fileno(stdout), _O_WTEXT);
_setmode(_fileno(stderr), _O_WTEXT);
if (argc != 2) {
_ftprintf(stderr, _T("usage: dumpgif file\n"));
return 1;
}
ptcFile = argv[1];
// ファイルのオープン
if (_tfopen_s(&g_pFile, ptcFile, _T("rb")) != 0) {
_ftprintf(stderr, _T("error: _tfopen_s[%s]\n"), ptcFile);
return 1;
}
// GIF Header
iStatus = ParseGIFHeader();
// Block
while (iStatus == 0) {
FileRead(&ucLabel, 1);
_tprintf(_T("\n*** Block[%#x]\n"), ucLabel);
switch (ucLabel) {
case 0x2C: // Image Block
iStatus = ParseImageBlock();
break;
case 0x21: // Extension Introducer
FileRead(&ucLabel, 1);
_tprintf(_T("Extension[%#x]\n"), ucLabel);
switch (ucLabel) {
case 0xF9: // Graphic Control Extension
iStatus = ParseGraphicControlExtension();
break;
case 0xFE: // Comment Extension
iStatus = ParseCommentExtension();
break;
// case 0x01: // Plain Text Extension
// ;
case 0xFF: // Application Extension
iStatus = ParseApplicationExtension();
break;
default:
iStatus = -1;
}
break;
case 0x3B: // Trailer
_tprintf(_T("Trailer\n"));
iStatus = 1;
break;
default:
iStatus = -1;
}
}
fclose(g_pFile);
return 0;
}
void FileRead(void* pvBuffer, size_t sizeCount)
{
if (fread(pvBuffer, 1, sizeCount, g_pFile) != sizeCount) {
_ftprintf(stderr, _T("error: fread\n"));
exit(1);
}
}
int HexDump(UCHAR* pucBuf, int iBuf)
{
int i;
for (i = 0; i < iBuf; i++) {
if (i % 16 == 0) {
_tprintf(_T("%.8x:"), i);
}
_tprintf(_T("%.2x%c"), pucBuf[i], _T(" - - - \n")[i % 16]);
}
if (i % 16) {
_tprintf(_T("\n"));
}
return 0;
}
int ParseBlockData(void)
{
UCHAR ucBlockSize;
UCHAR aucDataValues[255];
for (;;) {
FileRead(&ucBlockSize, 1);
_tprintf(_T("Block Data[%u]\n"), ucBlockSize);
if (ucBlockSize == 0) {
break;
}
FileRead(aucDataValues, ucBlockSize);
HexDump(aucDataValues, ucBlockSize);
}
return 0;
}
int ParseColorTable(int iNum)
{
SRGBTriple sct; // ColorTable
int i;
for (i = 0; i < iNum; i++) {
FileRead(&sct, 3);
_tprintf(_T("%.2x%.2x%.2x%.2x%c"),
i, sct.rgbtRed, sct.rgbtGreen, sct.rgbtBlue,
" \n"[i % 8]);
}
return 0;
}
int ParseGIFHeader(void)
{
SGIFHeader sgifh;
int iNumGCT;
_tprintf(_T("*** GIF Header\n"));
FileRead(&sgifh, 13);
if (strncmp(sgifh.acSignature, "GIF", 3) != 0) {
return -1;
}
dprintf(_T("%.3S"), sgifh.acSignature);
dprintf(_T("%.3S"), sgifh.acVersion);
dprintf(_T("%u"), sgifh.usLogicalScreenWidth);
dprintf(_T("%u"), sgifh.usLogicalScreenHeight);
dprintf(_T("%u"), sgifh.ucbf.GlobalColorTableFlag);
dprintf(_T("%u"), sgifh.ucbf.ColorResolution);
dprintf(_T("%u"), sgifh.ucbf.SortFlag);
dprintf(_T("%u"), sgifh.ucbf.SizeofGlobalColorTable);
dprintf(_T("%u"), sgifh.ucBackgroundColorIndex);
dprintf(_T("%u"), sgifh.ucPixelAspectRatio);
iNumGCT = 0;
if (sgifh.ucbf.GlobalColorTableFlag) {
iNumGCT = 2 << sgifh.ucbf.SizeofGlobalColorTable;
}
_tprintf(_T("Global Color Table[%d]\n"), iNumGCT);
ParseColorTable(iNumGCT);
return 0;
}
int ParseGraphicControlExtension(void)
{
SGraphicControlExtension sgce;
_tprintf(_T("Graphic Control Extension\n"));
FileRead(&sgce, 6);
dprintf(_T("%u"), sgce.ucBlockSize);
dprintf(_T("%u"), sgce.ucbf.Reserved);
dprintf(_T("%u"), sgce.ucbf.DisposalMethod);
dprintf(_T("%u"), sgce.ucbf.UserInputFlag);
dprintf(_T("%u"), sgce.ucbf.TransparentColorFlag);
dprintf(_T("%u"), sgce.usDelayTime);
dprintf(_T("%u"), sgce.ucTransparentColorIndex);
dprintf(_T("%#x"), sgce.ucBlockTerminator);
return 0;
}
int ParseCommentExtension(void)
{
_tprintf(_T("Comment Extension\n"));
return ParseBlockData();
}
int ParseApplicationExtension(void)
{
SApplicationExtension sae;
_tprintf(_T("Application Extension\n"));
FileRead(&sae, 12);
dprintf(_T("%u"), sae.ucBlockSize);
dprintf(_T("%.8S"), sae.acApplicationIdentifier);
dprintf(_T("%.3S"), sae.acApplicationAuthenticationCode);
return ParseBlockData();
}
int ParseImageBlock(void)
{
SImageBlock sib;
int iNumLCT;
_tprintf(_T("Image Block\n"));
FileRead(&sib, 9);
dprintf(_T("%u"), sib.usImageLeftPosition);
dprintf(_T("%u"), sib.usImageTopPosition);
dprintf(_T("%u"), sib.usImageWidth);
dprintf(_T("%u"), sib.usImageHeight);
dprintf(_T("%u"), sib.ucbf.LocalColorTableFlag);
dprintf(_T("%u"), sib.ucbf.InterlaceFlag);
dprintf(_T("%u"), sib.ucbf.SortFlag);
dprintf(_T("%u"), sib.ucbf.Reserved);
dprintf(_T("%u"), sib.ucbf.SizeofLocalColorTable);
iNumLCT = 0;
if (sib.ucbf.LocalColorTableFlag) {
iNumLCT = 2 << sib.ucbf.SizeofLocalColorTable;
}
_tprintf(_T("Local Color Table[%d]\n"), iNumLCT);
ParseColorTable(iNumLCT);
FileRead(&g_ucLZWMinimumCodeSize, 1);
dprintf(_T("%u"), g_ucLZWMinimumCodeSize);
g_iClearCode = 1 << g_ucLZWMinimumCodeSize;
g_iEoICode = g_iClearCode + 1;
g_iCodeSize = g_ucLZWMinimumCodeSize + 1;
g_iImageData = sib.usImageWidth * sib.usImageHeight;
g_pucImageData = (UCHAR*)malloc(g_iImageData);
if (g_pucImageData == NULL) {
_ftprintf(stderr, _T("error: malloc\n"));
return -1;
}
g_iImageIndex = 0;
FetchCodeData(0);
ParseImageData();
HexDump(g_pucImageData, g_iImageData);
free(g_pucImageData);
return 0;
}
int ParseImageData(void)
{
UCHAR ucBlockSize;
UCHAR aucDataValues[255];
g_pucCodeData = aucDataValues;
for (;;) {
FileRead(&ucBlockSize, 1);
_tprintf(_T("Block Data[%u]\n"), ucBlockSize);
if (ucBlockSize == 0) {
break;
}
FileRead(aucDataValues, ucBlockSize);
HexDump(aucDataValues, ucBlockSize);
g_iCodeData = ucBlockSize;
g_iCodeIndex = 0;
LZWDecode();
}
return 0;
}
int LZWDecode(void)
{
int iCode;
while (1) {
iCode = FetchCodeData(g_iCodeSize);
if (iCode < 0) {
return 1;
}
if (iCode == g_iClearCode) {
// 辞書テーブルのクリア
g_iDicIndex = g_iClearCode + 1;
g_aiDicTable[g_iDicIndex] = g_iImageIndex;
g_iCodeSize = g_ucLZWMinimumCodeSize + 1;
continue;
}
if (iCode == g_iEoICode) {
break;
}
if (g_iDicIndex < iCode) {
fprintf(stderr, "error: StringTable %u %d\n",
g_iDicIndex, iCode);
return -1;
}
AddImageData(iCode);
AddDicTable();
if ((1 << g_iCodeSize) <= g_iDicIndex) {
g_iCodeSize++;
}
}
return 0;
}
// コードの取得
// 戻り値:-1=失敗 0以上=コード
int FetchCodeData(int iCodeSize)
{
static UINT uiBits;
static int iBits;
int iCode;
if (iCodeSize <= 0) {
uiBits = 0;
iBits = 0;
return -1;
}
while (iBits < iCodeSize) {
if (g_iCodeData <= g_iCodeIndex) {
return -1;
}
uiBits |= g_pucCodeData[g_iCodeIndex++] << iBits;
iBits += 8;
}
iCode = uiBits & ((1 << iCodeSize) - 1);
uiBits >>= iCodeSize;
iBits -= iCodeSize;
return iCode;
}
// 画像データの追加
int AddImageData(int iCode)
{
int iEnd;
int i;
if (iCode < g_iClearCode) {
if (g_iImageData <= g_iImageIndex) {
return -1;
}
g_pucImageData[g_iImageIndex++] = iCode;
return 0;
}
iEnd = g_aiDicTable[iCode];
for (i = g_aiDicTable[iCode - 1]; i <= iEnd; i++) {
if (g_iImageData <= g_iImageIndex) {
return -1;
}
g_pucImageData[g_iImageIndex++] = g_pucImageData[i];
}
return 0;
}
// 辞書テーブルの追加
int AddDicTable(void)
{
if (_countof(g_aiDicTable) <= ++g_iDicIndex) {
return -1;
}
g_aiDicTable[g_iDicIndex] = g_iImageIndex;
return 0;
}