高级文件格式分析续(申请邀请码)
下面试着反成C:(测试成功)
并用SDK来写:
MyBmp.h:
struct Record
{
char id;
char value;
};
struct BmpRecord
{
int id;
char str[16];
};
struct BmpHeader
{
BYTE UNKNOW1;
BYTE UNKNOW2;
BYTE BitCount;
BYTE UNKNOW4;
WORD BmpWidth;
WORD BmpHeight;
};
//typedef char BYTE;
Record G_BmpInfoTable1[0x100];
Record G_BmpInfoTable2[0x100];
BYTE G_UnDeForTable[0x100];
DWORD G_UnDeTable[0x26];
DWORD NT_Dim[0x12];
DWORD *Image;
DWORD *Tail;
DWORD ImageData;
DWORD OffsetImageData;
DWORD *OffsetImage;
BYTE *Res;
DWORD BmpWidth;
BmpHeader *Bmp_Head;
BmpHeader G_BmpHead;
DWORD *BmpData=0;
DWORD G_UnTable[0x26] =
{0x2, 0x1, 0x0, 0x3,
0x4, 0x5, 0x0A, 0x0B,
0x0C, 0x0D, 0x0E, 0x0F,
0x16, 0x17, 0x18, 0x19,
0x1A, 0x1B, 0x1C,0x1D,
0x6, 0x8, 0x7, 0x9,
0x10, 0x11, 0x12, 0x13,
0x14, 0x15, 0x1E, 0x1F,
0x20,0x21, 0x22, 0x23,
0x24,0x25};
DWORD G_UnTable_0[0x12]=
{-1,0,1,-1, 0,-2,0,-3,
0,-4,0,-5 ,0,-6,0,-7,0,-8};
DWORD G_UnTable_1[0x12]=
{0,-1,-1,-1,-2,0,-3,0,
-4,0,-5,0,-6,0,-7,0,-8,0};
DWORD G_UnTable_2[0x12] =
{-1 ,0 ,1 ,-1 ,-2, -2, -2, -1,0 ,1 ,2 ,2 ,-3, -3, -3, -3,
-2 ,-1};
MapReader.cpp:
// MapReader.cpp : Defines the entry point for the application.
//
#include "stdafx.h"
#include "MyBmp.h"
#include "resource.h"
#define MAX_LOADSTRING 100
// Global Variables:
HINSTANCE hInst; // current instance
TCHAR szTitle[MAX_LOADSTRING]; // The title bar text
TCHAR szWindowClass[MAX_LOADSTRING]; // The title bar text
// Foward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
HRESULT FileRead();
void LookBmpData(LPVOID lp,LPVOID nlp,int id);
void InitTable(DWORD nWidth);
DWORD* UnDecode(BYTE* lp,int BmpWidth);
void DrawBmp(HWND hwnd,DWORD* lp);
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
MSG msg;
HACCEL hAccelTable;
// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_MAPREADER, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_MAPREADER);
FileRead();
// Main message loop:
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
// COMMENTS:
//
// This function and its usage is only necessary if you want this code
// to be compatible with Win32 systems prior to the 'RegisterClassEx'
// function that was added to Windows 95. It is important to call this function
// so that the application will get 'well formed' small icons associated
// with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_MAPREADER);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = (LPCSTR)IDC_MAPREADER;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL);
return RegisterClassEx(&wcex);
}
//
// FUNCTION: InitInstance(HANDLE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance; // Store instance handle in our global variable
hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
TCHAR szHello[MAX_LOADSTRING];
LoadString(hInst, IDS_HELLO, szHello, MAX_LOADSTRING);
switch (message)
{
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, (LPCTSTR)IDD_ABOUTBOX, hWnd, (DLGPROC)About);
break;
case IDM_EXIT:
delete BmpData;
DestroyWindow(hWnd);
break;
case IDC_TEST:
DrawBmp(hWnd,BmpData);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code here...
RECT rt;
GetClientRect(hWnd, &rt);
DrawText(hdc, szHello, strlen(szHello), &rt, DT_CENTER);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Mesage handler for about box.
LRESULT CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return TRUE;
}
break;
}
return FALSE;
}
HRESULT FileRead()
{
int BmpId=0x5;
BmpRecord br[200];
HANDLE hFile;
DWORD dwSize,dwByRead;
hFile=CreateFile("parts.nme",GENERIC_READ,0,0,OPEN_EXISTING,0,0);
if(INVALID_HANDLE_VALUE==hFile) return E_FAIL;
dwSize=GetFileSize(hFile,NULL);
dwSize-=4;
const DWORD nSize=dwSize;
BYTE *buf=new BYTE[dwSize];
ReadFile(hFile,buf,dwSize,&dwByRead,NULL);
memset(br,0,200*sizeof(BmpRecord));
int j=0;
for(int i=0;i<200;i++)
{
br[i].id=i;
int k=0;
while(buf[j]!=0)
{
br[i].str[k]=buf[j];
j++;
k++;
}
br[i].str[k]=0;
j++;
}
delete buf;
CloseHandle(hFile);
hFile=CreateFile("parts.atm",GENERIC_READ,0,0,OPEN_EXISTING,0,0);
if(INVALID_HANDLE_VALUE==hFile) return E_FAIL;
dwSize=GetFileSize(hFile,NULL);
dwSize-=4;
buf=new BYTE[dwSize];
ReadFile(hFile,buf,dwSize,&dwByRead,NULL);
DWORD DetOffset,DetSize;
DetOffset=*(DWORD*)(buf+4+0x14*BmpId);
DetSize=*(DWORD*)(buf+8+0x14*BmpId);
DWORD uObjSize;
uObjSize=*(DWORD*)(buf+((BmpId+BmpId*4)*4)+16);
delete buf;
CloseHandle(hFile);
hFile=CreateFile("parts.det",GENERIC_READ,0,0,OPEN_EXISTING,0,0);
if(INVALID_HANDLE_VALUE==hFile) return E_FAIL;
buf=new BYTE[DetSize];
SetFilePointer(hFile,DetOffset,0,FILE_BEGIN);
ReadFile(hFile,buf,DetSize,&dwByRead,NULL);
BYTE* nbuf=new BYTE [uObjSize];
memset(nbuf,0,uObjSize);
DWORD PreOffset;
long preNum;
j=0;
int k=0;
for(i=0;i<DetSize;i++)
{
if(buf[i]==0xFF)
{
k=i;
i++;
if(buf[i]!=0xff)
{
PreOffset=buf[i]>>2;
PreOffset++;
preNum=buf[i] & 0x80000003;
if(preNum<0)
{
preNum--;
preNum |=0xfffffffc;
preNum++;
}
preNum+=3;
if(preNum>0)
{
for(;preNum>0;preNum--)
{
nbuf[j]=nbuf[j-PreOffset];
j++;
}
j--;
}
else
{
j--;
}
}
else
{
nbuf[j]=0xff;
}
}
else
{
nbuf[j]=buf[i];
}
j++;
}
delete buf;
CloseHandle(hFile);
Bmp_Head=(BmpHeader*)nbuf;
InitTable(Bmp_Head->BmpWidth);
BmpData=UnDecode(nbuf,Bmp_Head->BmpWidth);
memcpy(&G_BmpHead,Bmp_Head,0x10);
delete nbuf;
/*
dwByRead=0x1d4c00;
DetSize=0x1d4c00;
hFile=CreateFile("bmp.mem",GENERIC_READ|GENERIC_WRITE,0,0,CREATE_ALWAYS,0,0);
WriteFile(hFile,BmpData,DetSize,&dwByRead,NULL);
CloseHandle(hFile);
*/
return S_OK;
}
DWORD* UnDecode(BYTE* lp,int BmpWidth)
{
DWORD prevId;
DWORD oldId;
DWORD OffsetPrevId;
long adjsig;
int i;
long Old_Offv;
BYTE Bt_num;
DWORD TmpSave;
BYTE Bt_value;
BYTE Ud;
DWORD Offset_Value;
DWORD Offset_Color;
DWORD Nt_value;
DWORD Od_value;
BYTE FindCode;
DWORD Exbt;
DWORD SubData;
int BmpSize=(Bmp_Head->BmpHeight)*(Bmp_Head->BmpWidth)+0x10;
Image=new DWORD[BmpSize];
memset(Image,0,BmpSize*4);
ImageData=*Image;
Res=(lp+0x10);
Tail=Image+BmpSize-0x10;
FindCode=0x80;
OffsetImageData=ImageData;
do
{
//if (Image==(DWORD*)(0xfb0040+0x77048)) _asm{int 3};
OffsetImage=Image;
Bt_value=G_BmpInfoTable1[FindCode].value;
Bt_num =G_BmpInfoTable1[FindCode].id;
if(!Bt_value)
{
do
{
Bt_value=G_BmpInfoTable2[*Res].value;
Bt_num +=G_BmpInfoTable2[*Res].id;
Res++;
}while(!Bt_value);
}
Ud=G_UnDeForTable[Bt_value];
Exbt=Bt_value;
Exbt+=0x100;
prevId=Bt_num-Ud;
if(Bt_num>Ud)
{
Exbt<<=Ud;
Exbt=(Exbt & 0xFFFFFF00)+(*Res);
prevId--;
Res++;
if(prevId>=8)
{
OffsetPrevId=prevId>>3;
prevId+=(0-OffsetPrevId)*8;
for(i=OffsetPrevId;i>0;i--)
{
Exbt <<=8;
Exbt+=*Res;
Res++;
}
}
Exbt*=2;
Exbt+=1;
Bt_num=prevId;
}
Exbt<<=Bt_num;
Od_value=(BYTE)G_UnDeTable[(Exbt>>8)-2];
FindCode=(BYTE)(Exbt & 0xFF);
if(Od_value<0x14)
{
if(Od_value<2)
{
//无修正,无重复,直接组合
ImageData=(((Res[0]<<8)+Res[1])<<8)+Res[2];
ImageData<<=8;
ImageData^=0x80000080;
ImageData>>=G_UnDeForTable[FindCode];
TmpSave=FindCode;
FindCode=ImageData & 0xFF;
ImageData>>=8;
ImageData^=(TmpSave<<0x10);
*Image=ImageData; //填充IMAGE数据
Image++;
Res+=3;
}
else
{
Nt_value=NT_Dim[Od_value-2];
Offset_Color=*(Image+Nt_value); //用于修正
Bt_value=G_BmpInfoTable1[FindCode].value;
Bt_num =G_BmpInfoTable1[FindCode].id;
if(!Bt_value)
{
do
{
Bt_value=G_BmpInfoTable2[*Res].value;
Bt_num +=G_BmpInfoTable2[*Res].id;
Res++;
}while(!Bt_value);
}
Ud=G_UnDeForTable[Bt_value];
Exbt=Bt_value;
Exbt+=0x100;
prevId=Bt_num-Ud;
if(Bt_num>Ud)
{
Exbt<<=Ud;
Exbt=(Exbt & 0xFFFFFF00)+(*Res);
prevId--;
Res++;
if(prevId>=8)
{
OffsetPrevId=prevId>>3;
prevId+=(0-OffsetPrevId)*8;
for(i=OffsetPrevId;i>0;i--)
{
Exbt <<=8;
Exbt+=*Res;
Res++;
}
}
Exbt*=2;
Exbt+=1;
Bt_num=prevId;
}
Exbt<<=Bt_num;
Offset_Color+=((0-((Exbt>>8)& 1) ^ ((Exbt>>8)-2))<<0xF);
FindCode=(BYTE)(Exbt & 0xFF);
Bt_value=G_BmpInfoTable1[FindCode].value;
Bt_num=G_BmpInfoTable1[FindCode].id;
if(!Bt_value)
{
do
{
Bt_value=G_BmpInfoTable2[*Res].value;
Bt_num +=G_BmpInfoTable2[*Res].id;
Res++;
}while(!Bt_value);
}
Ud=G_UnDeForTable[Bt_value];
Exbt=Bt_value;
Exbt+=0x100;
prevId=Bt_num-Ud;
if(Bt_num>Ud)
{
Exbt<<=Ud;
Exbt=(Exbt & 0xFFFFFF00)+(*Res);
prevId--;
Res++;
if(prevId>=8)
{
OffsetPrevId=prevId>>3;
prevId+=(0-OffsetPrevId)*8;
for(i=OffsetPrevId;i>0;i--)
{
Exbt <<=8;
Exbt+=*Res;
Res++;
}
}
Exbt*=2;
Exbt+=1;
Bt_num=prevId;
}
Exbt<<=Bt_num;
Offset_Color+=((0-((Exbt>>8)& 1) ^ ((Exbt>>8)-2))<<0x7); //色差
FindCode=(BYTE)(Exbt & 0xFF);
Bt_value=G_BmpInfoTable1[FindCode].value;
Bt_num=G_BmpInfoTable1[FindCode].id;
if(!Bt_value)
{
do
{
Bt_value=G_BmpInfoTable2[*Res].value;
Bt_num +=G_BmpInfoTable2[*Res].id;
Res++;
}while(!Bt_value);
}
Ud=G_UnDeForTable[Bt_value];
Exbt=Bt_value;
Exbt+=0x100;
prevId=Bt_num-Ud;
if(Bt_num>Ud)
{
Exbt<<=Ud;
Exbt=(Exbt & 0xFFFFFF00)+(*Res);
prevId--;
Res++;
if(prevId>=8)
{
OffsetPrevId=prevId>>3;
prevId+=(0-OffsetPrevId)*8;
for(i=OffsetPrevId;i>0;i--)
{
Exbt <<=8;
Exbt+=*Res;
Res++;
}
}
Exbt*=2;
Exbt+=1;
Bt_num=prevId;
}
Exbt<<=Bt_num;
Old_Offv=(-((Exbt>>8) &1)^((Exbt>>8)-2));
if(Old_Offv>=0)
{adjsig=0;}
else
{adjsig=-1;}
Offset_Color+=(Old_Offv-adjsig)>>1; //保留符号位
*Image=Offset_Color; //填充IMAGE数据
FindCode=Exbt & 0xFF;
Image++;
}
}
else
{
Bt_value=G_BmpInfoTable1[FindCode].value;
Bt_num=G_BmpInfoTable1[FindCode].id;
if(!Bt_value)
{
do
{
Bt_value=G_BmpInfoTable2[*Res].value;
Bt_num+=G_BmpInfoTable2[*Res].id;
Res++;
}while(!Bt_value);
}
Ud=G_UnDeForTable[Bt_value];
Exbt=Bt_value;
Exbt+=0x100;
prevId=Bt_num-Ud;
if(Bt_num>Ud)
{
Exbt<<=Ud;
Exbt=(Exbt & 0xFFFFFF00)+(*Res);
prevId--;
Res++;
if(prevId>=8)
{
OffsetPrevId=prevId>>3;
prevId+=(0-OffsetPrevId)*8;
for(i=OffsetPrevId;i>0;i--)
{
Exbt <<=8;
Exbt+=*Res;
Res++;
}
}
Exbt*=2;
Exbt+=1;
Bt_num=prevId;
}
Exbt<<=Bt_num;
oldId=Exbt>>8;
Offset_Value=((Exbt>>8)& 1)-1;
FindCode=(BYTE)(Exbt & 0xFF);
Bt_value=G_BmpInfoTable1[FindCode].value;
Bt_num=G_BmpInfoTable1[FindCode].id;
if(!Bt_value)
{
do
{
Bt_value=G_BmpInfoTable2[*Res].value;
Bt_num+=G_BmpInfoTable2[*Res].id;
Res++;
}while(!Bt_value);
}
Ud=G_UnDeForTable[Bt_value];
Exbt=Bt_value;
Exbt+=0x100;
prevId=Bt_num-Ud;
//SubData=Bt_value-Ud;
if(Bt_num>Ud)
{
//416a35
Exbt<<=Ud;
Exbt=(Exbt & 0xFFFFFF00)+(*Res);
prevId--;
Res++;
if(prevId>=8)
{
OffsetPrevId=prevId>>3;
prevId+=(0-OffsetPrevId)*8;
for(i=OffsetPrevId;i>0;i--)
{
Exbt <<=8;
Exbt+=*Res;
Res++;
}
}
Exbt*=2;
Exbt+=1;
Bt_num=prevId;
}
/*
DWROD CalcTmp;
if(Bt_num>=0)
{CalcTmp=0;}
esle{CalcTmp=-1}
*/
//Offset_Value^=(((Exbt<<Bt_num)>>8)-2)-(((((Exbt>>8)>>1)-CalceTmp)+Offset_Value)*BmpWidth);
Exbt<<=Bt_num;
if(oldId>=0)
{TmpSave=0;}
else
{TmpSave=-1;}
Old_Offv=(((oldId-TmpSave)>>1)+Offset_Value)*BmpWidth;
Offset_Value^=(Exbt>>8)-2;
Offset_Value-=Old_Offv;
//Offset_Value^=(((Exbt>>8)-2)-((((Exbt>>8)>>1)+Offset_Value)*BmpWidth));
OffsetImage+=Offset_Value;
FindCode=(BYTE)(Exbt & 0xFF);
Bt_value=G_BmpInfoTable1[FindCode].value;
Bt_num=G_BmpInfoTable1[FindCode].id;
if(!Bt_value)
{
do
{
Bt_value=G_BmpInfoTable2[*Res].value;
Bt_num+=G_BmpInfoTable2[*Res].id;
Res++;
}while(!Bt_value);
}
Ud=G_UnDeForTable[Bt_value];
Exbt=Bt_value;
Exbt+=0x100;
prevId=Bt_num-Ud;
if(Bt_num>Ud)
{
Exbt<<=Ud;
Exbt=(Exbt & 0xFFFFFF00)+(*Res);
prevId--;
Res++;
if(prevId>=8)
{
OffsetPrevId=prevId>>3;
prevId+=(0-OffsetPrevId)*8;
for(i=OffsetPrevId;i>0;i--)
{
Exbt <<=8;
Exbt+=*Res;
Res++;
}
}
Exbt*=2;
Exbt+=1;
Bt_num=prevId;
}
Exbt<<=Bt_num;
Offset_Value=(Exbt>>8);
Offset_Value--;
FindCode=(BYTE)(Exbt & 0xFF);
if(Od_value==0x26)
{
if(Offset_Value>0)
{
for(i=Offset_Value;i>0;i--)
{
*Image=*OffsetImage; //填充IMAGE数据
Image++;
OffsetImage++;
}
}
}
else
{
if(Offset_Value>0)
{
SubData=NT_Dim[(Od_value-0x14)];
for(int i=Offset_Value;i>0;i--)
{
*(Image)=*(Image+SubData)-*(OffsetImage+SubData)+*(OffsetImage); //填充IMAGE数据
Image++;
OffsetImage++;
}
}
}
}
}while(Image<Tail); ;数据未读完
Image-=(BmpSize-0x10);
return Image;
}
void InitTable(DWORD nWidth)
{
char a,b,x;
a=b=x=0;
char i;
for(BYTE y=0;y<=0xFF;y++)
{
i=y;
a=i;
b=0;
y=i;
if(i)
{
if(i<0)
{
do
{
a <<=1;
b++;
}while(a<0);
}
if((a<<=1)==0) b--;
}
b++;
G_BmpInfoTable1[y].id=b;
G_BmpInfoTable1[y].value=a;
a=i;
b=0;
if(i)
{
if(i<0)
{
do
{
a <<=1;
b++;
}while(a<0);
}
a<<=1;
x=a;
}
a=1<<b;
a +=x;
G_BmpInfoTable2[y].id=b;
G_BmpInfoTable2[y].value=a;
a=0;
b=i;
if(i & 0x7f)
{
do
{
b<<=1;
a++;
}while(b & 0x7f);
}
G_UnDeForTable[y]=a;
if(y==0xff) break;
}
DWORD TmpNum;
for(i=0;i<0x26;i++)
{
TmpNum=G_UnTable[i];
G_UnDeTable[TmpNum]=i;
}
G_UnDeTable[1]=0x26;
for(i=0;i<18;i++)
{
NT_Dim[i]=(G_UnTable_1[i] * nWidth)+G_UnTable_0[i];
}
}
void DrawBmp(HWND hwnd,DWORD* lp) //仅用于测试
{
HDC hdc;
hdc=GetDC(hwnd);
LPBITMAPINFO pBit = (LPBITMAPINFO) new BYTE[ sizeof(BITMAPINFO) + 8 ];
//pBit->bmiHeader.biWidth
DWORD tmp[5];
tmp[0]=0x28;
tmp[1]=0x320;
tmp[2]=0x258;
tmp[3]=0x00200001;
tmp[4]=0;
pBit=(BITMAPINFO*)tmp;
StretchDIBits(hdc,0,0,0x320,0x258,0,0,0x320,0x258,lp,pBit,DIB_RGB_COLORS,SRCCOPY);
ReleaseDC(hwnd,hdc);
}
以上代码经测试,可完美的读出图片。
