OpenRaider  0.1.4-dev
Open Source Tomb Raider Game Engine implementation
pcx.cpp
Go to the documentation of this file.
1 
11 #include <fstream>
12 
13 #include "global.h"
14 #include "Log.h"
15 #include "utils/pcx.h"
16 
17 int pcxCheck(const char* filename) {
18  assert(filename != nullptr);
19  assert(filename[0] != '\0');
20 
21  std::ifstream file(filename, std::ios::in | std::ios::binary);
22 
23  // Read raw PCX header, 128 bytes
24  unsigned char* header = new unsigned char[128];
25 
26  // Basic validation
27  if (!file.read((char*)(&header[0]), 128)) {
28  Log::get(LOG_ERROR) << "File not big enough for valid PCX header!" << Log::endl;
29  delete [] header;
30  return -1;
31  }
32 
33  if (header[0] != 0x0A) {
34  Log::get(LOG_ERROR) << "Magic number at file start is wrong (" << header[0] << " != 0x0A)" <<
35  Log::endl;
36  delete [] header;
37  return -2;
38  }
39 
40  if ((header[1] != 0) && ((header[1] < 2) || (header[1] > 5))) {
41  // Valid: 0, 2, 3, 4, 5
42  Log::get(LOG_ERROR) << "Unknown PCX file format version (" << header[1] << ")" << Log::endl;
43  delete [] header;
44  return -3;
45  }
46 
47  if ((header[2] != 0) && (header[2] != 1)) {
48  Log::get(LOG_ERROR) << "Unknown PCX file encoding (" << header[2] << ")" << Log::endl;
49  delete [] header;
50  return -4;
51  }
52 
53  if (header[3] != 8) {
54  Log::get(LOG_ERROR) << "Only supporting 8bit (" << header[3] << "bit)" << Log::endl;
55  delete [] header;
56  return -5;
57  }
58 
59  if (header[64] != 0) {
60  Log::get(LOG_ERROR) << "Reserved field is used (" << header[64] << " != 0)" << Log::endl;
61  delete [] header;
62  return -6;
63  }
64 
65  delete [] header;
66  return 0;
67 }
68 
69 int pcxLoad(const char* filename, unsigned char** image,
70  unsigned int* width, unsigned int* height,
71  ColorMode* mode, unsigned int* bpp) {
72  assert(filename != nullptr);
73  assert(filename[0] != '\0');
74  assert(image != nullptr);
75  assert(width != nullptr);
76  assert(height != nullptr);
77  assert(mode != nullptr);
78  assert(bpp != nullptr);
79 
80  std::ifstream file(filename, std::ios::in | std::ios::binary);
81 
82  // Read raw PCX header, 128 bytes
83  unsigned char* header = new unsigned char[128];
84 
85  // Basic validation
86  if (!file.read((char*)(&header[0]), 128)) {
87  Log::get(LOG_ERROR) << "File not big enough for valid PCX header!" << Log::endl;
88  delete [] header;
89  return -1;
90  }
91 
92  if (header[0] != 0x0A) {
93  Log::get(LOG_ERROR) << "Magic number at file start is wrong (" << header[0] << " != 0x0A)" <<
94  Log::endl;
95  delete [] header;
96  return -2;
97  }
98 
99  if ((header[1] != 0) && ((header[1] < 2) || (header[1] > 5))) {
100  // Valid: 0, 2, 3, 4, 5
101  Log::get(LOG_ERROR) << "Unknown PCX file format version (" << header[1] << ")" << Log::endl;
102  delete [] header;
103  return -3;
104  }
105 
106  if ((header[2] != 0) && (header[2] != 1)) {
107  Log::get(LOG_ERROR) << "Unknown PCX file encoding (" << header[2] << ")" << Log::endl;
108  delete [] header;
109  return -4;
110  }
111 
112  if (header[3] != 8) {
113  Log::get(LOG_ERROR) << "Only supporting 8bit (" << header[3] << "bit)" << Log::endl;
114  delete [] header;
115  return -5;
116  }
117 
118  if (header[64] != 0) {
119  Log::get(LOG_ERROR) << "Reserved field is used (" << header[64] << " != 0)" << Log::endl;
120  delete [] header;
121  return -6;
122  }
123 
124  // Read header informations
125  bool versionFive = (header[1] == 5);
126  bool compressed = (header[2] == 1);
127  //unsigned char bitsPerPixel = header[3];
128  unsigned int xMin = header[4] | (header[5] << 8);
129  unsigned int yMin = header[6] | (header[7] << 8);
130  unsigned int xMax = header[8] | (header[9] << 8);
131  unsigned int yMax = header[10] | (header[11] << 8);
132  //unsigned int hDPI = header[12] | (header[13] << 8);
133  //unsigned int vDPI = header[14] | (header[15] << 8);
134  //unsigned char *colormap = header + 16;
135  unsigned char nPlanes = header[65];
136  unsigned int bytesPerLine = header[66] | (header[67] << 8);
137  //unsigned int paletteInfo = header[68] | (header[69] << 8);
138  //unsigned int hScreenSize = header[70] | (header[71] << 8); // Only in some versionFive files
139  //unsigned int vScreenSize = header[72] | (header[73] << 8); // Only in some versionFive files
140 
141  delete [] header;
142 
143  // Calculations
144  *width = xMax - xMin + 1;
145  *height = yMax - yMin + 1;
146  unsigned long totalBytes = nPlanes * bytesPerLine; // total bytes per scan line
147  unsigned long imageSize = totalBytes** height;
148  unsigned char* buffer = new unsigned char[imageSize];
149  unsigned long b = 0;
150 
151  // Read encoded pixel data
152  for (unsigned long i = 0; i < imageSize;) {
153  unsigned int n = 1; // Run-length-encoding assumes 1
154  int c = file.get();
155  if (!file) {
156  Log::get(LOG_ERROR) << "Could not read data (" << i
157  << (file.eof() ? " EOF" : "") << ")" << Log::endl;
158  delete [] buffer;
159  return -7;
160  }
161 
162  // Run-Length-Encoding
163  if (compressed) {
164  if ((c & 0xC0) == 0xC0) {
165  n = c & 0x3F;
166  c = file.get();
167  if (!file) {
168  Log::get(LOG_ERROR) << "Could not read data rle (" << i
169  << (file.eof() ? " EOF" : "") << ")" << Log::endl;
170  delete [] buffer;
171  return -8;
172  }
173  }
174  }
175 
176  for (unsigned int j = 0; j < n; j++)
177  buffer[b++] = (unsigned char)c;
178 
179  i += n;
180  }
181 
182  // Read color palette
183  unsigned char* palette = nullptr;
184  if (versionFive) {
185  int c = file.get();
186  if ((c == 12) && file) {
187  palette = new unsigned char[768];
188  for (unsigned int i = 0; i < 768; i++) {
189  palette[i] = (unsigned char)file.get();
190  if (!file) {
191  Log::get(LOG_ERROR) << "Could not read 256 color palette (" << i << ")" << Log::endl;
192  delete [] buffer;
193  delete [] palette;
194  return -9;
195  }
196  }
197  }
198  }
199 
200  // Bring buffer into preferred format
201  unsigned long size = *width** height * 4;
202  *image = new unsigned char[size];
203  for (unsigned int y = 0; y < *height; y++) {
204  for (unsigned int x = 0; x < *width; x++) {
205  unsigned long baseIndex = (x + (y** width)) * 4;
206  unsigned char alpha = 255, red = 0, green = 0, blue = 0;
207 
208  if (palette != nullptr) {
209  if (nPlanes == 1) {
210  red = palette[buffer[(y * totalBytes) + x] * 3];
211  green = palette[(buffer[(y * totalBytes) + x] * 3) + 1];
212  blue = palette[(buffer[(y * totalBytes) + x] * 3) + 2];
213  } else {
214  Log::get(LOG_ERROR) << "Unsupported number of planes (" << nPlanes << ")" << Log::endl;
215  delete [] buffer;
216  delete [] palette;
217  delete [] *image;
218  *image = nullptr;
219  return -10;
220  }
221  } else {
222  if ((nPlanes == 3) || (nPlanes == 4)) {
223  red = buffer[(y * totalBytes) + x];
224  green = buffer[(y * totalBytes) + *width + x];
225  blue = buffer[(y * totalBytes) + (2 * *width) + x];
226  if (nPlanes == 4)
227  alpha = buffer[(y * totalBytes) + (3 * *width) + x];
228  } else if (nPlanes == 1) {
229  red = green = blue = buffer[(y * totalBytes) + x];
230  } else {
231  Log::get(LOG_ERROR) << "Unsupported number of planes (" << nPlanes << ")" << Log::endl;
232  delete [] buffer;
233  delete [] palette;
234  delete [] *image;
235  *image = nullptr;
236  return -11;
237  }
238  }
239 
240  (*image)[baseIndex + 0] = red;
241  (*image)[baseIndex + 1] = green;
242  (*image)[baseIndex + 2] = blue;
243  (*image)[baseIndex + 3] = alpha;
244  }
245  }
246 
247  *mode = ColorMode::RGBA;
248  *bpp = 32;
249 
250  delete [] buffer;
251  delete [] palette;
252  return 0;
253 }
254 
PCX image reader.
int pcxCheck(const char *filename)
Check if a file is a valid PCX image.
Definition: pcx.cpp:17
Included everywhere.
static LogLevel & get(int level)
Definition: Log.cpp:14
Global Logging Utility.
#define assert(x)
Definition: global.h:124
static const char endl
Definition: Log.h:35
#define LOG_ERROR
Definition: Log.h:19
int pcxLoad(const char *filename, unsigned char **image, unsigned int *width, unsigned int *height, ColorMode *mode, unsigned int *bpp)
Load a PCX image file into a buffer.
Definition: pcx.cpp:69
ColorMode