8086 MS-DOS .COM ไฟล์ / BMP, ขนาดไฟล์ที่ส่งออก = 2192 ไบต์
Encoder
โปรแกรมเปลี่ยนไฟล์เขียนเป็น C ใช้สองอาร์กิวเมนต์: ไฟล์อินพุตและไฟล์เอาต์พุต ไฟล์อินพุตเป็นภาพ RGB 64x64 RAW (หมายถึงเป็นเพียง 4096 RGB สามเท่า) จำนวนสีถูก จำกัด ไว้ที่ 4 เพื่อให้จานสีสามารถสั้นที่สุด มันตรงไปตรงมามากในการกระทำของมัน; มันเพียงสร้างจานสี, แพ็คพิกเซลเป็นคู่ไบต์และติดกาวมันพร้อมกับส่วนหัวที่ทำไว้ล่วงหน้าและโปรแกรมถอดรหัส
#include <stdio.h>
#include <stdlib.h>
#define MAXPAL 4
#define IMAGESIZE 64 * 64
int main(int argc, char **argv)
{
FILE *fin, *fout;
unsigned char *imgdata = malloc(IMAGESIZE * 3), *outdata = calloc(IMAGESIZE / 2, 1);
unsigned palette[MAXPAL] = {0};
int pal_size = 0;
if (!(fin = fopen(argv[1], "rb")))
{
fprintf(stderr, "Could not open \"%s\".\n", argv[1]);
exit(1);
}
if (!(fout = fopen(argv[2], "wb")))
{
fprintf(stderr, "Could not open \"%s\".\n", argv[2]);
exit(2);
}
fread(imgdata, 1, IMAGESIZE * 3, fin);
for (int i = 0; i < IMAGESIZE; i++)
{
// BMP saves the palette in BGR order
unsigned col = (imgdata[i * 3] << 16) | (imgdata[i * 3 + 1] << 8) | (imgdata[i * 3 + 2]), palindex;
int is_in_pal = 0;
for (int j = 0; j < pal_size; j++)
{
if (palette[j] == col)
{
palindex = j;
is_in_pal = 1;
}
}
if (!is_in_pal)
{
if (pal_size == MAXPAL)
{
fprintf(stderr, "Too many unique colours in input image.\n");
exit(3);
}
palindex = pal_size;
palette[pal_size++] = col;
}
// High nibble is left-most pixel of the pair
outdata[i / 2] |= (palindex << !(i & 1) * 4);
}
char BITMAPFILEHEADER[14] = {
0x42, 0x4D, // "BM" magic marker
0x90, 0x08, 0x00, 0x00, // FileSize
0x00, 0x00, // Reserved1
0x00, 0x00, // Reserved2
0x90, 0x00, 0x00, 0x00 // ImageOffset
};
char BITMAPINFOHEADER[40] = {
0x28, 0x00, 0x00, 0x00, // StructSize
0x40, 0x00, 0x00, 0x00, // ImageWidth
0x40, 0x00, 0x00, 0x00, // ImageHeight
0x01, 0x00, // Planes
0x04, 0x00, // BitsPerPixel
0x00, 0x00, 0x00, 0x00, // CompressionType (0 = none)
0x00, 0x00, 0x00, 0x00, // RawImagDataSize (0 is fine for non-compressed,)
0x00, 0x00, 0x00, 0x90, // HorizontalRes
// db 0, 0, 0
// nop
0xEB, 0x1A, 0x90, 0x90, // VerticalRes
// jmp Decoder
// nop
// nop
0x04, 0x00, 0x00, 0x00, // NumPaletteColours
0x00, 0x00, 0x00, 0x00, // NumImportantColours (0 = all)
};
char DECODER[74] = {
0xB8, 0x13, 0x00, 0xCD, 0x10, 0xBA, 0x00, 0xA0, 0x8E, 0xC2, 0xBA,
0xC8, 0x03, 0x31, 0xC0, 0xEE, 0x42, 0xBE, 0x38, 0x01, 0xB1, 0x04,
0xFD, 0x51, 0xB1, 0x03, 0xAC, 0xD0, 0xE8, 0xD0, 0xE8, 0xEE, 0xE2,
0xF8, 0x83, 0xC6, 0x07, 0x59, 0xE2, 0xEF, 0xFC, 0xB9, 0x00, 0x08,
0xBE, 0x90, 0x01, 0xBF, 0xC0, 0x4E, 0xAC, 0xD4, 0x10, 0x86, 0xC4,
0xAB, 0xF7, 0xC7, 0x3F, 0x00, 0x75, 0x04, 0x81, 0xEF, 0x80, 0x01,
0xE2, 0xEE, 0x31, 0xC0, 0xCD, 0x16, 0xCD, 0x20,
};
fwrite(BITMAPFILEHEADER, 1, 14, fout);
fwrite(BITMAPINFOHEADER, 1, 40, fout);
fwrite(palette, 4, 4, fout);
fwrite(DECODER, 1, 74, fout);
// BMPs are stored upside-down, because why not
for (int i = 64; i--; )
fwrite(outdata + i * 32, 1, 32, fout);
fclose(fin);
fclose(fout);
return 0;
}
ไฟล์ที่ส่งออก
ไฟล์ที่ส่งออกเป็นไฟล์ BMP ซึ่งสามารถเปลี่ยนชื่อ. COM และทำงานในสภาพแวดล้อม DOS มันจะเปลี่ยนเป็นโหมดวิดีโอ 13h และแสดงภาพ
ไฟล์ BMP มีส่วนหัวแรกเป็น BITMAPFILEHEADER ซึ่งประกอบด้วย ImageOffset ซึ่งเป็นส่วนหนึ่งของสิ่งอื่น ๆ ซึ่งหมายถึงตำแหน่งที่อยู่ในไฟล์ที่ข้อมูลภาพเริ่มขึ้น หลังจากนี้ BITMAPINFOHEADER มาพร้อมกับข้อมูลการถอดรหัส / การเข้ารหัสต่างๆตามด้วยจานสีหากมีการใช้งาน ImageOffset สามารถมีค่าที่ชี้ไปที่ส่วนท้ายของส่วนหัวใด ๆ ทำให้เราสามารถสร้างช่องว่างสำหรับตัวถอดรหัสให้อยู่ในนั้นประมาณ:
BITMAPFILEHEADER
BITMAPINFOHEADER
PALETTE
<gap>
IMAGE DATA
ปัญหาอีกประการหนึ่งคือการเข้าสู่การถอดรหัส BITMAPFILEHEADER และ BITMAPINFOHEADER สามารถแก้ไขเพื่อให้แน่ใจว่าเป็นรหัสเครื่องตามกฎหมาย (ซึ่งไม่ได้สร้างสถานะที่ไม่สามารถกู้คืนได้) แต่พาเล็ตนั้นมีเล่ห์เหลี่ยม แน่นอนว่าเราสามารถทำพาเลตต์ได้นานขึ้นและใส่รหัสเครื่องไว้ที่นั่น แต่ฉันเลือกที่จะใช้ฟิลด์ biXPelsPerMeter และ biYPelsPerMeter แทนเพื่อทำการจัดเรียงโค้ดให้ถูกต้อง แน่นอนว่าฟิลด์เหล่านี้จะมีขยะอยู่ในนั้น แต่โปรแกรมดูภาพใด ๆ ที่ฉันทดสอบด้วยการแสดงภาพที่ดี การพิมพ์อาจทำให้ได้ผลลัพธ์ที่แปลกประหลาด
มันเท่าที่ฉันรู้ว่าเป็นไปตามมาตรฐาน
หนึ่งสามารถสร้างไฟล์ที่สั้นลงหากJMP
คำแนะนำถูกใส่ไว้ในหนึ่งในเขตข้อมูลที่สงวนไว้ใน BITMAPFILEHEADER สิ่งนี้จะช่วยให้เราสามารถจัดเก็บความสูงของภาพเป็น -64 แทนที่จะเป็น 64 ซึ่งในดินแดนมหัศจรรย์มหัศจรรย์ของไฟล์ BMP หมายความว่าข้อมูลภาพถูกจัดเก็บอย่างถูกวิธีซึ่งจะช่วยให้ถอดรหัสได้ง่ายขึ้น
ถอดรหัส
ไม่มีเทคนิคเฉพาะในตัวถอดรหัส จานสีบรรจุโดยตัวเข้ารหัสและแสดงที่นี่ด้วยค่าหุ่น มันอาจจะสั้นลงเล็กน้อยถ้ามันไม่ได้กลับไปที่ DOS ด้วยปุ่มกด แต่มันก็ไม่สนุกที่จะทดสอบถ้าไม่มีมัน หากคุณรู้สึกว่าต้องทำคุณสามารถเปลี่ยนคำสั่งสามคำสั่งล่าสุดjmp $
เป็นบันทึกสองสามไบต์ (อย่าลืมอัปเดตส่วนหัวของไฟล์ถ้าคุณทำ!)
BMP จัดเก็บพาเล็ตเป็น BGR ( ไม่ใช่ RGB) triplets เต็มไปด้วยเลขศูนย์ สิ่งนี้ทำให้การตั้งค่าจานสี VGA น่ารำคาญยิ่งกว่าปกติ ความจริงที่ว่า BMP นั้นถูกเก็บไว้แบบกลับหัวเท่านั้นจะเพิ่มรสชาติ (และขนาด) เท่านั้น
แสดงไว้ที่นี่ในรูปแบบ NASM:
Palette:
db 0, 0, 0, 0
db 0, 0, 0, 0
db 0, 0, 0, 0
db 0, 0, 0, 0
Decoder:
; Set screen mode
mov ax, 0x13
int 0x10
mov dx, 0xa000
mov es, dx
; Prepare to set palette
mov dx, 0x3c8
xor ax, ax
out dx, al
inc dx
mov si, Palette + 2
mov cl, 4
std
pal_loop:
push cx
mov cl, 3
pal_inner:
lodsb
shr al, 1
shr al, 1
out dx, al
loop pal_inner
add si, 7
pop cx
loop pal_loop
cld
; Copy image data to video memory
mov cx, 64 * 64 / 2
mov si, ImageData
mov di, 20160
img_loop:
lodsb
aam 16
xchg al, ah
stosw
test di, 63
jnz skip
sub di, 384
skip:
loop img_loop
; Eat a keypress
xor ax, ax
int 0x16
; Return to DOS
int 0x20
ImageData:
.exe
ส่วนหนึ่งของความท้าทายและเมื่อดูมันจะ.png
มีพิกเซลที่แก้ไขตาม.exe
รหัสนี้ ได้รับอนุญาตนี้ตราบใดที่.png
เรายังสามารถดูได้? รูปภาพเอาต์พุตต้องมีสีอย่างน้อย 4 สีหรือไม่