/*------------------------------------------------------------------------- | SAE - Scripted Amiga Emulator | https://github.com/naTmeg/ScriptedAmigaEmulator | | Copyright (C) 2012-2016 Rupert Hausberger | | This program is free software; you can redistribute it and/or | modify it under the terms of the GNU General Public License | as published by the Free Software Foundation; either version 2 | of the License, or (at your option) any later version. | | This program is distributed in the hope that it will be useful, | but WITHOUT ANY WARRANTY; without even the implied warranty of | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | GNU General Public License for more details. | | Note: ported from WinUAE 3.2.x -------------------------------------------------------------------------*/ function SAEO_Hardfile_Data() { //hardfiledata this.virtsize = 0; //u64, virtual size this.physsize = 0; //u64, physical size (dynamic disk) this.offset = 0; //u64 this.ci = new SAEO_Config_Mount_Info(); this.handle = null; //struct hardfilehandle * this.handle_valid = 0; //this.dangerous = 0; //this.flags = 0; this.cache = null; //u8 * this.cache_valid = 0; this.cache_offset = 0; //u64 this.vendor_id = ""; //[8 + 1] this.product_id = ""; //[16 + 1] this.product_rev = ""; //[4 + 1] // geometry from possible RDSK block //int rdbcylinders; //int rdbsectors; //int rdbheads; this.virtual_rdb = null; this.virtual_size = 0; //u64 //int unitnum; //FS this.byteswap = false; this.adide = false; this.hfd_type = 0; //virtual hard disk this.vhd_header = null; this.vhd_bamoffset = 0; //u32 this.vhd_bamsize = 0; //u32 this.vhd_blocksize = 0; //u32 this.vhd_sectormap = null; this.vhd_sectormapblock = 0; //u64 this.vhd_bitmapsize = 0; //u32 this.vhd_footerblock = 0; //u64 //void *chd_handle; this.drive_empty = false; //TCHAR *emptyname; }; function SAEO_Hardfile_Data_HD() { //hd_hardfiledata this.hfd = new SAEO_Hardfile_Data(); this.size = 0; //u64 this.cyls = 0; this.heads = 0; this.secspertrack = 0; this.cyls_def = 0; this.secspertrack_def = 0; this.heads_def = 0; this.ansi_version = 0; }; /*---------------------------------*/ function SAEO_Hardfile() { const HFD_VHD_FIXED = 2; const HFD_VHD_DYNAMIC = 3; //const WITH_CHD = 1; //const HFD_CHD_HD = 4; //const HFD_CHD_OTHER = 5; /*---------------------------------*/ function getchsgeometry(size, ptr, mode) { //int *pcyl, int *phead, int *psectorspertrack, int mode) { var spt, head, cyl; var total = Math.floor(size / 512); if (typeof mode == "undefined") mode = 0; if (mode == 1) { // old-style head=1, spt=32 always mode head = 1; spt = 32; cyl = Math.floor(total / (head * spt)); } else { var sptt = new Array(4); sptt[0] = 63; sptt[1] = 127; sptt[2] = 255; sptt[3] = -1; for (var i = 0; sptt[i] >= 0; i++) { var maxhead = sptt[i] < 255 ? 16 : 255; spt = sptt[i]; for (head = 4; head <= maxhead; head++) { cyl = Math.floor(total / (head * spt)); if (size <= 512 * 1024 * 1024) { if (cyl <= 1023) break; } else { if (cyl < 16383) break; if (cyl < 32767 && head >= 5) break; if (cyl <= 65535) break; } if (maxhead > 16) { head *= 2; head--; } } if (head <= 16) break; } } if (head > 16) head--; ptr.cyl = cyl; ptr.head = head; ptr.sectorspertrack = spt; } /*void getchsgeometry_hdf(struct hardfiledata *hfd, uae_u64 size, int *pcyl, int *phead, int *psectorspertrack) { uae_u8 block[512]; int i; uae_u64 minsize = 512 * 1024 * 1024; if (size <= minsize) { *phead = 1; *psectorspertrack = 32; } memset (block, 0, sizeof block); if (hfd) { hdf_read(hfd, block, 0, 512); if (block[0] == 'D' && block[1] == 'O' && block[2] == 'S') { int mode; for (mode = 0; mode < 2; mode++) { uae_u32 rootblock; uae_u32 chk = 0; getchsgeometry(size, pcyl, phead, psectorspertrack, mode); rootblock = (2 + ((*pcyl) * (*phead) * (*psectorspertrack) - 1)) / 2; memset (block, 0, sizeof block); hdf_read(hfd, block, (uae_u64)rootblock * 512, 512); for (i = 0; i < 512; i += 4) chk += (block[i] << 24) | (block[i + 1] << 16) | (block[i + 2] << 8) | (block[i + 3] << 0); if (!chk && block[0] == 0 && block[1] == 0 && block[2] == 0 && block[3] == 2 && block[4] == 0 && block[5] == 0 && block[6] == 0 && block[7] == 0 && block[8] == 0 && block[9] == 0 && block[10] == 0 && block[11] == 0 && block[508] == 0 && block[509] == 0 && block[510] == 0 && block[511] == 1) { return; } } } } getchsgeometry(size, pcyl, phead, psectorspertrack, size <= minsize ? 1 : 2); }*/ function getchspgeometry(total, ptr, idegeometry) { //, int *pcyl, int *phead, int *psectorspertrack, bool idegeometry) blocks = Math.floor(total / 512); if (blocks > 16515072) { /* >8G, CHS=16383/16/63 */ ptr.cyl = 16383; ptr.head = 16; ptr.sectorspertrack = 63; return; } if (idegeometry) { ptr.head = 16; ptr.sectorspertrack = 63; ptr.cyl = Math.floor(blocks / (ptr.sectorspertrack * ptr.head)); return; } //getchsgeometry(total, pcyl, phead, psectorspertrack); getchsgeometry(total, ptr); } function getchshd(hfd, ptr) { //int *pcyl, int *phead, int *psectorspertrack) { //getchspgeometry(hfd.virtsize, pcyl, phead, psectorspertrack, false); getchspgeometry(hfd.virtsize, ptr, false); } /*---------------------------------*/ function gl(p, po) { //OPT return ((p[po] << 24) | (p[po+1] << 16) | (p[po+2] << 8) | p[po+3]) >>> 0; } /*-----------------------------------------------------------------------*/ function rl(p, po) { po <<= 2; return ((p[po] << 24) | (p[po+1] << 16) | (p[po+2] << 8) | p[po+3]) >>> 0; } function pl(p, po, v) { po <<= 2; p[po ] = v >>> 24; p[po+1] = (v >>> 16) & 0xff; p[po+2] = (v >>> 8) & 0xff; p[po+3] = v & 0xff; } function ps(p, po, max, src) { //OWN const space = ' '.charCodeAt(0); var len = src.length; po <<= 2; p[po++] = Math.min(len, max); for (var i = 0; i < max; i++) p[po++] = i < len ? src.charCodeAt(i) : space; } function rdb_crc(p,po) { var sum = 0; //u32 var blocksize = rl(p, po + 1); for (var i = 0; i < blocksize; i++) { sum += rl(p, po + i); if (sum > 0xffffffff) sum -= 0x100000000; } sum = -sum; if (sum < 0) sum += 0x100000000; pl(p, po + 2, sum); } function create_virtual_rdb(hfd) { var cyl = hfd.ci.surfaces * hfd.ci.sectors; var cyls = 262144 / (cyl * 512); var size = cyl * cyls * 512; SAEF_log("hardfile.create_virtual_rdb() cyl %d, cyls %d, size %d", cyl, cyls, size); var rdb = new Uint8Array(size); SAEF_memset(rdb,0, 0, size); //OWN hfd.virtual_rdb = rdb; hfd.virtual_size = size; pl(rdb, 0, 0x5244534b); pl(rdb, 1, 64); pl(rdb, 2, 0); // chksum pl(rdb, 3, 7); // hostid pl(rdb, 4, 512); // blockbytes pl(rdb, 5, 0); // flags pl(rdb, 6, -1); // badblock pl(rdb, 7, 1); // part pl(rdb, 8, -1); // fs pl(rdb, 9, -1); // driveinit pl(rdb, 10, -1); // reserved pl(rdb, 11, -1); // reserved pl(rdb, 12, -1); // reserved pl(rdb, 13, -1); // reserved pl(rdb, 14, -1); // reserved pl(rdb, 15, -1); // reserved pl(rdb, 16, hfd.ci.highcyl); pl(rdb, 17, hfd.ci.sectors); pl(rdb, 18, hfd.ci.surfaces); pl(rdb, 19, hfd.ci.interleave ? 1 : 0); // interleave pl(rdb, 20, 0); // park pl(rdb, 21, -1); // res pl(rdb, 22, -1); // res pl(rdb, 23, -1); // res pl(rdb, 24, 0); // writeprecomp pl(rdb, 25, 0); // reducedwrite pl(rdb, 26, 0); // steprate pl(rdb, 27, -1); // res pl(rdb, 28, -1); // res pl(rdb, 29, -1); // res pl(rdb, 30, -1); // res pl(rdb, 31, -1); // res pl(rdb, 32, 0); // rdbblockslo pl(rdb, 33, cyl * cyls); // rdbblockshi pl(rdb, 34, cyls); // locyl pl(rdb, 35, hfd.ci.highcyl + cyls); // hicyl pl(rdb, 36, cyl); // cylblocks pl(rdb, 37, 0); // autopark pl(rdb, 38, 2); // highrdskblock pl(rdb, 39, -1); // res ps(rdb, 40, 8, hfd.vendor_id); ps(rdb, 42, 16, hfd.product_id); ps(rdb, 46, 4, hfd.product_rev); rdb_crc(rdb, 0); //var part = rdb + 512; var part = 512 >> 2; pl(rdb, part+0, 0x50415254); pl(rdb, part+1, 64); pl(rdb, part+2, 0); pl(rdb, part+3, 0); pl(rdb, part+4, -1); pl(rdb, part+5, 1); // 1 = bootable, 3 = bootable + noautomount pl(rdb, part+6, -1); pl(rdb, part+7, -1); pl(rdb, part+8, 0); // devflags ps(rdb, part+9, 30, hfd.ci.devname); //denv = part + 128; var denv = part + (128 >> 2); pl(rdb, denv+0, 80); pl(rdb, denv+1, 512 >> 2); pl(rdb, denv+2, 0); // secorg pl(rdb, denv+3, hfd.ci.surfaces); pl(rdb, denv+4, hfd.ci.blocksize >> 9); // / 512); pl(rdb, denv+5, hfd.ci.sectors); pl(rdb, denv+6, hfd.ci.reserved); pl(rdb, denv+7, 0); // prealloc pl(rdb, denv+8, hfd.ci.interleave ? 1 : 0); // interleave pl(rdb, denv+9, cyls); // lowcyl pl(rdb, denv+10, hfd.ci.highcyl + cyls - 1); pl(rdb, denv+11, hfd.ci.buffers); pl(rdb, denv+12, hfd.ci.bufmemtype); pl(rdb, denv+13, hfd.ci.maxtransfer); pl(rdb, denv+14, hfd.ci.mask); pl(rdb, denv+15, hfd.ci.bootpri); pl(rdb, denv+16, hfd.ci.dostype); rdb_crc(rdb, part); hfd.virtsize += size; } /*-----------------------------------------------------------------------*/ this.hdf_hd_open = function(hfd) { if (hdf_open(hfd.hfd) <= 0) return 0; var ci = hfd.hfd.ci; if (ci.physical_geometry) { hfd.cyls = ci.pcyls; hfd.heads = ci.pheads; hfd.secspertrack = ci.psecs; } else if (ci.highcyl && ci.surfaces && ci.sectors) { hfd.cyls = ci.highcyl; hfd.heads = ci.surfaces; hfd.secspertrack = ci.sectors; } else { var ptr = {}; getchshd(hfd.hfd, ptr); //&hfd.cyls, &hfd.heads, &hfd.secspertrack); hfd.cyls = ptr.cyl; hfd.heads = ptr.head; hfd.secspertrack = ptr.sectorspertrack; } hfd.cyls_def = hfd.cyls; hfd.secspertrack_def = hfd.secspertrack; hfd.heads_def = hfd.heads; if (ci.surfaces && ci.sectors) { var buf = new Uint8Array(512); buf[0] = 0; this.hdf_read(hfd.hfd, buf, 0, 512); if (buf[0] != 0 && SAEF_CompareArray(buf, SAEF_String2Array("RDSK"), 4) != 0) { ci.highcyl = Math.floor(Math.floor(hfd.hfd.virtsize / ci.blocksize) / (ci.sectors * ci.surfaces)); ci.dostype = rl(buf,0); SAEF_warn("hardfile.hdf_hd_open() no RDSK, dostype 0x%08x, highcyl %d", ci.dostype, ci.highcyl); create_virtual_rdb(hfd.hfd); while (ci.highcyl * ci.surfaces * ci.sectors > hfd.cyls_def * hfd.secspertrack_def * hfd.heads_def) hfd.cyls_def++; } } hfd.size = hfd.hfd.virtsize; return 1; } this.hdf_hd_close = function(hfd) { if (hfd !== null) hdf_close(hfd.hfd); } /*-----------------------------------------------------------------------*/ //function hdf_open(hfd, pname) { function hdf_open(hfd, file) { if (typeof file == "undefined") file = null; //if ((!pname || pname[0] == 0) && hfd.ci.rootdir[0] == 0) //if (!pname) pname = hfd.ci.rootdir; if (file === null) { //OWN if (hfd.ci.file.size == 0) return 0; file = hfd.ci.file.clone(); } hfd.byteswap = false; hfd.adide = false; hfd.hfd_type = 0; /*#ifdef WITH_CHD TCHAR nametmp[MAX_DPATH]; _tcscpy (nametmp, pname); TCHAR *ext = _tcsrchr (nametmp, '.'); if (ext && !_tcsicmp (ext, _T(".chd"))) { bool chd_readonly = false; struct zfile *zf = null; if (!hfd.ci.readonly) zf = SAEF_ZFile_fopen(nametmp, "rb+"); if (!zf) { chd_readonly = true; zf = SAEF_ZFile_fopen(nametmp, "rb"); } if (zf) { int err = CHDERR_FILE_NOT_WRITEABLE; hard_disk_file *chdf; chd_file *cf = new chd_file(); if (!chd_readonly) err = cf.open(*zf, true, null); if (err == CHDERR_FILE_NOT_WRITEABLE) { chd_readonly = true; err = cf.open(*zf, false, null); } if (err != CHDERR_NONE) { SAEF_ZFile_fclose(zf); delete cf; goto end; } chdf = hard_disk_open(cf); if (!chdf) { hfd.ci.readonly = true; hfd.hfd_type = HFD_CHD_OTHER; hfd.chd_handle = cf; } else { hfd.hfd_type = HFD_CHD_HD; hfd.chd_handle = chdf; } if (chd_readonly) hfd.ci.readonly = true; hfd.virtsize = cf.logical_bytes(); hfd.handle_valid = -1; write_log(_T("CHD '%s' mounted as %s, %s.\n"), pname, chdf ? _T("HD") : _T("OTHER"), hfd.ci.readonly ? _T("read only") : _T("read/write")); return 1; } } #endif*/ var ret = hdf_open_target(hfd, file); if (ret <= 0) return ret; var tmp = new Uint8Array(512); if (hdf_read_target(hfd, tmp,0, 0, 512) != 512) { //goto nonvhd; SAEF_log("hardfile.hdf_open() no VHD-image, file samller than 512 bytes"); hfd.hfd_type = 0; return 1; } var v = gl(tmp, 8); // features if ((v & 3) != 2) { SAEF_log("hardfile.hdf_open() no VHD-image, wrong file features %d != 2", v & 3); //goto nonvhd; hfd.hfd_type = 0; return 1; } v = gl(tmp, 8 + 4); // version if ((v >>> 16) != 1) { SAEF_log("hardfile.hdf_open() no VHD-image, wrong file version %d != 1", v >>> 16); //goto nonvhd; hfd.hfd_type = 0; return 1; } hfd.hfd_type = gl(tmp, 8 + 4 + 4 + 8 + 4 + 4 + 4 + 4 + 8 + 8 + 4); if (hfd.hfd_type != HFD_VHD_FIXED && hfd.hfd_type != HFD_VHD_DYNAMIC) { SAEF_log("hardfile.hdf_open() no VHD-image, wrong file type %d", hfd.hfd_type); //goto nonvhd; hfd.hfd_type = 0; return 1; } v = gl(tmp, 8 + 4 + 4 + 8 + 4 + 4 + 4 + 4 + 8 + 8 + 4 + 4); if (v == 0) { SAEF_log("hardfile.hdf_open() no VHD-image, error 1"); //goto nonvhd; hfd.hfd_type = 0; return 1; } var cs = vhd_checksum(tmp, 8 + 4 + 4 + 8 + 4 + 4 + 4 + 4 + 8 + 8 + 4 + 4); if (cs != v) { SAEF_log("hardfile.hdf_open() no VHD-image, wrong file checksum 0x%08x != 0x%08x", cs, v); //goto nonvhd; hfd.hfd_type = 0; return 1; } var tmp2 = new Uint8Array(512); if (hdf_read_target(hfd, tmp2,0, hfd.physsize - tmp2.length, 512) != 512) { SAEF_warn("hardfile.hdf_open() file read error"); hdf_close_target(hfd); return 0; //goto end; } if (SAEF_CompareArray(tmp, tmp2) != 0) { SAEF_log("hardfile.hdf_open() no VHD-image, error 2"); //goto nonvhd; hfd.hfd_type = 0; return 1; } hfd.vhd_footerblock = hfd.physsize - 512; //hfd.virtsize = gl(tmp, 8 + 4 + 4 + 8 + 4 + 4 + 4 + 4 + 8) << 32; //ATT //hfd.virtsize |= gl(tmp, 8 + 4 + 4 + 8 + 4 + 4 + 4 + 4 + 8 + 4); hfd.virtsize = gl(tmp, 8 + 4 + 4 + 8 + 4 + 4 + 4 + 4 + 8) * 0x100000000; hfd.virtsize += gl(tmp, 8 + 4 + 4 + 8 + 4 + 4 + 4 + 4 + 8 + 4); if (hfd.hfd_type == HFD_VHD_DYNAMIC) { var fail = true; hfd.vhd_bamoffset = gl(tmp, 8 + 4 + 4 + 4); if (hfd.vhd_bamoffset > 0 && hfd.vhd_bamoffset < hfd.physsize) { if (hdf_read_target(hfd, tmp,0, hfd.vhd_bamoffset, 512) == 512) { v = gl(tmp, 8 + 8 + 8 + 4 + 4 + 4); if (vhd_checksum(tmp, 8 + 8 + 8 + 4 + 4 + 4) == v) { v = gl(tmp, 8 + 8 + 8); if ((v >>> 16) == 1) { //version hfd.vhd_blocksize = gl(tmp, 8 + 8 + 8 + 4 + 4); hfd.vhd_bamoffset = gl(tmp, 8 + 8 + 4); hfd.vhd_bamsize = (Math.floor((hfd.virtsize + hfd.vhd_blocksize - 1) / hfd.vhd_blocksize) * 4 + 511) & ~511; var size = hfd.vhd_bamoffset + hfd.vhd_bamsize; hfd.vhd_header = new Uint8Array(size); if (hdf_read_target(hfd, hfd.vhd_header,0, 0, size) == size) { hfd.vhd_sectormap = new Uint8Array(512); hfd.vhd_sectormapblock = -1; hfd.vhd_bitmapsize = (Math.floor(hfd.vhd_blocksize / (8 * 512)) + 511) & ~511; fail = false; } } } } } if (fail) { hdf_close_target(hfd); return 0; } } SAEF_log("hardfile.hdf_open() HDF is VHD %s image, virtual size=%dK (%x %d)", hfd.hfd_type == HFD_VHD_FIXED ? "fixed" : "dynamic", Math.floor(hfd.virtsize / 1024), hfd.virtsize, hfd.virtsize); hdf_init_cache(hfd); return 1; /*nonvhd: hfd.hfd_type = 0; return 1; end: hdf_close_target (hfd); return 0;*/ } function hdf_close(hfd) { hdf_flush_cache(hfd); hdf_close_target(hfd); /* #ifdef WITH_CHD if (hfd.hfd_type == HFD_CHD_OTHER) { chd_file *cf = (chd_file*)hfd.chd_handle; cf.close(); delete cf; } else if (hfd.hfd_type == HFD_CHD_HD) { hard_disk_file *chdf = (hard_disk_file*)hfd.chd_handle; chd_file *cf = hard_disk_get_chd(chdf); hard_disk_close(chdf); cf.close(); delete cf; } hfd.chd_handle = null; #endif*/ hfd.hfd_type = 0; //xfree(hfd.vhd_header); hfd.vhd_header = null; //xfree(hfd.vhd_sectormap); hfd.vhd_sectormap = null; } /*int hdf_dup(struct hardfiledata *dhfd, const struct hardfiledata *shfd) { return hdf_dup_target(dhfd, shfd); }*/ /*-----------------------------------------------------------------------*/ function vhd_checksum(p, offset) { var sum = 0; //u32 for (var i = 0; i < 512; i++) { if (offset >= 0 && i >= offset && i < offset + 4) continue; sum += p[i]; if (sum > 0xffffffff) sum -= 0x100000000; } return ~sum >>> 0; } function vhd_read(hfd, data, offset, len) { //uae_u8 *dataptr = (uae_u8*)data; var dataptr = 0; //SAEF_log("hardfile.vhd_read() %08x %08x", offset, len); //SAEF_log("hardfile.vhd_read() %d %d", offset, len); var read = 0; //u64 if (offset & 511) return read; if (len & 511) return read; while (len > 0) { var bamoffset = Math.floor(offset / hfd.vhd_blocksize) * 4 + hfd.vhd_bamoffset; //u32 var sectoroffset = gl(hfd.vhd_header, bamoffset); //u32 if (sectoroffset == 0xffffffff) { //memset(dataptr, 0, 512); SAEF_memset(data,dataptr, 0, 512); read += 512; } else { var bitmapoffsetbits = Math.floor(offset / 512) % (hfd.vhd_blocksize >> 9); //int var bitmapoffsetbytes = Math.floor(bitmapoffsetbits / 8); //int var sectormapblock = sectoroffset * 512 + (bitmapoffsetbytes & ~511); //u64 if (hfd.vhd_sectormapblock != sectormapblock) { // read sector bitmap //SAEF_log("hardfile.vhd_read() BM %08x", sectormapblock); if (hdf_read_target(hfd, hfd.vhd_sectormap,0, sectormapblock, 512) != 512) { SAEF_warn("hardfile.vhd_read() bitmap read error"); return read; } hfd.vhd_sectormapblock = sectormapblock; } // block allocated in bitmap? if (hfd.vhd_sectormap[bitmapoffsetbytes & 511] & (1 << (7 - (bitmapoffsetbits & 7)))) { // read data block var block = sectoroffset * 512 + hfd.vhd_bitmapsize + bitmapoffsetbits * 512; //u64 //SAEF_log("hardfile.vhd_read() DB %08x", block); if (hdf_read_target(hfd, data,dataptr, block, 512) != 512) { SAEF_warn("hardfile.vhd_read() data read error"); return read; } } else { //memset(dataptr, 0, 512); SAEF_memset(data,dataptr, 0, 512); } read += 512; } len -= 512; dataptr += 512; offset += 512; } return read; } function vhd_write_enlarge(hfd, bamoffset) { var len = hfd.vhd_blocksize + hfd.vhd_bitmapsize + 512; if (!hdf_resize_target(hfd, hfd.physsize + len - 512)) { SAEF_warn("hardfile.vhd_write_enlarge() failure"); return false; } // add footer (same as 512 byte header) var buf = new Uint8Array(len); SAEF_memset(buf,0, 0, len - 512); //OWN buf.set(hfd.vhd_header.subarray(0, 512), len - 512); //memcpy (buf + len - 512, hfd->vhd_header, 512); var v = hdf_write_target(hfd, buf,0, hfd.vhd_footerblock, len); delete buf; if (v != len) { SAEF_warn("hardfile.vhd_write_enlarge() footer write error"); return false; } // write new offset to BAM var block = Math.floor(hfd.vhd_footerblock / 512); hfd.vhd_header[bamoffset + 0] = block >>> 24; hfd.vhd_header[bamoffset + 1] = (block >>> 16) & 0xff; hfd.vhd_header[bamoffset + 2] = (block >>> 8) & 0xff; hfd.vhd_header[bamoffset + 3] = block & 0xff; // write to disk if (hdf_write_target(hfd, hfd.vhd_header,hfd.vhd_bamoffset, hfd.vhd_bamoffset, hfd.vhd_bamsize) != hfd.vhd_bamsize) { SAEF_warn("hardfile.vhd_write_enlarge() bam write error"); return false; } hfd.vhd_footerblock += len - 512; return true; } function vhd_write(hfd, data, offset, len) { //uae_u8 *dataptr = (uae_u8*)v; var dataptr = 0; //SAEF_log("hardfile.vhd_read() %08x %08x", offset, len); //SAEF_log("hardfile.vhd_write() %d %d", offset, len); var written = 0; //u64 if (offset & 511) return written; if (len & 511) return written; while (len > 0) { var bamoffset = Math.floor(offset / hfd.vhd_blocksize) * 4 + hfd.vhd_bamoffset; //u32 var sectoroffset = gl(hfd.vhd_header, bamoffset); //u32 if (sectoroffset == 0xffffffff) { if (!vhd_write_enlarge(hfd, bamoffset)) return written; continue; } else { var bitmapoffsetbits = Math.floor(offset / 512) % (hfd.vhd_blocksize >> 9); //int var bitmapoffsetbytes = Math.floor(bitmapoffsetbits / 8); //int var sectormapblock = sectoroffset * 512 + (bitmapoffsetbytes & ~511); //u64 if (hfd.vhd_sectormapblock != sectormapblock) { // read sector bitmap //SAEF_log("hardfile.vhd_write() BM %08x", sectormapblock); if (hdf_read_target(hfd, hfd.vhd_sectormap,0, sectormapblock, 512) != 512) { SAEF_warn("hardfile.vhd_write() bitmap read error"); return written; } hfd.vhd_sectormapblock = sectormapblock; } // write data var block = sectoroffset * 512 + hfd.vhd_bitmapsize + bitmapoffsetbits * 512; //u64 //SAEF_log("hardfile.vhd_write() DB %08x", block); if (hdf_write_target(hfd, data,dataptr, block, 512) != 512) { SAEF_warn("hardfile.vhd_write() data write error"); return written; } // block already allocated in bitmap? if (!(hfd.vhd_sectormap[bitmapoffsetbytes & 511] & (1 << (7 - (bitmapoffsetbits & 7))))) { // no, we need to mark it allocated and write the modified bitmap back to the disk hfd.vhd_sectormap[bitmapoffsetbytes & 511] |= (1 << (7 - (bitmapoffsetbits & 7))); if (hdf_write_target(hfd, hfd.vhd_sectormap,0, sectormapblock, 512) != 512) { SAEF_warn("hardfile.vhd_write() bam write error"); return written; } } written += 512; } len -= 512; dataptr += 512; offset += 512; } return written; } /*int vhd_create (const TCHAR *name, uae_u64 size, uae_u32 dostype) { struct hardfiledata hfd; struct zfile *zf; uae_u8 *b; int cyl, cylsec, head, tracksec; uae_u32 crc, blocksize, batsize, batentrysize; int ret, i; time_t tm; if (size >= (uae_u64)10 * 1024 * 1024 * 1024) blocksize = 2 * 1024 * 1024; else blocksize = 512 * 1024; batsize = (size + blocksize - 1) / blocksize; batentrysize = batsize; batsize *= 4; batsize += 511; batsize &= ~511; ret = 0; b = NULL; zf = SAEF_ZFile_fopen(name, "wb", 0); if (!zf) goto end; b = xcalloc (uae_u8, 512 + 1024 + batsize + 512); if (SAEF_ZFile_fwrite(b,0, 512 + 1024 + batsize + 512, 1, zf) != 1) goto end; memset (&hfd, 0, sizeof hfd); hfd.virtsize = hfd.physsize = size; hfd.ci.blocksize = 512; strcpy ((char*)b, "conectix"); // cookie b[0x0b] = 2; // features b[0x0d] = 1; // version b[0x10 + 6] = 2; // data offset // time stamp tm = time (NULL) - 946684800; b[0x18] = tm >> 24; b[0x19] = tm >> 16; b[0x1a] = tm >> 8; b[0x1b] = tm >> 0; strcpy ((char*)b + 0x1c, "vpc "); // creator application b[0x21] = 5; // creator version strcpy ((char*)b + 0x24, "Wi2k"); // creator host os // original and current size b[0x28] = b[0x30] = size >> 56; b[0x29] = b[0x31] = size >> 48; b[0x2a] = b[0x32] = size >> 40; b[0x2b] = b[0x33] = size >> 32; b[0x2c] = b[0x34] = size >> 24; b[0x2d] = b[0x35] = size >> 16; b[0x2e] = b[0x36] = size >> 8; b[0x2f] = b[0x37] = size >> 0; getchs2 (&hfd, &cyl, &cylsec, &head, &tracksec); // cylinders b[0x38] = cyl >> 8; b[0x39] = cyl; // heads b[0x3a] = head; // sectors per track b[0x3b] = tracksec; // disk type b[0x3c + 3] = HFD_VHD_DYNAMIC; get_guid_target (b + 0x44); crc = vhd_checksum (b, -1); b[0x40] = crc >> 24; b[0x41] = crc >> 16; b[0x42] = crc >> 8; b[0x43] = crc >> 0; // write header SAEF_ZFile_fseek(zf, 0, SEEK_SET); SAEF_ZFile_fwrite(b,0, 512, 1, zf); // write footer SAEF_ZFile_fseek(zf, 512 + 1024 + batsize, SEEK_SET); SAEF_ZFile_fwrite(b,0, 512, 1, zf); // dynamic disk header memset (b, 0, 1024); // cookie strcpy ((char*)b, "cxsparse"); // data offset for (i = 0; i < 8; i++) b[0x08 + i] = 0xff; // table offset (bat) b[0x10 + 6] = 0x06; // version b[0x19] = 1; // max table entries b[0x1c] = batentrysize >> 24; b[0x1d] = batentrysize >> 16; b[0x1e] = batentrysize >> 8; b[0x1f] = batentrysize >> 0; b[0x20] = blocksize >> 24; b[0x21] = blocksize >> 16; b[0x22] = blocksize >> 8; b[0x23] = blocksize >> 0; crc = vhd_checksum (b, -1); b[0x24] = crc >> 24; b[0x25] = crc >> 16; b[0x26] = crc >> 8; b[0x27] = crc >> 0; // write dynamic header SAEF_ZFile_fseek(zf, 512, SEEK_SET); SAEF_ZFile_fwrite(b,0, 1024, 1, zf); // bat memset (b, 0, batsize); memset (b, 0xff, batentrysize * 4); SAEF_ZFile_fwrite(b,0, batsize, 1, zf); SAEF_ZFile_fclose(zf); zf = NULL; if (dostype) { uae_u8 bootblock[512] = { 0 }; bootblock[0] = dostype >> 24; bootblock[1] = dostype >> 16; bootblock[2] = dostype >> 8; bootblock[3] = dostype >> 0; if (hdf_open(&hfd, file) > 0) { vhd_write(&hfd, bootblock, 0, 512); hdf_close(&hfd); } } ret = 1; end: xfree (b); SAEF_ZFile_fclose(zf); return ret; }*/ /*-----------------------------------------------------------------------*/ function hdf_read2(hfd, buffer, offset, len) { if (hfd.hfd_type == HFD_VHD_DYNAMIC) return vhd_read(hfd, buffer, offset, len); else if (hfd.hfd_type == HFD_VHD_FIXED) return hdf_read_target(hfd, buffer,0, offset + 512, len); /* #ifdef WITH_CHD else if (hfd.hfd_type == HFD_CHD_OTHER) { chd_file *cf = (chd_file*)hfd.chd_handle; if (cf.read_bytes(offset, buffer, len) == CHDERR_NONE) return len; return 0; } else if (hfd.hfd_type == HFD_CHD_HD) { hard_disk_file *chdf = (hard_disk_file*)hfd.chd_handle; hard_disk_info *chdi = hard_disk_get_info(chdf); chd_file *cf = hard_disk_get_chd(chdf); uae_u8 *buf = (uae_u8*)buffer; int got = 0; offset /= chdi.sectorbytes; while (len > 0) { if (cf.read_units(offset, buf) != CHDERR_NONE) return got; got += chdi.sectorbytes; buf += chdi.sectorbytes; len -= chdi.sectorbytes; offset++; } return got; } #endif*/ else return hdf_read_target(hfd, buffer,0, offset, len); } function hdf_write2(hfd, buffer, offset, len) { if (hfd.hfd_type == HFD_VHD_DYNAMIC) return vhd_write(hfd, buffer, offset, len); else if (hfd.hfd_type == HFD_VHD_FIXED) return hdf_write_target(hfd, buffer,0, offset + 512, len); /* #ifdef WITH_CHD else if (hfd.hfd_type == HFD_CHD_OTHER) return 0; else if (hfd.hfd_type == HFD_CHD_HD) { if (hfd.ci.readonly) return 0; hard_disk_file *chdf = (hard_disk_file*)hfd.chd_handle; hard_disk_info *chdi = hard_disk_get_info(chdf); chd_file *cf = hard_disk_get_chd(chdf); uae_u8 *buf = (uae_u8*)buffer; int got = 0; offset /= chdi.sectorbytes; while (len > 0) { if (cf.write_units(offset, buf) != CHDERR_NONE) return got; got += chdi.sectorbytes; buf += chdi.sectorbytes; len -= chdi.sectorbytes; offset++; } return got; } #endif*/ else return hdf_write_target(hfd, buffer,0, offset, len); } function hdf_cache_read(hfd, buffer, offset, len) { return hdf_read2(hfd, buffer, offset, len); } function hdf_cache_write(hfd, buffer, offset, len) { return hdf_write2(hfd, buffer, offset, len); } function hdf_init_cache(hfd) {} function hdf_flush_cache(hdf) {} /*-----------------------------------------------------------------------*/ function adide_decode(v, len) { SAEF_warn("hardfile.adide_decode() 0x%04x %d", v, len); /*int i; uae_u8 *buffer = (uae_u8*)v; for (i = 0; i < len; i += 2) { uae_u8 *b = buffer + i; uae_u16 w = (b[0] << 8) | (b[1] << 0); uae_u16 o = SAER.ide.adide_decode_word(w); b[0] = o >> 8; b[1] = o >> 0; }*/ } function adide_encode(v, len) { SAEF_warn("hardfile.adide_encode() 0x%04x %d", v, len); /*int i; uae_u8 *buffer = (uae_u8*)v; for (i = 0; i < len; i += 2) { uae_u8 *b = buffer + i; uae_u16 w = (b[0] << 8) | (b[1] << 0); uae_u16 o = SAER.ide.adide_encode_word(w); b[0] = o >> 8; b[1] = o >> 0; }*/ } function hdf_byteswap(v, len) { SAEF_warn("hardfile.hdf_byteswap() 0x%04x %d", v, len); /*int i; uae_u8 *b = (uae_u8*)v; for (i = 0; i < len; i += 2) { uae_u8 tmp = b[i]; b[i] = b[i + 1]; b[i + 1] = tmp; }*/ } /*int hdf_read_rdb (struct hardfiledata *hfd, void *buffer, uae_u64 offset, int len) { int v; v = hdf_read (hfd, buffer, offset, len); if (v > 0 && offset < 16 * 512 && !hfd.byteswap && !hfd.adide) { uae_u8 *buf = (uae_u8*)buffer; bool changed = false; if (buf[0] == 0x39 && buf[1] == 0x10 && buf[2] == 0xd3 && buf[3] == 0x12) { // AdIDE encoded "CPRM" hfd.adide = true; changed = true; write_log (_T("HDF: adide scrambling detected\n")); } else if (!memcmp (buf, "DRKS", 4)) { hfd.byteswap = true; changed = true; write_log (_T("HDF: byteswapped RDB detected\n")); } if (changed) v = hdf_read (hfd, buffer, offset, len); } return v; }*/ this.hdf_read = function(hfd, buffer, offset, len) { var v; //SAEF_log("hardfile.hdf_read() %04x-%08x (%d) %08x (%d)", Math.floor(offset / 0x100000000), Math.floor(offset % 0x100000000), Math.floor(offset / hfd.ci.blocksize), len >>> 0, Math.floor(len / hfd.ci.blocksize)); if (!hfd.adide) { v = hdf_cache_read(hfd, buffer, offset, len); } else { offset += 512; v = hdf_cache_read(hfd, buffer, offset, len); adide_decode(buffer, len); } if (hfd.byteswap) hdf_byteswap(buffer, len); return v; } this.hdf_write = function(hfd, buffer, offset, len) { var v; //SAEF_log("hardfile.hdf_write() %04x-%08x (%d) %08x (%d)", Math.floor(offset / 0x100000000), Math.floor(offset % 0x100000000), Math.floor(offset / hfd.ci.blocksize), len >>> 0, Math.floor(len / hfd.ci.blocksize)); if (hfd.byteswap) hdf_byteswap(buffer, len); if (!hfd.adide) v = hdf_cache_write(hfd, buffer, offset, len); else { offset += 512; adide_encode(buffer, len); v = hdf_cache_write(hfd, buffer, offset, len); adide_decode(buffer, len); } if (hfd.byteswap) hdf_byteswap(buffer, len); return v; } /*-----------------------------------------------------------------------*/ /* target */ const CACHE_SIZE = 16384; function hardfilehandle() { this.zf = null; this.firstwrite = false; }; //function hdf_open_target(hfd, pname) { function hdf_open_target(hfd, file) { //hfd.flags = 0; hfd.drive_empty = false; hdf_close(hfd); hfd.cache = new Uint8Array(CACHE_SIZE); //(uae_u8*)VirtualAlloc (null, CACHE_SIZE, MEM_COMMIT, PAGE_READWRITE); /*if (!hfd.cache) { SAEF_warn("VirtualAlloc(%d) failed, error %d", CACHE_SIZE, GetLastError()); hdf_close(hfd); return -1; }*/ hfd.cache_valid = 0; hfd.virtual_size = 0; hfd.virtual_rdb = null; hfd.vendor_id = "UAE"; hfd.product_id = file.name.substr(0, Math.min(file.name.length, 16-1)); hfd.product_rev = "0.1"; SAEF_log("hardfile.hdf_open_target() attempting to open HDF '%s'... (%d bytes)", file.name, file.size); hfd.handle = new hardfilehandle(); //hfd.handle.zf = SAEF_ZFile_fopen(pname, "rb", ZFD_NORMAL); hfd.handle.zf = SAEF_ZFile_fopen_file(file); if (hfd.handle.zf === null) { hdf_close(hfd); return -1; } SAEF_ZFile_fseek(hfd.handle.zf, 0, SEEK_END); hfd.physsize = hfd.virtsize = SAEF_ZFile_ftell(hfd.handle.zf); SAEF_ZFile_fseek(hfd.handle.zf, 0, SEEK_SET); hfd.handle_valid = 1; SAEF_log("hardfile.hdf_open_target() HDF '%s' opened (size %dK, empty %d)", file.name, Math.floor(hfd.physsize / 1024), hfd.drive_empty ? 1:0); return 1; /*if (hfd.handle_valid || hfd.drive_empty) { SAEF_log("hardfile.hdf_open_target() HDF '%s' opened (size %dK, mode %d, empty %d)", file.name, Math.floor(hfd.physsize / 1024), hfd.handle_valid, hfd.drive_empty); return 1; } hdf_close(hfd); return -1;*/ } function freehandle(h) { if (h !== null) { if (h.zf !== null) { SAEF_ZFile_fclose(h.zf); h.zf = null; } } } function hdf_close_target(hfd) { freehandle(hfd.handle); //xfree(hfd.handle); //xfree(hfd.emptyname); //hfd.emptyname = null; hfd.handle = null; hfd.handle_valid = 0; //if (hfd.cache) VirtualFree(hfd.cache, 0, MEM_RELEASE); hfd.cache = null; hfd.cache_valid = 0; //xfree(hfd.virtual_rdb); hfd.virtual_rdb = null; hfd.virtual_size = 0; hfd.drive_empty = false; //hfd.dangerous = 0; } /*---------------------------------*/ /*int hdf_dup_target(struct hardfiledata *dhfd, const struct hardfiledata *shfd) { if (!shfd.handle_valid) return 0; freehandle (dhfd.handle); struct zfile *zf = SAEF_ZFile_dup(shfd.handle.zf); if (!zf) return 0; dhfd.handle.zf = zf; dhfd.handle_valid = 1; dhfd.cache = (uae_u8*)VirtualAlloc (null, CACHE_SIZE, MEM_COMMIT, PAGE_READWRITE); dhfd.cache_valid = 0; if (!dhfd.cache) { hdf_close(dhfd); return 0; } return 1; }*/ function hdf_resize_target(hfd, newsize) { /*DWORD ret, err; if (newsize >= 0x80000000) { LONG highword = (DWORD)(newsize >> 32); ret = SetFilePointer (hfd.handle.h, (DWORD)newsize, &highword, FILE_BEGIN); } else { ret = SetFilePointer (hfd.handle.h, (DWORD)newsize, null, FILE_BEGIN); } err = GetLastError (); if (ret == INVALID_SET_FILE_POINTER && err != NO_ERROR) { write_log (_T("hdf_resize_target: SetFilePointer() %d\n"), err); return 0; } if (SetEndOfFile (hfd.handle.h)) { hfd.physsize = newsize; return 1; } err = GetLastError (); write_log (_T("hdf_resize_target: SetEndOfFile() %d\n"), err); return 0;*/ SAEF_ZFile_resize(hfd.handle.zf, newsize); hfd.physsize = newsize; return true; } /*---------------------------------*/ function hdf_seek(hfd, offset) { if (hfd.handle_valid == 0) { SAEF_warn("hardfile.hdf_seek() hdf handle is not valid. bug."); //abort(); return -1; } if (offset >= hfd.physsize - hfd.virtual_size) { SAEF_warn("hardfile.hdf_seek() tried to seek out of bounds! (%X >= %X - %X)", offset, hfd.physsize, hfd.virtual_size); //abort(); return -1; } offset += hfd.offset; if (offset & (hfd.ci.blocksize - 1)) { SAEF_warn("hardfile.hdf_seek() fail, offset = %X not aligned to blocksize %d! (%X & %04X = %04X)", offset, hfd.ci.blocksize, offset, hfd.ci.blocksize, offset & (hfd.ci.blocksize - 1)); //abort(); return -1; } if (SAEF_ZFile_fseek(hfd.handle.zf, offset, SEEK_SET) != 0) { SAEF_warn("hardfile.hdf_seek() common seek error"); return -1; } return 0; } /*---------------------------------*/ function poscheck(hfd, len) { var pos = SAEF_ZFile_ftell(hfd.handle.zf); if (len < 0) { SAEF_warn("hardfile.poscheck() fail, negative length! (%d)", len); //abort(); return -1; } if (pos < hfd.offset) { SAEF_warn("hardfile.poscheck() fail, offset out of bounds! (%d < %d)", pos, hfd.offset); //abort(); return -1; } if (pos >= hfd.offset + hfd.physsize - hfd.virtual_size || pos >= hfd.offset + hfd.physsize + len - hfd.virtual_size) { SAEF_warn("hardfile.poscheck() fail, offset out of bounds! (%d >= %d, LEN=%d)", pos, hfd.offset + hfd.physsize, len); //abort(); return -1; } if (pos & (hfd.ci.blocksize - 1)) { SAEF_warn("hardfile.poscheck() fail, offset not aligned to blocksize! (%X & %X = %04X)", pos, hfd.ci.blocksize, pos & hfd.ci.blocksize); //abort(); return -1; } return 0; } /*---------------------------------*/ function isincache(hfd, offset, len) { if (!hfd.cache_valid) return -1; if (offset >= hfd.cache_offset && offset + len <= hfd.cache_offset + CACHE_SIZE) return offset - hfd.cache_offset; return -1; } function hdf_read_target_2(hfd, buffer,buffero, offset, len) { //hdf_read_2() if (offset == 0) hfd.cache_valid = 0; var coffset = isincache(hfd, offset, len); if (coffset >= 0) { buffer.set(hfd.cache.subarray(coffset, coffset + len), buffero); //memcpy (buffer, hfd->cache + coffset, len); return len; } hfd.cache_offset = offset; if (offset + CACHE_SIZE > hfd.offset + (hfd.physsize - hfd.virtual_size)) hfd.cache_offset = hfd.offset + (hfd.physsize - hfd.virtual_size) - CACHE_SIZE; if (hdf_seek(hfd, hfd.cache_offset) == -1) return -1; if (poscheck(hfd, CACHE_SIZE) == -1) return -1; var outlen = SAEF_ZFile_fread(hfd.cache,0, 1, CACHE_SIZE, hfd.handle.zf); hfd.cache_valid = 0; if (outlen != CACHE_SIZE) return 0; hfd.cache_valid = 1; coffset = isincache(hfd, offset, len); if (coffset >= 0) { buffer.set(hfd.cache.subarray(coffset, coffset + len), buffero); //memcpy (buffer, hfd->cache + coffset, len); return len; } SAEF_error("hardfile.hdf_read_target_2() cache bug! offset %d, len %d", offset, len); hfd.cache_valid = 0; return 0; } function hdf_read_target(hfd, buffer,buffero, offset, len) { var got = 0; //var p = buffer; var p = buffero; if (hfd.drive_empty) return 0; if (offset < hfd.virtual_size) { var len2 = offset + len <= hfd.virtual_size ? len : hfd.virtual_size - offset; if (!hfd.virtual_rdb) return 0; buffer.set(hfd.virtual_rdb.subarray(offset, offset + len2), p); //memcpy(buffer, hfd.virtual_rdb + offset, len2); return len2; } offset -= hfd.virtual_size; while (len > 0) { var maxlen; var ret; if (hfd.physsize < CACHE_SIZE) { hfd.cache_valid = 0; if (hdf_seek(hfd, offset) == -1) return -1; if (poscheck(hfd, len) == -1) return -1; ret = SAEF_ZFile_fread(buffer,p, 1, len, hfd.handle.zf); maxlen = len; } else { maxlen = len > CACHE_SIZE ? CACHE_SIZE : len; ret = hdf_read_target_2(hfd, buffer,p, offset, maxlen); } if (ret < 0) return ret; got += ret; if (ret != maxlen) return got; offset += maxlen; p += maxlen; len -= maxlen; } return got; } /*---------------------------------*/ function hdf_write_target_2(hfd, buffer,buffero, offset, len) { //hdf_write_2() if (hfd.ci.readonly) return 0; //if (hfd.dangerous) //return 0; hfd.cache_valid = 0; if (hdf_seek(hfd, offset) == -1) return -1; if (poscheck(hfd, len) == -1) return -1; hfd.cache.set(buffer.subarray(buffero, buffero + len)); //memcpy(hfd.cache, buffer, len); return SAEF_ZFile_fwrite(hfd.cache,0, 1, len, hfd.handle.zf); } function hdf_write_target(hfd, buffer,buffero, offset, len) { var got = 0; //var p = buffer; var p = buffero; if (hfd.drive_empty) return 0; if (offset < hfd.virtual_size) return len; offset -= hfd.virtual_size; while (len > 0) { var maxlen = len > CACHE_SIZE ? CACHE_SIZE : len; var ret = hdf_write_target_2(hfd, buffer,p, offset, maxlen); if (ret < 0) return ret; got += ret; if (ret != maxlen) return got; offset += maxlen; p += maxlen; len -= maxlen; } return got; } /*-----------------------------------------------------------------------*/ this.reset = function() {} //hardfile_reset() }