Thread: Help Needed With Finding Image Data In DOS Game File

Threaded View

Previous Post Previous Post   Next Post Next Post
  1. #3
    Registered User
    Join Date
    Dec 2017
    Posts
    1,644
    The first thing to do is to put the code in code tags so the indentation is retained.
    The second thing to do is to paste the text without extra markup (copy and/or paste as plain text).
    Copying the code from the page source and removing markup (and translating >, < " & ) yields the following.
    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;
    }
    Last edited by john.c; 07-06-2023 at 08:13 AM.
    A little inaccuracy saves tons of explanation. - H.H. Munro

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. can't write .bmp header or image data to output file
    By MWJack in forum C Programming
    Replies: 0
    Last Post: 12-12-2015, 02:05 AM
  2. Replies: 5
    Last Post: 02-10-2014, 10:49 PM
  3. c programming problems. reading data file and finding max
    By drewdude92 in forum C Programming
    Replies: 1
    Last Post: 02-21-2013, 10:34 PM
  4. Finding and Replacing Data in a File
    By DrC in forum C Programming
    Replies: 6
    Last Post: 03-20-2011, 10:09 AM
  5. How to write image data to binary PGM file format(P5)?
    By tommy_chai in forum C Programming
    Replies: 6
    Last Post: 11-03-2007, 10:52 PM

Tags for this Thread