ภาพแสดงตัวเอง [ปิด]


11

พื้นหลัง

มี.ZIPไฟล์ที่แตกไฟล์เอง โดยทั่วไปแล้วพวกเขาจะมีนามสกุล.EXE(และโดยการเรียกใช้ไฟล์พวกเขาจะถูกแตกออก) แต่เมื่อเปลี่ยนชื่อเป็น.ZIPคุณสามารถเปิดไฟล์ด้วยซอฟต์แวร์ ZIP แตก

(สิ่งนี้เป็นไปได้เนื่องจาก.EXEไฟล์ต้องการส่วนหัวที่แน่นอน แต่.ZIPไฟล์ต้องการส่วนท้ายที่แน่นอนดังนั้นจึงเป็นไปได้ที่จะสร้างไฟล์ที่ทั้งสองมี.EXEส่วนหัวและส่วน.ZIPท้าย)

งานของคุณ:

สร้างโปรแกรมที่สร้างไฟล์ภาพ "แสดงตนเอง":

  • โปรแกรมจะถ่ายภาพ 64x64 บางภาพ (สนับสนุนอย่างน้อย 4 สี) เป็นอินพุตและไฟล์ "รวม" เป็นเอาต์พุต
  • ไฟล์ที่ส่งออกของโปรแกรมจะได้รับการยอมรับว่าเป็นไฟล์ภาพโดยโปรแกรมดูรูปภาพทั่วไป
  • เมื่อเปิดไฟล์ที่ส่งออกด้วยโปรแกรมดูภาพภาพอินพุตจะปรากฏขึ้น
  • ไฟล์ที่ส่งออกจะต้องได้รับการยอมรับว่าเป็นไฟล์ที่ปฏิบัติการได้สำหรับระบบปฏิบัติการหรือคอมพิวเตอร์ทุกประเภท

    (หากไฟล์สำหรับระบบปฏิบัติการผิดปกติหรือคอมพิวเตอร์ถูกสร้างขึ้นมันจะดีถ้ามีตัวจำลองพีซีโอเพนซอร์สอยู่อย่างไรก็ตามไม่จำเป็นต้องทำเช่นนี้)

  • เมื่อเรียกใช้งานไฟล์เอาต์พุตภาพอินพุตจะแสดงขึ้น
  • เป็นไปได้ว่าจำเป็นต้องเปลี่ยนชื่อไฟล์ (เช่นจาก.PNGเป็น.COM)
  • ไม่จำเป็นว่าโปรแกรมและไฟล์เอาต์พุตจะทำงานบนระบบปฏิบัติการเดียวกัน ตัวอย่างเช่นโปรแกรมอาจเป็นโปรแกรม Windows และไฟล์เอาต์พุตที่สามารถเรียกใช้งานบน Commodore C64

เกณฑ์การชนะ

  • โปรแกรมที่สร้างไฟล์เอาต์พุตที่เล็กที่สุดชนะ
  • หากขนาดของไฟล์เอาต์พุตแตกต่างกันไปขึ้นอยู่กับอิมเมจอินพุต (ตัวอย่างเช่นเนื่องจากโปรแกรมบีบอัดอิมเมจ) ไฟล์เอาต์พุตที่ใหญ่ที่สุดที่เป็นไปได้ที่สร้างโดยโปรแกรมที่แสดงอิมเมจ 64x64 ที่มีจำนวนสูงสุด 4 สี

ยังไงซะ

ฉันมีความคิดสำหรับการเขียนโปรแกรมปริศนาต่อไปนี้เมื่ออ่านคำถามนี้ใน StackOverflow


ฉันได้เพิ่มแท็กเงื่อนไขการชนะ (ความท้าทายของรหัสร่วมกับ metagolf - ผลลัพธ์ที่สั้นที่สุด) สำหรับภาพอินพุต 64x64 คุณมีภาพตัวอย่างบ้างไหม? รูปภาพจะต้องเหมือนกันเมื่อดูหรือไม่ หรือภาพออกและภาพอินพุตแตกต่างกันได้หรือไม่ เพื่อให้เป็นรูปธรรมมากขึ้น: สมมติว่าเราเพิ่มรหัสบางประเภทสำหรับ.exeส่วนหนึ่งของความท้าทายและเมื่อดูมันจะ.pngมีพิกเซลที่แก้ไขตาม.exeรหัสนี้ ได้รับอนุญาตนี้ตราบใดที่.pngเรายังสามารถดูได้? รูปภาพเอาต์พุตต้องมีสีอย่างน้อย 4 สีหรือไม่
Kevin Cruijssen

2
คุณจะกำหนด "โปรแกรมดูรูปภาพทั่วไป" ได้อย่างไร ตัวอย่างเช่นอินเทอร์เน็ตเบราว์เซอร์ที่มี HTML "code" จะนับหรือไม่
Jo King

@KevinCruijssen เมื่อตีความว่าเป็นไฟล์ภาพไฟล์ที่ส่งออกจะแสดงภาพเดียวกันกับไฟล์อินพุต: ความกว้างและความสูงเท่ากันเป็นพิกเซลและพิกเซลแต่ละพิกเซลจะมีสีเดียวกัน หากรูปแบบไฟล์ไม่รองรับจานสีเดียวกันสีของแต่ละพิกเซลจะใกล้เคียงที่สุด เช่นเดียวกับไฟล์ที่ตีความว่าเป็นไฟล์ที่เรียกใช้งานได้ หากไฟล์ที่ส่งออกแสดงถึงโปรแกรม "เต็มหน้าจอ" มันอาจแสดงภาพที่ใดก็ได้บนหน้าจอ (กึ่งกลางขอบบนซ้าย, ... ) หรือขยายให้เต็มขนาดหน้าจอ
Martin Rosenau

1
@JoKing "การรับรู้โดยผู้ดูรูปภาพทั่วไป" หมายความว่ารูปแบบไฟล์สามารถอ่านได้โดยคอมพิวเตอร์ส่วนใหญ่ที่มีซอฟต์แวร์ที่ติดตั้งล่วงหน้า (เช่น HTML) หรือผู้ใช้จำนวนมากดาวน์โหลดเครื่องมือฟรีเพื่อดูไฟล์ ( เช่น PDF) ฉันจะบอกว่า HTML + JavaScript สามารถมองเห็นเป็นรหัสอย่างไรก็ตาม "โปรแกรมดูรูปภาพ" ไม่ควรรันโค้ด! ดังนั้นจะอนุญาตให้บอกว่าเว็บเบราว์เซอร์เป็น "โปรแกรมดูรูปภาพ" แต่ในกรณีนี้ HTML ไม่ใช่ "รหัส" หรือคุณอาจกล่าวได้ว่า HTML + JS เป็น "รหัส" แต่ในกรณีนี้เว็บเบราว์เซอร์ไม่ใช่ "โปรแกรมดูรูปภาพ"
Martin Rosenau

2
เป็นเรื่องน่าเศร้าที่เห็นคำถามที่น่าสนใจเช่นนี้ปิดลง เท่าที่ฉันเข้าใจข้อกังวลใด ๆ ควรได้รับการแก้ไขก่อนที่จะเปิดคำถามอีกครั้ง สิ่งสำคัญในการแสดงความคิดเห็นคือคำว่า "โปรแกรมดูรูปภาพทั่วไป" ซึ่งเพียงพอที่จะคลุมเครือและภาพที่แสดงในสถานะ (ตามความกังวลของ @ KevinCruijssen) ไม่มีการเปลี่ยนแปลงโดยการมีอยู่ของรหัสที่สามารถใช้งานได้ . การแก้ไขที่เกี่ยวข้องกับข้อกังวลเหล่านั้นจะเพียงพอหรือไม่ (ฉันขอสารภาพว่าไม่เข้าใจ "ความกำกวมสี่สีสี่สี")
Gastropner

คำตอบ:


5

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:

ดี ฉันยังคิดเกี่ยวกับคู่ BMP / MS-DOS COM; ฉันจะใช้มันหากไม่มีคำตอบภายในหนึ่งสัปดาห์ อย่างไรก็ตามฉันต้องการมากกว่า 10K: เนื่องจากฉันไม่คิดว่ารีจิสเตอร์จะถูกกำหนดค่าเริ่มต้นเป็นศูนย์ฉันจะวางคำสั่งการกระโดดที่ไฟล์ออฟเซ็ต 2 และเนื่องจากฟิลด์นี้ถูกตีความว่าเป็น "ขนาดไฟล์" ในไฟล์ BMP ฉันจะต้องกรอกไฟล์ BMP ด้วยไบต์ "ดัมมี่" เพื่อให้แน่ใจว่าฟิลด์ "ขนาดไฟล์" แทนขนาดไฟล์ที่ถูกต้อง
Martin Rosenau

@MartinRosenau ที่จริงผมจะต้องไม่ถือว่าบางส่วนของค่าลงทะเบียนที่ฉันมักจะทำ (ตามfysnet.net/yourhelp.htm ) ตั้งแต่ส่วนหัวบังคับลงทะเบียนและแม้กระทั่งไบต์แรกของ PSP, necessating มากกว่าint 0x20 ret
Gastropner
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.