Friday, April 30, 2010

Trigraphs

One of the surprising things to me during the reading of c++ standard were trigraphs.
A trigraph (Greek: tri- = three, -graph = write) is a combination of three symbols, most commonly letters used to represent a single speech sound.

Trigraph
Replacement
??=
 #
??/
\
??'
^
??(
[
??)
]
??!
|
??<
{
??>
}
??-
~

The trigraph sequences are used for compatibility of source code written in ISO-646 or EBCDIC charset.

Trigraphs have been proposed for removal in C++0x.
You can read about it in the paper N2910 that discusses trigraph deprecation.

Here is another interesting article - EBCDIC and the P-BIT (The Biggest Computer Goof Ever).

GNU C guide says: "You don't need this brain damage." The GCC compiler has trigraph support disabled by default.

??=include <stdio.h>
??=include <windows.h>

void main()
??<    
    unsigned char *a = new unsigned char??(2??);
    a??(0??) = 0x6; // 00000110 = 6
    a??(1??) = 0xc; // 00001100 = 12
    
    printf("%d or %d = %d\n", a[0], a[1], a[0] ??! a[1]); // 0110 | 1100 = 1110 = 14
    printf("%d xor %d = %d\n", a[0], a[1], a[0] ??' a[1]); // 0110 ^ 1100 = 1010 = 10
    printf("not 0x%08X = 0x%08X\n", 0x12345678, ??-0x12345678); // ~12345678 = EDCAB987

    printf("Trigraphs!!!\n"); 
    
    // comment ??/
    comment too!!!
??>

Support for trigraphs in Visual Studio 2010 is disabled by default. So use the /Zc:trigraphs compiler option to enable trigraphs support.

Sunday, April 25, 2010

XNB reverse engineering

XNB is XNA Game Studio Express XNA Framework Content Pipeline Binary File.

About two years ago, my friends wrote the game in XNA. XNA has not been installed on my PC at the time, but I was curious to see the game content, especially images from this game.
And to my great disappointment, all the contents had an unusual format XNB.

So I decided to analyze this file format and to write a simple converter.
First of all I mentioned that the XNB files size is too big.
I assumed that the image is stored in the XNB as a 32-bit uncompressed image. Unfortunately, I later discovered that XNB can store compressed images.

After some binary data analysis, I almost completely decoded the structure of XNB header.

XNB files may contain several different resources. Besides images whose content type is "Microsoft.Xna.Framework.Content.Texture2DReader" they can store Sprite Fonts, Lists, Vectors, Rectangles et al.

I convert XNB file containing only the uncompressed image to uncompressed 32-bit TGA file.

Here the source code:
#ifndef XNBFILE_H
#define XNBFILE_H

#include <stdio.h>
#include <windows.h>

class XnbFile
{
private:

    #define XNB_CONTENT_TEXTURE2D "Microsoft.Xna.Framework.Content.Texture2DReader"

    #define XNB_IDENT (('w' << 24) + ('B' << 16) + ('N' << 8) + 'X')

    struct ColorRGBA
    {
        unsigned char R; 
        unsigned char G; 
        unsigned char B; 
        unsigned char A;
    };
    
    #pragma pack (push, 1)

    struct XnbHead
    {
        int ident;
        WORD version;
    };    
    
    struct XnbAsset
    {    
        unsigned char text_len;
        char *text;
    };

    struct XnbInfo
    {
        unsigned int file_size;
        unsigned char num_assets;        
    };

    struct XnbImage
    {        
        unsigned char flagA[6]; // probably compression method
        unsigned int width;
        unsigned int height;
        unsigned char flagB[4]; // unknown
        unsigned char flagC[4]; // unknown
    };

    #pragma pack (pop, 1)

public:

    XnbFile() {}
    ~XnbFile() {}

    void LoadXnb(const char *name)
    {
        XnbHead header;
        XnbInfo info;
        XnbAsset asset;
        XnbImage img;        

        FILE *f = fopen(name, "rb");
        if (!f) return;

        fread(&header, 1, sizeof(header), f);

        if (header.ident != XNB_IDENT)
        {
            printf("ID of xnb file is incorrect!\n");
            return;
        }
        
        char *versions[4] = { "1.0", "2.0", "3.0", "3.1" };
        printf("The version of XNA is %s\n", versions[header.version - 1]);

        fread(&info, 1, sizeof(info), f);        
        fread(&asset.text_len, 1, 1, f);

        asset.text = new char[asset.text_len + 1];
        fread(asset.text, 1, asset.text_len, f);
        asset.text[asset.text_len] = '\0';

        fseek(f, 4, SEEK_CUR); 

        if(!strcmp(asset.text, XNB_CONTENT_TEXTURE2D))
        {
            fread(&img, 1, sizeof(img), f);

            ColorRGBA *data = new ColorRGBA[img.width * img.height];
            fread(data, 1, img.width * img.height * 4, f);

            char newname[256];
            strcpy(newname, name);
            strcat(newname, ".tga");

            SaveToTga(newname, (unsigned char*)data, img.width, img.height);
        }
        
        fclose(f);        
    }

    void SaveToTga(const char *name, const unsigned char *data, int w, int h) 
    {
        FILE *f = fopen(name,"wb");
        if (!f) return;

        unsigned char *buf;
        buf = new unsigned char[18 + w * h * 4]; // 18 bytes for header
        memset(buf, 0, 18);
        buf[2] = 2;
        buf[12] = w % 256;
        buf[13] = w / 256;
        buf[14] = h % 256;
        buf[15] = h / 256;
        buf[16] = 32;
        buf[17] = 0x28;
        memcpy(buf + 18, data, w * h * 4);
        fwrite(buf, 1, 18 + w * h * 4, f);

        delete buf;

        fclose(f);    
    }

};

#endif //XNBFILE_H

Tuesday, April 20, 2010

Adding the build counter line to MSVS output

Sometimes it’s very important to know how much builds you have built.
Here is the simple way how to do it.
Everything you need is to make build counter console application.

Source code of buildcounter.cpp:
#include <stdio.h>

void main()
{
    FILE *file1 = fopen("build.txt", "r");
    int build = 0;
    if (file1) fscanf(file1, "%d", &build); else return;
    fclose(file1);
    
    printf("Build %d\n", build);

    FILE *file2 = fopen("build.txt", "w+");
    if (file2) fprintf(file2, "%d\n", ++build); else return;
    fclose(file2);
}
As you can see current build is written to the file build.txt.

Thereafter you need to add your buildcounter.exe to pre-build event command line.


Finally, you'll get the following result:

Thursday, April 15, 2010

Set locale и русские буквы в консоли

Часто при написании консольных приложений можно увидеть следующую картину:


Возникает вопрос: как выводить русские буквы в консоль?

Для этого необходимо в коде добавить setlocale(LC_ALL, "rus");
и подключить модуль locale.h

Source code of test.cpp:

#include <stdio.h>
#include <locale.h>
#include <windows.h>

void main()
{
    setlocale(LC_ALL, "rus");
    printf("%s", "Привет мир!!!\n");
    system("pause");
}

В итоге получим:

Monday, April 12, 2010

Compiling code from the command line

Иногда, требуется собрать код написанный в блокноте.

Source code of test.cpp:

#include <stdio.h>
#include <windows.h>

void main()
{
     printf("Hello world!!!\n");
}

Для того чтобы скомпилить данный код, достаточно создать cmd или bat файл со следующим содержанием:

@echo off
call "%VS80COMNTOOLS%vsvars32.bat"
cl /EHsc /Fetest.exe /O2 test.cpp
test.exe

Где номер 80 в %VS80COMNTOOLS% зависит от версии Visual Studio.
Так для 2005 = 80, для 2008 = 90, а для 2010 = 100.