#include "std.h"
#include "resource.h"
#include "emul.h"
#include "vars.h"
#include "snapshot.h"
#include "tape.h"
#include "memory.h"
#include "opendlg.h"
#include "draw.h"
#include "config.h"
#include "z80.h"
#include "sshot_png.h"
#include "funcs.h"
#include "util.h"
int readSP();
int readSNA48();
int readSNA128();
int readZ80();
int writeSNA(FILE*);
int load_arc(char *fname);
SNAP what_is(char *filename)
{
FILE *ff = fopen(filename, "rb");
if (!ff) return snNOFILE;
snapsize = unsigned(fread(snbuf, 1, sizeof snbuf, ff));
fclose(ff);
if (snapsize == sizeof snbuf) return snTOOLARGE;
SNAP type = snUNKNOWN;
char *ptr = strrchr(filename, '.');
unsigned ext = ptr? (*(unsigned*)(ptr+1) | 0x20202020U) : 0;
if (snapsize < 32) return type;
if (snapsize == 131103 || snapsize == 147487) type = snSNA_128;
if (snapsize == 49179) type = snSNA_48;
if (ext == WORD4('t','a','p',' ')) type = snTAP;
if (ext == WORD4('z','8','0',' ')) type = snZ80;
if(ext == WORD4('p', 'a', 'l', ' ') && (snapsize == sizeof(comp.comp_pal)))
{
type = snPAL;
return type;
}
if (conf.trdos_present)
{
if (!snbuf[13] && snbuf[14] && (int)snapsize == snbuf[14]*256+17) type = snHOB;
if (snapsize >= 8192 && !(snapsize & 0xFF) && ext == WORD4('t','r','d',' ')) type = snTRD;
if (snapsize >= 8192 && ext == WORD4('i','s','d',' '))
type = snISD;
if (snapsize >= 8192 && ext == WORD4('p','r','o',' '))
type = snPRO;
if(snapsize >= 8192 && ext == WORD4('d', 's', 'k', ' '))
type = snDSK;
if(snapsize >= 8192 && ext == WORD4('i', 'p', 'f', ' '))
type = snIPF;
if (!memcmp(snbuf, "SINCLAIR", 8))
{
unsigned nfiles = snbuf[8];
unsigned nsec = 0;
for(unsigned i = 0; i < nfiles; i++)
{
nsec += snbuf[9+14*i+13];
}
if(snapsize >= 9 + nfiles * 14 + nsec * 0x100)
type = snSCL;
}
if (!memcmp(snbuf, "FDI", 3) && *(unsigned short*)(snbuf+4) <= MAX_CYLS && *(unsigned short*)(snbuf+6) <= 2) type = snFDI;
if (((*(short*)snbuf|0x2020) == WORD2('t','d')) && snbuf[4] >= 10 && snbuf[4] <= 21 && snbuf[9] <= 2) type = snTD0;
if (*(unsigned*)snbuf == WORD4('U','D','I','!') && *(unsigned*)(snbuf+4)==snapsize-4 && snbuf[9] < MAX_CYLS && snbuf[10]<2 && !snbuf[8]) type = snUDI;
}
if (snapsize > 10 && !memcmp(snbuf, "ZXTape!\x1A", 8))
{
type = snTZX;
}
if (snapsize > 0x22 && !memcmp(snbuf, "Compressed Square Wave\x1A", 23)) type = snCSW;
if (*(unsigned short*)snbuf == WORD2('S','P') && *(unsigned short*)(snbuf+2)+0x26 == (int)snapsize) type = snSP;
return type;
}
static int readPAL();
int loadsnap(char *filename)
{
if (load_arc(filename))
return 1;
SNAP type = what_is(filename);
if (type >= snHOB)
{
if (trd_toload == -1U)
{
unsigned last = -1U;
for (unsigned i = 0; i < 4; i++) if (trd_loaded[i]) last = i;
trd_toload = (last == -1U)? 0 : ((type == snHOB)? last : (last+1) & 3);
}
for (unsigned k = 0; k < 4; k++)
{
if (k != trd_toload && !stricmp(comp.fdd[k].name, filename))
{
static char err[] = "This disk image is already loaded to drive X:\n"
"Do you still want to load it to drive Y:?";
err[43] = char(k+'A');
err[84] = char(trd_toload+'A');
if (MessageBox(GetForegroundWindow(), err, "Warning", MB_YESNO | MB_ICONWARNING) == IDNO) return 1;
}
}
FDD *drive = comp.fdd + trd_toload;
if (!drive->test())
return 0;
comp.wd.Eject(trd_toload);
int ok = drive->read(type);
if (ok)
{
if (*conf.appendboot)
drive->addboot();
strcpy(drive->name, filename);
if (GetFileAttributes(filename) & FILE_ATTRIBUTE_READONLY)
conf.trdos_wp[trd_toload] = 1;
drive->snaptype = (type == snHOB || type == snSCL)? snTRD : type;
trd_loaded[trd_toload] = 1;
//---------Alone Coder
char *name = filename;
for (char *x = name; *x; x++)
if (*x == '\\') name = x+1;
strcpy(temp.LastSnapName, name);
char *p = strrchr(temp.LastSnapName, '.');
if(p)
{
*p = 0;
}
char wintitle[0x200];
strcpy(wintitle,name);
strcat(wintitle," - UnrealSpeccy");
SetWindowText(wnd, wintitle);
//~---------Alone Coder
}
return ok;
}
if (type == snSP) return readSP();
if (type == snSNA_48) return readSNA48();
if (type == snSNA_128) return readSNA128();
if (type == snZ80) return readZ80();
if (type == snTAP) return readTAP();
if (type == snTZX) return readTZX();
if (type == snCSW) return readCSW();
if(type == snPAL)
{
return readPAL();
}
return 0;
}
int readSNA128()
{
if(conf.mem_model == MM_PENTAGON && conf.Sna128Lock)
{
conf.ramsize = 128;
temp.ram_mask = u8((conf.ramsize-1) >> 4);
}
hdrSNA128 *hdr = (hdrSNA128*)snbuf;
reset(hdr->trdos? RM_DOS : RM_SOS);
cpu.alt.af = hdr->altaf;
cpu.alt.bc = hdr->altbc;
cpu.alt.de = hdr->altde;
cpu.alt.hl = hdr->althl;
cpu.af = hdr->af;
cpu.bc = hdr->bc;
cpu.de = hdr->de;
cpu.hl = hdr->hl;
cpu.ix = hdr->ix;
cpu.iy = hdr->iy;
cpu.sp = hdr->sp;
cpu.pc = hdr->pc;
cpu.i = hdr->i;
cpu.r_low = hdr->r;
cpu.r_hi = hdr->r & 0x80;
cpu.im = hdr->im;
cpu.iff1 = cpu.iff2 = (hdr->iff >> 2) & 1;
comp.p7FFD = hdr->p7FFD;
comp.pFE = hdr->pFE;
comp.border_attr = comp.pFE & 7;
memcpy(memory+PAGE*5, hdr->page5, PAGE);
memcpy(memory+PAGE*2, hdr->page2, PAGE);
memcpy(memory+PAGE*(hdr->p7FFD & 7), hdr->active_page, PAGE);
unsigned char *newpage = snbuf+0xC01F;
unsigned char mapped = u8(0x24U | (1 << (hdr->p7FFD & 7)));
for (unsigned char i = 0; i < 8; i++)
{
if (!(mapped & (1 << i)))
{
memcpy(memory + PAGE*i, newpage, PAGE);
newpage += PAGE;
}
}
set_banks();
return 1;
}
int readSNA48()
{
//conf.mem_model = MM_PENTAGON; conf.ramsize = 128; // molodcov_alex
reset(RM_SOS);
hdrSNA128 *hdr = (hdrSNA128*)snbuf;
cpu.alt.af = hdr->altaf; cpu.alt.bc = hdr->altbc;
cpu.alt.de = hdr->altde; cpu.alt.hl = hdr->althl;
cpu.af = hdr->af; cpu.bc = hdr->bc; cpu.de = hdr->de; cpu.hl = hdr->hl;
cpu.ix = hdr->ix; cpu.iy = hdr->iy; cpu.sp = hdr->sp;
cpu.i = hdr->i; cpu.r_low = hdr->r; cpu.r_hi = hdr->r & 0x80; cpu.im = hdr->im;
cpu.iff1 = cpu.iff2 = (hdr->iff >> 2) & 1; comp.p7FFD = 0x30;
comp.pEFF7 |= EFF7_LOCKMEM; //Alone Coder
comp.pFE = hdr->pFE; comp.border_attr = comp.pFE & 7;
memcpy(memory+PAGE*5, hdr->page5, PAGE);
memcpy(memory+PAGE*2, hdr->page2, PAGE);
memcpy(memory+PAGE*0, hdr->active_page, PAGE);
cpu.pc = cpu.DirectRm(cpu.sp)+0x100*cpu.DirectRm(cpu.sp+1); cpu.sp += 2;
set_banks(); return 1;
}
int readSP()
{
//conf.mem_model = MM_PENTAGON; conf.ramsize = 128; // molodcov_alex
reset(RM_SOS);
hdrSP *hdr = (hdrSP*)snbuf;
cpu.alt.af = hdr->altaf; cpu.alt.bc = hdr->altbc;
cpu.alt.de = hdr->altde; cpu.alt.hl = hdr->althl;
cpu.af = hdr->af; cpu.bc = hdr->bc; cpu.de = hdr->de; cpu.hl = hdr->hl;
cpu.ix = hdr->ix; cpu.iy = hdr->iy; cpu.sp = hdr->sp; cpu.pc = hdr->pc;
cpu.i = hdr->i; cpu.r_low = hdr->r; cpu.r_hi = hdr->r & 0x80;
cpu.iff1 = (hdr->flags & 1);
cpu.im = 1 + ((hdr->flags >> 1) & 1);
cpu.iff2 = (hdr->flags >> 2) & 1;
comp.p7FFD = 0x30;
comp.pEFF7 |= EFF7_LOCKMEM; //Alone Coder
comp.pFE = hdr->pFE; comp.border_attr = comp.pFE & 7;
for (unsigned i = 0; i < hdr->len; i++)
cpu.DirectWm(hdr->start + i, snbuf[i + 0x26]);
set_banks(); return 1;
}
int writeSNA(FILE *ff)
{
/* if (conf.ramsize != 128) {
MessageBox(GetForegroundWindow(), "SNA format can hold only\r\n128kb memory models", "Save", MB_ICONERROR);
return 0;
}*/ //Alone Coder
hdrSNA128 *hdr = (hdrSNA128*)snbuf;
hdr->trdos = (comp.flags & CF_TRDOS)? 1 : 0;
hdr->altaf = u16(cpu.alt.af); hdr->altbc = u16(cpu.alt.bc);
hdr->altde = u16(cpu.alt.de); hdr->althl = u16(cpu.alt.hl);
hdr->af = u16(cpu.af); hdr->bc = u16(cpu.bc); hdr->de = u16(cpu.de); hdr->hl = u16(cpu.hl);
hdr->ix = u16(cpu.ix); hdr->iy = u16(cpu.iy); hdr->sp = u16(cpu.sp); hdr->pc = u16(cpu.pc);
hdr->i = cpu.i; hdr->r = (cpu.r_low & 0x7F)+cpu.r_hi; hdr->im = cpu.im;
hdr->iff = cpu.iff2 ? 4 : 0;
hdr->p7FFD = comp.p7FFD;
hdr->pFE = comp.pFE; comp.border_attr = comp.pFE & 7;
unsigned savesize = sizeof(hdrSNA128);
unsigned char mapped = u8(0x24U | (1 << (comp.p7FFD & 7)));
if (comp.p7FFD == 0x30)
{ // save 48k
mapped = 0xFF;
savesize = 0xC01B;
hdr->sp -= 2;
cpu.DirectWm(hdr->sp, cpu.pcl);
cpu.DirectWm(hdr->sp+1, cpu.pch);
}
memcpy(hdr->page5, memory+PAGE*5, PAGE);
memcpy(hdr->page2, memory+PAGE*2, PAGE);
memcpy(hdr->active_page, memory+PAGE*(comp.p7FFD & 7), PAGE);
if (fwrite(hdr, 1, savesize, ff) != savesize) return 0;
for (unsigned char i = 0; i < 8; i++)
if (!(mapped & (1 << i))) {
if (fwrite(memory + PAGE*i, 1, PAGE, ff) != PAGE) return 0;
}
return 1;
}
static void unpack_page(unsigned char *dst, unsigned dstlen, unsigned char *src, unsigned srclen)
{
memset(dst, 0, dstlen);
while (srclen > 0 && dstlen > 0)
{
if (srclen >= 4 && src[0] == 0xED && src[1] == 0xED)
{
size_t len = src[2];
memset(dst, src[3], len);
dstlen -= len;
srclen -= 4;
src += 4;
dst += len;
}
else
{
*dst++ = *src++;
dstlen--;
srclen--;
}
}
}
int readZ80()
{
//conf.mem_model = MM_PENTAGON; conf.ramsize = 128; // molodcov_alex
hdrZ80 *hdr = (hdrZ80*)snbuf;
unsigned char *ptr = snbuf + 30;
unsigned char model48k = (hdr->model < 3);
reset((model48k|(hdr->p7FFD & 0x10)) ? RM_SOS : RM_128);
if (hdr->flags == 0xFF)
hdr->flags = 1;
if (hdr->pc == 0)
{ // 2.01
ptr += 2 + hdr->len;
hdr->pc = hdr->newpc;
memset(RAM_BASE_M, 0, PAGE*8); // clear 128k - first 8 pages
unsigned char * const p48[] =
{
base_sos_rom, nullptr, nullptr, nullptr,
RAM_BASE_M+2*PAGE, RAM_BASE_M+0*PAGE, nullptr, nullptr,
RAM_BASE_M+5*PAGE, nullptr, nullptr, nullptr
};
unsigned char * const p128[] =
{
base_sos_rom, base_dos_rom, base_128_rom, RAM_BASE_M+0*PAGE,
RAM_BASE_M+1*PAGE, RAM_BASE_M+2*PAGE, RAM_BASE_M+3*PAGE, RAM_BASE_M+4*PAGE,
RAM_BASE_M+5*PAGE, RAM_BASE_M+6*PAGE, RAM_BASE_M+7*PAGE, nullptr
};
while (ptr < snbuf+snapsize)
{
unsigned len = *(unsigned short*)ptr;
if (ptr[2] > 11)
return 0;
unsigned char *dstpage = model48k ? p48[ptr[2]] : p128[ptr[2]];
if (!dstpage)
return 0;
ptr += 3;
if (len == 0xFFFF)
{
len = PAGE;
memcpy(dstpage, ptr, len);
}
else
unpack_page(dstpage, PAGE, ptr, len);
ptr += len;
}
}
else
{
unsigned len = snapsize - 30;
unsigned char *mem48 = ptr;
if (hdr->flags & 0x20)
unpack_page(mem48 = snbuf + 4*PAGE, 3*PAGE, ptr, len);
memcpy(memory + PAGE*5, mem48, PAGE);
memcpy(memory + PAGE*2, mem48 + PAGE, PAGE);
memcpy(memory + PAGE*0, mem48 + 2*PAGE, PAGE);
model48k = 1;
}
cpu.a = hdr->a; cpu.f = hdr->f;
cpu.bc = hdr->bc; cpu.de = hdr->de; cpu.hl = hdr->hl;
cpu.alt.bc = hdr->bc1; cpu.alt.de = hdr->de1; cpu.alt.hl = hdr->hl1;
cpu.alt.a = hdr->a1; cpu.alt.f = hdr->f1;
cpu.pc = hdr->pc; cpu.sp = hdr->sp; cpu.ix = hdr->ix; cpu.iy = hdr->iy;
cpu.i = hdr->i; cpu.r_low = hdr->r & 0x7F;
cpu.r_hi = u8((hdr->flags & 1U) << 7U);
comp.pFE = (hdr->flags >> 1) & 7;
comp.border_attr = comp.pFE;
cpu.iff1 = hdr->iff1; cpu.iff2 = hdr->iff2; cpu.im = (hdr->im & 3);
comp.p7FFD = (model48k) ? 0x30 : hdr->p7FFD;
if(hdr->len == 55) // version 3.0 (with 1ffd)
comp.p1FFD = hdr->p1FFD;
if (model48k)
comp.pEFF7 |= EFF7_LOCKMEM; //Alone Coder
set_banks();
return 1;
}
static int readPAL()
{
memcpy(comp.comp_pal, snbuf, snapsize);
temp.comp_pal_changed = 1;
if(conf.ula_plus)
{
comp.ula_plus_en = true; // ┼ёыш яЁшёєЄёЄтєхЄ ULA+, Єю ёЁрчє ш ръЄштшЁєхь хх (ўЄюс√ шчьхэхэш ярышЄЁ√ с√ыш тшфэ√)
}
return 1;
}
static int writePAL(FILE *ff)
{
if(fwrite(comp.comp_pal, 1, sizeof(comp.comp_pal), ff) != sizeof(comp.comp_pal))
{
return 0;
}
return 1;
}
#define arctmp ((char*)rbuf)
static char *arc_fname;
static INT_PTR CALLBACK ArcDlg(HWND dlg, UINT msg, WPARAM wp, LPARAM lp)
{
(void)lp;
if (msg == WM_INITDIALOG) {
for (char *dst = arctmp; *dst; dst += strlen(dst)+1)
SendDlgItemMessage(dlg, IDC_ARCLIST, LB_ADDSTRING, 0, (LPARAM)dst);
SendDlgItemMessage(dlg, IDC_ARCLIST, LB_SETCURSEL, 0, 0);
return 1;
}
if ((msg == WM_COMMAND && wp == IDCANCEL) ||
(msg == WM_SYSCOMMAND && (wp & 0xFFF0) == SC_CLOSE)) EndDialog(dlg, 0);
if (msg == WM_COMMAND && (LOWORD(wp) == IDOK || (HIWORD(wp)==LBN_DBLCLK && LOWORD(wp) == IDC_ARCLIST)))
{
int n = int(SendDlgItemMessage(dlg, IDC_ARCLIST, LB_GETCURSEL, 0, 0));
char *dst = arctmp;
for (int q = 0; q < n; q++) dst += strlen(dst)+1;
arc_fname = dst;
EndDialog(dlg, 0);
}
return 0;
}
static bool filename_ok(char *fname)
{
for (char *wc = skiparc; *wc; wc += strlen(wc)+1)
if (wcmatch(fname, wc)) return 0;
return 1;
}
int load_arc(char *fname)
{
char *ext = strrchr(fname, '.'); if (!ext) return 0;
ext++;
char *cmdtmp;
char done = 0;
for (char *x = arcbuffer; *x; x = cmdtmp + strlen(cmdtmp)+1) {
cmdtmp = x + strlen(x)+1;
if (stricmp(ext, x)) continue;
char dir[0x200]; GetCurrentDirectory(sizeof dir, dir);
char tmp[0x200]; GetTempPath(sizeof tmp, tmp);
char d1[0x20]; sprintf(d1, "us%08lX", GetTickCount());
SetCurrentDirectory(tmp);
CreateDirectory(d1, nullptr);
SetCurrentDirectory(d1);
color();
char cmdln[0x200]; sprintf(cmdln, cmdtmp, fname);
STARTUPINFO si = { sizeof si };
si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_HIDE;
PROCESS_INFORMATION pi;
unsigned flags = CREATE_NEW_CONSOLE;
HANDLE hc = CreateFile("CONOUT$", GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
if(hc != INVALID_HANDLE_VALUE)
{
CloseHandle(hc);
flags = 0;
}
if (CreateProcess(nullptr, cmdln, nullptr, nullptr, 0, flags, nullptr, nullptr, &si, &pi)) {
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD code; GetExitCodeProcess(pi.hProcess, &code);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
if (!code || MessageBox(GetForegroundWindow(), "Broken archive", nullptr, MB_ICONERROR | MB_OKCANCEL) == IDOK) {
WIN32_FIND_DATA fd; HANDLE h;
char *dst = arctmp; unsigned nfiles = 0;
if ((h = FindFirstFile("*.*", &fd)) != INVALID_HANDLE_VALUE) {
do {
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && filename_ok(fd.cFileName)) {
strcpy(dst, fd.cFileName); dst += strlen(dst)+1;
nfiles++;
}
} while (FindNextFile(h, &fd));
FindClose(h);
}
*dst = 0; arc_fname = nullptr;
if (nfiles == 1) arc_fname = arctmp;
if (nfiles > 1)
DialogBox(hIn, MAKEINTRESOURCE(IDD_ARC), GetForegroundWindow(), ArcDlg);
if (!nfiles) MessageBox(GetForegroundWindow(), "Empty archive!", nullptr, MB_ICONERROR | MB_OK);
char buf[0x200]; strcpy(buf, tmp); strcat(buf, "\\");
strcat(buf, d1);
strcat(buf, "\\");
if(arc_fname)
{
strcat(buf, arc_fname);
arc_fname = buf;
}
if (arc_fname && !(done = (char)loadsnap(arc_fname))) MessageBox(GetForegroundWindow(), "loading error", arc_fname, MB_ICONERROR);
if (!done) done = -1;
}
// delete junk
SetCurrentDirectory(tmp);
SetCurrentDirectory(d1);
WIN32_FIND_DATA fd; HANDLE h;
if ((h = FindFirstFile("*.*", &fd)) != INVALID_HANDLE_VALUE) {
do { DeleteFile(fd.cFileName); } while (FindNextFile(h, &fd));
FindClose(h);
}
}
SetCurrentDirectory(tmp);
RemoveDirectory(d1);
SetCurrentDirectory(dir);
eat(); if (done) return done > 0 ? 1 : 0;
}
eat(); return 0;
}
void opensnap(unsigned index)
{
char mask[0x200]; *mask = 0;
for(char *x = arcbuffer; *x; )
{
strcat(mask, ";*.");
strcat(mask, x);
x += strlen(x) + 1;
x += strlen(x) + 1;
}
char fline[0x400];
const char *src = "all (sna,z80,sp,tap,tzx,csw,trd,scl,fdi,td0,udi,isd,pro,dsk,ipf,hobeta,pal)\0"
"*.sna;*.z80;*.sp;*.tap;*.tzx;*.csw;*.trd;*.scl;*.td0;*.udi;*.fdi;*.isd;*.pro;*.dsk;*.ipf;*.$?;*.!?;*.pal<\0"
"Disk B (trd,scl,fdi,td0,udi,isd,pro,dsk,ipf,hobeta)\0*.trd;*.scl;*.fdi;*.udi;*.td0;*.isd;*.pro;*.dsk;*.ipf;*.$?<\0"
"Disk C (trd,scl,fdi,td0,udi,isd,pro,dsk,ipf,hobeta)\0*.trd;*.scl;*.fdi;*.udi;*.td0;*.isd;*.pro;*.dsk;*.ipf;*.$?<\0"
"Disk D (trd,scl,fdi,td0,udi,isd,pro,dsk,ipf,hobeta)\0*.trd;*.scl;*.fdi;*.udi;*.td0;*.isd;*.pro;*.dsk;*.ipf;*.$?<\0\0>";
if (!conf.trdos_present)
src = "ZX files (sna,z80,tap,tzx,csw,pal)\0*.sna;*.z80;*.tap;*.tzx;*.csw;*.pal<\0\0>";
for(char *dst = fline; *src != '>'; src++)
{
if(*src == '<')
{
strcpy(dst, mask);
dst += strlen(dst);
}
else
{
*dst++ = *src;
}
}
OPENFILENAME ofn = { };
char fname[0x200]; *fname = 0;
char dir[0x200]; GetCurrentDirectory(sizeof dir, dir);
ofn.lStructSize = (WinVerMajor < 5) ? OPENFILENAME_SIZE_VERSION_400 : sizeof(OPENFILENAME);
ofn.hwndOwner = GetForegroundWindow();
ofn.lpstrFilter = fline;
ofn.lpstrFile = fname; ofn.nMaxFile = sizeof fname;
ofn.lpstrTitle = "Load Snapshot / Disk / Tape";
ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
ofn.nFilterIndex = DWORD(index);
ofn.lpstrInitialDir = dir;
// __debugbreak();
if (GetSnapshotFileName(&ofn, 0))
{
trd_toload = ofn.nFilterIndex-1;
if (!loadsnap(fname))
MessageBox(GetForegroundWindow(), fname, "loading error", MB_ICONERROR);
}
eat();
}
static const int mx_typs = (1+4*6);
static unsigned char snaps[mx_typs];
static unsigned exts[mx_typs];
static unsigned drvs[mx_typs];
static unsigned snp;
static void addref(LPSTR &ptr, unsigned char sntype, const char *ref, unsigned drv, unsigned ext)
{
strcpy(ptr, ref); ptr += strlen(ptr)+1;
strcpy(ptr, ref+strlen(ref)+1); ptr += strlen(ptr)+1;
drvs[snp] = drv; exts[snp] = ext; snaps[snp++] = sntype;
}
void savesnap(int diskindex)
{
again:
OPENFILENAME ofn = { };
char fname[0x200]; *fname = 0;
if (diskindex >= 0) {
strcpy(fname, comp.fdd[diskindex].name);
size_t ln = strlen(fname);
if (ln > 4 && (*(unsigned*)(fname+ln-4) | WORD4(0,0x20,0x20,0x20)) == WORD4('.','s','c','l'))
*(unsigned*)(fname+ln-4) = WORD4('.','t','r','d');
}
snp = 1; char types[600], *ptr = types;
if (diskindex < 0)
{
exts[snp] = WORD4('s','n','a',0); snaps[snp] = snSNA_128; // default
addref(ptr, snSNA_128, "ZX-Spectrum 128K snapshot (SNA)\0*.sna", -1U, WORD4('s','n','a',0));
exts[snp] = WORD4('p', 'a', 'l', 0); snaps[snp] = snPAL;
addref(ptr, snPAL, "Palette (ULA+)\0*.pal", -1U, WORD4('p', 'a', 'l', 0));
}
ofn.lStructSize = (WinVerMajor < 5) ? OPENFILENAME_SIZE_VERSION_400 : sizeof(OPENFILENAME);
ofn.nFilterIndex = 1;
if (conf.trdos_present)
{
static char mask[] = "Disk A (TRD)\0*.trd";
static const char ex[][3] = { {'T','R','D'}, {'F','D','I'},{'T','D','0'},{'U','D','I'},{'I','S','D'},{'P','R','O'}};
static const unsigned ex2[] = { snTRD, snFDI, snTD0, snUDI, snISD, snPRO };
for (unsigned n = 0; n < 4; n++)
{
if (!comp.fdd[n].rawdata)
continue;
if (diskindex >= 0 && unsigned(diskindex) != n)
continue;
mask[5] = char('A'+n);
for (size_t i = 0; i < sizeof ex/sizeof(ex[0]); i++)
{
if (unsigned(diskindex) == n && ex2[i] == comp.fdd[n].snaptype)
ofn.nFilterIndex = snp;
memcpy(mask+8, ex[i], 3);
memcpy(mask+15, ex[i], 3);
addref(ptr, u8(ex2[i]), mask, n, (*(const unsigned*)ex[i] & 0xFFFFFF) | 0x202020);
}
}
}
ofn.lpstrFilter = types; *ptr = 0;
ofn.lpstrFile = fname; ofn.nMaxFile = sizeof fname;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
ofn.hwndOwner = GetForegroundWindow();
char *path = strrchr(fname, '\\');
if (path)
{ // check if directory exists (for files opened from archive)
char x = *path; *path = 0;
unsigned atr = GetFileAttributes(fname); *path = x;
if (atr == -1U || !(atr & FILE_ATTRIBUTE_DIRECTORY)) *fname = 0;
} else path = fname;
path = strrchr(path, '.'); if (path) *path = 0; // delete extension
if (GetSnapshotFileName(&ofn, 1))
{
char *fn = strrchr(ofn.lpstrFile, '\\');
fn = fn? fn+1 : ofn.lpstrFile;
char *extpos = strrchr(fn, '.');
if (!extpos || stricmp(extpos+1, (char*)&exts[ofn.nFilterIndex]))
{
char *dst = fn + strlen(fn); *dst++ = '.';
*(unsigned*)dst = exts[ofn.nFilterIndex];
}
if (GetFileAttributes(ofn.lpstrFile) != INVALID_FILE_ATTRIBUTES &&
IDNO == MessageBox(GetForegroundWindow(), "File exists. Overwrite ?", "Save", MB_ICONQUESTION | MB_YESNO))
goto again;
FILE *ff = fopen(ofn.lpstrFile, "wb");
if (ff)
{
int res = 0;
FDD *saveto = comp.fdd + drvs[ofn.nFilterIndex];
switch (snaps[ofn.nFilterIndex])
{
case snSNA_128: res = writeSNA(ff); break;
case snPAL: res = writePAL(ff); break;
case snTRD: res = saveto->write_trd(ff); break;
case snUDI: res = saveto->write_udi(ff); break;
case snFDI: res = saveto->write_fdi(ff); break;
case snTD0: res = saveto->write_td0(ff); break;
case snISD: res = saveto->write_isd(ff); break;
case snPRO: res = saveto->write_pro(ff); break;
}
fclose(ff);
if (!res)
MessageBox(GetForegroundWindow(), "write error", "Save", MB_ICONERROR);
else if (drvs[ofn.nFilterIndex]!=-1U)
{
comp.fdd[drvs[ofn.nFilterIndex]].optype=0;
strcpy(comp.fdd[drvs[ofn.nFilterIndex]].name, ofn.lpstrFile);
//---------Alone Coder
char *name = ofn.lpstrFile;
for (char *x = name; *x; x++)
{
if (*x == '\\')
name = x+1;
}
char wintitle[0x200];
strcpy(wintitle,name);
strcat(wintitle," - UnrealSpeccy");
SetWindowText(wnd, wintitle);
//~---------Alone Coder
}
}
else
MessageBox(GetForegroundWindow(), "Can't open file for writing", "Save", MB_ICONERROR);
}
eat();
}
void ConvPal8ToBgr24(u8 *dst, u8 *scrbuf, int dx)
{
u8 *ds = dst;
for(unsigned i = 0; i < temp.oy; i++) // convert to BGR24 format
{
unsigned char *src = scrbuf + int(i)*dx;
for (unsigned y = 0; y < temp.ox; y++)
{
ds[0] = pal0[src[y]].peBlue;
ds[1] = pal0[src[y]].peGreen;
ds[2] = pal0[src[y]].peRed;
ds += 3;
}
ds = (PBYTE)(ULONG_PTR(ds + 3) & ~ULONG_PTR(3)); // ърцфр ёЄЁюър т√Ёртэхэр эр 4
}
}
void ConvRgb15ToBgr24(u8 *dst, u8 *scrbuf, int dx)
{
u8 *ds = dst;
for(unsigned i = 0; i < temp.oy; i++) // convert to BGR24 format
{
unsigned char *src = scrbuf + int(i)*dx;
for (unsigned y = 0; y < temp.ox; y++)
{
unsigned xx;
xx = *(unsigned*)(src + y*2);
ds[0] = u8((xx & 0x1F)<<3);
ds[1] = u8((xx & 0x03E0)>>2);
ds[2] = u8((xx & 0x7C00)>>7);
ds += 3;
}
ds = (PBYTE)(ULONG_PTR(ds + 3) & ~ULONG_PTR(3)); // ърцфр ёЄЁюър т√Ёртэхэр эр 4
}
}
void ConvRgb16ToBgr24(u8 *dst, u8 *scrbuf, int dx)
{
u8 *ds = dst;
for(unsigned i = 0; i < temp.oy; i++) // convert to BGR24 format
{
unsigned char *src = scrbuf + int(i)*dx;
for (unsigned y = 0; y < temp.ox; y++)
{
unsigned xx;
xx = *(unsigned*)(src + y*2);
ds[0] = u8((xx&0x1F)<<3);
ds[1] = u8((xx&0x07E0)>>3);
ds[2] = u8((xx&0xF800)>>8);
ds += 3;
}
ds = (PBYTE)(ULONG_PTR(ds + 3) & ~ULONG_PTR(3)); // ърцфр ёЄЁюър т√Ёртэхэр эр 4
}
}
void ConvYuy2ToBgr24(u8 *dst, u8 *scrbuf, int dx)
{
u8 *ds = dst;
for(unsigned i = 0; i < temp.oy; i++) // convert to BGR24 format
{
unsigned char *src = scrbuf + int(i)*dx;
for (unsigned y = 0; y < temp.ox; y++)
{
unsigned xx;
xx = *(unsigned*)(src + y*2);
int u = src[y/2*4+1], v = src[y/2*4+3], Y = src[y*2];
int r = (int)(.4732927654e-2*u-255.3076403+1.989858012*v+.9803921569*Y);
int g = (int)(-.9756592292*v+186.0716700-.4780256930*u+.9803921569*Y);
int b = (int)(.9803921569*Y+2.004732928*u-255.3076403-.1014198783e-1*v); // mapple rulez!
if (r < 0) r = 0; if (r > 255) r = 255;
if (g < 0) g = 0; if (g > 255) g = 255;
if (b < 0) b = 0; if (b > 255) b = 255;
ds[0] = u8(b);
ds[1] = u8(g);
ds[2] = u8(r);
ds += 3;
}
ds = (PBYTE)(ULONG_PTR(ds + 3) & ~ULONG_PTR(3)); // ърцфр ёЄЁюър т√Ёртэхэр эр 4
}
}
void ConvBgr32ToBgr24(u8 *dst, u8 *scrbuf, int dx)
{
u8 *ds = dst;
for(unsigned i = 0; i < temp.oy; i++) // convert to BGR24 format
{
unsigned char *src = scrbuf + int(i)*dx;
for (unsigned x = 0; x < temp.ox; x++)
{
ds[0] = src[0];
ds[1] = src[1];
ds[2] = src[2];
src += 4;
ds += 3;
}
ds = (PBYTE)(ULONG_PTR(ds + 3) & ~ULONG_PTR(3)); // ърцфр ёЄЁюър т√Ёртэхэр эр 4
}
}
TColorConverter ConvBgr24 = nullptr;
static void SaveBmp(FILE *File, u8 *ds)
{
static u8 bmpheader32[]=
{
// BITMAPFILEHEADER
0x42,0x4d, // Type
0x36,0x10,0x0e,0x00, // Size
0x00,0x00, // Reserved1
0x00,0x00, // Reserved2
0x36,0x00,0x00,0x00, // OffBits
// BITMAPINFOHEADER
0x28,0x00,0x00,0x00, // Size
0x80,0x02,0x00,0x00, // Width
0xe0,0x01,0x00,0x00, // Height
0x01,0x00, // Planes
0x18,0x00, // BitCount
0x00,0x00,0x00,0x00, // Compression
0x00,0x10,0x0e,0x00, // SizeImage
0x00,0x00,0x00,0x00, // XPixelsPerMeter
0x00,0x00,0x00,0x00, // YPixelsPerMeter
0x00,0x00,0x00,0x00, // ClrUsed
0x00,0x00,0x00,0x00 // ClrImportant
};
*(unsigned*)(bmpheader32 + 2) = temp.ox * temp.oy * 3 + sizeof(bmpheader32); // filesize
*(unsigned*)(bmpheader32 + 0x12) = temp.ox;
*(unsigned*)(bmpheader32 + 0x16) = temp.oy;
fwrite(bmpheader32, 1, sizeof(bmpheader32), File);
for(int y = int(temp.oy - 1); y >= 0 ; y--)
{
fwrite(ds + ((unsigned(y) * temp.ox * 3 + 3) & ~3U), 1, temp.ox * 3, File);
}
}
static void SavePng(FILE *File, u8 *ds)
{
static png_color bkgColor = {127, 127, 127};
if(!temp.PngSupport)
return;
PngSaveImage(File, ds, int(temp.ox), int(temp.oy), bkgColor);
}
#define MAKE_RGBQUAD(r,g,b) (ULONG) (u32(b) | (u32(g)<<8) | (u32(r)<<16))
static void SaveBmp16c(FILE *File, const u8 *ds)
{
static u8 bmpheader32[]=
{
// BITMAPFILEHEADER
0x42,0x4d, // Type
0x36,0x10,0x0e,0x00, // Size = 320*200/2 + sizeof(bmpheader32)
0x00,0x00, // Reserved1
0x00,0x00, // Reserved2
0x76,0x00,0x00,0x00, // OffBits
// BITMAPINFOHEADER
0x28,0x00,0x00,0x00, // Size
0x40,0x01,0x00,0x00, // Width = 320
0xc8,0x00,0x00,0x00, // Height = 200
0x01,0x00, // Planes
0x04,0x00, // BitCount = 4
0x00,0x00,0x00,0x00, // Compression = BI_RGB
0x00,0x10,0x0e,0x00, // SizeImage
0x00,0x00,0x00,0x00, // XPixelsPerMeter
0x00,0x00,0x00,0x00, // YPixelsPerMeter
0x00,0x00,0x00,0x00, // ClrUsed
0x00,0x00,0x00,0x00, // ClrImportant
// PALETTE
0,0,0,0, // 0 bgra
0,0,0,0, // 1 bgra
0,0,0,0, // 2 bgra
0,0,0,0, // 3 bgra
0,0,0,0, // 4 bgra
0,0,0,0, // 5 bgra
0,0,0,0, // 6 bgra
0,0,0,0, // 7 bgra
0,0,0,0, // 8 bgra
0,0,0,0, // 9 bgra
0,0,0,0, // 10 bgra
0,0,0,0, // 11 bgra
0,0,0,0, // 12 bgra
0,0,0,0, // 13 bgra
0,0,0,0, // 14 bgra
0,0,0,0 // 15 bgra
};
*(unsigned*)(bmpheader32 + 2) = 320 * 200 / 2 + sizeof(bmpheader32); // filesize
for(unsigned i = 0; i < 16; i++)
{
// ╘юЁьрЄ фрээ√ї ярышЄЁ√ Gg0Rr0Bb
// ╨рёъырфър ярышЄЁ√ ¤ьєы ЄюЁр - ULA+
unsigned PalIdx = ((i & 8) << 1) | (i & 7);
u8 r = u8(u32((comp.comp_pal[PalIdx] >> 3) & 3) * 255 / 3);
u8 g = u8(u32((comp.comp_pal[PalIdx] >> 6) & 3) * 255 / 3);
u8 b = u8(u32(comp.comp_pal[PalIdx] & 3) * 255 / 3);
*(PULONG)(bmpheader32 + 54 + 4*i) = MAKE_RGBQUAD(r,g,b);
}
fwrite(bmpheader32, 1, sizeof(bmpheader32), File);
fwrite(ds, 1, 320 * 200 / 2, File);
}
static void ConvPal16cAtm1(u8 *Buf)
{
static const int ega0_ofs = -4*PAGE;
static const int ega1_ofs = 0;
static const int ega2_ofs = -4*PAGE+0x2000;
static const int ega3_ofs = 0x2000;
for (int y = 200 - 1; y >= 0; y--)
{
const u8 *src = temp.base + y*40;
for(unsigned x = 0; x < 320; x += 8)
{
u8 v0 = src[ega0_ofs];
u8 v1 = src[ega1_ofs];
u8 v2 = src[ega2_ofs];
u8 v3 = src[ega3_ofs];
Buf[0] = u8(((((v0 >> 3) & 8) | (v0 & 0x7)) << 4) |
(((v0 & 0x80) | ((v0 << 1) & 0x70)) >> 4));
Buf[1] = u8(((((v1 >> 3) & 8) | (v1 & 0x7)) << 4) |
(((v1 & 0x80) | ((v1 << 1) & 0x70)) >> 4));
Buf[2] = u8(((((v2 >> 3) & 8) | (v2 & 0x7)) << 4) |
(((v2 & 0x80) | ((v2 << 1) & 0x70)) >> 4));
Buf[3] = u8(((((v3 >> 3) & 8) | (v3 & 0x7)) << 4) |
(((v3 & 0x80) | ((v3 << 1) & 0x70)) >> 4));
src++;
Buf += 4;
}
}
}
#define ConvPal16cAtm2 ConvPal16cAtm1
typedef void (*TSaver)(FILE *File, u8 *ds);
void main_scrshot()
{
char fname[FILENAME_MAX];
static unsigned sshot = 0;
static const char *Format[] = { "scr", "bmp", "png" };
static const TSaver Saver[] = { SaveBmp, SavePng };
const char *Ext = Format[conf.scrshot];
if (conf.scrshot == 0 &&
(
((conf.mem_model == MM_ATM710 || conf.mem_model == MM_ATM3) && ((comp.pFF77 & 7) == 0)) ||
((conf.mem_model == MM_ATM450) && (((comp.aFE >> 5) & 3) == 0))
)
)
{
Ext = Format[1];
}
snprintf(fname, _countof(fname), "%s\\%s_%06u.%s", conf.scrshot_dir,
temp.LastSnapName[0] ? temp.LastSnapName : "sshot", sshot, Ext);
fname[FILENAME_MAX-1] = 0;
FILE *fileShot = fopen(fname, "wb");
if (!fileShot)
return;
if (conf.scrshot == 0)
{
switch(conf.mem_model)
{
case MM_ATM710:
case MM_ATM3:
{
switch(comp.pFF77 & 7)
{
case 0: // EGA 320x200 16c
{
u8 *Buf = (u8 *)malloc(320*200/2);
ConvPal16cAtm2(Buf);
SaveBmp16c(fileShot, Buf);
free(Buf);
}
break;
case 3:
goto standard_zx_mode;
}
}
break;
case MM_ATM450:
switch((comp.aFE >> 5) & 3)
{
case 0: // EGA 320x200 16c
{
u8 *Buf = (u8 *)malloc(320*200/2);
ConvPal16cAtm1(Buf);
SaveBmp16c(fileShot, Buf);
free(Buf);
}
break;
case 3:
goto standard_zx_mode;
}
break;
default:
standard_zx_mode:;
fwrite(temp.base, 1, 6912, fileShot);
}
}
else
{
unsigned dx = temp.ox * temp.obpp / 8;
unsigned char *scrbuf_unaligned = (unsigned char*)malloc(dx * temp.oy + CACHE_LINE);
unsigned char *scrbuf = (unsigned char*)align_by(scrbuf_unaligned, CACHE_LINE);
memset(scrbuf, 0, dx * temp.oy);
renders[conf.render].func(scrbuf, dx); // render to memory buffer (PAL8, YUY2, RGB15, RGB16, RGB32)
u8 *ds = (u8 *)malloc(((temp.ox * 3 + 3) & ~3U) * temp.oy);
ConvBgr24(ds, scrbuf, int(dx));
Saver[conf.scrshot - 1](fileShot, ds);
free(ds);
free(scrbuf_unaligned);
}
fclose(fileShot);
sprintf(statusline, "saving %s", strrchr(fname, '\\') + 1);
statcnt = 30;
sshot++;
}
/*
static void VideoFrameSaver()
{
char fname[FILENAME_MAX];
static unsigned FrameNum = 0;
static const char *Format[] = { "scr", "bmp", "png" };
static const TSaver Saver[] = { SaveBmp, SavePng };
sprintf(fname, "video%06u.%s", FrameNum, Format[conf.scrshot]);
addpath(fname);
FILE *fileShot = fopen(fname, "wb");
if (!fileShot)
return;
if (conf.scrshot == 0)
{
fwrite(temp.base, 1, 6912, fileShot);
}
else
{
unsigned dx = temp.ox * temp.obpp / 8;
unsigned char *scrbuf_unaligned = (unsigned char*)malloc(dx * temp.oy + CACHE_LINE);
unsigned char *scrbuf = (unsigned char*)align_by(scrbuf_unaligned, CACHE_LINE);
memset(scrbuf, 0, dx * temp.oy);
renders[conf.render].func(scrbuf, dx); // render to memory buffer (PAL8, YUY2, RGB15, RGB16, RGB32)
u8 *ds = (u8 *)malloc(((temp.ox * 3 + 3) & ~3) * temp.oy);
ConvBgr24(ds, scrbuf, dx);
Saver[conf.scrshot - 1](fileShot, ds);
free(ds);
free(scrbuf_unaligned);
}
fclose(fileShot);
FrameNum++;
}
static void VideoNullSaver()
{
}
TVideoSaver VideoSaver = VideoNullSaver;
void main_savevideo()
{
static bool StartSave = false;
if(!StartSave)
{
sprintf(statusline, "start saving video");
StartSave = true;
VideoSaver = VideoFrameSaver;
}
else
{
sprintf(statusline, "stop saving video");
StartSave = false;
VideoSaver = VideoNullSaver;
}
statcnt = 30;
}
*/