// 20020425
// wojciech bruzda
//
// PCX to ASCII converter
// usage: $ g++ ascii.cpp -o ascii; ./ascii file_name.pcx

// file_name.pcx must be 512 x 512 x 1bbp x 72dpi only! -- http://online-converting.com/image/convert2pcx/

#include <fstream>

#define PCX_N 512
#define ASCII_INVERT 1 // pseudo-inverting: +1 or -1

const char* ASCII_DUMP ="\
0000000000000000000000000000000000000000000000000000000000000000\
0111111010000001101001011000000110111101100110011000000101111110\
0111111011111111110110111111111111000011111001111111111101111110\
0110110011111110111111101111111001111100001110000001000000000000\
0001000000111000011111001111111001111100001110000001000000000000\
0011100001111100001110001111111011111110011111000011100001111100\
0001000000010000001110000111110011111110011111000011100001111100\
0000000000000000000110000011110000111100000110000000000000000000\
1111111111111111111001111100001111000011111001111111111111111111\
0000000000111100011001100100001001000010011001100011110000000000\
1111111111000011100110011011110110111101100110011100001111111111\
0000111100000111000011110111110111001100110011001100110001111000\
0011110001100110011001100110011000111100000110000111111000011000\
0011111100110011001111110011000000110000011100001111000011100000\
0111111101100011011111110110001101100011011001111110011011000000\
1001100101011010001111001110011111100111001111000101101010011001\
1000000011100000111110001111111011111000111000001000000000000000\
0000001000001110001111101111111000111110000011100000001000000000\
0001100000111100011111100001100000011000011111100011110000011000\
0110011001100110011001100110011001100110000000000110011000000000\
0111111111011011110110110111101100011011000110110001101100000000\
0011111001100011001110000110110001101100001110001100110001111000\
0000000000000000000000000000000001111110011111100111111000000000\
0001100000111100011111100001100001111110001111000001100011111111\
0001100000111100011111100001100000011000000110000001100000000000\
0001100000011000000110000001100001111110001111000001100000000000\
0000000000011000000011001111111000001100000110000000000000000000\
0000000000110000011000001111111001100000001100000000000000000000\
0000000000000000110000001100000011000000111111100000000000000000\
0000000000100100011001101111111101100110001001000000000000000000\
0000000000011000001111000111111011111111111111110000000000000000\
0000000011111111111111110111111000111100000110000000000000000000\
0000000000000000000000000000000000000000000000000000000000000000\
0011000001111000011110000111100000110000000000000011000000000000\
0110110001101100011011000000000000000000000000000000000000000000\
0110110001101100111111100110110011111110011011000110110000000000\
0011000001111100110000000111100000001100111110000011000000000000\
0000000011000110110011000001100000110000011001101100011000000000\
0011100001101100001110000111011011011100110011000111011000000000\
0110000001100000110000000000000000000000000000000000000000000000\
0001100000110000011000000110000001100000001100000001100000000000\
0110000000110000000110000001100000011000001100000110000000000000\
0000000001100110001111001111111100111100011001100000000000000000\
0000000000110000001100001111110000110000001100000000000000000000\
0000000000000000000000000000000000000000001100000011000001100000\
0000000000000000000000001111110000000000000000000000000000000000\
0000000000000000000000000000000000000000001100000011000000000000\
0000011000001100000110000011000001100000110000001000000000000000\
0111110011000110110011101101111011110110111001100111110000000000\
0011000001110000001100000011000000110000001100001111110000000000\
0111100011001100000011000011100001100000110011001111110000000000\
0111100011001100000011000011100000001100110011000111100000000000\
0001110000111100011011001100110011111110000011000001111000000000\
1111110011000000111110000000110000001100110011000111100000000000\
0011100001100000110000001111100011001100110011000111100000000000\
1111110011001100000011000001100000110000001100000011000000000000\
0111100011001100110011000111100011001100110011000111100000000000\
0111100011001100110011000111110000001100000110000111000000000000\
0000000000110000001100000000000000000000001100000011000000000000\
0000000000110000001100000000000000000000001100000011000001100000\
0001100000110000011000001100000001100000001100000001100000000000\
0000000000000000111111000000000000000000111111000000000000000000\
0110000000110000000110000000110000011000001100000110000000000000\
0111100011001100000011000001100000110000000000000011000000000000\
0111110011000110110111101101111011011110110000000111100000000000\
0011000001111000110011001100110011111100110011001100110000000000\
1111110001100110011001100111110001100110011001101111110000000000\
0011110001100110110000001100000011000000011001100011110000000000\
1111100001101100011001100110011001100110011011001111100000000000\
0111111001100000011000000111100001100000011000000111111000000000\
0111111001100000011000000111100001100000011000000110000000000000\
0011110001100110110000001100000011001110011001100011111000000000\
1100110011001100110011001111110011001100110011001100110000000000\
0111100000110000001100000011000000110000001100000111100000000000\
0001111000001100000011000000110011001100110011000111100000000000\
1110011001100110011011000111100001101100011001101110011000000000\
0110000001100000011000000110000001100000011000000111111000000000\
1100011011101110111111101111111011010110110001101100011000000000\
1100011011100110111101101101111011001110110001101100011000000000\
0011100001101100110001101100011011000110011011000011100000000000\
1111110001100110011001100111110001100000011000001111000000000000\
0111100011001100110011001100110011011100011110000001110000000000\
1111110001100110011001100111110001101100011001101110011000000000\
0111100011001100111000000111000000011100110011000111100000000000\
1111110000110000001100000011000000110000001100000011000000000000\
1100110011001100110011001100110011001100110011001111110000000000\
1100110011001100110011001100110011001100011110000011000000000000\
1100011011000110110001101101011011111110111011101100011000000000\
1100011011000110011011000011100000111000011011001100011000000000\
1100110011001100110011000111100000110000001100000111100000000000\
1111111000000110000011000001100000110000011000001111111000000000\
0111100001100000011000000110000001100000011000000111100000000000\
1100000001100000001100000001100000001100000001100000001000000000\
0111100000011000000110000001100000011000000110000111100000000000\
0001000000111000011011001100011000000000000000000000000000000000\
0000000000000000000000000000000000000000000000000000000011111111\
0011000000110000000110000000000000000000000000000000000000000000\
0000000000000000011110000000110001111100110011000111011000000000\
1110000001100000011000000111110001100110011001101101110000000000\
0000000000000000011110001100110011000000110011000111100000000000\
0001110000001100000011000111110011001100110011000111011000000000\
0000000000000000011110001100110011111100110000000111100000000000\
0011100001101100011000001111000001100000011000001111000000000000\
0000000000000000011101101100110011001100011111000000110011111000\
1110000001100000011011000111011001100110011001101110011000000000\
0011000000000000011100000011000000110000001100000111100000000000\
0000110000000000000011000000110000001100110011001100110001111000\
1110000001100000011001100110110001111000011011001110011000000000\
0111000000110000001100000011000000110000001100000111100000000000\
0000000000000000110011001111111011111110110101101100011000000000\
0000000000000000111110001100110011001100110011001100110000000000\
0000000000000000011110001100110011001100110011000111100000000000\
0000000000000000110111000110011001100110011111000110000011110000\
0000000000000000011101101100110011001100011111000000110000011110\
0000000000000000110111000111011001100110011000001111000000000000\
0000000000000000011111001100000001111000000011001111100000000000\
0001000000110000011111000011000000110000001101000001100000000000\
0000000000000000110011001100110011001100110011000111011000000000\
0000000000000000110011001100110011001100011110000011000000000000\
0000000000000000110001101101011011111110111111100110110000000000\
0000000000000000110001100110110000111000011011001100011000000000\
0000000000000000110011001100110011001100011111000000110011111000\
0000000000000000111111001001100000110000011001001111110000000000\
0001110000110000001100001110000000110000001100000001110000000000\
0001100000011000000110000000000000011000000110000001100000000000\
1110000000110000001100000001110000110000001100001110000000000000\
0111011011011100000000000000000000000000000000000000000000000000\
0000000000010000001110000110110011000110110001101111111000000000"; // who remembers old x86 architecture? :)

int main(int ac, char** av) {
    FILE *F = fopen(av[1], "r");
    fseek(F, 0L, SEEK_END);
    int PCX_L = ftell(F), *PCX_T = new int[PCX_L - 128];
    fseek(F, 128L, SEEK_SET);
    for (int i = 128; i < PCX_L; i++) { // === for (int i = 128; i < PCX_L; PCX_T[i++ - 128] = fgetc(F));
        fseek(F, 0L, SEEK_CUR);
        PCX_T[i - 128] = fgetc(F);
    }
    fclose(F); // sometimes unexpected sequence of '-1s' emerges...
    bool *PCX = new bool[PCX_N * PCX_N], *ASCII = new bool[128 * 64], BLOCK[64];
    int j = 0;
    for (int i = 0; i < PCX_L - 128; i++) { 
        if ((PCX_T[i] & 0xC0) == 0xC0) { // transfer RLE data into linear array: PCX_T
            int RLE = PCX_T[i] & 0x3F, T = PCX_T[++i];
            while (RLE--) {
                for (int k = 8; k--; PCX[j + k] = T % 2, T >>= 1);
                    j += 8;
                    T = PCX_T[i];
                }
        } else {
            for (int k = 8; k--; PCX[j + k] = PCX_T[i] % 2, PCX_T[i] >>= 1); // raw data
            j += 8;
        }
    }
    /* ASCII_DUMP contains the first 127 ASCII characters in a decimal-binary form like this:

       0xC3 = 00111100 ---> ASCII # 63
       0x66 = 01100110
       0x06 = 00000110
       0x0C = 00001100
       0x18 = 00011000
       0x00 = 00000000
       0x18 = 00011000
       0x00 = 00000000

       the data is taken from the old BIOS ROM:
       0xF000:0xFA6E - 0xF000:0xFE66
       0xC000:0x2D20 - 0xC000:0x3118 */

//    ifstream in("ASCII_DUMP.TXT");
//    string ascii;
    j = 0;
//    while (getline(in, ascii)) {
    while (j < 64*127) {
        for (int i = 0; i < 64; ASCII[j + i] = ASCII_DUMP[j + i++] - 48);
        j += 64;
    }

    /* PCX_T is an array with a logical structure of 8x8-bytes blocks as follows:

       PCX_T = [ BLOCK#0000 8x8 | BLOCK#0001 8x8 ... BLOCK#0064 8x8 ]
               [ BLOCK#0065 8x8 |            ... ...            ... ]
               [            ... |            ... ...            ... ]
               [            ... |            ... ... BLOCK#4096 8x8 ] = 512 x 512

       Given sub-array S of dimension 8x8 try to find such ASCII character A that H(S ^ A) is
       the smallest possible, where H denotes the complementary Hamming measure. */

    for (int B = 0; B < 64 * 64; B++) {
    // NOTE that ((B >> 6) << 12) != (B << 6) since it is integer division
        int BLOCK_CS = ((B >> 6) << 12) + (B << 3) % PCX_N;
        int BLOCK_IP = 0, b = 0;
        for (j = 0; j < 64; j++) { BLOCK[j] = PCX[BLOCK_CS + BLOCK_IP + b];
            if (++b == 8) {
                b = 0;
                BLOCK_IP += PCX_N;
            }
        }
        int MIN_H = 64, MIN_A; // trick with extrema...

        for (int A = 0; A < 64 * 128; A += 64) {
            int H = 0;
            for (j = 0; j < 64; ASCII[A + j] - BLOCK[j] ? H += ASCII_INVERT : H, j++);
            if (H < MIN_H) {
                MIN_H = H;
                MIN_A = A;
            }
        }

        if (!(B % 64))
            printf("\n");
        printf("%c", (char) (32 + ((MIN_A) % (127 - 32))));
    }
    delete [] PCX, PCX_T, ASCII;
    printf("\n\n");
    return 0;
}

