DVX_GUI/tools/bmp2raw.c

128 lines
3.5 KiB
C

// bmp2raw.c -- Convert a 320x200x256 BMP to raw VGA splash format
//
// Output format:
// 768 bytes: palette (256 x 3 bytes RGB, 6-bit VGA values)
// 64000 bytes: pixels (320x200, top-to-bottom scanline order)
//
// Total output: 64768 bytes
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define VGA_W 320
#define VGA_H 200
#define PALETTE_ENTRIES 256
#define PIXEL_COUNT (VGA_W * VGA_H)
int main(int argc, char **argv) {
if (argc != 3) {
fprintf(stderr, "Usage: bmp2raw <input.bmp> <output.raw>\n");
return 1;
}
FILE *fp = fopen(argv[1], "rb");
if (!fp) {
fprintf(stderr, "Cannot open %s\n", argv[1]);
return 1;
}
// Read BMP header
uint8_t hdr[54];
if (fread(hdr, 1, 54, fp) != 54) {
fprintf(stderr, "Bad BMP header\n");
fclose(fp);
return 1;
}
if (hdr[0] != 'B' || hdr[1] != 'M') {
fprintf(stderr, "Not a BMP file\n");
fclose(fp);
return 1;
}
uint32_t pixelOffset = hdr[10] | (hdr[11] << 8) | (hdr[12] << 16) | (hdr[13] << 24);
int32_t width = hdr[18] | (hdr[19] << 8) | (hdr[20] << 16) | (hdr[21] << 24);
int32_t height = hdr[22] | (hdr[23] << 8) | (hdr[24] << 16) | (hdr[25] << 24);
uint16_t bpp = hdr[28] | (hdr[29] << 8);
if (width != VGA_W || abs(height) != VGA_H || bpp != 8) {
fprintf(stderr, "BMP must be %dx%d 8bpp (got %dx%d %dbpp)\n", VGA_W, VGA_H, width, abs(height), bpp);
fclose(fp);
return 1;
}
bool topDown = (height < 0);
if (height < 0) {
height = -height;
}
// Read palette (starts at offset 54 for BITMAPINFOHEADER, may vary)
uint32_t dibSize = hdr[14] | (hdr[15] << 8) | (hdr[16] << 16) | (hdr[17] << 24);
uint32_t palOffset = 14 + dibSize; // BMP file header (14) + DIB header
fseek(fp, palOffset, SEEK_SET);
uint8_t bmpPal[PALETTE_ENTRIES * 4];
if (fread(bmpPal, 4, PALETTE_ENTRIES, fp) != PALETTE_ENTRIES) {
fprintf(stderr, "Cannot read palette\n");
fclose(fp);
return 1;
}
// Convert palette: BMP is BGRA 8-bit, VGA is RGB 6-bit
uint8_t vgaPal[PALETTE_ENTRIES * 3];
for (int i = 0; i < PALETTE_ENTRIES; i++) {
vgaPal[i * 3 + 0] = bmpPal[i * 4 + 2] >> 2; // R
vgaPal[i * 3 + 1] = bmpPal[i * 4 + 1] >> 2; // G
vgaPal[i * 3 + 2] = bmpPal[i * 4 + 0] >> 2; // B
}
// Read pixel data
fseek(fp, pixelOffset, SEEK_SET);
// BMP rows are padded to 4-byte boundaries (320 is already aligned)
uint8_t pixels[PIXEL_COUNT];
if (topDown) {
// Already top-to-bottom
if (fread(pixels, 1, PIXEL_COUNT, fp) != PIXEL_COUNT) {
fprintf(stderr, "Cannot read pixel data\n");
fclose(fp);
return 1;
}
} else {
// Bottom-to-top — flip
for (int y = VGA_H - 1; y >= 0; y--) {
if (fread(pixels + y * VGA_W, 1, VGA_W, fp) != VGA_W) {
fprintf(stderr, "Cannot read row %d\n", VGA_H - 1 - y);
fclose(fp);
return 1;
}
}
}
fclose(fp);
// Write output
FILE *out = fopen(argv[2], "wb");
if (!out) {
fprintf(stderr, "Cannot create %s\n", argv[2]);
return 1;
}
fwrite(vgaPal, 1, sizeof(vgaPal), out);
fwrite(pixels, 1, PIXEL_COUNT, out);
fclose(out);
printf("Converted %s -> %s (%d bytes)\n", argv[1], argv[2], (int)(sizeof(vgaPal) + PIXEL_COUNT));
return 0;
}