Subversion Repositories pentevo

Rev

Rev 798 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed | ?url?

  1. #include "std.h"
  2.  
  3. #include "resource.h"
  4. #include "emul.h"
  5. #include "vars.h"
  6. #include "snapshot.h"
  7. #include "tape.h"
  8. #include "memory.h"
  9. #include "opendlg.h"
  10. #include "draw.h"
  11. #include "config.h"
  12. #include "z80.h"
  13. #include "sshot_png.h"
  14. #include "funcs.h"
  15.  
  16. #include "util.h"
  17.  
  18. int readSP();
  19. int readSNA48();
  20. int readSNA128();
  21. int readZ80();
  22. int writeSNA(FILE*);
  23.  
  24. int load_arc(char *fname);
  25.  
  26. SNAP what_is(char *filename)
  27. {
  28.    FILE *ff = fopen(filename, "rb");
  29.    if (!ff) return snNOFILE;
  30.    snapsize = unsigned(fread(snbuf, 1, sizeof snbuf, ff));
  31.    fclose(ff);
  32.    if (snapsize == sizeof snbuf) return snTOOLARGE;
  33.    SNAP type = snUNKNOWN;
  34.    char *ptr = strrchr(filename, '.');
  35.    unsigned ext = ptr? (*(unsigned*)(ptr+1) | 0x20202020U) : 0;
  36.    if (snapsize < 32) return type;
  37.  
  38.    if (snapsize == 131103 || snapsize == 147487) type = snSNA_128;
  39.    if (snapsize == 49179) type = snSNA_48;
  40.    if (ext == WORD4('t','a','p',' ')) type = snTAP;
  41.    if (ext == WORD4('z','8','0',' ')) type = snZ80;
  42.    if(ext == WORD4('p', 'a', 'l', ' ') && (snapsize == sizeof(comp.comp_pal)))
  43.    {
  44.        type = snPAL;
  45.        return type;
  46.    }
  47.    if (conf.trdos_present)
  48.    {
  49.       if (!snbuf[13] && snbuf[14] && (int)snapsize == snbuf[14]*256+17) type = snHOB;
  50.       if (snapsize >= 8192 && !(snapsize & 0xFF) && ext == WORD4('t','r','d',' ')) type = snTRD;
  51.  
  52.       if (snapsize >= 8192 && ext == WORD4('i','s','d',' '))
  53.           type = snISD;
  54.  
  55.       if (snapsize >= 8192 && ext == WORD4('p','r','o',' '))
  56.           type = snPRO;
  57.  
  58.       if(snapsize >= 8192 && ext == WORD4('d', 's', 'k', ' '))
  59.           type = snDSK;
  60.  
  61.       if(snapsize >= 8192 && ext == WORD4('i', 'p', 'f', ' '))
  62.           type = snIPF;
  63.  
  64.       if (!memcmp(snbuf, "SINCLAIR", 8))
  65.       {
  66.           unsigned nfiles = snbuf[8];
  67.           unsigned nsec = 0;
  68.           for(unsigned i = 0; i < nfiles; i++)
  69.           {
  70.               nsec += snbuf[9+14*i+13];
  71.           }
  72.          
  73.           if(snapsize >= 9 + nfiles * 14 + nsec * 0x100)
  74.               type = snSCL;
  75.       }
  76.       if (!memcmp(snbuf, "FDI", 3) && *(unsigned short*)(snbuf+4) <= MAX_CYLS && *(unsigned short*)(snbuf+6) <= 2) type = snFDI;
  77.       if (((*(short*)snbuf|0x2020) == WORD2('t','d')) && snbuf[4] >= 10 && snbuf[4] <= 21 && snbuf[9] <= 2) type = snTD0;
  78.       if (*(unsigned*)snbuf == WORD4('U','D','I','!') && *(unsigned*)(snbuf+4)==snapsize-4 && snbuf[9] < MAX_CYLS && snbuf[10]<2 && !snbuf[8]) type = snUDI;
  79.    }
  80.    if (snapsize > 10 && !memcmp(snbuf, "ZXTape!\x1A", 8))
  81.    {
  82.        type = snTZX;
  83.    }
  84.    if (snapsize > 0x22 && !memcmp(snbuf, "Compressed Square Wave\x1A", 23)) type = snCSW;
  85.    if (*(unsigned short*)snbuf == WORD2('S','P') && *(unsigned short*)(snbuf+2)+0x26 == (int)snapsize) type = snSP;
  86.    return type;
  87. }
  88.  
  89. static int readPAL();
  90.  
  91. int loadsnap(char *filename)
  92. {
  93.    if (load_arc(filename))
  94.        return 1;
  95.    SNAP type = what_is(filename);
  96.  
  97.    if (type >= snHOB)
  98.    {
  99.  
  100.       if (trd_toload == -1U)
  101.       {
  102.          unsigned last = -1U;
  103.          for (unsigned i = 0; i < 4; i++) if (trd_loaded[i]) last = i;
  104.          trd_toload = (last == -1U)? 0 : ((type == snHOB)? last : (last+1) & 3);
  105.       }
  106.  
  107.       for (unsigned k = 0; k < 4; k++)
  108.       {
  109.          if (k != trd_toload && !stricmp(comp.fdd[k].name, filename))
  110.          {
  111.             static char err[] = "This disk image is already loaded to drive X:\n"
  112.                                 "Do you still want to load it to drive Y:?";
  113.             err[43] = char(k+'A');
  114.             err[84] = char(trd_toload+'A');
  115.             if (MessageBox(GetForegroundWindow(), err, "Warning", MB_YESNO | MB_ICONWARNING) == IDNO) return 1;
  116.          }
  117.       }
  118.  
  119.       FDD *drive = comp.fdd + trd_toload;
  120.       if (!drive->test())
  121.           return 0;
  122.       comp.wd.Eject(trd_toload);
  123.       int ok = drive->read(type);
  124.       if (ok)
  125.       {
  126.          if (*conf.appendboot)
  127.              drive->addboot();
  128.          strcpy(drive->name, filename);
  129.          if (GetFileAttributes(filename) & FILE_ATTRIBUTE_READONLY)
  130.             conf.trdos_wp[trd_toload] = 1;
  131.          drive->snaptype = (type == snHOB || type == snSCL)? snTRD : type;
  132.          trd_loaded[trd_toload] = 1;
  133. //---------Alone Coder
  134.          char *name = filename;
  135.          for (char *x = name; *x; x++)
  136.              if (*x == '\\') name = x+1;
  137.          strcpy(temp.LastSnapName, name);
  138.          char *p = strrchr(temp.LastSnapName, '.');
  139.          if(p)
  140.          {
  141.              *p = 0;
  142.          }
  143.          char wintitle[0x200];
  144.          strcpy(wintitle,name);
  145.          strcat(wintitle," - UnrealSpeccy");
  146.          SetWindowText(wnd, wintitle);
  147. //~---------Alone Coder
  148.       }
  149.       return ok;
  150.    }
  151.  
  152.    if (type == snSP) return readSP();
  153.    if (type == snSNA_48) return readSNA48();
  154.    if (type == snSNA_128) return readSNA128();
  155.    if (type == snZ80) return readZ80();
  156.    if (type == snTAP) return readTAP();
  157.    if (type == snTZX) return readTZX();
  158.    if (type == snCSW) return readCSW();
  159.    if(type == snPAL)
  160.    {
  161.        return readPAL();
  162.    }
  163.  
  164.    return 0;
  165. }
  166.  
  167. int readSNA128()
  168. {
  169.    if(conf.mem_model == MM_PENTAGON && conf.Sna128Lock)
  170.    {
  171.        conf.ramsize = 128;
  172.        temp.ram_mask = u8((conf.ramsize-1) >> 4);
  173.    }
  174.  
  175.    hdrSNA128 *hdr = (hdrSNA128*)snbuf;
  176.    reset(hdr->trdos? RM_DOS : RM_SOS);
  177.    cpu.alt.af = hdr->altaf;
  178.    cpu.alt.bc = hdr->altbc;
  179.    cpu.alt.de = hdr->altde;
  180.    cpu.alt.hl = hdr->althl;
  181.    cpu.af = hdr->af;
  182.    cpu.bc = hdr->bc;
  183.    cpu.de = hdr->de;
  184.    cpu.hl = hdr->hl;
  185.    cpu.ix = hdr->ix;
  186.    cpu.iy = hdr->iy;
  187.    cpu.sp = hdr->sp;
  188.    cpu.pc = hdr->pc;
  189.    cpu.i = hdr->i;
  190.    cpu.r_low = hdr->r;
  191.    cpu.r_hi = hdr->r & 0x80;
  192.    cpu.im = hdr->im;
  193.    cpu.iff1 = cpu.iff2 = (hdr->iff >> 2) & 1;
  194.    comp.p7FFD = hdr->p7FFD;
  195.    comp.pFE = hdr->pFE;
  196.    comp.border_attr = comp.pFE & 7;
  197.    memcpy(memory+PAGE*5, hdr->page5, PAGE);
  198.    memcpy(memory+PAGE*2, hdr->page2, PAGE);
  199.    memcpy(memory+PAGE*(hdr->p7FFD & 7), hdr->active_page, PAGE);
  200.    unsigned char *newpage = snbuf+0xC01F;
  201.    unsigned char mapped = u8(0x24U | (1 << (hdr->p7FFD & 7)));
  202.    for (unsigned char i = 0; i < 8; i++)
  203.    {
  204.       if (!(mapped & (1 << i)))
  205.       {
  206.          memcpy(memory + PAGE*i, newpage, PAGE);
  207.          newpage += PAGE;
  208.       }
  209.    }
  210.    set_banks();
  211.    return 1;
  212. }
  213.  
  214. int readSNA48()
  215. {
  216.    //conf.mem_model = MM_PENTAGON; conf.ramsize = 128;  // molodcov_alex
  217.    reset(RM_SOS);
  218.    hdrSNA128 *hdr = (hdrSNA128*)snbuf;
  219.    cpu.alt.af = hdr->altaf; cpu.alt.bc = hdr->altbc;
  220.    cpu.alt.de = hdr->altde; cpu.alt.hl = hdr->althl;
  221.    cpu.af = hdr->af; cpu.bc = hdr->bc; cpu.de = hdr->de; cpu.hl = hdr->hl;
  222.    cpu.ix = hdr->ix; cpu.iy = hdr->iy; cpu.sp = hdr->sp;
  223.    cpu.i = hdr->i; cpu.r_low = hdr->r; cpu.r_hi = hdr->r & 0x80; cpu.im = hdr->im;
  224.    cpu.iff1 = cpu.iff2 = (hdr->iff >> 2) & 1; comp.p7FFD = 0x30;
  225.    comp.pEFF7 |= EFF7_LOCKMEM; //Alone Coder
  226.    comp.pFE = hdr->pFE; comp.border_attr = comp.pFE & 7;
  227.    memcpy(memory+PAGE*5, hdr->page5, PAGE);
  228.    memcpy(memory+PAGE*2, hdr->page2, PAGE);
  229.    memcpy(memory+PAGE*0, hdr->active_page, PAGE);
  230.    cpu.pc = cpu.DirectRm(cpu.sp)+0x100*cpu.DirectRm(cpu.sp+1); cpu.sp += 2;
  231.    set_banks(); return 1;
  232. }
  233.  
  234. int readSP()
  235. {
  236.    //conf.mem_model = MM_PENTAGON; conf.ramsize = 128;  // molodcov_alex
  237.    reset(RM_SOS);
  238.    hdrSP *hdr = (hdrSP*)snbuf;
  239.    cpu.alt.af = hdr->altaf; cpu.alt.bc = hdr->altbc;
  240.    cpu.alt.de = hdr->altde; cpu.alt.hl = hdr->althl;
  241.    cpu.af = hdr->af; cpu.bc = hdr->bc; cpu.de = hdr->de; cpu.hl = hdr->hl;
  242.    cpu.ix = hdr->ix; cpu.iy = hdr->iy; cpu.sp = hdr->sp; cpu.pc = hdr->pc;
  243.    cpu.i = hdr->i; cpu.r_low = hdr->r; cpu.r_hi = hdr->r & 0x80;
  244.    cpu.iff1 = (hdr->flags & 1);
  245.    cpu.im = 1 + ((hdr->flags >> 1) & 1);
  246.    cpu.iff2 = (hdr->flags >> 2) & 1;
  247.    comp.p7FFD = 0x30;
  248.    comp.pEFF7 |= EFF7_LOCKMEM; //Alone Coder
  249.    comp.pFE = hdr->pFE; comp.border_attr = comp.pFE & 7;
  250.    for (unsigned i = 0; i < hdr->len; i++)
  251.       cpu.DirectWm(hdr->start + i, snbuf[i + 0x26]);
  252.    set_banks(); return 1;
  253. }
  254.  
  255. int writeSNA(FILE *ff)
  256. {
  257. /*   if (conf.ramsize != 128) {
  258.       MessageBox(GetForegroundWindow(), "SNA format can hold only\r\n128kb memory models", "Save", MB_ICONERROR);
  259.       return 0;
  260.    }*/ //Alone Coder
  261.    hdrSNA128 *hdr = (hdrSNA128*)snbuf;
  262.    hdr->trdos = (comp.flags & CF_TRDOS)? 1 : 0;
  263.    hdr->altaf = u16(cpu.alt.af); hdr->altbc = u16(cpu.alt.bc);
  264.    hdr->altde = u16(cpu.alt.de); hdr->althl = u16(cpu.alt.hl);
  265.    hdr->af = u16(cpu.af); hdr->bc = u16(cpu.bc); hdr->de = u16(cpu.de); hdr->hl = u16(cpu.hl);
  266.    hdr->ix = u16(cpu.ix); hdr->iy = u16(cpu.iy); hdr->sp = u16(cpu.sp); hdr->pc = u16(cpu.pc);
  267.    hdr->i = cpu.i; hdr->r = (cpu.r_low & 0x7F)+cpu.r_hi; hdr->im = cpu.im;
  268.    hdr->iff = cpu.iff2 ? 4 : 0;
  269.    hdr->p7FFD = comp.p7FFD;
  270.    hdr->pFE = comp.pFE; comp.border_attr = comp.pFE & 7;
  271.    unsigned savesize = sizeof(hdrSNA128);
  272.    unsigned char mapped = u8(0x24U | (1 << (comp.p7FFD & 7)));
  273.    if (comp.p7FFD == 0x30)
  274.    { // save 48k
  275.       mapped = 0xFF;
  276.       savesize = 0xC01B;
  277.       hdr->sp -= 2;
  278.       cpu.DirectWm(hdr->sp, cpu.pcl);
  279.       cpu.DirectWm(hdr->sp+1, cpu.pch);
  280.    }
  281.    memcpy(hdr->page5, memory+PAGE*5, PAGE);
  282.    memcpy(hdr->page2, memory+PAGE*2, PAGE);
  283.    memcpy(hdr->active_page, memory+PAGE*(comp.p7FFD & 7), PAGE);
  284.    if (fwrite(hdr, 1, savesize, ff) != savesize) return 0;
  285.    for (unsigned char i = 0; i < 8; i++)
  286.       if (!(mapped & (1 << i))) {
  287.          if (fwrite(memory + PAGE*i, 1, PAGE, ff) != PAGE) return 0;
  288.       }
  289.    return 1;
  290. }
  291.  
  292. static void unpack_page(unsigned char *dst, unsigned dstlen, unsigned char *src, unsigned srclen)
  293. {
  294.    memset(dst, 0, dstlen);
  295.    while (srclen > 0 && dstlen > 0)
  296.    {
  297.       if (srclen >= 4 && src[0] == 0xED && src[1] == 0xED)
  298.       {
  299.          size_t len = src[2];
  300.          memset(dst, src[3], len);
  301.          dstlen -= len;
  302.          srclen -= 4;
  303.          src += 4;
  304.          dst += len;
  305.       }
  306.       else
  307.       {
  308.           *dst++ = *src++;
  309.           dstlen--;
  310.           srclen--;
  311.       }
  312.    }
  313. }
  314.  
  315. int readZ80()
  316. {
  317.    //conf.mem_model = MM_PENTAGON; conf.ramsize = 128;  // molodcov_alex
  318.    hdrZ80 *hdr = (hdrZ80*)snbuf;
  319.    unsigned char *ptr = snbuf + 30;
  320.    unsigned char model48k = (hdr->model < 3);
  321.    reset((model48k|(hdr->p7FFD & 0x10)) ? RM_SOS : RM_128);
  322.    if (hdr->flags == 0xFF)
  323.        hdr->flags = 1;
  324.    if (hdr->pc == 0)
  325.    { // 2.01
  326.       ptr += 2 + hdr->len;
  327.       hdr->pc = hdr->newpc;
  328.       memset(RAM_BASE_M, 0, PAGE*8); // clear 128k - first 8 pages
  329.  
  330.       unsigned char * const p48[] =
  331.       {
  332.              base_sos_rom, nullptr, nullptr, nullptr,
  333.              RAM_BASE_M+2*PAGE, RAM_BASE_M+0*PAGE, nullptr, nullptr,
  334.              RAM_BASE_M+5*PAGE, nullptr, nullptr, nullptr
  335.       };
  336.       unsigned char * const p128[] =
  337.       {
  338.              base_sos_rom, base_dos_rom, base_128_rom, RAM_BASE_M+0*PAGE,
  339.              RAM_BASE_M+1*PAGE, RAM_BASE_M+2*PAGE, RAM_BASE_M+3*PAGE, RAM_BASE_M+4*PAGE,
  340.              RAM_BASE_M+5*PAGE, RAM_BASE_M+6*PAGE, RAM_BASE_M+7*PAGE, nullptr
  341.       };
  342.  
  343.       while (ptr < snbuf+snapsize)
  344.       {
  345.          unsigned len = *(unsigned short*)ptr;
  346.          if (ptr[2] > 11)
  347.              return 0;
  348.          unsigned char *dstpage = model48k ? p48[ptr[2]] : p128[ptr[2]];
  349.          if (!dstpage)
  350.              return 0;
  351.          ptr += 3;
  352.          if (len == 0xFFFF)
  353.          {
  354.              len = PAGE;
  355.              memcpy(dstpage, ptr, len);
  356.          }
  357.          else
  358.              unpack_page(dstpage, PAGE, ptr, len);
  359.          ptr += len;
  360.       }
  361.    }
  362.    else
  363.    {
  364.       unsigned len = snapsize - 30;
  365.       unsigned char *mem48 = ptr;
  366.       if (hdr->flags & 0x20)
  367.          unpack_page(mem48 = snbuf + 4*PAGE, 3*PAGE, ptr, len);
  368.       memcpy(memory + PAGE*5, mem48, PAGE);
  369.       memcpy(memory + PAGE*2, mem48 + PAGE, PAGE);
  370.       memcpy(memory + PAGE*0, mem48 + 2*PAGE, PAGE);
  371.       model48k = 1;
  372.    }
  373.    cpu.a = hdr->a; cpu.f = hdr->f;
  374.    cpu.bc = hdr->bc; cpu.de = hdr->de; cpu.hl = hdr->hl;
  375.    cpu.alt.bc = hdr->bc1; cpu.alt.de = hdr->de1; cpu.alt.hl = hdr->hl1;
  376.    cpu.alt.a = hdr->a1; cpu.alt.f = hdr->f1;
  377.    cpu.pc = hdr->pc; cpu.sp = hdr->sp; cpu.ix = hdr->ix; cpu.iy = hdr->iy;
  378.    cpu.i = hdr->i; cpu.r_low = hdr->r & 0x7F;
  379.    cpu.r_hi = u8((hdr->flags & 1U) << 7U);
  380.    comp.pFE = (hdr->flags >> 1) & 7;
  381.    comp.border_attr = comp.pFE;
  382.    cpu.iff1 = hdr->iff1; cpu.iff2 = hdr->iff2; cpu.im = (hdr->im & 3);
  383.    comp.p7FFD = (model48k) ? 0x30 : hdr->p7FFD;
  384.  
  385.    if(hdr->len == 55) // version 3.0 (with 1ffd)
  386.        comp.p1FFD = hdr->p1FFD;
  387.  
  388.    if (model48k)
  389.        comp.pEFF7 |= EFF7_LOCKMEM; //Alone Coder
  390.  
  391.    set_banks();
  392.  
  393.    return 1;
  394. }
  395.  
  396. static int readPAL()
  397. {
  398.     memcpy(comp.comp_pal, snbuf, snapsize);
  399.     temp.comp_pal_changed = 1;
  400.     if(conf.ula_plus)
  401.     {
  402.         comp.ula_plus_en = true; // ┼ёыш яЁшёєЄёЄтєхЄ ULA+, Єю ёЁрчє ш ръЄштшЁєхь хх (ўЄюс√ шчьхэхэш  ярышЄЁ√ с√ыш тшфэ√)
  403.     }
  404.     return 1;
  405. }
  406.  
  407. static int writePAL(FILE *ff)
  408. {
  409.     if(fwrite(comp.comp_pal, 1, sizeof(comp.comp_pal), ff) != sizeof(comp.comp_pal))
  410.     {
  411.         return 0;
  412.     }
  413.     return 1;
  414. }
  415.  
  416. #define arctmp ((char*)rbuf)
  417. static char *arc_fname;
  418. static INT_PTR CALLBACK ArcDlg(HWND dlg, UINT msg, WPARAM wp, LPARAM lp)
  419. {
  420.    (void)lp;
  421.  
  422.    if (msg == WM_INITDIALOG) {
  423.       for (char *dst = arctmp; *dst; dst += strlen(dst)+1)
  424.          SendDlgItemMessage(dlg, IDC_ARCLIST, LB_ADDSTRING, 0, (LPARAM)dst);
  425.       SendDlgItemMessage(dlg, IDC_ARCLIST, LB_SETCURSEL, 0, 0);
  426.       return 1;
  427.    }
  428.    if ((msg == WM_COMMAND && wp == IDCANCEL) ||
  429.        (msg == WM_SYSCOMMAND && (wp & 0xFFF0) == SC_CLOSE)) EndDialog(dlg, 0);
  430.    if (msg == WM_COMMAND && (LOWORD(wp) == IDOK || (HIWORD(wp)==LBN_DBLCLK && LOWORD(wp) == IDC_ARCLIST)))
  431.    {
  432.       int n = int(SendDlgItemMessage(dlg, IDC_ARCLIST, LB_GETCURSEL, 0, 0));
  433.       char *dst = arctmp;
  434.       for (int q = 0; q < n; q++) dst += strlen(dst)+1;
  435.       arc_fname = dst;
  436.       EndDialog(dlg, 0);
  437.    }
  438.    return 0;
  439. }
  440.  
  441. static bool filename_ok(char *fname)
  442. {
  443.    for (char *wc = skiparc; *wc; wc += strlen(wc)+1)
  444.       if (wcmatch(fname, wc)) return 0;
  445.    return 1;
  446. }
  447.  
  448. int load_arc(char *fname)
  449. {
  450.    char *ext = strrchr(fname, '.'); if (!ext) return 0;
  451.    ext++;
  452.    char *cmdtmp;
  453.    char done = 0;
  454.    for (char *x = arcbuffer; *x; x = cmdtmp + strlen(cmdtmp)+1) {
  455.       cmdtmp = x + strlen(x)+1;
  456.       if (stricmp(ext, x)) continue;
  457.  
  458.       char dir[0x200]; GetCurrentDirectory(sizeof dir, dir);
  459.       char tmp[0x200]; GetTempPath(sizeof tmp, tmp);
  460.       char d1[0x20]; sprintf(d1, "us%08lX", GetTickCount());
  461.       SetCurrentDirectory(tmp);
  462.       CreateDirectory(d1, nullptr);
  463.       SetCurrentDirectory(d1);
  464.  
  465.       color();
  466.       char cmdln[0x200]; sprintf(cmdln, cmdtmp, fname);
  467.       STARTUPINFO si = { sizeof si };
  468.       si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE;
  469.       PROCESS_INFORMATION pi;
  470.       unsigned flags = CREATE_NEW_CONSOLE;
  471.       HANDLE hc = CreateFile("CONOUT$", GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
  472.       if(hc != INVALID_HANDLE_VALUE)
  473.       {
  474.           CloseHandle(hc);
  475.           flags = 0;
  476.       }
  477.       if (CreateProcess(nullptr, cmdln, nullptr, nullptr, 0, flags, nullptr, nullptr, &si, &pi)) {
  478.          WaitForSingleObject(pi.hProcess, INFINITE);
  479.          DWORD code; GetExitCodeProcess(pi.hProcess, &code);
  480.          CloseHandle(pi.hThread);
  481.          CloseHandle(pi.hProcess);
  482.          if (!code || MessageBox(GetForegroundWindow(), "Broken archive", nullptr, MB_ICONERROR | MB_OKCANCEL) == IDOK) {
  483.             WIN32_FIND_DATA fd; HANDLE h;
  484.             char *dst = arctmp; unsigned nfiles = 0;
  485.             if ((h = FindFirstFile("*.*", &fd)) != INVALID_HANDLE_VALUE) {
  486.                do {
  487.                   if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && filename_ok(fd.cFileName)) {
  488.                      strcpy(dst, fd.cFileName); dst += strlen(dst)+1;
  489.                      nfiles++;
  490.                   }
  491.                } while (FindNextFile(h, &fd));
  492.                FindClose(h);
  493.             }
  494.             *dst = 0; arc_fname = nullptr;
  495.             if (nfiles == 1) arc_fname = arctmp;
  496.             if (nfiles > 1)
  497.                 DialogBox(hIn, MAKEINTRESOURCE(IDD_ARC), GetForegroundWindow(), ArcDlg);
  498.             if (!nfiles) MessageBox(GetForegroundWindow(), "Empty archive!", nullptr, MB_ICONERROR | MB_OK);
  499.             char buf[0x200]; strcpy(buf, tmp); strcat(buf, "\\");
  500.             strcat(buf, d1);
  501.             strcat(buf, "\\");
  502.             if(arc_fname)
  503.             {
  504.                 strcat(buf, arc_fname);
  505.                 arc_fname = buf;
  506.             }
  507.             if (arc_fname && !(done = (char)loadsnap(arc_fname))) MessageBox(GetForegroundWindow(), "loading error", arc_fname, MB_ICONERROR);
  508.             if (!done) done = -1;
  509.          }
  510.          // delete junk
  511.          SetCurrentDirectory(tmp);
  512.          SetCurrentDirectory(d1);
  513.          WIN32_FIND_DATA fd; HANDLE h;
  514.          if ((h = FindFirstFile("*.*", &fd)) != INVALID_HANDLE_VALUE) {
  515.             do { DeleteFile(fd.cFileName); } while (FindNextFile(h, &fd));
  516.             FindClose(h);
  517.          }
  518.       }
  519.       SetCurrentDirectory(tmp);
  520.       RemoveDirectory(d1);
  521.       SetCurrentDirectory(dir);
  522.       eat(); if (done) return done > 0 ? 1 : 0;
  523.    }
  524.    eat(); return 0;
  525. }
  526.  
  527. void opensnap(unsigned index)
  528. {
  529.    char mask[0x200]; *mask = 0;
  530.    for(char *x = arcbuffer; *x; )
  531.    {
  532.        strcat(mask, ";*.");
  533.        strcat(mask, x);
  534.        x += strlen(x) + 1;
  535.        x += strlen(x) + 1;
  536.    }
  537.  
  538.    char fline[0x400];
  539.    const char *src = "all (sna,z80,sp,tap,tzx,csw,trd,scl,fdi,td0,udi,isd,pro,dsk,ipf,hobeta,pal)\0"
  540.                "*.sna;*.z80;*.sp;*.tap;*.tzx;*.csw;*.trd;*.scl;*.td0;*.udi;*.fdi;*.isd;*.pro;*.dsk;*.ipf;*.$?;*.!?;*.pal<\0"
  541.                "Disk B (trd,scl,fdi,td0,udi,isd,pro,dsk,ipf,hobeta)\0*.trd;*.scl;*.fdi;*.udi;*.td0;*.isd;*.pro;*.dsk;*.ipf;*.$?<\0"
  542.                "Disk C (trd,scl,fdi,td0,udi,isd,pro,dsk,ipf,hobeta)\0*.trd;*.scl;*.fdi;*.udi;*.td0;*.isd;*.pro;*.dsk;*.ipf;*.$?<\0"
  543.                "Disk D (trd,scl,fdi,td0,udi,isd,pro,dsk,ipf,hobeta)\0*.trd;*.scl;*.fdi;*.udi;*.td0;*.isd;*.pro;*.dsk;*.ipf;*.$?<\0\0>";
  544.    if (!conf.trdos_present)
  545.       src = "ZX files (sna,z80,tap,tzx,csw,pal)\0*.sna;*.z80;*.tap;*.tzx;*.csw;*.pal<\0\0>";
  546.    for(char *dst = fline; *src != '>'; src++)
  547.    {
  548.        if(*src == '<')
  549.        {
  550.            strcpy(dst, mask);
  551.            dst += strlen(dst);
  552.        }
  553.        else
  554.        {
  555.            *dst++ = *src;
  556.        }
  557.    }
  558.  
  559.    OPENFILENAME ofn = { };
  560.    char fname[0x200]; *fname = 0;
  561.    char dir[0x200]; GetCurrentDirectory(sizeof dir, dir);
  562.  
  563.    ofn.lStructSize = (WinVerMajor < 5) ? OPENFILENAME_SIZE_VERSION_400 : sizeof(OPENFILENAME);
  564.    ofn.hwndOwner = GetForegroundWindow();
  565.    ofn.lpstrFilter = fline;
  566.    ofn.lpstrFile = fname; ofn.nMaxFile = sizeof fname;
  567.    ofn.lpstrTitle = "Load Snapshot / Disk / Tape";
  568.    ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
  569.    ofn.nFilterIndex = DWORD(index);
  570.    ofn.lpstrInitialDir = dir;
  571. //   __debugbreak();
  572.    if (GetSnapshotFileName(&ofn, 0))
  573.    {
  574.       trd_toload = ofn.nFilterIndex-1;
  575.       if (!loadsnap(fname))
  576.           MessageBox(GetForegroundWindow(), fname, "loading error", MB_ICONERROR);
  577.    }
  578.    eat();
  579. }
  580.  
  581. static const int mx_typs = (1+4*6);
  582. static unsigned char snaps[mx_typs];
  583. static unsigned exts[mx_typs];
  584. static unsigned drvs[mx_typs];
  585. static unsigned snp;
  586. static void addref(LPSTR &ptr, unsigned char sntype, const char *ref, unsigned drv, unsigned ext)
  587. {
  588.    strcpy(ptr, ref); ptr += strlen(ptr)+1;
  589.    strcpy(ptr, ref+strlen(ref)+1); ptr += strlen(ptr)+1;
  590.    drvs[snp] = drv; exts[snp] = ext; snaps[snp++] = sntype;
  591. }
  592.  
  593. void savesnap(int diskindex)
  594. {
  595. again:
  596.    OPENFILENAME ofn = { };
  597.    char fname[0x200]; *fname = 0;
  598.    if (diskindex >= 0) {
  599.       strcpy(fname, comp.fdd[diskindex].name);
  600.       size_t ln = strlen(fname);
  601.       if (ln > 4 && (*(unsigned*)(fname+ln-4) | WORD4(0,0x20,0x20,0x20)) == WORD4('.','s','c','l'))
  602.          *(unsigned*)(fname+ln-4) = WORD4('.','t','r','d');
  603.    }
  604.  
  605.    snp = 1; char types[600], *ptr = types;
  606.  
  607.    if (diskindex < 0)
  608.    {
  609.       exts[snp] = WORD4('s','n','a',0); snaps[snp] = snSNA_128; // default
  610.       addref(ptr, snSNA_128, "ZX-Spectrum 128K snapshot (SNA)\0*.sna", -1U, WORD4('s','n','a',0));
  611.  
  612.       exts[snp] = WORD4('p', 'a', 'l', 0); snaps[snp] = snPAL;
  613.       addref(ptr, snPAL, "Palette (ULA+)\0*.pal", -1U, WORD4('p', 'a', 'l', 0));
  614.    }
  615.  
  616.    ofn.lStructSize = (WinVerMajor < 5) ? OPENFILENAME_SIZE_VERSION_400 : sizeof(OPENFILENAME);
  617.    ofn.nFilterIndex = 1;
  618.  
  619.    if (conf.trdos_present)
  620.    {
  621.       static char mask[] = "Disk A (TRD)\0*.trd";
  622.       static const char ex[][3] = { {'T','R','D'}, {'F','D','I'},{'T','D','0'},{'U','D','I'},{'I','S','D'},{'P','R','O'}};
  623.       static const unsigned ex2[] = { snTRD, snFDI, snTD0, snUDI, snISD, snPRO };
  624.  
  625.       for (unsigned n = 0; n < 4; n++)
  626.       {
  627.          if (!comp.fdd[n].rawdata)
  628.              continue;
  629.          if (diskindex >= 0 && unsigned(diskindex) != n)
  630.              continue;
  631.          mask[5] = char('A'+n);
  632.  
  633.          for (size_t i = 0; i < sizeof ex/sizeof(ex[0]); i++)
  634.          {
  635.             if (unsigned(diskindex) == n && ex2[i] == comp.fdd[n].snaptype)
  636.                 ofn.nFilterIndex = snp;
  637.             memcpy(mask+8, ex[i], 3);
  638.             memcpy(mask+15, ex[i], 3);
  639.             addref(ptr, u8(ex2[i]), mask, n, (*(const unsigned*)ex[i] & 0xFFFFFF) | 0x202020);
  640.          }
  641.       }
  642.    }
  643.    ofn.lpstrFilter = types; *ptr = 0;
  644.    ofn.lpstrFile = fname; ofn.nMaxFile = sizeof fname;
  645.    ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
  646.    ofn.hwndOwner = GetForegroundWindow();
  647.    char *path = strrchr(fname, '\\');
  648.    if (path)
  649.    { // check if directory exists (for files opened from archive)
  650.       char x = *path; *path = 0;
  651.       unsigned atr = GetFileAttributes(fname); *path = x;
  652.       if (atr == -1U || !(atr & FILE_ATTRIBUTE_DIRECTORY)) *fname = 0;
  653.    } else path = fname;
  654.    path = strrchr(path, '.'); if (path) *path = 0; // delete extension
  655.  
  656.    if (GetSnapshotFileName(&ofn, 1))
  657.    {
  658.       char *fn = strrchr(ofn.lpstrFile, '\\');
  659.       fn = fn? fn+1 : ofn.lpstrFile;
  660.       char *extpos = strrchr(fn, '.');
  661.       if (!extpos || stricmp(extpos+1, (char*)&exts[ofn.nFilterIndex]))
  662.       {
  663.          char *dst = fn + strlen(fn); *dst++ = '.';
  664.          *(unsigned*)dst = exts[ofn.nFilterIndex];
  665.       }
  666.       if (GetFileAttributes(ofn.lpstrFile) != INVALID_FILE_ATTRIBUTES &&
  667.             IDNO == MessageBox(GetForegroundWindow(), "File exists. Overwrite ?", "Save", MB_ICONQUESTION | MB_YESNO))
  668.          goto again;
  669.  
  670.       FILE *ff = fopen(ofn.lpstrFile, "wb");
  671.       if (ff)
  672.       {
  673.          int res = 0;
  674.          FDD *saveto = comp.fdd + drvs[ofn.nFilterIndex];
  675.          switch (snaps[ofn.nFilterIndex])
  676.          {
  677.             case snSNA_128: res = writeSNA(ff); break;
  678.             case snPAL: res = writePAL(ff); break;
  679.             case snTRD: res = saveto->write_trd(ff); break;
  680.             case snUDI: res = saveto->write_udi(ff); break;
  681.             case snFDI: res = saveto->write_fdi(ff); break;
  682.             case snTD0: res = saveto->write_td0(ff); break;
  683.             case snISD: res = saveto->write_isd(ff); break;
  684.             case snPRO: res = saveto->write_pro(ff); break;
  685.          }
  686.          fclose(ff);
  687.          if (!res)
  688.              MessageBox(GetForegroundWindow(), "write error", "Save", MB_ICONERROR);
  689.          else if (drvs[ofn.nFilterIndex]!=-1U)
  690.          {
  691.              comp.fdd[drvs[ofn.nFilterIndex]].optype=0;
  692.              strcpy(comp.fdd[drvs[ofn.nFilterIndex]].name, ofn.lpstrFile);
  693.  
  694.              //---------Alone Coder
  695.              char *name = ofn.lpstrFile;
  696.              for (char *x = name; *x; x++)
  697.              {
  698.                  if (*x == '\\')
  699.                      name = x+1;
  700.              }
  701.              char wintitle[0x200];
  702.              strcpy(wintitle,name);
  703.              strcat(wintitle," - UnrealSpeccy");
  704.              SetWindowText(wnd, wintitle);
  705.              //~---------Alone Coder
  706.          }
  707.       }
  708.       else
  709.           MessageBox(GetForegroundWindow(), "Can't open file for writing", "Save", MB_ICONERROR);
  710.    }
  711.    eat();
  712. }
  713.  
  714. void ConvPal8ToBgr24(u8 *dst, u8 *scrbuf, int dx)
  715. {
  716.     u8 *ds = dst;
  717.     for(unsigned i = 0; i < temp.oy; i++) // convert to BGR24 format
  718.     {
  719.        unsigned char *src = scrbuf + int(i)*dx;
  720.        for (unsigned y = 0; y < temp.ox; y++)
  721.        {
  722.           ds[0] = pal0[src[y]].peBlue;
  723.           ds[1] = pal0[src[y]].peGreen;
  724.           ds[2] = pal0[src[y]].peRed;
  725.           ds += 3;
  726.        }
  727.        ds = (PBYTE)(ULONG_PTR(ds + 3) & ~ULONG_PTR(3)); // ърцфр  ёЄЁюър т√Ёртэхэр эр 4
  728.     }
  729. }
  730.  
  731. void ConvRgb15ToBgr24(u8 *dst, u8 *scrbuf, int dx)
  732. {
  733.     u8 *ds = dst;
  734.     for(unsigned i = 0; i < temp.oy; i++) // convert to BGR24 format
  735.     {
  736.        unsigned char *src = scrbuf + int(i)*dx;
  737.        for (unsigned y = 0; y < temp.ox; y++)
  738.        {
  739.           unsigned xx;
  740.           xx = *(unsigned*)(src + y*2);
  741.  
  742.           ds[0] = u8((xx & 0x1F)<<3);
  743.           ds[1] = u8((xx & 0x03E0)>>2);
  744.           ds[2] = u8((xx & 0x7C00)>>7);
  745.           ds += 3;
  746.        }
  747.        ds = (PBYTE)(ULONG_PTR(ds + 3) & ~ULONG_PTR(3)); // ърцфр  ёЄЁюър т√Ёртэхэр эр 4
  748.     }
  749. }
  750.  
  751. void ConvRgb16ToBgr24(u8 *dst, u8 *scrbuf, int dx)
  752. {
  753.     u8 *ds = dst;
  754.     for(unsigned i = 0; i < temp.oy; i++) // convert to BGR24 format
  755.     {
  756.        unsigned char *src = scrbuf + int(i)*dx;
  757.        for (unsigned y = 0; y < temp.ox; y++)
  758.        {
  759.           unsigned xx;
  760.           xx = *(unsigned*)(src + y*2);
  761.  
  762.           ds[0] = u8((xx&0x1F)<<3);
  763.           ds[1] = u8((xx&0x07E0)>>3);
  764.           ds[2] = u8((xx&0xF800)>>8);
  765.           ds += 3;
  766.        }
  767.        ds = (PBYTE)(ULONG_PTR(ds + 3) & ~ULONG_PTR(3)); // ърцфр  ёЄЁюър т√Ёртэхэр эр 4
  768.     }
  769. }
  770.  
  771. void ConvYuy2ToBgr24(u8 *dst, u8 *scrbuf, int dx)
  772. {
  773.     u8 *ds = dst;
  774.     for(unsigned i = 0; i < temp.oy; i++) // convert to BGR24 format
  775.     {
  776.         unsigned char *src = scrbuf + int(i)*dx;
  777.         for (unsigned y = 0; y < temp.ox; y++)
  778.         {
  779.             unsigned xx;
  780.             xx = *(unsigned*)(src + y*2);
  781.  
  782.             int u = src[y/2*4+1], v = src[y/2*4+3], Y = src[y*2];
  783.             int r = (int)(.4732927654e-2*u-255.3076403+1.989858012*v+.9803921569*Y);
  784.             int g = (int)(-.9756592292*v+186.0716700-.4780256930*u+.9803921569*Y);
  785.             int b = (int)(.9803921569*Y+2.004732928*u-255.3076403-.1014198783e-1*v); // mapple rulez!
  786.             if (r < 0) r = 0; if (r > 255) r = 255;
  787.             if (g < 0) g = 0; if (g > 255) g = 255;
  788.             if (b < 0) b = 0; if (b > 255) b = 255;
  789.  
  790.             ds[0] = u8(b);
  791.             ds[1] = u8(g);
  792.             ds[2] = u8(r);
  793.             ds += 3;
  794.         }
  795.         ds = (PBYTE)(ULONG_PTR(ds + 3) & ~ULONG_PTR(3)); // ърцфр  ёЄЁюър т√Ёртэхэр эр 4
  796.     }
  797. }
  798.  
  799. void ConvBgr32ToBgr24(u8 *dst, u8 *scrbuf, int dx)
  800. {
  801.     u8 *ds = dst;
  802.     for(unsigned i = 0; i < temp.oy; i++) // convert to BGR24 format
  803.     {
  804.        unsigned char *src = scrbuf + int(i)*dx;
  805.        for (unsigned x = 0; x < temp.ox; x++)
  806.        {
  807.           ds[0] = src[0];
  808.           ds[1] = src[1];
  809.           ds[2] = src[2];
  810.           src += 4;
  811.           ds += 3;
  812.        }
  813.        ds = (PBYTE)(ULONG_PTR(ds + 3) & ~ULONG_PTR(3)); // ърцфр  ёЄЁюър т√Ёртэхэр эр 4
  814.     }
  815. }
  816.  
  817.  
  818. TColorConverter ConvBgr24 = nullptr;
  819.  
  820. static void SaveBmp(FILE *File, u8 *ds)
  821. {
  822.      static u8 bmpheader32[]=
  823.      {
  824.             // BITMAPFILEHEADER
  825.             0x42,0x4d,           // Type
  826.             0x36,0x10,0x0e,0x00, // Size
  827.             0x00,0x00,           // Reserved1
  828.             0x00,0x00,           // Reserved2
  829.             0x36,0x00,0x00,0x00, // OffBits
  830.             // BITMAPINFOHEADER
  831.             0x28,0x00,0x00,0x00, // Size
  832.             0x80,0x02,0x00,0x00, // Width
  833.             0xe0,0x01,0x00,0x00, // Height
  834.             0x01,0x00,           // Planes
  835.             0x18,0x00,           // BitCount
  836.             0x00,0x00,0x00,0x00, // Compression
  837.             0x00,0x10,0x0e,0x00, // SizeImage
  838.             0x00,0x00,0x00,0x00, // XPixelsPerMeter
  839.             0x00,0x00,0x00,0x00, // YPixelsPerMeter
  840.             0x00,0x00,0x00,0x00, // ClrUsed
  841.             0x00,0x00,0x00,0x00  // ClrImportant
  842.      };
  843.  
  844.     *(unsigned*)(bmpheader32 + 2) = temp.ox * temp.oy * 3 + sizeof(bmpheader32); // filesize
  845.     *(unsigned*)(bmpheader32 + 0x12) = temp.ox;
  846.     *(unsigned*)(bmpheader32 + 0x16) = temp.oy;
  847.     fwrite(bmpheader32, 1, sizeof(bmpheader32), File);
  848.  
  849.     for(int y = int(temp.oy - 1); y >= 0 ; y--)
  850.     {
  851.         fwrite(ds + ((unsigned(y) * temp.ox * 3 + 3) & ~3U), 1, temp.ox * 3, File);
  852.     }
  853. }
  854.  
  855. static void SavePng(FILE *File, u8 *ds)
  856. {
  857.     static png_color bkgColor = {127, 127, 127};
  858.  
  859.     if(!temp.PngSupport)
  860.         return;
  861.  
  862.     PngSaveImage(File, ds, int(temp.ox), int(temp.oy), bkgColor);
  863. }
  864.  
  865. #define MAKE_RGBQUAD(r,g,b) (ULONG) (u32(b) | (u32(g)<<8) | (u32(r)<<16))
  866.  
  867. static void SaveBmp16c(FILE *File, const u8 *ds)
  868. {
  869.      static u8 bmpheader32[]=
  870.      {
  871.             // BITMAPFILEHEADER
  872.             0x42,0x4d,           // Type
  873.             0x36,0x10,0x0e,0x00, // Size = 320*200/2 + sizeof(bmpheader32)
  874.             0x00,0x00,           // Reserved1
  875.             0x00,0x00,           // Reserved2
  876.             0x76,0x00,0x00,0x00, // OffBits
  877.             // BITMAPINFOHEADER
  878.             0x28,0x00,0x00,0x00, // Size
  879.             0x40,0x01,0x00,0x00, // Width = 320
  880.             0xc8,0x00,0x00,0x00, // Height = 200
  881.             0x01,0x00,           // Planes
  882.             0x04,0x00,           // BitCount = 4
  883.             0x00,0x00,0x00,0x00, // Compression = BI_RGB
  884.             0x00,0x10,0x0e,0x00, // SizeImage
  885.             0x00,0x00,0x00,0x00, // XPixelsPerMeter
  886.             0x00,0x00,0x00,0x00, // YPixelsPerMeter
  887.             0x00,0x00,0x00,0x00, // ClrUsed
  888.             0x00,0x00,0x00,0x00, // ClrImportant
  889.             // PALETTE
  890.             0,0,0,0,             //  0 bgra
  891.             0,0,0,0,             //  1 bgra
  892.             0,0,0,0,             //  2 bgra
  893.             0,0,0,0,             //  3 bgra
  894.             0,0,0,0,             //  4 bgra
  895.             0,0,0,0,             //  5 bgra
  896.             0,0,0,0,             //  6 bgra
  897.             0,0,0,0,             //  7 bgra
  898.             0,0,0,0,             //  8 bgra
  899.             0,0,0,0,             //  9 bgra
  900.             0,0,0,0,             // 10 bgra
  901.             0,0,0,0,             // 11 bgra
  902.             0,0,0,0,             // 12 bgra
  903.             0,0,0,0,             // 13 bgra
  904.             0,0,0,0,             // 14 bgra
  905.             0,0,0,0              // 15 bgra
  906.      };
  907.  
  908.     *(unsigned*)(bmpheader32 + 2) = 320 * 200 / 2 + sizeof(bmpheader32); // filesize
  909.     for(unsigned i = 0; i < 16; i++)
  910.     {
  911.         // ╘юЁьрЄ фрээ√ї ярышЄЁ√ Gg0Rr0Bb
  912.         // ╨рёъырфър ярышЄЁ√ ¤ьєы ЄюЁр - ULA+
  913.         unsigned PalIdx = ((i & 8) << 1) | (i & 7);
  914.         u8 r = u8(u32((comp.comp_pal[PalIdx] >> 3) & 3) * 255 / 3);
  915.         u8 g = u8(u32((comp.comp_pal[PalIdx] >> 6) & 3) * 255 / 3);
  916.         u8 b = u8(u32(comp.comp_pal[PalIdx] & 3) * 255 / 3);
  917.  
  918.         *(PULONG)(bmpheader32 + 54 + 4*i) = MAKE_RGBQUAD(r,g,b);
  919.     }
  920.     fwrite(bmpheader32, 1, sizeof(bmpheader32), File);
  921.     fwrite(ds, 1, 320 * 200 / 2, File);
  922. }
  923.  
  924. static void ConvPal16cAtm1(u8 *Buf)
  925. {
  926.     static const int ega0_ofs = -4*PAGE;
  927.     static const int ega1_ofs = 0;
  928.     static const int ega2_ofs = -4*PAGE+0x2000;
  929.     static const int ega3_ofs = 0x2000;
  930.  
  931.    for (int y = 200 - 1; y >= 0; y--)
  932.    {
  933.       const u8 *src = temp.base + y*40;
  934.  
  935.       for(unsigned x = 0; x < 320; x += 8)
  936.       {
  937.           u8 v0 = src[ega0_ofs];
  938.           u8 v1 = src[ega1_ofs];
  939.           u8 v2 = src[ega2_ofs];
  940.           u8 v3 = src[ega3_ofs];
  941.           Buf[0] = u8(((((v0 >> 3) & 8) | (v0 & 0x7)) << 4) |
  942.                    (((v0 & 0x80) | ((v0 << 1) & 0x70)) >> 4));
  943.           Buf[1] = u8(((((v1 >> 3) & 8) | (v1 & 0x7)) << 4) |
  944.                    (((v1 & 0x80) | ((v1 << 1) & 0x70)) >> 4));
  945.           Buf[2] = u8(((((v2 >> 3) & 8) | (v2 & 0x7)) << 4) |
  946.                    (((v2 & 0x80) | ((v2 << 1) & 0x70)) >> 4));
  947.           Buf[3] = u8(((((v3 >> 3) & 8) | (v3 & 0x7)) << 4) |
  948.                    (((v3 & 0x80) | ((v3 << 1) & 0x70)) >> 4));
  949.          
  950.           src++;
  951.           Buf += 4;
  952.       }
  953.    }
  954.  
  955. }
  956.  
  957. #define ConvPal16cAtm2 ConvPal16cAtm1
  958.  
  959. typedef void (*TSaver)(FILE *File, u8 *ds);
  960.  
  961. void main_scrshot()
  962. {
  963.    char fname[FILENAME_MAX];
  964.    static unsigned sshot = 0;
  965.    static const char *Format[] = { "scr", "bmp", "png" };
  966.    static const TSaver Saver[] = { SaveBmp, SavePng };
  967.  
  968.    const char *Ext = Format[conf.scrshot];
  969.  
  970.    if (conf.scrshot == 0 &&
  971.        (
  972.         ((conf.mem_model == MM_ATM710 || conf.mem_model == MM_ATM3) && ((comp.pFF77 & 7) == 0)) ||
  973.         ((conf.mem_model == MM_ATM450) && (((comp.aFE >> 5) & 3) == 0))
  974.        )
  975.       )
  976.    {
  977.        Ext = Format[1];
  978.    }
  979.  
  980.    snprintf(fname, _countof(fname), "%s\\%s_%06u.%s", conf.scrshot_dir,
  981.        temp.LastSnapName[0] ? temp.LastSnapName : "sshot", sshot, Ext);
  982.    fname[FILENAME_MAX-1] = 0;
  983.  
  984.    FILE *fileShot = fopen(fname, "wb");
  985.    if (!fileShot)
  986.       return;
  987.  
  988.    if (conf.scrshot == 0)
  989.    {
  990.       switch(conf.mem_model)
  991.       {
  992.       case MM_ATM710:
  993.       case MM_ATM3:
  994.           {
  995.               switch(comp.pFF77 & 7)
  996.               {
  997.               case 0: // EGA 320x200 16c
  998.                   {
  999.                       u8 *Buf = (u8 *)malloc(320*200/2);
  1000.                       ConvPal16cAtm2(Buf);
  1001.                       SaveBmp16c(fileShot, Buf);
  1002.                       free(Buf);
  1003.                   }
  1004.               break;
  1005.               case 3:
  1006.                   goto standard_zx_mode;
  1007.               }
  1008.           }
  1009.       break;
  1010.  
  1011.       case MM_ATM450:
  1012.           switch((comp.aFE >> 5) & 3)
  1013.           {
  1014.           case 0: // EGA 320x200 16c
  1015.               {
  1016.                   u8 *Buf = (u8 *)malloc(320*200/2);
  1017.                   ConvPal16cAtm1(Buf);
  1018.                   SaveBmp16c(fileShot, Buf);
  1019.                   free(Buf);
  1020.               }
  1021.           break;
  1022.  
  1023.           case 3:
  1024.               goto standard_zx_mode;
  1025.           }
  1026.       break;
  1027.  
  1028.       default:
  1029. standard_zx_mode:;
  1030.           fwrite(temp.base, 1, 6912, fileShot);
  1031.       }
  1032.    }
  1033.    else
  1034.    {
  1035.       unsigned dx = temp.ox * temp.obpp / 8;
  1036.       unsigned char *scrbuf_unaligned = (unsigned char*)malloc(dx * temp.oy + CACHE_LINE);
  1037.       unsigned char *scrbuf = (unsigned char*)align_by(scrbuf_unaligned, CACHE_LINE);
  1038.       memset(scrbuf, 0, dx * temp.oy);
  1039.       renders[conf.render].func(scrbuf, dx); // render to memory buffer (PAL8, YUY2, RGB15, RGB16, RGB32)
  1040.  
  1041.       u8 *ds = (u8 *)malloc(((temp.ox * 3 + 3) & ~3U) * temp.oy);
  1042.       ConvBgr24(ds, scrbuf, int(dx));
  1043.  
  1044.       Saver[conf.scrshot - 1](fileShot, ds);
  1045.  
  1046.       free(ds);
  1047.       free(scrbuf_unaligned);
  1048.    }
  1049.    fclose(fileShot);
  1050.    sprintf(statusline, "saving %s", strrchr(fname, '\\') + 1);
  1051.    statcnt = 30;
  1052.    sshot++;
  1053. }
  1054. /*
  1055. static void VideoFrameSaver()
  1056. {
  1057.    char fname[FILENAME_MAX];
  1058.    static unsigned FrameNum = 0;
  1059.    static const char *Format[] = { "scr", "bmp", "png" };
  1060.    static const TSaver Saver[] = { SaveBmp, SavePng };
  1061.  
  1062.    sprintf(fname, "video%06u.%s", FrameNum, Format[conf.scrshot]);
  1063.    addpath(fname);
  1064.  
  1065.    FILE *fileShot = fopen(fname, "wb");
  1066.    if (!fileShot)
  1067.       return;
  1068.  
  1069.    if (conf.scrshot == 0)
  1070.    {
  1071.       fwrite(temp.base, 1, 6912, fileShot);
  1072.    }
  1073.    else
  1074.    {
  1075.        unsigned dx = temp.ox * temp.obpp / 8;
  1076.        unsigned char *scrbuf_unaligned = (unsigned char*)malloc(dx * temp.oy + CACHE_LINE);
  1077.        unsigned char *scrbuf = (unsigned char*)align_by(scrbuf_unaligned, CACHE_LINE);
  1078.        memset(scrbuf, 0, dx * temp.oy);
  1079.        renders[conf.render].func(scrbuf, dx); // render to memory buffer (PAL8, YUY2, RGB15, RGB16, RGB32)
  1080.  
  1081.        u8 *ds = (u8 *)malloc(((temp.ox * 3 + 3) & ~3) * temp.oy);
  1082.        ConvBgr24(ds, scrbuf, dx);
  1083.  
  1084.        Saver[conf.scrshot - 1](fileShot, ds);
  1085.  
  1086.        free(ds);
  1087.        free(scrbuf_unaligned);
  1088.    }
  1089.    fclose(fileShot);
  1090.    FrameNum++;
  1091. }
  1092.  
  1093. static void VideoNullSaver()
  1094. {
  1095.  
  1096. }
  1097.  
  1098. TVideoSaver VideoSaver = VideoNullSaver;
  1099.  
  1100. void main_savevideo()
  1101. {
  1102.    static bool StartSave = false;
  1103.  
  1104.    if(!StartSave)
  1105.    {
  1106.        sprintf(statusline, "start saving video");
  1107.        StartSave = true;
  1108.        VideoSaver = VideoFrameSaver;
  1109.    }
  1110.    else
  1111.    {
  1112.        sprintf(statusline, "stop saving video");
  1113.        StartSave = false;
  1114.        VideoSaver = VideoNullSaver;
  1115.    }
  1116.  
  1117.    statcnt = 30;
  1118. }
  1119. */
  1120.  
  1121.