//Заголовок PCX
struct PCXHEADER
{
BYTE     manuf;                  //Всегда =10 для Paintbrush
BYTE     hard;                   //Информация о версии  5  = Version 30
BYTE     encod;                  //Групповое кодирование(=1)
BYTE     bitpx;                  //Бит на точку, 8
WORD     x1;                     //Размеры картинки(включит)
WORD     y1;                     //(Xmin, Ymin) - (Xmax - Ymax)
WORD     x2;                     //in pixels
WORD     y2;                     //
WORD     hres;                   //Горизразрешение дисплея
WORD     vres;                   //Вертикразрешение дисплея
BYTE     clrma[48];              //Палитра
BYTE     vmode;                  //(игнорируется)
BYTE     nplanes;                //Кол-во плоскостей (вер25=0)
WORD     bplin;                  //Байт на строку
WORD     palinfo;                //Информо палитре (1=цв,2=сер) How to interpret palette - 1 = color/BW, 2 = grayscale
WORD     shres;                  //Разрешение сканнера
WORD     svres;
BYTE     xtra[54];               //Доппустое место (фильтр)
};

struct RGBpal
{
BYTE R;
BYTE G;
BYTE B;
};


DWORD LpBufferRGB;  //хранение RGB PCX файла
PCXHEADER HeaderPCX;              //заголок PCX
DWORD XSIZE, YSIZE;

DWORD LoadPCX(DWORD LpFileNamePCX)
{
static DWORD byte_load;
static DWORD hFile_pcx;
static DWORD ReRLE_Size, BufferRGB_Size;
static DWORD LpReDATA;
static DWORD LpAnalizDATA;
static DWORD AnalizSize;
static DWORD LpPal, LpEndRLE;


//Очистка PCX заголовка
ZeroMemory(#HeaderPCX, sizeof(HeaderPCX));

//открываем файл
hFile_pcx=CreateFile(LpFileNamePCX,GENERIC_READ,0,0,OPEN_EXISTING,0,0);
//Проверяет точно ли открыт файл
IF (hFile_pcx==INVALID_HANDLE_VALUE) return FALSE;


//Читаем заголовк PCX файла
ReadFile(hFile_pcx,#HeaderPCX,sizeof(HeaderPCX),#byte_load,0);
IF (EAX==INVALID_HANDLE_VALUE) return FALSE;

IF (HeaderPCX.manuf!=10) return FALSE;    //Проверка заголовка у PCX файлов первый байт всегда равен 10
IF (HeaderPCX.hard!=5)   return FALSE;    //Проверяем номер версии
IF (HeaderPCX.bitpx!=8)  return FALSE;    //бит на точку

//    Сначала определите размер изображения, вычислив
//      [XSIZE = Xmax - Xmin + 1] и
//      [YSIZE = Ymax - Ymin + 1].


XSIZE=HeaderPCX.x2-HeaderPCX.x1+1;
YSIZE=HeaderPCX.y2-HeaderPCX.y1+1;
ReRLE_Size=XSIZE*YSIZE;


//берем память под разкомпрессированные данные
EBX=XSIZE*YSIZE;
LpReDATA=LocalAlloc(LPTR,EBX);
IF (LpReDATA==0) return FALSE;


//Узнаем размер файла без заголовка
GetFileSize(hFile_pcx, 0);
$sub eax,sizeof(HeaderPCX);
AnalizSize=EAX;

//Берем память под анализируемые данные
LocalAlloc(LPTR,AnalizSize);
IF (EAX==0) return FALSE;
LpAnalizDATA=EAX;



//Считаем компрессированные данные вместе с палитрой RGB (769 байт)
ReadFile(hFile_pcx,LpAnalizDATA,AnalizSize,#byte_load,0);
IF (EAX==INVALID_HANDLE_VALUE) return FALSE;


/*
     Теперь  вы  можете  начать  декодирование первой строки
развертки - прочитайте первый байт данных из файла. Если два
старших бита  этого байта  равны 1,  оставшиеся шесть  битов
показывают сколько раз  следует повторить СЛЕДУЮЩИЙ  байт из
файла. Если это не так, то этот байт сам является данными  с
повторителем равным  1. Продолжайте  декодирование до  конца
строки, ведя подсчет  количества байтов, переданных  в буфер
вывода.

      Для каждого байта прочитать X,
        если старшие два бита X оба равны 1, то
          repeatcount = (6 младших битов X)
          data = (следующий байт после X)
        else
          repeatcount = 1
          data = X

*/

$mov edi, LpReDATA         //указатель, куда будут  писаться данные
                           //после RLE разжатия
$mov esi, LpAnalizDATA     //esi указывает на первый байт данных
$mov eax, AnalizSize       //размер считанных данных
$add eax, esi              //получаем указатель на конец блока данных
$sub eax, 769              //откатываемся назад на первый байт палитры
$mov LpEndRLE, eax
$inc eax
$mov LpPal, eax            //запоминаем указатель на палитру


/*
для напоминания
              Команда LODSB передает байт, расположенный по адресу ESI
         в AL,   а также инкрементирует или декрементирует ESI
              Команда STOSB копирует байт,  расположенный в AL,  в место
         памяти, на  которое указывает EDI, а также инкрементирует или
         декрементирует EDI
разжимаем
*/

loop_procces_RLE:
$xor ecx, ecx
$xor eax, eax
$lodsb
$inc ecx             //Инкримент ecx, так как если не будет сжатия то один раз комадой stosb копируем байт al по адресу edi
$cmp al, 11000000b   //Смотрим сжаты байты или нет
$jb  NoRLE           //если al < 11000000b то нет сжатия
$and al, 00111111b   //если сжаты то обнуляем два старших бита
$xor ecx, ecx        //так что бы cl указвал количество повторений
$mov cl, al          //следующего байта
$lodsb               //загружаем байт, который повторяетс cl раз
NoRLE:
$rep $stosb
IF (ESI<LpEndRLE) $jmp loop_procces_RLE;


//Резервируем память под готовую картинку RGB RGB RGB RGB RGB...

$mov eax, ReRLE_Size
$imul eax, 3             //Потому что нужно сохранять три состовляющие R, G, B
$mov BufferRGB_Size, eax
LpBufferRGB=LocalAlloc(LPTR,BufferRGB_Size);
IF (LpBufferRGB==0) RETURN FALSE;



//Записываем картинку
$mov esi, LpReDATA         //Откуда
$mov edi, LpBufferRGB      //Куда
$mov ecx, ReRLE_Size
Loop_CopyRGB:
$xor eax, eax
$lodsb                      //загрузили в al байт по указателю ESI

$imul eax, sizeof(RGBpal); //3 байта структура одной состовляющей цвета
$mov edx, eax
$add edx, LpPal            //Узнали где хранится состовляющая


$xor eax, eax
$mov al, DSBYTE [EDX+#RGBpal.R]
$stosb


$xor eax, eax
$mov al, DSBYTE [EDX+#RGBpal.G]
$stosb

$xor eax, eax
$mov al, DSBYTE [EDX+#RGBpal.B]
$stosb

$dec ecx
$jnz Loop_CopyRGB






//Освобождаем память
IF (LpReDATA!=0) LocalFree(LpReDATA);
IF (LpAnalizDATA!=0) LocalFree(LpAnalizDATA);
IF (hFile_pcx!=0) CloseHandle(hFile_pcx);     //закрываем файл


return LpBufferRGB;
//EAX=

}
