Help Needed With Finding Image Data In DOS Game File
I have been analysing, an File called FW190AS.PAC from a 16 Bit Game called Secret Weapons Of The Luftwaffe, in a Hex Editor, the Bytes are displayed in Little Endian format. My aim with this game, is to eventually change The Planes shapes in the game for the outside View, i.e. they are basic Bitmap images, to other shaped Planes. There is another File called FW190A.PAC which also contains images for different views of the Plane and other info. My feeling the 'S' at the end of the name of the File, refers to Shape.
Through trial and error and narrowing down byte sections in the FW190AS.PAC File, I have noticed for example, that when I change byte values, at certain positions at offset 1030 shown in the Hex Editor, one of the views at an angle of the FW190 plane i.e. Focke Wolf 190,
A line attached to the plane, on the right side appears, when I look out the rear view mirror view, changing the bytes produces different coloured sections on the line, each time I change the values. Also when I Change Bytes For Offsets 1000 to 10F0, with Bytes from the Corresponding Offsets in BF109GS.PAC, gives a certain angle view of the BF109G, not facing forwards but Northeast for the FW190A. When I change bytes in offset 1720 in FW190AS.PAC the Upwards View looks different.
Also If I do the same with the bytes, from offsets 1100 to 1400 in the BF109GS.PAC File, to offsets 1100 to 1400 in the FW190AS.PAC File I get the BF109G shown in the opening movie in the game instead of the FW190A.
I also believe that the different angle Views, for the outside views of the Planes in the game, are at different offsets in the shape Files. I am convinced too, that the Bytes for the different, outside angle views of the Planes, are at different offsets for the different planes' Shape Files, as the BF109GS.PAC bytes for the offsets 1000 to 10F0, give a different view for the BF109G, to the bytes from 1000 to 10F0 Offsets in FW190AS.PAC, which is a forward-facing view i.e. for the FW190A. This would make sense, as the total number of offsets, is different in each plane's shape file.
I have attached links to Screenshots of the FW190, and you will notice the colour change, in sections of the line out of the FW190A, also In the climbing Plane View changes, when changing bytes in offset 1720, changing the first byte to 07, puts a black line on the left wing, near where it is attached to the fuselage, and also a bitmap, of the original view of the FW190A.
the SWOTL Plane Shape Test 19 and SWOTL Plane Shape Test 20 Screenshot Files I link to amongst others correspond to the Files I named FW190AS (19).PAC and FW190AS (20).PAC respectively, those will need to change back to FW190AS.PAC to be used in the game, choose the Scramble from Brandis Mission, in Historical Missions for the FW190A, and look out of the rearview mirror, to see the new view, it sometimes takes a few seconds, to change to that, bytes were changed at offset 1030.
Here is a C Program, very kindly written by john.c on this forum for me, that extracts the images from Secret Weapons of the Luftwaffe Files, and saves them as a bitmap, some are in colour most output Greyscale images:-
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.
I watched the following Youtube Video, and particularly looked, at the Fonts Section of the video :-
Reverse Engineering a Classic Video Game (BioForge) - YouTube
I noticed that when I opened the BF109GS.PAC and FW190AS.PAC Files in HxD Hex Editor there were repeating Bytes at the top few Offsets, i.e. 01 04 0D 31 01 04 0D 31 01 04 0D 31, on the first offset in the FW190AS.PAC File, then some more repeats in the BF109GS.PAC File, i.e. 01 07 0C 11 01 07 0C 11 01 06 0C 12 01 06 0C 12 01 06 0C 12 01 06 0D 11 01 06 0D, could these repeats be akin, to what the man in the video says they could be, for the Bioforge Game ? And what sections of code i.e. offsets do they correspond to here, are they saying, where the offsets and bytes, for each of the angle views are in the File?
Here is a link to the game, where you can download it from:-
Download Secret Weapons of the Luftwaffe (DOS) game - Abandonware DOS
Here is a link, to the Files I uploaded :-
https://www.dropbox.com/t/oglKujqxi6BrFVGJ
The pacswotl and pnlswotl Folders amongst other files, contain the batch Files and compiled C program.
Any help and info would be very much appreciated.