Hi john c.
I will try and find out, some information, as regards the RLE asap, and get an suitable image, of the cockpit view. Many thanks for all you have done, so far as regards this thread. It is much appreciated.
Eddie
Hi john c.
I will try and find out, some information, as regards the RLE asap, and get an suitable image, of the cockpit view. Many thanks for all you have done, so far as regards this thread. It is much appreciated.
Eddie
Hi john.c,
I typed what you said, I should, type on the command line in Visual Studio, and all that happens is Hello World ! is outputted, and nothing is done to the P51DD.PNL File. I am not sure why that is the case ? Could you suggest where I have gone wrong ? I have attached an Screenshot, of the ME110 Cockpit view as a Jpeg File.
I have remembered something, that the person who wrote the original Code said.
He said that some Files, might have more than one image in, as DOS Games had different graphics Formats, i.e. CGA, EGA, VGA, MCGA etc, so that is probably why the P51DD.PNL File had another image output ?
Best Regards
Eddie Winch
Last edited by eddywinch82; 01-18-2023 at 09:57 AM.
Comparing that picture to the mangled output gave me an idea and it looks like I figured it out. Apparently the older game used only 32 grayscale values instead of 64. So the single-byte values have a 3-bit count instead of a 2-bit count. Changing the code to account for that (and modifying the bmp palette) currently gives the following. I will try to figure out the correct palette tonight (or later today if I get some time).
It would be useful if you could upload a better shot of the ME110 cockpit.
Just grab the window, not the entire desktop (presumably there's a way to do that).
And save it as a png or gif (losseless compression), not a jpg (lossy compression).
All truths are half-truths. - A.N. Whitehead
Hi john c.
I am amazed by what you have done well done ))
Here is a better .png screenshot :-
Hi john.c,
How are you getting on with this ?
Eddie
The PNL files you've given us contain 4 chunks.
The first seems to be a smallish fairly simple image but I can't decode it.
The second is the cockpit image.
The third and forth chunks contain palettes of 64 colors and some other data that I can't decipher (and are the same as each other).
Applying the palette to the image yields this:
The PAC file does not contain a palette so I don't know how to set it's palette of 32 colors correctly.
I call the older file V1 files and the newer ones V2 files.
This is the current program.
If the program is called pacdump:
$ pacdump 110DASH.PAC # process file as V1
$ pacdump -n P51BD.PNL # process file as V2 (default gray palette)
$ pacdump -n -p2 P51BD.PNL # process file as V2 using chunk 2 as palette
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; }
All truths are half-truths. - A.N. Whitehead
Hi john.c,
You are amazing, I wish I had your coding skills, thankyou so much, for what you done so far.
I was just wondering, could you alter the latest code, so that multiple files, can be processed at the same time from a certain folder ? Like with the original code I posted ? And be able to save the output files to a folder of choice ?
Or would I use *.PNL to process all files ?
Regards
Eddie Winch ))
It would be interesting if you could upload a couple more examples of each type of file.
Here's a version of the program that can process multiple files and put them into a given directory.
The directory is given after the -d flag (abc in the e.g.).
$ pacdump -n -p 2 -d abc *.PNL # decode all PNL file to directory abc
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 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; const char *dirname; } Info; Info info = { .min_width = INT_MAX, .n_palette = -1, .filename = NULL, .dirname = NULL }; int version = 1; // 1 for older files, 2 for newer 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 = (version == 1 ? 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 = (version == 1 ? 3 : 2); const int size = (version == 1 ? 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.dirname); strcat(outfilename, "/"); strcat(outfilename, info.filename); char *p = strrchr(outfilename, '.'); if (p) *p = '\0'; sprintf(outfilename + strlen(outfilename), "-out%d.bmp", info.output_file_index); // Make the directory if it doesn't exist (mkdir is same on Windows and linux) char cmd[100]; sprintf(cmd, "mkdir %s", info.dirname); system(cmd); 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) { printf("File: %s\n", info.filename); 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 value and count-1 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; // case 0xFA: // chunk 0 of the PNL files use this // break; // but I don't know what it does default: if (version == 1) { 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) { printf("Usage: %s [-n] [-p N] [-d DIRECTORY] INPUT_FILE\n", argv[0]); exit(EXIT_FAILURE); } while (**++argv == '-') { if (strcmp(*argv, "-n") == 0) version = 2; 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 if (strncmp(*argv, "-d", 2) == 0) { if (argv[0][2]) info.dirname = &argv[0][2]; else { if (*++argv == NULL) { fprintf(stderr, "Error: Missing parameters\n"); exit(EXIT_FAILURE); } info.dirname = *argv; } } else { fprintf(stderr, "Error: Unknown switch: %s\n", *argv); exit(EXIT_FAILURE); } } if (!*argv) { fprintf(stderr, "Error: No filename\n"); exit(EXIT_FAILURE); } for ( ; *argv; ++argv) { info.output_file_index = 0; info.filename = *argv; FILE *fp = fopen(info.filename, "rb"); if (!fp) { perror(info.filename); exit(EXIT_FAILURE); } process(fp); fclose(fp); } return 0; }
All truths are half-truths. - A.N. Whitehead
Hi john.c,
Here I have Uploaded, 4 Files as a .zip File. The .PAC Files are the German and British Fighter Aircraft Dashboard Files, from Their Finest Hour. And the two .PNL Files, are the BF109 Dashboard and FW190 Dashboard Files, from Secret Weapons Of The Luftwaffe.
Eddie )
Hi john.c,
I built the program as a c one, but when I run the Program from the Command Line in Visual Studio, it just outputs Hello World !, no Folder is created with output Files etc, could you tell me what I am doing wrong ?
Regards
Eddie Winch )
Obviously it makes no sense for it to print Hello World, so you must be running the wrong program. I have no idea what you are doing wrong.
All truths are half-truths. - A.N. Whitehead
Hi john.c,
I have sorted the problem now, I tried compiling the program from the command line, and it works now, what should I type to select all files i.e. *.PNL from a directory i.e. pnl ?
I use from the command prompt, or the command line in Visual Studio :-
pac-misc -n -p 2 -d raw2 *.PNL
It didn't work with the $ in front of pac-misc
but it says :-
*.PNL: Invalid argument
The program works, with individual .PNL Files though.
Last edited by eddywinch82; 01-20-2023 at 06:29 PM.
This is painful.It didn't work with the $ in front of pac-misc
I'm taking a break.
All truths are half-truths. - A.N. Whitehead
This worked for BFDASH.PAC :-
pac-misc -d raw1 BFDASH.PAC
And this worked for HE162D.PNL :-
pac-misc -n -p 2 -d raw2 HE162D.PNL
And I have attached the output .bmp Files,
From BFDASH.PAC and BFLWING.PAC
British Fighters Dashboard and Left Wing Views :-
Last edited by eddywinch82; 01-20-2023 at 07:49 PM.
Hi john.c,
I have attached two .zip Files, one is from the game Battlehawks 1942 i.e BH1942, which was the first game in the WW2 Game trilogy. The two Files, have the same output .bmp issue as there was, in the game Their Finest Hour with the 110DASH.PAC File. The other .zip File, has two files from Their Finest Hour, i.e. BOB1940 with the same issue, could you analyse the files, and fix that issue for me, if that is okay ?
Also could you find a way, to work on all files at once ? as *.PAC or *.PNL doesn't work, producing an error. And be able to process the files, when they are in another directory ?
Regards
Eddie Winch