2022-07-29 06:04:05 -07:00
|
|
|
#include "pcx.h"
|
|
|
|
|
|
|
|
#include "memory_manager.h"
|
|
|
|
|
2022-09-23 05:43:44 -07:00
|
|
|
namespace fallout {
|
|
|
|
|
2022-07-29 06:04:05 -07:00
|
|
|
// 0x519DC8
|
|
|
|
unsigned char gPcxLastRunLength = 0;
|
|
|
|
|
|
|
|
// 0x519DC9
|
|
|
|
unsigned char gPcxLastValue = 0;
|
|
|
|
|
|
|
|
// NOTE: The reading method in this function is a little bit odd. It does not
|
|
|
|
// use high level reading functions, which can read right into struct. Instead
|
|
|
|
// they read everything into temporary variables. There are no error checks.
|
|
|
|
//
|
|
|
|
// 0x4961D4
|
|
|
|
void pcxReadHeader(PcxHeader* pcxHeader, File* stream)
|
|
|
|
{
|
|
|
|
pcxHeader->identifier = fileReadChar(stream);
|
|
|
|
pcxHeader->version = fileReadChar(stream);
|
|
|
|
pcxHeader->encoding = fileReadChar(stream);
|
|
|
|
pcxHeader->bitsPerPixel = fileReadChar(stream);
|
|
|
|
|
|
|
|
short minX;
|
|
|
|
fileRead(&minX, 2, 1, stream);
|
|
|
|
pcxHeader->minX = minX;
|
|
|
|
|
|
|
|
short minY;
|
|
|
|
fileRead(&minY, 2, 1, stream);
|
|
|
|
pcxHeader->minY = minY;
|
|
|
|
|
|
|
|
short maxX;
|
|
|
|
fileRead(&maxX, 2, 1, stream);
|
|
|
|
pcxHeader->maxX = maxX;
|
|
|
|
|
|
|
|
short maxY;
|
|
|
|
fileRead(&maxY, 2, 1, stream);
|
|
|
|
pcxHeader->maxY = maxY;
|
|
|
|
|
|
|
|
short horizontalResolution;
|
|
|
|
fileRead(&horizontalResolution, 2, 1, stream);
|
|
|
|
pcxHeader->horizontalResolution = horizontalResolution;
|
|
|
|
|
|
|
|
short verticalResolution;
|
|
|
|
fileRead(&verticalResolution, 2, 1, stream);
|
|
|
|
pcxHeader->verticalResolution = verticalResolution;
|
|
|
|
|
|
|
|
for (int index = 0; index < 48; index++) {
|
|
|
|
pcxHeader->palette[index] = fileReadChar(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
pcxHeader->reserved1 = fileReadChar(stream);
|
|
|
|
pcxHeader->planeCount = fileReadChar(stream);
|
|
|
|
|
|
|
|
short bytesPerLine;
|
|
|
|
fileRead(&bytesPerLine, 2, 1, stream);
|
|
|
|
pcxHeader->bytesPerLine = bytesPerLine;
|
|
|
|
|
|
|
|
short paletteType;
|
|
|
|
fileRead(&paletteType, 2, 1, stream);
|
|
|
|
pcxHeader->paletteType = paletteType;
|
|
|
|
|
|
|
|
short horizontalScreenSize;
|
|
|
|
fileRead(&horizontalScreenSize, 2, 1, stream);
|
|
|
|
pcxHeader->horizontalScreenSize = horizontalScreenSize;
|
|
|
|
|
|
|
|
short verticalScreenSize;
|
|
|
|
fileRead(&verticalScreenSize, 2, 1, stream);
|
|
|
|
pcxHeader->verticalScreenSize = verticalScreenSize;
|
|
|
|
|
|
|
|
for (int index = 0; index < 54; index++) {
|
|
|
|
pcxHeader->reserved2[index] = fileReadChar(stream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x49636C
|
|
|
|
int pcxReadLine(unsigned char* data, int size, File* stream)
|
|
|
|
{
|
|
|
|
unsigned char runLength = gPcxLastRunLength;
|
|
|
|
unsigned char value = gPcxLastValue;
|
|
|
|
|
|
|
|
int uncompressedSize = 0;
|
|
|
|
int index = 0;
|
|
|
|
do {
|
|
|
|
uncompressedSize += runLength;
|
|
|
|
while (runLength > 0 && index < size) {
|
|
|
|
data[index] = value;
|
|
|
|
runLength--;
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
|
|
|
|
gPcxLastRunLength = runLength;
|
|
|
|
gPcxLastValue = value;
|
|
|
|
|
|
|
|
if (runLength != 0) {
|
|
|
|
uncompressedSize -= runLength;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
value = fileReadChar(stream);
|
|
|
|
if ((value & 0xC0) == 0xC0) {
|
|
|
|
gPcxLastRunLength = value & 0x3F;
|
|
|
|
value = fileReadChar(stream);
|
|
|
|
runLength = gPcxLastRunLength;
|
|
|
|
} else {
|
|
|
|
runLength = 1;
|
|
|
|
}
|
|
|
|
} while (index < size);
|
|
|
|
|
|
|
|
gPcxLastRunLength = runLength;
|
|
|
|
gPcxLastValue = value;
|
|
|
|
|
|
|
|
return uncompressedSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x49641C
|
|
|
|
int pcxReadPalette(PcxHeader* pcxHeader, unsigned char* palette, File* stream)
|
|
|
|
{
|
|
|
|
if (pcxHeader->version != 5) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
long pos = fileTell(stream);
|
|
|
|
long size = fileGetSize(stream);
|
|
|
|
fileSeek(stream, size - 769, SEEK_SET);
|
|
|
|
if (fileReadChar(stream) != 12) {
|
|
|
|
fileSeek(stream, pos, SEEK_SET);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int index = 0; index < 768; index++) {
|
|
|
|
palette[index] = fileReadChar(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
fileSeek(stream, pos, SEEK_SET);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x496494
|
|
|
|
unsigned char* pcxRead(const char* path, int* widthPtr, int* heightPtr, unsigned char* palette)
|
|
|
|
{
|
|
|
|
File* stream = fileOpen(path, "rb");
|
|
|
|
if (stream == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
PcxHeader pcxHeader;
|
|
|
|
pcxReadHeader(&pcxHeader, stream);
|
|
|
|
|
|
|
|
int width = pcxHeader.maxX - pcxHeader.minX + 1;
|
|
|
|
int height = pcxHeader.maxY - pcxHeader.minY + 1;
|
|
|
|
|
|
|
|
*widthPtr = width;
|
|
|
|
*heightPtr = height;
|
|
|
|
|
|
|
|
int bytesPerLine = pcxHeader.planeCount * pcxHeader.bytesPerLine;
|
|
|
|
unsigned char* data = (unsigned char*)internal_malloc_safe(bytesPerLine * height, __FILE__, __LINE__); // "..\\int\\PCX.C", 195
|
|
|
|
if (data == NULL) {
|
|
|
|
// NOTE: This code is unreachable, internal_malloc_safe never fails.
|
|
|
|
fileClose(stream);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
gPcxLastRunLength = 0;
|
|
|
|
gPcxLastValue = 0;
|
|
|
|
|
|
|
|
unsigned char* ptr = data;
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
|
|
pcxReadLine(ptr, bytesPerLine, stream);
|
|
|
|
ptr += width;
|
|
|
|
}
|
|
|
|
|
|
|
|
pcxReadPalette(&pcxHeader, palette, stream);
|
|
|
|
|
|
|
|
fileClose(stream);
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
2022-09-23 05:43:44 -07:00
|
|
|
|
|
|
|
} // namespace fallout
|