Files
Client/rylCoder_16.02.2008_src/addons/DevIL/src-IL/src/il_pcx.c
LGram16 dd97ddec92 Restructure repository to include all source folders
Move git root from Client/ to src/ to track all source code:
- Client: Game client source (moved to Client/Client/)
- Server: Game server source
- GameTools: Development tools
- CryptoSource: Encryption utilities
- database: Database scripts
- Script: Game scripts
- rylCoder_16.02.2008_src: Legacy coder tools
- GMFont, Game: Additional resources

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 20:17:20 +09:00

734 lines
17 KiB
C

//-----------------------------------------------------------------------------
//
// ImageLib Sources
// Copyright (C) 2000-2002 by Denton Woods
// Last modified: 09/01/2003 <--Y2K Compliant! =]
//
// Filename: src-IL/src/il_pcx.c
//
// Description: Reads and writes from/to a .pcx file.
//
//-----------------------------------------------------------------------------
#include "il_internal.h"
#ifndef IL_NO_PCX
#include "il_pcx.h"
#include "il_manip.h"
//! Checks if the file specified in FileName is a valid .pcx file.
ILboolean ilIsValidPcx(ILconst_string FileName) {
ILHANDLE PcxFile;
ILboolean bPcx = IL_FALSE;
if (!iCheckExtension(FileName, IL_TEXT("pcx"))) {
ilSetError(IL_INVALID_EXTENSION);
return bPcx;
}
PcxFile = iopenr(FileName);
if (PcxFile == NULL) {
ilSetError(IL_COULD_NOT_OPEN_FILE);
return bPcx;
}
bPcx = ilIsValidPcxF(PcxFile);
icloser(PcxFile);
return bPcx;
}
//! Checks if the ILHANDLE contains a valid .pcx file at the current position.
ILboolean ilIsValidPcxF(ILHANDLE File) {
ILuint FirstPos;
ILboolean bRet;
iSetInputFile(File);
FirstPos = itell();
bRet = iIsValidPcx();
iseek(FirstPos, IL_SEEK_SET);
return bRet;
}
//! Checks if Lump is a valid .pcx lump.
ILboolean ilIsValidPcxL(const ILvoid *Lump, ILuint Size) {
iSetInputLump(Lump, Size);
return iIsValidPcx();
}
// Internal function obtain the .pcx header from the current file.
ILboolean iGetPcxHead(PCXHEAD *Head) {
Head->Manufacturer = igetc();
Head->Version = igetc();
Head->Encoding = igetc();
Head->Bpp = igetc();
Head->Xmin = GetLittleUShort();
Head->Ymin = GetLittleUShort();
Head->Xmax = GetLittleUShort();
Head->Ymax = GetLittleUShort();
Head->HDpi = GetLittleUShort();
Head->VDpi = GetLittleUShort();
iread(Head->ColMap, 1, 48);
Head->Reserved = igetc();
Head->NumPlanes = igetc();
Head->Bps = GetLittleUShort();
Head->PaletteInfo = GetLittleUShort();
Head->HScreenSize = GetLittleUShort();
Head->VScreenSize = GetLittleUShort();
iread(Head->Filler, 1, 54);
return IL_TRUE;
}
// Internal function to get the header and check it.
ILboolean iIsValidPcx() {
PCXHEAD Head;
if (!iGetPcxHead(&Head))
return IL_FALSE;
iseek(-(ILint)sizeof(PCXHEAD), IL_SEEK_CUR);
return iCheckPcx(&Head);
}
// Internal function used to check if the HEADER is a valid .pcx header.
// Should we also do a check on Header->Bpp?
ILboolean iCheckPcx(PCXHEAD *Header) {
ILuint Test;
// Got rid of the Reserved check, because I've seen some .pcx files with invalid values in it.
if (Header->Manufacturer != 10 || Header->Encoding != 1/* || Header->Reserved != 0*/)
return IL_FALSE;
// Try to support all pcx versions, as they only differ in allowed formats...
// Let's hope it works.
if(Header->Version != 5 && Header->Version != 0 && Header->Version != 2 &&
Header->VDpi != 3 && Header->VDpi != 4)
return IL_FALSE;
// See if the padding size is correct
Test = Header->Xmax - Header->Xmin + 1;
if (Header->Bpp >= 8) {
if (Test & 1) {
if (Header->Bps != Test + 1)
return IL_FALSE;
}
else {
if (Header->Bps != Test) // No padding
return IL_FALSE;
}
}
/* for (i = 0; i < 54; i++) { useless check
if (Header->Filler[i] != 0)
return IL_FALSE;
} */
return IL_TRUE;
}
//! Reads a .pcx file
ILboolean ilLoadPcx(ILconst_string FileName) {
ILHANDLE PcxFile;
ILboolean bPcx = IL_FALSE;
PcxFile = iopenr(FileName);
if (PcxFile == NULL) {
ilSetError(IL_COULD_NOT_OPEN_FILE);
return bPcx;
}
bPcx = ilLoadPcxF(PcxFile);
icloser(PcxFile);
return bPcx;
}
//! Reads an already-opened .pcx file
ILboolean ilLoadPcxF(ILHANDLE File) {
ILuint FirstPos;
ILboolean bRet;
iSetInputFile(File);
FirstPos = itell();
bRet = iLoadPcxInternal();
iseek(FirstPos, IL_SEEK_SET);
return bRet;
}
//! Reads from a memory "lump" that contains a .pcx
ILboolean ilLoadPcxL(const ILvoid *Lump, ILuint Size) {
iSetInputLump(Lump, Size);
return iLoadPcxInternal();
}
// Internal function used to load the .pcx.
ILboolean iLoadPcxInternal() {
PCXHEAD Header;
ILboolean bPcx = IL_FALSE;
if (iCurImage == NULL) {
ilSetError(IL_ILLEGAL_OPERATION);
return bPcx;
}
if (!iGetPcxHead(&Header))
return IL_FALSE;
if (!iCheckPcx(&Header)) {
ilSetError(IL_INVALID_FILE_HEADER);
return IL_FALSE;
}
bPcx = iUncompressPcx(&Header);
ilFixImage();
return bPcx;
}
// Internal function to uncompress the .pcx (all .pcx files are rle compressed)
ILboolean iUncompressPcx(PCXHEAD *Header) {
//changed decompression loop 2003-09-01
//From the pcx spec: "There should always
//be a decoding break at the end of each scan line.
//But there will not be a decoding break at the end of
//each plane within each scan line."
//This is now handled correctly (hopefully ;) )
ILubyte ByteHead, Colour, *ScanLine /* For all planes */;
ILuint ScanLineSize;
ILuint c, i, x, y;
if (Header->Bpp < 8) {
/*ilSetError(IL_FORMAT_NOT_SUPPORTED);
return IL_FALSE;*/
return iUncompressSmall(Header);
}
if (!ilTexImage(Header->Xmax - Header->Xmin + 1, Header->Ymax - Header->Ymin + 1, 1, Header->NumPlanes, 0, IL_UNSIGNED_BYTE, NULL))
return IL_FALSE;
iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
switch (iCurImage->Bpp)
{
case 1:
iCurImage->Format = IL_COLOUR_INDEX;
iCurImage->Pal.PalType = IL_PAL_RGB24;
iCurImage->Pal.PalSize = 256 * 3; // Need to find out for sure...
iCurImage->Pal.Palette = (ILubyte*)ialloc(iCurImage->Pal.PalSize);
if (iCurImage->Pal.Palette == NULL) {
return IL_FALSE;
}
break;
//case 2: // No 16-bit images in the pcx format!
case 3:
iCurImage->Format = IL_RGB;
iCurImage->Pal.Palette = NULL;
iCurImage->Pal.PalSize = 0;
iCurImage->Pal.PalType = IL_PAL_NONE;
break;
case 4:
iCurImage->Format = IL_RGBA;
iCurImage->Pal.Palette = NULL;
iCurImage->Pal.PalSize = 0;
iCurImage->Pal.PalType = IL_PAL_NONE;
break;
default:
ilSetError(IL_ILLEGAL_FILE_VALUE);
return IL_FALSE;
}
ScanLineSize = iCurImage->Bpp*Header->Bps;
ScanLine = (ILubyte*)ialloc(ScanLineSize);
if (ScanLine == NULL) {
return IL_FALSE;
}
//changed 2003-09-01
//having the decoding code twice is error-prone,
//so I made iUnCache() smart enough to grasp
//if iPreCache() wasn't called and call it
//anyways.
if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST)
iPreCache(iCurImage->SizeOfData / 4);
for (y = 0; y < iCurImage->Height; y++) {
x = 0;
//read scanline
while (x < ScanLineSize) {
if (iread(&ByteHead, 1, 1) != 1) {
iUnCache();
goto file_read_error;
}
if ((ByteHead & 0xC0) == 0xC0) {
ByteHead &= 0x3F;
if (iread(&Colour, 1, 1) != 1) {
iUnCache();
goto file_read_error;
}
if (x + ByteHead > ScanLineSize) {
iUnCache();
goto file_read_error;
}
for (i = 0; i < ByteHead; i++) {
ScanLine[x++] = Colour;
}
}
else {
ScanLine[x++] = ByteHead;
}
}
//convert plane-separated scanline into index, rgb or rgba pixels.
//there might be a padding byte at the end of each scanline...
for (x = 0; x < iCurImage->Width; x++) {
for(c = 0; c < iCurImage->Bpp; c++) {
iCurImage->Data[y * iCurImage->Bps + x * iCurImage->Bpp + c] =
ScanLine[x + c * Header->Bps];
}
}
}
iUnCache();
// Read in the palette
if (Header->Version == 5 && iCurImage->Bpp == 1) {
x = itell();
if (iread(&ByteHead, 1, 1) == 0) { // If true, assume that we have a luminance image.
ilGetError(); // Get rid of the IL_FILE_READ_ERROR.
iCurImage->Format = IL_LUMINANCE;
if (iCurImage->Pal.Palette)
ifree(iCurImage->Pal.Palette);
iCurImage->Pal.PalSize = 0;
iCurImage->Pal.PalType = IL_PAL_NONE;
}
else {
if (ByteHead != 12) // Some Quake2 .pcx files don't have this byte for some reason.
iseek(-1, IL_SEEK_CUR);
if (iread(iCurImage->Pal.Palette, 1, iCurImage->Pal.PalSize) != iCurImage->Pal.PalSize)
goto file_read_error;
}
}
ifree(ScanLine);
return IL_TRUE;
file_read_error:
ifree(ScanLine);
//added 2003-09-01
ilSetError(IL_FILE_READ_ERROR);
return IL_FALSE;
}
ILboolean iUncompressSmall(PCXHEAD *Header) {
ILuint i = 0, j, k, c, d, x, y, Bps;
ILubyte HeadByte, Colour, Data = 0, *ScanLine;
if (!ilTexImage(Header->Xmax - Header->Xmin + 1, Header->Ymax - Header->Ymin + 1, 1, 1, 0, IL_UNSIGNED_BYTE, NULL)) {
return IL_FALSE;
}
iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
switch (Header->NumPlanes)
{
case 1:
iCurImage->Format = IL_LUMINANCE;
break;
case 4:
iCurImage->Format = IL_COLOUR_INDEX;
break;
default:
ilSetError(IL_ILLEGAL_FILE_VALUE);
return IL_FALSE;
}
if (Header->NumPlanes == 1 && Header->Bpp == 1) {
for (j = 0; j < iCurImage->Height; j++) {
i = 0; //number of written pixels
while (i < iCurImage->Width) {
if (iread(&HeadByte, 1, 1) != 1)
return IL_FALSE;
if (HeadByte >= 192) {
HeadByte -= 192;
if (iread(&Data, 1, 1) != 1)
return IL_FALSE;
for (c = 0; c < HeadByte; c++) {
k = 128;
for (d = 0; d < 8 && i < iCurImage->Width; d++) {
iCurImage->Data[j * iCurImage->Width + i++] = ((Data & k) != 0 ? 255 : 0);
k >>= 1;
}
}
}
else {
k = 128;
for (c = 0; c < 8 && i < iCurImage->Width; c++) {
iCurImage->Data[j * iCurImage->Width + i++] = ((HeadByte & k) != 0 ? 255 : 0);
k >>= 1;
}
}
}
//if(Data != 0)
//changed 2003-09-01:
//There has to be an even number of bytes per line in a pcx.
//One byte can hold up to 8 bits, so Width/8 bytes
//are needed to hold a 1 bit per pixel image line.
//If Width/8 is even no padding is needed,
//one pad byte has to be read otherwise.
//(let's hope the above is true ;-))
if(!((iCurImage->Width >> 3) & 0x1))
igetc(); // Skip pad byte
}
}
else if (Header->NumPlanes == 4 && Header->Bpp == 1){ // 4-bit images
//changed decoding 2003-09-10 (was buggy)...could need a speedup
Bps = Header->Bps * Header->NumPlanes * 8;
iCurImage->Pal.Palette = (ILubyte*)ialloc(16 * 3); // Size of palette always (48 bytes).
ScanLine = (ILubyte*)ialloc(Bps);
if (iCurImage->Pal.Palette == NULL || ScanLine == NULL) {
ifree(ScanLine);
ifree(iCurImage->Pal.Palette);
return IL_FALSE;
}
memcpy(iCurImage->Pal.Palette, Header->ColMap, 16 * 3);
iCurImage->Pal.PalSize = 16 * 3;
iCurImage->Pal.PalType = IL_PAL_RGB24;
memset(iCurImage->Data, 0, iCurImage->SizeOfData);
if (iGetHint(IL_MEM_SPEED_HINT) == IL_FASTEST)
iPreCache(iCurImage->SizeOfData / 4);
for (y = 0; y < iCurImage->Height; y++) {
x = 0;
while (x < Bps) {
if (iread(&HeadByte, 1, 1) != 1) {
iUnCache();
ifree(ScanLine);
return IL_FALSE;
}
if ((HeadByte & 0xC0) == 0xC0) {
HeadByte &= 0x3F;
if (iread(&Colour, 1, 1) != 1) {
iUnCache();
ifree(ScanLine);
return IL_FALSE;
}
for (i = 0; i < HeadByte; i++) {
k = 128;
for (j = 0; j < 8 && x < Bps; j++) {
ScanLine[x++] = (Colour & k)?1:0;
k >>= 1;
}
}
}
else {
k = 128;
for (j = 0; j < 8 && x < Bps; j++) {
ScanLine[x++] = (HeadByte & k)?1:0;
k >>= 1;
}
}
}
for (x = 0; x < iCurImage->Width; x++) { // 'Cleverly' ignores the pad bytes. ;)
for(c = 0; c < Header->NumPlanes; c++)
iCurImage->Data[y * iCurImage->Width + x] |= ScanLine[x + c*Header->Bps*8] << c;
}
}
iUnCache();
ifree(ScanLine);
}
else {
ilSetError(IL_FORMAT_NOT_SUPPORTED);
return IL_FALSE;
}
return IL_TRUE;
}
//! Writes a .pcx file
ILboolean ilSavePcx(ILconst_string FileName) {
ILHANDLE PcxFile;
ILboolean bPcx = IL_FALSE;
if (ilGetBoolean(IL_FILE_MODE) == IL_FALSE) {
if (iFileExists(FileName)) {
ilSetError(IL_FILE_ALREADY_EXISTS);
return IL_FALSE;
}
}
PcxFile = iopenw(FileName);
if (PcxFile == NULL) {
ilSetError(IL_COULD_NOT_OPEN_FILE);
return bPcx;
}
bPcx = ilSavePcxF(PcxFile);
iclosew(PcxFile);
return bPcx;
}
//! Writes a .pcx to an already-opened file
ILboolean ilSavePcxF(ILHANDLE File) {
iSetOutputFile(File);
return iSavePcxInternal();
}
//! Writes a .pcx to a memory "lump"
ILboolean ilSavePcxL(ILvoid *Lump, ILuint Size) {
iSetOutputLump(Lump, Size);
return iSavePcxInternal();
}
// Internal function used to save the .pcx.
ILboolean iSavePcxInternal() {
ILuint i, c, PalSize;
ILpal *TempPal;
ILimage *TempImage = iCurImage;
ILubyte *TempData;
if (iCurImage == NULL) {
ilSetError(IL_ILLEGAL_OPERATION);
return IL_FALSE;
}
switch (iCurImage->Format)
{
case IL_LUMINANCE:
TempImage = iConvertImage(iCurImage, IL_COLOUR_INDEX, IL_UNSIGNED_BYTE);
if (TempImage == NULL)
return IL_FALSE;
break;
case IL_BGR:
TempImage = iConvertImage(iCurImage, IL_RGB, IL_UNSIGNED_BYTE);
if (TempImage == NULL)
return IL_FALSE;
break;
case IL_BGRA:
TempImage = iConvertImage(iCurImage, IL_RGBA, IL_UNSIGNED_BYTE);
if (TempImage == NULL)
return IL_FALSE;
break;
default:
if (iCurImage->Bpc > 1) {
TempImage = iConvertImage(iCurImage, iCurImage->Format, IL_UNSIGNED_BYTE);
if (TempImage == NULL)
return IL_FALSE;
}
}
if (TempImage->Origin != IL_ORIGIN_UPPER_LEFT) {
TempData = iGetFlipped(TempImage);
if (TempData == NULL) {
if (TempImage != iCurImage) {
ilCloseImage(TempImage);
}
return IL_FALSE;
}
}
else {
TempData = TempImage->Data;
}
iputc(0xA); // Manufacturer - always 10
iputc(0x5); // Version Number - always 5
iputc(0x1); // Encoding - always 1
iputc(0x8); // Bits per channel
SaveLittleUShort(0); // X Minimum
SaveLittleUShort(0); // Y Minimum
SaveLittleUShort((ILushort)(iCurImage->Width - 1));
SaveLittleUShort((ILushort)(iCurImage->Height - 1));
SaveLittleUShort(0);
SaveLittleUShort(0);
// Useless palette info?
for (i = 0; i < 48; i++) {
iputc(0);
}
iputc(0x0); // Reserved - always 0
iputc(iCurImage->Bpp); // Number of planes - only 1 is supported right now
SaveLittleUShort((ILushort)(iCurImage->Width & 1 ? iCurImage->Width + 1 : iCurImage->Width)); // Bps
SaveLittleUShort(0x1); // Palette type - ignored?
// Mainly filler info
for (i = 0; i < 58; i++) {
iputc(0x0);
}
// Output data
for (i = 0; i < TempImage->Height; i++) {
for (c = 0; c < TempImage->Bpp; c++) {
encLine(TempData + TempImage->Bps * i + c, TempImage->Width, (ILubyte)(TempImage->Bpp - 1));
}
}
// Automatically assuming we have a palette...dangerous!
// Also assuming 3 bpp palette
iputc(0xC); // Pad byte must have this value
// If the current image has a palette, take care of it
if (TempImage->Format == IL_COLOUR_INDEX) {
// If the palette in .pcx format, write it directly
if (TempImage->Pal.PalType == IL_PAL_RGB24) {
iwrite(TempImage->Pal.Palette, 1, TempImage->Pal.PalSize);
}
else {
TempPal = iConvertPal(&TempImage->Pal, IL_PAL_RGB24);
if (TempPal == NULL) {
if (TempImage->Origin == IL_ORIGIN_LOWER_LEFT)
ifree(TempData);
if (TempImage != iCurImage)
ilCloseImage(TempImage);
return IL_FALSE;
}
iwrite(TempPal->Palette, 1, TempPal->PalSize);
ifree(TempPal->Palette);
ifree(TempPal);
}
}
// If the palette is not all 256 colours, we have to pad it.
PalSize = 768 - iCurImage->Pal.PalSize;
for (i = 0; i < PalSize; i++) {
iputc(0x0);
}
if (TempImage->Origin == IL_ORIGIN_LOWER_LEFT)
ifree(TempData);
if (TempImage != iCurImage)
ilCloseImage(TempImage);
return IL_TRUE;
}
// Routine used from ZSoft's pcx documentation
ILuint encput(ILubyte byt, ILubyte cnt) {
if (cnt) {
if ((cnt == 1) && (0xC0 != (0xC0 & byt))) {
if (IL_EOF == iputc(byt))
return(0); /* disk write error (probably full) */
return(1);
}
else {
if (IL_EOF == iputc((ILubyte)((ILuint)0xC0 | cnt)))
return (0); /* disk write error */
if (IL_EOF == iputc(byt))
return (0); /* disk write error */
return (2);
}
}
return (0);
}
/* This subroutine encodes one scanline and writes it to a file.
It returns number of bytes written into outBuff, 0 if failed. */
ILuint encLine(ILubyte *inBuff, ILint inLen, ILubyte Stride)
{
ILubyte _this, last;
ILint srcIndex, i;
ILint total;
ILubyte runCount; // max single runlength is 63
total = 0;
runCount = 1;
last = *(inBuff);
// Find the pixel dimensions of the image by calculating
//[XSIZE = Xmax - Xmin + 1] and [YSIZE = Ymax - Ymin + 1].
//Then calculate how many bytes are in a "run"
for (srcIndex = 1; srcIndex < inLen; srcIndex++) {
inBuff += Stride;
_this = *(++inBuff);
if (_this == last) { // There is a "run" in the data, encode it
runCount++;
if (runCount == 63) {
if (! (i = encput(last, runCount)))
return (0);
total += i;
runCount = 0;
}
}
else { // No "run" - _this != last
if (runCount) {
if (! (i = encput(last, runCount)))
return(0);
total += i;
}
last = _this;
runCount = 1;
}
} // endloop
if (runCount) { // finish up
if (! (i = encput(last, runCount)))
return (0);
if (inLen % 2)
iputc(0);
return (total + i);
}
else {
if (inLen % 2)
iputc(0);
}
return (total);
}
#endif//IL_NO_PCX