Code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#define MAX_W 600
#define MAX_H 300
#define HEADER_SIZE 54
#define PALETTE_ENTRIES 256
#define PALETTE_SIZE (PALETTE_ENTRIES * 4) // 4 bytes per entry
#define DATA_OFFSET (HEADER_SIZE + PALETTE_SIZE)
typedef unsigned char Byte;
typedef enum Mode { V1, V2 } Mode; // V1 for older files, V2 for newer
typedef struct Info {
Byte image[MAX_H][MAX_W];
int min_width, max_width;
int width, height;
int adder;
int output_file_index;
int n_palette;
const char *filename;
} Info;
Info info = {
.min_width = INT_MAX,
.n_palette = -1,
.filename = NULL
};
Mode ProgramMode = V1;
void reset() {
memset(info.image, 0, MAX_H * MAX_W);
info.min_width = INT_MAX;
info.max_width = 0;
info.width = 0;
info.height = 0;
info.adder = 0;
}
void default_palette(Byte *pal) {
const int shift = (ProgramMode == V1 ? 3 : 2);
for (int color = 0; color < PALETTE_ENTRIES; color++) {
*pal++ = color << shift;
*pal++ = color << shift;
*pal++ = color << shift;
*pal++ = 0;
}
}
void fill_palette(Byte *pal, const char *infilename, int n_palette) {
if (n_palette == -1) {
default_palette(pal);
return;
}
FILE *f = fopen(infilename, "rb");
int byte, cnt = 0;
const int shift = (ProgramMode == V1 ? 3 : 2);
const int size = (ProgramMode == V1 ? 32 : 64);
while ((byte = fgetc(f)) != EOF) {
if (byte == 0xFF && ++cnt == n_palette) {
for (int i = 0; i < size; ++i) {
int r = fgetc(f), g = fgetc(f), b = fgetc(f);
pal[i * 4 ] = b << shift;
pal[i * 4 + 1] = g << shift;
pal[i * 4 + 2] = r << shift;
}
break;
}
}
fclose(f);
}
void save_bmp() {
Byte header[HEADER_SIZE] = {
0x42, 0x4D, // 0: BM signature
-1, -1, -1, -1, // 2: filesize
0, 0, 0, 0, // 6: reserved
-2, -2, -2, -2, // 10: offset to pixel array
0x28, 0, 0, 0, // 14: DIB header size
-3, -3, -3, -3, // 18: width
-4, -4, -4, -4, // 22: height
0x01, 0, // 26: planes
0x08, 0 // 28: bits per pixel
};
int pad = (4 - info.max_width % 4) % 4;
int file_size = DATA_OFFSET + (info.max_width + pad) * info.height;
Byte *output = malloc(file_size);
memcpy(output, header, HEADER_SIZE);
*(int*)(output + 2) = file_size;
*(int*)(output + 10) = DATA_OFFSET;
*(int*)(output + 18) = info.max_width;
*(int*)(output + 22) = -info.height;
Byte *pal = output + HEADER_SIZE;
if (info.n_palette == -1)
default_palette(pal);
else
fill_palette(pal, info.filename, info.n_palette);
for (int h = 0, pos = DATA_OFFSET; h < info.height; h++) {
for (int w = 0; w < info.max_width; w++)
output[pos++] = info.image[h][w];
for (int p = 0; p < pad; p++)
output[pos++] = 0;
}
// Output filenames are: filename-outN.bmp where N is 0,1,...
char outfilename[100];
strcpy(outfilename, info.filename);
char *p = strrchr(outfilename, '.');
if (p) *p = '\0';
sprintf(outfilename + strlen(outfilename), "-out%d.bmp",
info.output_file_index);
FILE *f = fopen(outfilename, "wb");
fwrite(output, 1, file_size, f);
fclose(f);
free(output);
}
void add(int value) {
if (info.height < MAX_H && info.width < MAX_W)
info.image[info.height][info.width++] = value;
}
void process(FILE *fp) {
for (int byte, prev = 0xFF; (byte = fgetc(fp)) != EOF; prev = byte) {
int count, value;
if (prev == 0xFF)
printf("Chunk: %d\n", info.output_file_index);
switch (byte) {
case 0xFF: // End of image
if (prev == 0xFE) {
if (info.min_width != info.max_width)
printf(" Error: min (%d) and max (%d) widths do not match\n",
info.min_width, info.max_width);
printf(" Width: %d Height: %d\n", info.max_width, info.height);
save_bmp();
}
reset();
++info.output_file_index;
break;
case 0xFE: // End of image line
if (info.width < info.min_width) info.min_width = info.width;
if (info.width > info.max_width) info.max_width = info.width;
++info.height;
info.width = 0;
break;
case 0xFD: // next two bytes are count-1 and value
count = fgetc(fp) + 1;
value = fgetc(fp);
for (int i = 0; i < count; ++i) add(value);
break;
case 0xFC: // next two bytes are count-1 and value
value = fgetc(fp); // orig. code had +1 here (but why?)
count = fgetc(fp) + 1;
for (int i = 0; i < count; ++i) // draw dithered line
add(i % 2 == 0 ? value : value + 1);
break;
case 0xFB: // Set adder used in default case
info.adder = fgetc(fp);
break;
default:
if (ProgramMode == V1) {
value = (byte >> 3) + info.adder; // High 5 bits are value
count = (byte & 7) + 1; // Low 3 bits are count-1
}
else {
value = (byte >> 2) + info.adder; // High 6 bits are value
count = (byte & 3) + 1; // Low 2 bits are count-1
}
for (int i = 0; i < count; ++i) add(value);
}
}
}
int main(int argc, char *argv[]) {
if (argc < 2 || argc > 5) {
printf("Usage: %s [-n] [-p N] INPUT_FILE\n", argv[0]);
exit(EXIT_FAILURE);
}
while (**++argv == '-') {
if (strcmp(*argv, "-n") == 0)
ProgramMode = V2;
else if (strncmp(*argv, "-p", 2) == 0) {
if (argv[0][2])
info.n_palette = atoi(&argv[0][2]);
else {
if (*++argv == NULL) {
fprintf(stderr, "Error: Missing parameters\n");
exit(EXIT_FAILURE);
}
info.n_palette = atoi(*argv);
}
}
else {
fprintf(stderr, "Error: Unknown switch: %s\n", *argv);
exit(EXIT_FAILURE);
}
}
if (!*argv) {
fprintf(stderr, "Error: No filename\n");
exit(EXIT_FAILURE);
}
info.filename = *argv;
FILE *fp = fopen(info.filename, "rb");
if (!fp) {
perror(info.filename);
exit(EXIT_FAILURE);
}
process(fp);
return 0;
}
Could someone look at the code, and tell me any changes that could be made to the Code i.e. adapt it ?, to extract the bitmap views from the S.PAC Files i.e. the Plane Shape Files, and save them as bitmaps? in the game. The .PAC Files are from the FE Folder in the game, and the .PNL Files from the CP Folder. the S.PAC Files are from the AC, i.e. Aircraft Folder in the game. and I have linked to some, of the output bitmap images, of.PAC and .PNL Files.