root/src/tools/translation/bmpinfo/bmpinfo.cpp
/*****************************************************************************/
// bmpinfo
// Written by Michael Wilber, Haiku Translation Kit Team
//
// Version:
//
// bmpinfo is a command line program for displaying information about
// BMP images.
//
//
// This application and all source files used in its construction, except
// where noted, are licensed under the MIT License, and have been written
// and are:
//
// Copyright (c) 2003 Haiku Project
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
/*****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ByteOrder.h>
#include <Catalog.h>
#include <File.h>
#include <TranslatorFormats.h>
#include <StorageDefs.h>

#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "bmpinfo"

#define BMP_NO_COMPRESS 0
#define BMP_RLE8_COMPRESS 1
#define BMP_RLE4_COMPRESS 2

struct BMPFileHeader {
        // for both MS and OS/2 BMP formats
        uint16 magic;                   // = 'BM'
        uint32 fileSize;                // file size in bytes
        uint32 reserved;                // equals 0
        uint32 dataOffset;              // file offset to actual image
};

struct MSInfoHeader {
        uint32 size;                    // size of this struct (40)
        uint32 width;                   // bitmap width
        uint32 height;                  // bitmap height
        uint16 planes;                  // number of planes, always 1?
        uint16 bitsperpixel;    // bits per pixel, (1,4,8,16 or 24)
        uint32 compression;             // type of compression
        uint32 imagesize;               // size of image data if compressed
        uint32 xpixperm;                // horizontal pixels per meter
        uint32 ypixperm;                // vertical pixels per meter
        uint32 colorsused;              // number of actually used colors
        uint32 colorsimportant; // number of important colors, zero = all
};

struct OS2InfoHeader {
        uint32 size;                    // size of this struct (12)
        uint16 width;                   // bitmap width
        uint16 height;                  // bitmap height
        uint16 planes;                  // number of planes, always 1?
        uint16 bitsperpixel;    // bits per pixel, (1,4,8,16 or 24)
};

void
print_bmp_info(BFile &file)
{
        uint8 buf[40];
        BMPFileHeader fh;

        ssize_t size = 14;
        if (file.Read(buf, size) != size) {
                printf(B_TRANSLATE("Error: unable to read BMP file header\n"));
                return;
        }

        // convert fileHeader to host byte order
        memcpy(&fh.magic, buf, 2);
        memcpy(&fh.fileSize, buf + 2, 4);
        memcpy(&fh.reserved, buf + 6, 4);
        memcpy(&fh.dataOffset, buf + 10, 4);
        swap_data(B_UINT16_TYPE, &fh.magic, sizeof(uint16),
                B_SWAP_BENDIAN_TO_HOST);
        swap_data(B_UINT32_TYPE, (reinterpret_cast<uint8 *> (&fh)) + 2,
                12, B_SWAP_LENDIAN_TO_HOST);

        printf(B_TRANSLATE("\nFile Header:\n"));
        printf(B_TRANSLATE("      magic: 0x%.4x (should be: 0x424d)\n"), fh.magic);
        printf(B_TRANSLATE("  file size: 0x%.8lx (%lu)\n"), fh.fileSize,
                fh.fileSize);
        printf(B_TRANSLATE("   reserved: 0x%.8lx (should be: 0x%.8x)\n"),
                fh.reserved, 0);
        printf(B_TRANSLATE("data offset: 0x%.8lx (%lu) (should be: >= 54 for MS "
                "format and >= 26 for OS/2 format)\n"), fh.dataOffset, fh.dataOffset);

        uint32 headersize = 0;
        if (file.Read(&headersize, 4) != 4) {
                printf(B_TRANSLATE("Error: unable to read info header size\n"));
                return;
        }
        swap_data(B_UINT32_TYPE, &headersize, 4, B_SWAP_LENDIAN_TO_HOST);

        if (headersize == sizeof(MSInfoHeader)) {
                // MS format
                MSInfoHeader msh;
                msh.size = headersize;
                if (file.Read(reinterpret_cast<uint8 *> (&msh) + 4, 36) != 36) {
                        printf(B_TRANSLATE("Error: unable to read entire MS info header\n"));
                        return;
                }

                // convert msheader to host byte order
                swap_data(B_UINT32_TYPE, reinterpret_cast<uint8 *> (&msh) + 4, 36,
                        B_SWAP_LENDIAN_TO_HOST);

                printf(B_TRANSLATE("\nMS Info Header:\n"));
                printf(B_TRANSLATE("     header size: 0x%.8lx (%lu) (should be: "
                        "40)\n"), msh.size, msh.size);
                printf(B_TRANSLATE("           width: %lu\n"), msh.width);
                printf(B_TRANSLATE("          height: %lu\n"), msh.height);
                printf(B_TRANSLATE("          planes: %u (should be: 1)\n"),
                        msh.planes);
                printf(B_TRANSLATE("  bits per pixel: %u (should be: 1,4,8,16,24 or "
                        "32)\n"), msh.bitsperpixel);
                if (msh.compression == BMP_NO_COMPRESS)
                        printf(B_TRANSLATE("     compression: none (%lu)\n"),
                                msh.compression);
                else if (msh.compression == BMP_RLE8_COMPRESS)
                        printf(B_TRANSLATE("     compression: RLE8 (%lu)\n"),
                                msh.compression);
                else if (msh.compression == BMP_RLE4_COMPRESS)
                        printf(B_TRANSLATE("     compression: RLE4 (%lu)\n"),
                                msh.compression);
                else
                        printf(B_TRANSLATE("     compression: unknown (%lu)\n"),
                                msh.compression);
                printf(B_TRANSLATE("      image size: 0x%.8lx (%lu)\n"), msh.imagesize,
                        msh.imagesize);
                printf(B_TRANSLATE("  x pixels/meter: %lu\n"), msh.xpixperm);
                printf(B_TRANSLATE("  y pixels/meter: %lu\n"), msh.ypixperm);
                printf(B_TRANSLATE("     colors used: %lu\n"), msh.colorsused);
                printf(B_TRANSLATE("colors important: %lu\n"), msh.colorsimportant);

        } else if (headersize == sizeof(OS2InfoHeader)) {
                // OS/2 format

                OS2InfoHeader os2;
                os2.size = headersize;
                if (file.Read(reinterpret_cast<uint8 *> (&os2) + 4, 8) != 8) {
                        printf(B_TRANSLATE("Error: unable to read entire OS/2 info "
                                "header\n"));
                        return;
                }

                // convert os2header to host byte order
                swap_data(B_UINT32_TYPE, reinterpret_cast<uint8 *> (&os2) + 4, 8,
                        B_SWAP_LENDIAN_TO_HOST);

                printf(B_TRANSLATE("\nOS/2 Info Header:\n"));
                printf(B_TRANSLATE("   header size: 0x%.8lx (%lu) (should be: 12)\n"),
                        os2.size, os2.size);
                printf(B_TRANSLATE("         width: %u\n"), os2.width);
                printf(B_TRANSLATE("        height: %u\n"), os2.height);
                printf(B_TRANSLATE("        planes: %u (should be: 1)\n"), os2.planes);
                printf(B_TRANSLATE("bits per pixel: %u (should be: 1,4,8 or 24)\n"),
                        os2.bitsperpixel);

        } else
                printf(B_TRANSLATE("Error: info header size (%lu) does not match MS "
                        "or OS/2 info header size\n"), headersize);
}

int
main(int argc, char **argv)
{
        printf("\n");

        if (argc == 1) {
                printf(B_TRANSLATE("bmpinfo - reports information about a BMP image "
                        "file\n"));
                printf(B_TRANSLATE("\nUsage:\n"));
                printf(B_TRANSLATE("bmpinfo filename.bmp\n"));
        }
        else {
                BFile file;

                for (int32 i = 1; i < argc; i++) {
                        if (file.SetTo(argv[i], B_READ_ONLY) != B_OK)
                                printf(B_TRANSLATE("\nError opening %s\n"), argv[i]);
                        else {
                                printf(B_TRANSLATE("\nBMP image information for: %s\n"),
                                        argv[i]);
                                print_bmp_info(file);
                        }
                }

        }

        printf("\n");

        return 0;
}