diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index c2ca3446f..72fca815d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -25,7 +25,7 @@ If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - OS: [e.g. Windows 10] - - 86Box version: [e.g. v2.06 build 2007] + - 86Box version: [e.g. v2.06 build 2007, saying "Latest from Jenkins" isn't helpful] - Build type: [i.e. regular, optimized, or dev] **Additional context** diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index b3a637010..277acdaee 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -34,5 +34,5 @@ jobs: install: 'make mingw-w64-i686-toolchain mingw-w64-i686-openal mingw-w64-i686-freetype mingw-w64-i686-SDL2 mingw-w64-i686-zlib mingw-w64-i686-libpng mingw-w64-i686-libvncserver' - uses: actions/checkout@v2 - name: make - run: make -fwin/makefile.mingw DEV_BUILD=${{ matrix.dev-build }} NEW_DYNAREC=${{ matrix.new-dynarec }} VNC=n + run: make -fwin/makefile.mingw -j DEV_BUILD=${{ matrix.dev-build }} NEW_DYNAREC=${{ matrix.new-dynarec }} VNC=n working-directory: ./src diff --git a/.gitignore b/.gitignore index 99e0528fc..5c05ee308 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ src/*.dmp src/NUL src/nvr/ src/roms/ +/.vs diff --git a/src/acpi.c b/src/acpi.c index 19d52b9bf..740d288df 100644 --- a/src/acpi.c +++ b/src/acpi.c @@ -39,6 +39,9 @@ #include <86box/i2c.h> +int acpi_rtc_status = 0; + + #ifdef ENABLE_ACPI_LOG int acpi_do_log = ENABLE_ACPI_LOG; @@ -119,6 +122,8 @@ acpi_reg_read_common_regs(int size, uint16_t addr, void *p) case 0x00: case 0x01: /* PMSTS - Power Management Status Register (IO) */ ret = (dev->regs.pmsts >> shift16) & 0xff; + if (addr == 0x01) + ret |= (acpi_rtc_status << 2); break; case 0x02: case 0x03: /* PMEN - Power Management Resume Enable Register (IO) */ @@ -127,6 +132,8 @@ acpi_reg_read_common_regs(int size, uint16_t addr, void *p) case 0x04: case 0x05: /* PMCNTRL - Power Management Control Register (IO) */ ret = (dev->regs.pmcntrl >> shift16) & 0xff; + if (addr == 0x05) + ret = (ret & 0xdf) | 0x02; /* Bit 5 is write-only. */ break; case 0x08: case 0x09: case 0x0a: case 0x0b: /* PMTMR - Power Management Timer Register (IO) */ @@ -138,7 +145,10 @@ acpi_reg_read_common_regs(int size, uint16_t addr, void *p) break; } - acpi_log("(%i) ACPI Read (%i) %02X: %02X\n", in_smm, size, addr, ret); +#ifdef ENABLE_ACPI_LOG + if (size != 1) + acpi_log("(%i) ACPI Read (%i) %02X: %02X\n", in_smm, size, addr, ret); +#endif return ret; } @@ -211,7 +221,10 @@ acpi_reg_read_intel(int size, uint16_t addr, void *p) break; } - acpi_log("(%i) ACPI Read (%i) %02X: %02X\n", in_smm, size, addr, ret); +#ifdef ENABLE_ACPI_LOG + if (size != 1) + acpi_log("(%i) ACPI Read (%i) %02X: %02X\n", in_smm, size, addr, ret); +#endif return ret; } @@ -285,7 +298,10 @@ acpi_reg_read_via_common(int size, uint16_t addr, void *p) break; } - acpi_log("(%i) ACPI Read (%i) %02X: %02X\n", in_smm, size, addr, ret); +#ifdef ENABLE_ACPI_LOG + if (size != 1) + acpi_log("(%i) ACPI Read (%i) %02X: %02X\n", in_smm, size, addr, ret); +#endif return ret; } @@ -338,7 +354,10 @@ acpi_reg_read_via(int size, uint16_t addr, void *p) break; } - acpi_log("(%i) ACPI Read (%i) %02X: %02X\n", in_smm, size, addr, ret); +#ifdef ENABLE_ACPI_LOG + if (size != 1) + acpi_log("(%i) ACPI Read (%i) %02X: %02X\n", in_smm, size, addr, ret); +#endif return ret; } @@ -372,7 +391,10 @@ acpi_reg_read_via_596b(int size, uint16_t addr, void *p) break; } - acpi_log("(%i) ACPI Read (%i) %02X: %02X\n", in_smm, size, addr, ret); +#ifdef ENABLE_ACPI_LOG + if (size != 1) + acpi_log("(%i) ACPI Read (%i) %02X: %02X\n", in_smm, size, addr, ret); +#endif return ret; } @@ -386,7 +408,10 @@ acpi_reg_read_smc(int size, uint16_t addr, void *p) ret = acpi_reg_read_common_regs(size, addr, p); - acpi_log("(%i) ACPI Read (%i) %02X: %02X\n", in_smm, size, addr, ret); +#ifdef ENABLE_ACPI_LOG + if (size != 1) + acpi_log("(%i) ACPI Read (%i) %02X: %02X\n", in_smm, size, addr, ret); +#endif return ret; } @@ -436,13 +461,18 @@ acpi_reg_write_common_regs(int size, uint16_t addr, uint8_t val, void *p) int shift16, sus_typ; addr &= 0x3f; - acpi_log("(%i) ACPI Write (%i) %02X: %02X\n", in_smm, size, addr, val); +#ifdef ENABLE_ACPI_LOG + if (size != 1) + acpi_log("(%i) ACPI Write (%i) %02X: %02X\n", in_smm, size, addr, val); +#endif shift16 = (addr & 1) << 3; switch (addr) { case 0x00: case 0x01: /* PMSTS - Power Management Status Register (IO) */ dev->regs.pmsts &= ~((val << shift16) & 0x8d31); + if ((addr == 0x01) && (val & 0x04)) + acpi_rtc_status = 0; acpi_update_irq(dev); break; case 0x02: case 0x03: @@ -452,8 +482,7 @@ acpi_reg_write_common_regs(int size, uint16_t addr, uint8_t val, void *p) break; case 0x04: case 0x05: /* PMCNTRL - Power Management Control Register (IO) */ - dev->regs.pmcntrl = ((dev->regs.pmcntrl & ~(0xff << shift16)) | (val << shift16)) & 0x3c07; - if (dev->regs.pmcntrl & 0x2000) { + if ((addr == 0x05) && (dev->regs.pmcntrl & 0x2000)) { sus_typ = (dev->regs.pmcntrl >> 10) & 7; switch (sus_typ) { case 0: @@ -479,8 +508,12 @@ acpi_reg_write_common_regs(int size, uint16_t addr, uint8_t val, void *p) resetx86(); break; + default: + dev->regs.pmcntrl = ((dev->regs.pmcntrl & ~(0xff << shift16)) | (val << shift16)) & 0x3c07; + break; } - } + } else + dev->regs.pmcntrl = ((dev->regs.pmcntrl & ~(0xff << shift16)) | (val << shift16)) & 0x3c07; break; } } @@ -493,7 +526,10 @@ acpi_reg_write_intel(int size, uint16_t addr, uint8_t val, void *p) int shift16, shift32; addr &= 0x3f; - acpi_log("(%i) ACPI Write (%i) %02X: %02X\n", in_smm, size, addr, val); +#ifdef ENABLE_ACPI_LOG + if (size != 1) + acpi_log("(%i) ACPI Write (%i) %02X: %02X\n", in_smm, size, addr, val); +#endif shift16 = (addr & 1) << 3; shift32 = (addr & 3) << 3; @@ -876,6 +912,8 @@ acpi_reg_read(uint16_t addr, void *p) ret = acpi_reg_read_common(1, addr, p); + acpi_log("ACPI: Read B %02X from %04X\n", ret, addr); + return ret; } @@ -924,6 +962,8 @@ acpi_aux_read_read(uint16_t addr, void *p) static void acpi_reg_writel(uint16_t addr, uint32_t val, void *p) { + acpi_log("ACPI: Write L %08X to %04X\n", val, addr); + acpi_reg_write_common(4, addr, val & 0xff, p); acpi_reg_write_common(4, addr + 1, (val >> 8) & 0xff, p); acpi_reg_write_common(4, addr + 2, (val >> 16) & 0xff, p); @@ -934,6 +974,8 @@ acpi_reg_writel(uint16_t addr, uint32_t val, void *p) static void acpi_reg_writew(uint16_t addr, uint16_t val, void *p) { + acpi_log("ACPI: Write W %04X to %04X\n", val, addr); + acpi_reg_write_common(2, addr, val & 0xff, p); acpi_reg_write_common(2, addr + 1, (val >> 8) & 0xff, p); } @@ -942,6 +984,8 @@ acpi_reg_writew(uint16_t addr, uint16_t val, void *p) static void acpi_reg_write(uint16_t addr, uint8_t val, void *p) { + acpi_log("ACPI: Write B %02X to %04X\n", val, addr); + acpi_reg_write_common(1, addr, val, p); } @@ -1196,6 +1240,8 @@ acpi_reset(void *priv) if (!strcmp(machines[machine].internal_name, "ficva503a")) dev->regs.gpi_val |= 0x00000004; } + + acpi_rtc_status = 0; } diff --git a/src/chipset/intel_sio.c b/src/chipset/intel_sio.c index cf587170a..d456c96df 100644 --- a/src/chipset/intel_sio.c +++ b/src/chipset/intel_sio.c @@ -105,7 +105,7 @@ sio_timer_read(uint16_t addr, void *priv) uint8_t ret = 0xff; if (!(addr & 0x0002)) { - sub_cycles((int)(PITCONST >> 32)); + cycles -= ((int) (PITCONST >> 32)); sio_timer_latch = timer_get_remaining_us(&dev->timer); @@ -126,7 +126,7 @@ sio_timer_readw(uint16_t addr, void *priv) uint16_t ret = 0xffff; if (!(addr & 0x0002)) { - sub_cycles((int)(PITCONST >> 32)); + cycles -= ((int) (PITCONST >> 32)); ret = timer_get_remaining_us(&dev->timer); } diff --git a/src/codegen/codegen_ops_x86-64.h b/src/codegen/codegen_ops_x86-64.h index ab68a535a..452f09937 100644 --- a/src/codegen/codegen_ops_x86-64.h +++ b/src/codegen/codegen_ops_x86-64.h @@ -1026,9 +1026,10 @@ static inline void MEM_LOAD_ADDR_EA_B(x86seg *seg) addbyte(0xeb); /*JMP done*/ addbyte(2+2+12+4+6); /*slowpath:*/ + addbyte(0x01); /*ADD ECX,EAX*/ + addbyte(0xc1); load_param_1_reg_32(REG_ECX); - load_param_2_reg_32(REG_EAX); - call_long((uintptr_t)readmemb386l); + call_long((uintptr_t)readmembl); addbyte(0x80); /*CMP abrt, 0*/ addbyte(0x7d); addbyte((uint8_t)cpu_state_offset(abrt)); @@ -1105,8 +1106,9 @@ static inline void MEM_LOAD_ADDR_EA_W(x86seg *seg) addbyte(0xeb); /*JMP done*/ addbyte(2+2+12+4+6); /*slowpath:*/ + addbyte(0x01); /*ADD ECX,EAX*/ + addbyte(0xc1); load_param_1_reg_32(REG_ECX); - load_param_2_reg_32(REG_EAX); call_long((uintptr_t)readmemwl); addbyte(0x80); /*CMP abrt, 0*/ addbyte(0x7d); @@ -1190,8 +1192,9 @@ static inline void MEM_LOAD_ADDR_EA_L(x86seg *seg) addbyte(0xeb); /*JMP done*/ addbyte(2+2+12+4+6); /*slowpath:*/ + addbyte(0x01); /*ADD ECX,EAX*/ + addbyte(0xc1); load_param_1_reg_32(REG_ECX); - load_param_2_reg_32(REG_EAX); call_long((uintptr_t)readmemll); addbyte(0x80); /*CMP abrt, 0*/ addbyte(0x7d); @@ -1269,8 +1272,9 @@ static inline void MEM_LOAD_ADDR_EA_Q(x86seg *seg) addbyte(0xeb); /*JMP done*/ addbyte(2+2+12+4+6); /*slowpath:*/ + addbyte(0x01); /*ADD ECX,EAX*/ + addbyte(0xc1); load_param_1_reg_32(REG_ECX); - load_param_2_reg_32(REG_EAX); call_long((uintptr_t)readmemql); addbyte(0x80); /*CMP abrt, 0*/ addbyte(0x7d); @@ -1394,10 +1398,11 @@ static inline void MEM_STORE_ADDR_EA_B(x86seg *seg, int host_reg) addbyte(0xeb); /*JMP done*/ addbyte(2+2+3+12+4+6); /*slowpath:*/ + addbyte(0x01); /*ADD ECX,EAX*/ + addbyte(0xc1); load_param_1_reg_32(REG_ECX); - load_param_2_reg_32(REG_EAX); - load_param_3_reg_32(host_reg); - call_long((uintptr_t)writememb386l); + load_param_2_reg_32(host_reg); + call_long((uintptr_t)writemembl); addbyte(0x80); /*CMP abrt, 0*/ addbyte(0x7d); addbyte((uint8_t)cpu_state_offset(abrt)); @@ -1485,9 +1490,10 @@ static inline void MEM_STORE_ADDR_EA_W(x86seg *seg, int host_reg) addbyte(0xeb); /*JMP done*/ addbyte(2+2+3+12+4+6); /*slowpath:*/ + addbyte(0x01); /*ADD ECX,EAX*/ + addbyte(0xc1); load_param_1_reg_32(REG_ECX); - load_param_2_reg_32(REG_EAX); - load_param_3_reg_32(host_reg); + load_param_2_reg_32(host_reg); call_long((uintptr_t)writememwl); addbyte(0x80); /*CMP abrt, 0*/ addbyte(0x7d); @@ -1574,9 +1580,10 @@ static inline void MEM_STORE_ADDR_EA_L(x86seg *seg, int host_reg) addbyte(0xeb); /*JMP done*/ addbyte(2+2+3+12+4+6); /*slowpath:*/ + addbyte(0x01); /*ADD ECX,EAX*/ + addbyte(0xc1); load_param_1_reg_32(REG_ECX); - load_param_2_reg_32(REG_EAX); - load_param_3_reg_32(host_reg); + load_param_2_reg_32(host_reg); call_long((uintptr_t)writememll); addbyte(0x80); /*CMP abrt, 0*/ addbyte(0x7d); @@ -1664,9 +1671,10 @@ static inline void MEM_STORE_ADDR_EA_Q(x86seg *seg, int host_reg, int host_reg2) addbyte(0xeb); /*JMP done*/ addbyte(2+2+3+12+4+6); /*slowpath:*/ + addbyte(0x01); /*ADD ECX,EAX*/ + addbyte(0xc1); load_param_1_reg_32(REG_ECX); - load_param_2_reg_32(REG_EAX); - load_param_3_reg_64(host_reg); + load_param_2_reg_64(host_reg); call_long((uintptr_t)writememql); addbyte(0x80); /*CMP abrt, 0*/ addbyte(0x7d); @@ -5763,9 +5771,10 @@ static inline int MEM_LOAD_ADDR_EA_B_NO_ABRT(x86seg *seg) addbyte(0xeb); /*JMP done*/ addbyte(2+2+12); /*slowpath:*/ + addbyte(0x01); /*ADD ECX,EAX*/ + addbyte(0xc1); load_param_1_reg_32(REG_ECX); - load_param_2_reg_32(REG_EAX); - call_long((uintptr_t)readmemb386l); + call_long((uintptr_t)readmembl); addbyte(0x89); /*MOV ECX, EAX*/ addbyte(0xc1); /*done:*/ @@ -5841,8 +5850,9 @@ static inline int MEM_LOAD_ADDR_EA_W_NO_ABRT(x86seg *seg) addbyte(0xeb); /*JMP done*/ addbyte(2+2+12); /*slowpath:*/ + addbyte(0x01); /*ADD ECX,EAX*/ + addbyte(0xc1); load_param_1_reg_32(REG_ECX); - load_param_2_reg_32(REG_EAX); call_long((uintptr_t)readmemwl); addbyte(0x89); /*MOV ECX, EAX*/ addbyte(0xc1); @@ -5918,8 +5928,9 @@ static inline int MEM_LOAD_ADDR_EA_L_NO_ABRT(x86seg *seg) addbyte(0xeb); /*JMP done*/ addbyte(2+2+12); /*slowpath:*/ + addbyte(0x01); /*ADD ECX,EAX*/ + addbyte(0xc1); load_param_1_reg_32(REG_ECX); - load_param_2_reg_32(REG_EAX); call_long((uintptr_t)readmemll); addbyte(0x89); /*MOV ECX, EAX*/ addbyte(0xc1); @@ -6023,10 +6034,11 @@ static inline void MEM_STORE_ADDR_EA_B_NO_ABRT(x86seg *seg, int host_reg) addbyte(0xeb); /*JMP done*/ addbyte(2+2+3+12); /*slowpath:*/ - load_param_3_reg_32(host_reg); - load_param_1_reg_32(REG_EBX); - load_param_2_reg_32(REG_EAX); - call_long((uintptr_t)writememb386l); + load_param_2_reg_32(host_reg); + addbyte(0x01); /*ADD ECX,EAX*/ + addbyte(0xc1); + load_param_1_reg_32(REG_ECX); + call_long((uintptr_t)writemembl); /*done:*/ } static inline void MEM_STORE_ADDR_EA_W_NO_ABRT(x86seg *seg, int host_reg) @@ -6107,9 +6119,10 @@ static inline void MEM_STORE_ADDR_EA_W_NO_ABRT(x86seg *seg, int host_reg) addbyte(0xeb); /*JMP done*/ addbyte(2+2+3+12); /*slowpath:*/ - load_param_3_reg_32(host_reg); - load_param_1_reg_32(REG_EBX); - load_param_2_reg_32(REG_EAX); + load_param_2_reg_32(host_reg); + addbyte(0x01); /*ADD ECX,EAX*/ + addbyte(0xc1); + load_param_1_reg_32(REG_ECX); call_long((uintptr_t)writememwl); /*done:*/ } @@ -6189,9 +6202,10 @@ static inline void MEM_STORE_ADDR_EA_L_NO_ABRT(x86seg *seg, int host_reg) addbyte(0xeb); /*JMP done*/ addbyte(2+2+3+12); /*slowpath:*/ - load_param_3_reg_32(host_reg); - load_param_1_reg_32(REG_EBX); - load_param_2_reg_32(REG_EAX); + load_param_2_reg_32(host_reg); + addbyte(0x01); /*ADD ECX,EAX*/ + addbyte(0xc1); + load_param_1_reg_32(REG_ECX); call_long((uintptr_t)writememll); /*done:*/ } diff --git a/src/codegen/codegen_x86.c b/src/codegen/codegen_x86.c index 8ad2222bd..31b3202e6 100644 --- a/src/codegen/codegen_x86.c +++ b/src/codegen/codegen_x86.c @@ -148,13 +148,14 @@ static uint32_t gen_MEM_LOAD_ADDR_EA_B() addbyte(0x3a); addbyte(0xc3); /*RET*/ - addbyte(0x50); /*slowpath: PUSH EAX*/ + addbyte(0x01); /*slowpath: ADD ESI,EAX*/ + addbyte(0xc6); addbyte(0x56); /*PUSH ESI*/ addbyte(0xe8); /*CALL readmembl*/ - addlong((uint32_t)readmemb386l - (uint32_t)(&codeblock[block_current].data[block_pos + 4])); - addbyte(0x83); /*ADD ESP, 8*/ + addlong((uint32_t)readmembl - (uint32_t)(&codeblock[block_current].data[block_pos + 4])); + addbyte(0x83); /*ADD ESP, 4*/ addbyte(0xc4); - addbyte(8); + addbyte(4); addbyte(0x80); /*CMP abrt, 0*/ addbyte(0x7d); addbyte((uint8_t)cpu_state_offset(abrt)); @@ -203,13 +204,14 @@ static uint32_t gen_MEM_LOAD_ADDR_EA_W() addbyte(0x3a); addbyte(0xc3); /*RET*/ - addbyte(0x50); /*slowpath: PUSH EAX*/ + addbyte(0x01); /*slowpath: ADD ESI,EAX*/ + addbyte(0xc6); addbyte(0x56); /*PUSH ESI*/ addbyte(0xe8); /*CALL readmemwl*/ addlong((uint32_t)readmemwl - (uint32_t)(&codeblock[block_current].data[block_pos + 4])); - addbyte(0x83); /*ADD ESP, 8*/ + addbyte(0x83); /*ADD ESP, 4*/ addbyte(0xc4); - addbyte(8); + addbyte(4); addbyte(0x80); /*CMP abrt, 0*/ addbyte(0x7d); addbyte((uint8_t)cpu_state_offset(abrt)); @@ -257,13 +259,14 @@ static uint32_t gen_MEM_LOAD_ADDR_EA_L() addbyte(0x3a); addbyte(0xc3); /*RET*/ - addbyte(0x50); /*slowpath: PUSH EAX*/ + addbyte(0x01); /*slowpath: ADD ESI,EAX*/ + addbyte(0xc6); addbyte(0x56); /*PUSH ESI*/ addbyte(0xe8); /*CALL readmemll*/ addlong((uint32_t)readmemll - (uint32_t)(&codeblock[block_current].data[block_pos + 4])); - addbyte(0x83); /*ADD ESP, 8*/ + addbyte(0x83); /*ADD ESP, 4*/ addbyte(0xc4); - addbyte(8); + addbyte(4); addbyte(0x80); /*CMP abrt, 0*/ addbyte(0x7d); addbyte((uint8_t)cpu_state_offset(abrt)); @@ -312,13 +315,14 @@ static uint32_t gen_MEM_LOAD_ADDR_EA_Q() addbyte(4); addbyte(0xc3); /*RET*/ - addbyte(0x50); /*slowpath: PUSH EAX*/ + addbyte(0x01); /*slowpath: ADD ESI,EAX*/ + addbyte(0xc6); addbyte(0x56); /*PUSH ESI*/ addbyte(0xe8); /*CALL readmemql*/ addlong((uint32_t)readmemql - (uint32_t)(&codeblock[block_current].data[block_pos + 4])); - addbyte(0x83); /*ADD ESP, 8*/ + addbyte(0x83); /*ADD ESP, 4*/ addbyte(0xc4); - addbyte(8); + addbyte(4); addbyte(0x80); /*CMP abrt, 0*/ addbyte(0x7d); addbyte((uint8_t)cpu_state_offset(abrt)); @@ -360,13 +364,14 @@ static uint32_t gen_MEM_STORE_ADDR_EA_B() addbyte(0xc3); /*RET*/ addbyte(0x51); /*slowpath: PUSH ECX*/ - addbyte(0x50); /*PUSH EAX*/ + addbyte(0x01); /*ADD EBX,EAX*/ + addbyte(0xC3); addbyte(0x53); /*PUSH EBX*/ - addbyte(0xe8); /*CALL writememb386l*/ - addlong((uint32_t)writememb386l - (uint32_t)(&codeblock[block_current].data[block_pos + 4])); - addbyte(0x83); /*ADD ESP, 12*/ + addbyte(0xe8); /*CALL writemembl*/ + addlong((uint32_t)writemembl - (uint32_t)(&codeblock[block_current].data[block_pos + 4])); + addbyte(0x83); /*ADD ESP, 8*/ addbyte(0xc4); - addbyte(12); + addbyte(8); addbyte(0x80); /*CMP abrt, 0*/ addbyte(0x7d); addbyte((uint8_t)cpu_state_offset(abrt)); @@ -414,13 +419,14 @@ static uint32_t gen_MEM_STORE_ADDR_EA_W() addbyte(0xc3); /*RET*/ addbyte(0x51); /*slowpath: PUSH ECX*/ - addbyte(0x50); /*PUSH EAX*/ + addbyte(0x01); /*ADD EBX,EAX*/ + addbyte(0xC3); addbyte(0x53); /*PUSH EBX*/ addbyte(0xe8); /*CALL writememwl*/ addlong((uint32_t)writememwl - (uint32_t)(&codeblock[block_current].data[block_pos + 4])); - addbyte(0x83); /*ADD ESP, 12*/ + addbyte(0x83); /*ADD ESP, 8*/ addbyte(0xc4); - addbyte(12); + addbyte(8); addbyte(0x80); /*CMP abrt, 0*/ addbyte(0x7d); addbyte((uint8_t)cpu_state_offset(abrt)); @@ -467,13 +473,14 @@ static uint32_t gen_MEM_STORE_ADDR_EA_L() addbyte(0xc3); /*RET*/ addbyte(0x51); /*slowpath: PUSH ECX*/ - addbyte(0x50); /*PUSH EAX*/ + addbyte(0x01); /*ADD EBX,EAX*/ + addbyte(0xC3); addbyte(0x53); /*PUSH EBX*/ addbyte(0xe8); /*CALL writememll*/ addlong((uint32_t)writememll - (uint32_t)(&codeblock[block_current].data[block_pos + 4])); - addbyte(0x83); /*ADD ESP, 12*/ + addbyte(0x83); /*ADD ESP, 8*/ addbyte(0xc4); - addbyte(12); + addbyte(8); addbyte(0x80); /*CMP abrt, 0*/ addbyte(0x7d); addbyte((uint8_t)cpu_state_offset(abrt)); @@ -525,13 +532,14 @@ static uint32_t gen_MEM_STORE_ADDR_EA_Q() addbyte(0x51); /*slowpath: PUSH ECX*/ addbyte(0x53); /*PUSH EBX*/ - addbyte(0x50); /*PUSH EAX*/ + addbyte(0x01); /*ADD EDX,EAX*/ + addbyte(0xC2); addbyte(0x52); /*PUSH EDX*/ addbyte(0xe8); /*CALL writememql*/ addlong((uint32_t)writememql - (uint32_t)(&codeblock[block_current].data[block_pos + 4])); - addbyte(0x83); /*ADD ESP, 16*/ + addbyte(0x83); /*ADD ESP, 12*/ addbyte(0xc4); - addbyte(16); + addbyte(12); addbyte(0x80); /*CMP abrt, 0*/ addbyte(0x7d); addbyte((uint8_t)cpu_state_offset(abrt)); @@ -575,13 +583,14 @@ static uint32_t gen_MEM_LOAD_ADDR_EA_B_NO_ABRT() addbyte(0x3a); addbyte(0xc3); /*RET*/ - addbyte(0x50); /*slowpath: PUSH EAX*/ + addbyte(0x01); /*slowpath: ADD ESI,EAX*/ + addbyte(0xc6); addbyte(0x56); /*PUSH ESI*/ addbyte(0xe8); /*CALL readmembl*/ - addlong((uint32_t)readmemb386l - (uint32_t)(&codeblock[block_current].data[block_pos + 4])); - addbyte(0x83); /*ADD ESP, 8*/ + addlong((uint32_t)readmembl - (uint32_t)(&codeblock[block_current].data[block_pos + 4])); + addbyte(0x83); /*ADD ESP, 4*/ addbyte(0xc4); - addbyte(8); + addbyte(4); #ifndef RELEASE_BUILD addbyte(0x80); /*CMP abrt, 0*/ addbyte(0x7d); @@ -644,13 +653,14 @@ static uint32_t gen_MEM_LOAD_ADDR_EA_W_NO_ABRT() addbyte(0x3a); addbyte(0xc3); /*RET*/ - addbyte(0x50); /*slowpath: PUSH EAX*/ + addbyte(0x01); /*slowpath: ADD ESI,EAX*/ + addbyte(0xc6); addbyte(0x56); /*PUSH ESI*/ addbyte(0xe8); /*CALL readmemwl*/ addlong((uint32_t)readmemwl - (uint32_t)(&codeblock[block_current].data[block_pos + 4])); - addbyte(0x83); /*ADD ESP, 8*/ + addbyte(0x83); /*ADD ESP, 4*/ addbyte(0xc4); - addbyte(8); + addbyte(4); #ifndef RELEASE_BUILD addbyte(0x80); /*CMP abrt, 0*/ addbyte(0x7d); @@ -712,13 +722,14 @@ static uint32_t gen_MEM_LOAD_ADDR_EA_L_NO_ABRT() addbyte(0x3a); addbyte(0xc3); /*RET*/ - addbyte(0x50); /*slowpath: PUSH EAX*/ + addbyte(0x01); /*slowpath: ADD ESI,EAX*/ + addbyte(0xc6); addbyte(0x56); /*PUSH ESI*/ addbyte(0xe8); /*CALL readmemll*/ addlong((uint32_t)readmemll - (uint32_t)(&codeblock[block_current].data[block_pos + 4])); - addbyte(0x83); /*ADD ESP, 8*/ + addbyte(0x83); /*ADD ESP, 4*/ addbyte(0xc4); - addbyte(8); + addbyte(4); addbyte(0x89); /*MOV ECX, EAX*/ addbyte(0xc1); #ifndef RELEASE_BUILD @@ -777,13 +788,14 @@ static uint32_t gen_MEM_STORE_ADDR_EA_B_NO_ABRT() addbyte(0xc3); /*RET*/ addbyte(0x51); /*slowpath: PUSH ECX*/ - addbyte(0x50); /*PUSH EAX*/ + addbyte(0x01); /*ADD EBX,EAX*/ + addbyte(0xc3); addbyte(0x53); /*PUSH EBX*/ - addbyte(0xe8); /*CALL writememb386l*/ - addlong((uint32_t)writememb386l - (uint32_t)(&codeblock[block_current].data[block_pos + 4])); - addbyte(0x83); /*ADD ESP, 12*/ + addbyte(0xe8); /*CALL writemembl*/ + addlong((uint32_t)writemembl - (uint32_t)(&codeblock[block_current].data[block_pos + 4])); + addbyte(0x83); /*ADD ESP, 8*/ addbyte(0xc4); - addbyte(12); + addbyte(8); #ifndef RELEASE_BUILD addbyte(0x80); /*CMP abrt, 0*/ addbyte(0x7d); @@ -843,13 +855,14 @@ static uint32_t gen_MEM_STORE_ADDR_EA_W_NO_ABRT() addbyte(0xc3); /*RET*/ addbyte(0x51); /*slowpath: PUSH ECX*/ - addbyte(0x50); /*PUSH EAX*/ + addbyte(0x01); /*ADD EBX,EAX*/ + addbyte(0xC3); addbyte(0x53); /*PUSH EBX*/ addbyte(0xe8); /*CALL writememwl*/ addlong((uint32_t)writememwl - (uint32_t)(&codeblock[block_current].data[block_pos + 4])); - addbyte(0x83); /*ADD ESP, 12*/ + addbyte(0x83); /*ADD ESP, 8*/ addbyte(0xc4); - addbyte(12); + addbyte(8); #ifndef RELEASE_BUILD addbyte(0x80); /*CMP abrt, 0*/ addbyte(0x7d); @@ -908,13 +921,14 @@ static uint32_t gen_MEM_STORE_ADDR_EA_L_NO_ABRT() addbyte(0xc3); /*RET*/ addbyte(0x51); /*slowpath: PUSH ECX*/ - addbyte(0x50); /*PUSH EAX*/ + addbyte(0x01); /*ADD EBX,EAX*/ + addbyte(0xC3); addbyte(0x53); /*PUSH EBX*/ addbyte(0xe8); /*CALL writememll*/ addlong((uint32_t)writememll - (uint32_t)(&codeblock[block_current].data[block_pos + 4])); - addbyte(0x83); /*ADD ESP, 12*/ + addbyte(0x83); /*ADD ESP, 8*/ addbyte(0xc4); - addbyte(12); + addbyte(8); #ifndef RELEASE_BUILD addbyte(0x80); /*CMP abrt, 0*/ addbyte(0x7d); diff --git a/src/config.c b/src/config.c index d36e11b26..a2aa1b19d 100644 --- a/src/config.c +++ b/src/config.c @@ -113,6 +113,7 @@ static list_t config_head; /* TODO: Backwards compatibility, get rid of this when enough time has passed. */ static int backwards_compat = 0; +static int backwards_compat2 = 0; #ifdef ENABLE_CONFIG_LOG @@ -487,6 +488,7 @@ load_general(void) confirm_reset = config_get_int(cat, "confirm_reset", 1); confirm_exit = config_get_int(cat, "confirm_exit", 1); + confirm_save = config_get_int(cat, "confirm_save", 1); #ifdef USE_LANGUAGE /* @@ -872,14 +874,16 @@ load_ports(void) } -/* Load "Other Peripherals" section. */ +/* Load "Storage Controllers" section. */ static void -load_other_peripherals(void) +load_storage_controllers(void) { - char *cat = "Other peripherals"; + char *cat = "Storage controllers"; char *p; - char temp[512]; - int c, free_p = 0; + int free_p = 0; + + /* TODO: Backwards compatibility, get rid of this when enough time has passed. */ + backwards_compat2 = (find_section(cat) == NULL); p = config_get_string(cat, "scsicard", NULL); if (p != NULL) @@ -920,19 +924,6 @@ load_other_peripherals(void) ide_ter_enabled = !!config_get_int(cat, "ide_ter", 0); ide_qua_enabled = !!config_get_int(cat, "ide_qua", 0); - - bugger_enabled = !!config_get_int(cat, "bugger_enabled", 0); - postcard_enabled = !!config_get_int(cat, "postcard_enabled", 0); - - for (c = 0; c < ISAMEM_MAX; c++) { - sprintf(temp, "isamem%d_type", c); - - p = config_get_string(cat, temp, "none"); - isamem_type[c] = isamem_get_from_internal_name(p); - } - - p = config_get_string(cat, "isartc_type", "none"); - isartc_type = isartc_get_from_internal_name(p); } @@ -1061,6 +1052,11 @@ load_hard_disks(void) * files. We should remove this before * finalizing this release! --FvK */ + /* + * ANOTHER NOTE: + * When loading differencing VHDs, the absolute path is required. + * So we should not convert absolute paths to relative. -sards + */ if (! wcsnicmp(wp, usr_path, wcslen(usr_path))) { /* * Yep, its absolute and prefixed @@ -1512,7 +1508,6 @@ load_other_removable_devices(void) sprintf(temp, "zip_%02i_iso_path", c+1); config_delete_var(cat, temp); } - memset(temp, 0x00, sizeof(temp)); for (c=0; c> 3)); -#else - d = readmemb386l(0, tr.base + t + (port >> 3)); -#endif cpl_override = 0; return d & (1 << (port & 7)); } diff --git a/src/cpu/386_common.h b/src/cpu/386_common.h index baf5a63e1..ed4bc84d7 100644 --- a/src/cpu/386_common.h +++ b/src/cpu/386_common.h @@ -21,50 +21,20 @@ #include -#ifdef USE_NEW_DYNAREC -#define readmemb(s,a) ((readlookup2[(uint32_t)((s)+(a))>>12]==LOOKUP_INV)?readmembl((s)+(a)): *(uint8_t *)(readlookup2[(uint32_t)((s)+(a))>>12] + (uintptr_t)((s) + (a)))) -#define readmemw(s,a) ((readlookup2[(uint32_t)((s)+(a))>>12]==LOOKUP_INV || (((s)+(a)) & 1))?readmemwl((s)+(a)):*(uint16_t *)(readlookup2[(uint32_t)((s)+(a))>>12]+(uintptr_t)((s)+(a)))) -#define readmeml(s,a) ((readlookup2[(uint32_t)((s)+(a))>>12]==LOOKUP_INV || (((s)+(a)) & 3))?readmemll((s)+(a)):*(uint32_t *)(readlookup2[(uint32_t)((s)+(a))>>12]+(uintptr_t)((s)+(a)))) -#define readmemq(s,a) ((readlookup2[(uint32_t)((s)+(a))>>12]==LOOKUP_INV || (((s)+(a)) & 7))?readmemql((s)+(a)):*(uint64_t *)(readlookup2[(uint32_t)((s)+(a))>>12]+(uintptr_t)((s)+(a)))) +#define readmemb(s,a) ((readlookup2[(uint32_t)((s)+(a))>>12]==LOOKUP_INV || (s)==0xFFFFFFFF)?readmembl((s)+(a)): *(uint8_t *)(readlookup2[(uint32_t)((s)+(a))>>12] + (uintptr_t)((s) + (a))) ) +#define readmemw(s,a) ((readlookup2[(uint32_t)((s)+(a))>>12]==LOOKUP_INV || (s)==0xFFFFFFFF || (((s)+(a)) & 1))?readmemwl((s)+(a)):*(uint16_t *)(readlookup2[(uint32_t)((s)+(a))>>12]+(uint32_t)((s)+(a)))) +#define readmeml(s,a) ((readlookup2[(uint32_t)((s)+(a))>>12]==LOOKUP_INV || (s)==0xFFFFFFFF || (((s)+(a)) & 3))?readmemll((s)+(a)):*(uint32_t *)(readlookup2[(uint32_t)((s)+(a))>>12]+(uint32_t)((s)+(a)))) +#define readmemq(s,a) ((readlookup2[(uint32_t)((s)+(a))>>12]==LOOKUP_INV || (s)==0xFFFFFFFF || (((s)+(a)) & 7))?readmemql((s)+(a)):*(uint64_t *)(readlookup2[(uint32_t)((s)+(a))>>12]+(uintptr_t)((s)+(a)))) -#define writememb(s,a,v) if (writelookup2[(uint32_t)((s)+(a))>>12]==LOOKUP_INV) writemembl((s)+(a),v); else *(uint8_t *)(writelookup2[(uint32_t)((s) + (a)) >> 12] + (uintptr_t)((s) + (a))) = v -#define writememw(s,a,v) if (writelookup2[(uint32_t)((s)+(a))>>12]==LOOKUP_INV || (((s)+(a)) & 1)) writememwl((s)+(a),v); else *(uint16_t *)(writelookup2[(uint32_t)((s) + (a)) >> 12] + (uintptr_t)((s) + (a))) = v -#define writememl(s,a,v) if (writelookup2[(uint32_t)((s)+(a))>>12]==LOOKUP_INV || (((s)+(a)) & 3)) writememll((s)+(a),v); else *(uint32_t *)(writelookup2[(uint32_t)((s) + (a)) >> 12] + (uintptr_t)((s) + (a))) = v -#define writememq(s,a,v) if (writelookup2[(uint32_t)((s)+(a))>>12]==LOOKUP_INV || (((s)+(a)) & 7)) writememql((s)+(a),v); else *(uint64_t *)(writelookup2[(uint32_t)((s) + (a)) >> 12] + (uintptr_t)((s) + (a))) = v -#else -#undef readmemb -#undef writememb - - -#define readmemb(s,a) ((readlookup2[(uint32_t)((s)+(a))>>12]==LOOKUP_INV || (s)==0xFFFFFFFF)?readmemb386l(s,a): *(uint8_t *)(readlookup2[(uint32_t)((s)+(a))>>12] + (uintptr_t)((s) + (a))) ) -#define readmemq(s,a) ((readlookup2[(uint32_t)((s)+(a))>>12]==LOOKUP_INV || (s)==0xFFFFFFFF || (((s)+(a)) & 7))?readmemql(s,a):*(uint64_t *)(readlookup2[(uint32_t)((s)+(a))>>12]+(uintptr_t)((s)+(a)))) - -#define writememb(s,a,v) if (writelookup2[(uint32_t)((s)+(a))>>12]==LOOKUP_INV || (s)==0xFFFFFFFF) writememb386l(s,a,v); else *(uint8_t *)(writelookup2[(uint32_t)((s) + (a)) >> 12] + (uintptr_t)((s) + (a))) = v - -#define writememw(s,a,v) if (writelookup2[(uint32_t)((s)+(a))>>12]==LOOKUP_INV || (s)==0xFFFFFFFF || (((s)+(a)) & 1)) writememwl(s,a,v); else *(uint16_t *)(writelookup2[(uint32_t)((s) + (a)) >> 12] + (uintptr_t)((s) + (a))) = v -#define writememl(s,a,v) if (writelookup2[(uint32_t)((s)+(a))>>12]==LOOKUP_INV || (s)==0xFFFFFFFF || (((s)+(a)) & 3)) writememll(s,a,v); else *(uint32_t *)(writelookup2[(uint32_t)((s) + (a)) >> 12] + (uintptr_t)((s) + (a))) = v -#define writememq(s,a,v) if (writelookup2[(uint32_t)((s)+(a))>>12]==LOOKUP_INV || (s)==0xFFFFFFFF || (((s)+(a)) & 7)) writememql(s,a,v); else *(uint64_t *)(writelookup2[(uint32_t)((s) + (a)) >> 12] + (uintptr_t)((s) + (a))) = v -#endif +#define writememb(s,a,v) if (writelookup2[(uint32_t)((s)+(a))>>12]==LOOKUP_INV || (s)==0xFFFFFFFF) writemembl((s)+(a),v); else *(uint8_t *)(writelookup2[(uint32_t)((s) + (a)) >> 12] + (uintptr_t)((s) + (a))) = v +#define writememw(s,a,v) if (writelookup2[(uint32_t)((s)+(a))>>12]==LOOKUP_INV || (s)==0xFFFFFFFF || (((s)+(a)) & 1)) writememwl((s)+(a),v); else *(uint16_t *)(writelookup2[(uint32_t)((s) + (a)) >> 12] + (uintptr_t)((s) + (a))) = v +#define writememl(s,a,v) if (writelookup2[(uint32_t)((s)+(a))>>12]==LOOKUP_INV || (s)==0xFFFFFFFF || (((s)+(a)) & 3)) writememll((s)+(a),v); else *(uint32_t *)(writelookup2[(uint32_t)((s) + (a)) >> 12] + (uintptr_t)((s) + (a))) = v +#define writememq(s,a,v) if (writelookup2[(uint32_t)((s)+(a))>>12]==LOOKUP_INV || (s)==0xFFFFFFFF || (((s)+(a)) & 7)) writememql((s)+(a),v); else *(uint64_t *)(writelookup2[(uint32_t)((s) + (a)) >> 12] + (uintptr_t)((s) + (a))) = v int checkio(int port); -#ifdef USE_NEW_DYNAREC -#define check_io_perm(port) if (!IOPLp || (cpu_state.eflags&VM_FLAG)) \ - { \ - int tempi = checkio(port); \ - if (cpu_state.abrt) return 1; \ - if (tempi) \ - { \ - if (cpu_state.eflags & VM_FLAG) \ - x86gpf_expected(NULL,0); \ - else \ - x86gpf(NULL,0); \ - return 1; \ - } \ - } -#else #define check_io_perm(port) if (msw&1 && ((CPL > IOPL) || (cpu_state.eflags&VM_FLAG))) \ { \ int tempi = checkio(port); \ @@ -78,7 +48,6 @@ int checkio(int port); return 1; \ } \ } -#endif #define SEG_CHECK_READ(seg) \ do \ @@ -347,32 +316,16 @@ static __inline void seteaq(uint64_t v) { if (seteaq_cwc()) return; -#ifdef USE_NEW_DYNAREC writememql(easeg + cpu_state.eaaddr, v); -#else - writememql(easeg, cpu_state.eaaddr, v); -#endif } -#ifdef USE_NEW_DYNAREC #define seteab(v) if (cpu_mod!=3) { CHECK_WRITE_COMMON(cpu_state.ea_seg, cpu_state.eaaddr, cpu_state.eaaddr); if (eal_w) *(uint8_t *)eal_w=v; else writemembl(easeg+cpu_state.eaaddr,v); } else if (cpu_rm&4) cpu_state.regs[cpu_rm&3].b.h=v; else cpu_state.regs[cpu_rm].b.l=v -#define seteaw(v) if (cpu_mod!=3) { CHECK_WRITE_COMMON(cpu_state.ea_seg, cpu_state.eaaddr, cpu_state.eaaddr + 1); if (eal_w) *(uint16_t *)eal_w=v; else writememwl(easeg+cpu_state.eaaddr,v); } else cpu_state.regs[cpu_rm].w=v -#define seteal(v) if (cpu_mod!=3) { CHECK_WRITE_COMMON(cpu_state.ea_seg, cpu_state.eaaddr, cpu_state.eaaddr + 3); if (eal_w) *eal_w=v; else writememll(easeg+cpu_state.eaaddr,v); } else cpu_state.regs[cpu_rm].l=v -#else -#define seteab(v) if (cpu_mod!=3) { CHECK_WRITE_COMMON(cpu_state.ea_seg, cpu_state.eaaddr, cpu_state.eaaddr); if (eal_w) *(uint8_t *)eal_w=v; else { writememb386l(easeg,cpu_state.eaaddr,v); } } else if (cpu_rm&4) cpu_state.regs[cpu_rm&3].b.h=v; else cpu_state.regs[cpu_rm].b.l=v -#define seteaw(v) if (cpu_mod!=3) { CHECK_WRITE_COMMON(cpu_state.ea_seg, cpu_state.eaaddr, cpu_state.eaaddr + 1); if (eal_w) *(uint16_t *)eal_w=v; else { writememwl(easeg,cpu_state.eaaddr,v); } } else cpu_state.regs[cpu_rm].w=v -#define seteal(v) if (cpu_mod!=3) { CHECK_WRITE_COMMON(cpu_state.ea_seg, cpu_state.eaaddr, cpu_state.eaaddr + 3); if (eal_w) *eal_w=v; else { writememll(easeg,cpu_state.eaaddr,v); } } else cpu_state.regs[cpu_rm].l=v -#endif +#define seteaw(v) if (cpu_mod!=3) { CHECK_WRITE_COMMON(cpu_state.ea_seg, cpu_state.eaaddr, cpu_state.eaaddr + 1); if (eal_w) *(uint16_t *)eal_w=v; else writememwl(easeg+cpu_state.eaaddr,v); } else cpu_state.regs[cpu_rm].w=v +#define seteal(v) if (cpu_mod!=3) { CHECK_WRITE_COMMON(cpu_state.ea_seg, cpu_state.eaaddr, cpu_state.eaaddr + 3); if (eal_w) *eal_w=v; else writememll(easeg+cpu_state.eaaddr,v); } else cpu_state.regs[cpu_rm].l=v -#ifdef USE_NEW_DYNAREC #define seteab_mem(v) if (eal_w) *(uint8_t *)eal_w=v; else writemembl(easeg+cpu_state.eaaddr,v); #define seteaw_mem(v) if (eal_w) *(uint16_t *)eal_w=v; else writememwl(easeg+cpu_state.eaaddr,v); #define seteal_mem(v) if (eal_w) *eal_w=v; else writememll(easeg+cpu_state.eaaddr,v); -#else -#define seteab_mem(v) if (eal_w) *(uint8_t *)eal_w=v; else writememb386l(easeg,cpu_state.eaaddr,v); -#define seteaw_mem(v) if (eal_w) *(uint16_t *)eal_w=v; else writememwl(easeg,cpu_state.eaaddr,v); -#define seteal_mem(v) if (eal_w) *eal_w=v; else writememll(easeg,cpu_state.eaaddr,v); -#endif #define getbytef() ((uint8_t)(fetchdat)); cpu_state.pc++ #define getwordf() ((uint16_t)(fetchdat)); cpu_state.pc+=2 diff --git a/src/cpu/386_dynarec.c b/src/cpu/386_dynarec.c index 663770bc3..4a8b3637e 100644 --- a/src/cpu/386_dynarec.c +++ b/src/cpu/386_dynarec.c @@ -349,6 +349,12 @@ exec386_dynarec_int(void) if (((cs + cpu_state.pc) >> 12) != pccache) CPU_BLOCK_END(); + if (cpu_end_block_after_ins) { + cpu_end_block_after_ins--; + if (!cpu_end_block_after_ins) + CPU_BLOCK_END(); + } + if (cpu_state.abrt) CPU_BLOCK_END(); if (smi_line) @@ -357,14 +363,8 @@ exec386_dynarec_int(void) CPU_BLOCK_END(); else if (nmi && nmi_enable && nmi_mask) CPU_BLOCK_END(); - else if ((cpu_state.flags & I_FLAG) && pic.int_pending) + else if ((cpu_state.flags & I_FLAG) && pic.int_pending && !cpu_end_block_after_ins) CPU_BLOCK_END(); - - if (cpu_end_block_after_ins) { - cpu_end_block_after_ins--; - if (!cpu_end_block_after_ins) - CPU_BLOCK_END(); - } } if (trap) { @@ -585,21 +585,21 @@ exec386_dynarec_dyn(void) #endif CPU_BLOCK_END(); + if (cpu_end_block_after_ins) { + cpu_end_block_after_ins--; + if (!cpu_end_block_after_ins) + CPU_BLOCK_END(); + } + if (smi_line) CPU_BLOCK_END(); else if (cpu_state.flags & T_FLAG) CPU_BLOCK_END(); else if (nmi && nmi_enable && nmi_mask) CPU_BLOCK_END(); - else if ((cpu_state.flags & I_FLAG) && pic.int_pending) + else if ((cpu_state.flags & I_FLAG) && pic.int_pending && !cpu_end_block_after_ins) CPU_BLOCK_END(); - if (cpu_end_block_after_ins) { - cpu_end_block_after_ins--; - if (!cpu_end_block_after_ins) - CPU_BLOCK_END(); - } - if (cpu_state.abrt) { if (!(cpu_state.abrt & ABRT_EXPECTED)) codegen_block_remove(); @@ -683,7 +683,7 @@ exec386_dynarec_dyn(void) CPU_BLOCK_END(); else if (nmi && nmi_enable && nmi_mask) CPU_BLOCK_END(); - else if ((cpu_state.flags & I_FLAG) && pic.int_pending) + else if ((cpu_state.flags & I_FLAG) && pic.int_pending && !cpu_end_block_after_ins) CPU_BLOCK_END(); if (cpu_end_block_after_ins) { @@ -756,19 +756,6 @@ exec386_dynarec(int cycs) exec386_dynarec_dyn(); } - cycdiff = oldcyc - cycles; - delta = tsc - oldtsc; - if (delta > 0) { - /* TSC has changed, this means interim timer processing has happened, - see how much we still need to add. */ - cycdiff -= delta; - if (cycdiff > 0) - tsc += cycdiff; - } else { - /* TSC has not changed. */ - tsc += cycdiff; - } - if (cpu_state.abrt) { flags_rebuild(); tempi = cpu_state.abrt & ABRT_MASK; @@ -819,6 +806,19 @@ exec386_dynarec(int cycs) } } + cycdiff = oldcyc - cycles; + delta = tsc - oldtsc; + if (delta > 0) { + /* TSC has changed, this means interim timer processing has happened, + see how much we still need to add. */ + cycdiff -= delta; + if (cycdiff > 0) + tsc += cycdiff; + } else { + /* TSC has not changed. */ + tsc += cycdiff; + } + if (cycdiff > 0) { if (TIMER_VAL_LESS_THAN_VAL(timer_target, (uint32_t) tsc)) timer_process_inline(); diff --git a/src/cpu/808x.c b/src/cpu/808x.c index 7e55a1b3a..bb579f473 100644 --- a/src/cpu/808x.c +++ b/src/cpu/808x.c @@ -271,6 +271,19 @@ sub_cycles(int c) } +void +resub_cycles(int old_cycles) +{ + int cyc_diff = 0; + + if (old_cycles > cycles) { + cyc_diff = old_cycles - cycles; + cycles = old_cycles; + sub_cycles(cyc_diff); + } +} + + #undef readmemb #undef readmemw #undef readmeml @@ -280,31 +293,43 @@ sub_cycles(int c) static void cpu_io(int bits, int out, uint16_t port) { + int old_cycles = cycles; + if (out) { wait(4, 1); if (bits == 16) { - if (is8086 && !(port & 1)) + if (is8086 && !(port & 1)) { + old_cycles = cycles; outw(port, AX); - else { + } else { wait(4, 1); + old_cycles = cycles; outb(port++, AL); outb(port, AH); } - } else + } else { + old_cycles = cycles; outb(port, AL); + } } else { wait(4, 1); if (bits == 16) { - if (is8086 && !(port & 1)) + if (is8086 && !(port & 1)) { + old_cycles = cycles; AX = inw(port); - else { + } else { wait(4, 1); + old_cycles = cycles; AL = inb(port++); AH = inb(port); } - } else + } else { + old_cycles = cycles; AL = inb(port); + } } + + resub_cycles(old_cycles); } @@ -1182,9 +1207,11 @@ check_interrupts(void) wait(3, 0); /* ACK to PIC */ temp = pic_irq_ack(); + wait(4, 1); wait(1, 0); /* ACK to PIC */ temp = pic_irq_ack(); + wait(4, 1); wait(1, 0); in_lock = 0; clear_lock = 0; diff --git a/src/cpu/cpu.c b/src/cpu/cpu.c index 536f52a84..5cb6f2c31 100644 --- a/src/cpu/cpu.c +++ b/src/cpu/cpu.c @@ -61,7 +61,6 @@ #endif #include "x87_timings.h" - #define CCR1_USE_SMI (1 << 1) #define CCR1_SMAC (1 << 2) #define CCR1_SM3 (1 << 7) @@ -239,6 +238,7 @@ uint64_t ecx1002ff_msr = 0; /* Some weird long MSR's used by i686 AMI & some Phoenix BIOSes */ uint64_t ecxf0f00250_msr = 0; uint64_t ecxf0f00258_msr = 0; +uint64_t ecxf0f00259_msr = 0; uint64_t star = 0; /* AMD K6-2+. */ @@ -3187,6 +3187,10 @@ void cpu_RDMSR() EAX = ecxf0f00258_msr & 0xffffffff; EDX = ecxf0f00258_msr >> 32; break; + case 0xf0f00259: + EAX = ecxf0f00259_msr & 0xffffffff; + EDX = ecxf0f00259_msr >> 32; + break; default: i686_invalid_rdmsr: cpu_log("RDMSR: Invalid MSR: %08X\n", ECX); @@ -3664,6 +3668,9 @@ void cpu_WRMSR() case 0xf0f00258: ecxf0f00258_msr = EAX | ((uint64_t)EDX << 32); break; + case 0xf0f00259: + ecxf0f00259_msr = EAX | ((uint64_t)EDX << 32); + break; default: i686_invalid_wrmsr: cpu_log("WRMSR: Invalid MSR: %08X\n", ECX); diff --git a/src/cpu/cpu_table.c b/src/cpu/cpu_table.c index 93349437e..ae4c3d60c 100644 --- a/src/cpu/cpu_table.c +++ b/src/cpu/cpu_table.c @@ -600,9 +600,9 @@ const cpu_family_t cpu_families[] = { .name = "Pentium OverDrive", .internal_name = "pentium_p54c_od3v", .cpus = { - {"125", CPU_PENTIUM, fpus_internal, 125000000, 3.0, 3520, 0x52c, 0x52c, 0, CPU_SUPPORTS_DYNAREC | CPU_REQUIRES_DYNAREC | CPU_FIXED_MULTIPLIER, 12,12,7,7, 16}, + {"125", CPU_PENTIUM, fpus_internal, 125000000, 3.0, 3520, 0x52c, 0x52c, 0, CPU_SUPPORTS_DYNAREC | CPU_REQUIRES_DYNAREC | CPU_FIXED_MULTIPLIER, 12,12,7,7, 15}, {"150", CPU_PENTIUM, fpus_internal, 150000000, 2.5, 3520, 0x52c, 0x52c, 0, CPU_SUPPORTS_DYNAREC | CPU_REQUIRES_DYNAREC | CPU_FIXED_MULTIPLIER, 15,15,7,7, 35/2}, - {"166", CPU_PENTIUM, fpus_internal, 166666666, 2.5, 3520, 0x52c, 0x52c, 0, CPU_SUPPORTS_DYNAREC | CPU_REQUIRES_DYNAREC | CPU_FIXED_MULTIPLIER, 15,15,7,7, 40}, + {"166", CPU_PENTIUM, fpus_internal, 166666666, 2.5, 3520, 0x52c, 0x52c, 0, CPU_SUPPORTS_DYNAREC | CPU_REQUIRES_DYNAREC | CPU_FIXED_MULTIPLIER, 15,15,7,7, 20}, {"", 0} } }, { diff --git a/src/cpu/x86seg.c b/src/cpu/x86seg.c index b15e4bd46..64ea5dd6f 100644 --- a/src/cpu/x86seg.c +++ b/src/cpu/x86seg.c @@ -808,8 +808,8 @@ void PUSHL(uint32_t v) { if (cpu_16bitbus) { - PUSHW(v & 0xffff); PUSHW(v >> 16); + PUSHW(v & 0xffff); } else { if (stack32) { writememl(ss, ESP - 4, v); diff --git a/src/device/isartc.c b/src/device/isartc.c index a14d7e82d..610bf0eae 100644 --- a/src/device/isartc.c +++ b/src/device/isartc.c @@ -388,7 +388,7 @@ mm67_read(uint16_t port, void *priv) uint8_t ret = 0xff; /* This chip is directly mapped on I/O. */ - sub_cycles(ISA_CYCLES(4)); + cycles -= ISA_CYCLES(4); switch(reg) { case MM67_ISTAT: /* IRQ status (RO) */ @@ -424,7 +424,7 @@ mm67_write(uint16_t port, uint8_t val, void *priv) #endif /* This chip is directly mapped on I/O. */ - sub_cycles(ISA_CYCLES(4)); + cycles -= ISA_CYCLES(4); switch(reg) { case MM67_ISTAT: /* intr status (RO) */ diff --git a/src/device/keyboard_at.c b/src/device/keyboard_at.c index cd521da8b..1164c7007 100644 --- a/src/device/keyboard_at.c +++ b/src/device/keyboard_at.c @@ -2047,7 +2047,7 @@ kbd_read(uint16_t port, void *priv) uint8_t ret = 0xff; if ((dev->flags & KBC_TYPE_MASK) >= KBC_TYPE_PS2_NOREF) - sub_cycles(ISA_CYCLES(8)); + cycles -= ISA_CYCLES(8); if (((dev->flags & KBC_VEN_MASK) == KBC_VEN_XI8088) && (port == 0x63)) port = 0x61; diff --git a/src/device/mouse_serial.c b/src/device/mouse_serial.c index 5ae1cd36b..8535e3466 100644 --- a/src/device/mouse_serial.c +++ b/src/device/mouse_serial.c @@ -173,7 +173,6 @@ sermouse_callback(struct serial_s *serial, void *priv) dev->format = 7; dev->transmit_period = sermouse_transmit_period(dev, 1200, -1); timer_stop(&dev->command_timer); - sub_cycles(ISA_CYCLES(8)); #ifdef USE_NEW_DYNAREC sermouse_timer_on(dev, 5000.0, 0); #else diff --git a/src/device/serial.c b/src/device/serial.c index b331d4d99..ed6915438 100644 --- a/src/device/serial.c +++ b/src/device/serial.c @@ -355,7 +355,7 @@ serial_write(uint16_t addr, uint8_t val, void *p) serial_log("UART: Write %02X to port %02X\n", val, addr); - sub_cycles(ISA_CYCLES(8)); + cycles -= ISA_CYCLES(8); switch (addr & 7) { case 0: @@ -513,7 +513,7 @@ serial_read(uint16_t addr, void *p) serial_t *dev = (serial_t *)p; uint8_t i, ret = 0; - sub_cycles(ISA_CYCLES(8)); + cycles -= ISA_CYCLES(8); switch (addr & 7) { case 0: diff --git a/src/disk/hdc_xtide.c b/src/disk/hdc_xtide.c index 7dc6ec095..50403d9ae 100644 --- a/src/disk/hdc_xtide.c +++ b/src/disk/hdc_xtide.c @@ -276,7 +276,7 @@ const device_t xtide_acculogic_device = { const device_t xtide_at_ps2_device = { "PS/2 AT XTIDE (1.1.5)", - DEVICE_AT, + DEVICE_ISA | DEVICE_AT, 0, xtide_at_ps2_init, xtide_at_close, NULL, { xtide_at_ps2_available }, NULL, NULL, diff --git a/src/disk/hdd_image.c b/src/disk/hdd_image.c index aa8bf8509..88e182d41 100644 --- a/src/disk/hdd_image.c +++ b/src/disk/hdd_image.c @@ -32,15 +32,22 @@ #include <86box/plat.h> #include <86box/random.h> #include <86box/hdd.h> +#include "minivhd/minivhd.h" +#include "minivhd/minivhd_internal.h" +#define HDD_IMAGE_RAW 0 +#define HDD_IMAGE_HDI 1 +#define HDD_IMAGE_HDX 2 +#define HDD_IMAGE_VHD 3 typedef struct { - FILE *file; - uint32_t base; - uint32_t pos, last_sector; - uint8_t type; - uint8_t loaded; + FILE *file; /* Used for HDD_IMAGE_RAW, HDD_IMAGE_HDI, and HDD_IMAGE_HDX. */ + MVHDMeta* vhd; /* Used for HDD_IMAGE_VHD. */ + uint32_t base; + uint32_t pos, last_sector; + uint8_t type; /* HDD_IMAGE_RAW, HDD_IMAGE_HDI, HDD_IMAGE_HDX, or HDD_IMAGE_VHD */ + uint8_t loaded; } hdd_image_t; @@ -49,27 +56,6 @@ hdd_image_t hdd_images[HDD_NUM]; static char empty_sector[512]; static char *empty_sector_1mb; - -#define VHD_OFFSET_COOKIE 0 -#define VHD_OFFSET_FEATURES 8 -#define VHD_OFFSET_VERSION 12 -#define VHD_OFFSET_DATA_OFFSET 16 -#define VHD_OFFSET_TIMESTAMP 24 -#define VHD_OFFSET_CREATOR 28 -#define VHD_OFFSET_CREATOR_VERS 32 -#define VHD_OFFSET_CREATOR_HOST 36 -#define VHD_OFFSET_ORIG_SIZE 40 -#define VHD_OFFSET_CURR_SIZE 48 -#define VHD_OFFSET_GEOM_CYL 56 -#define VHD_OFFSET_GEOM_HEAD 58 -#define VHD_OFFSET_GEOM_SPT 59 -#define VHD_OFFSET_TYPE 60 -#define VHD_OFFSET_CHECKSUM 64 -#define VHD_OFFSET_UUID 68 -#define VHD_OFFSET_SAVED_STATE 84 -#define VHD_OFFSET_RESERVED 85 - - #ifdef ENABLE_HDD_IMAGE_LOG int hdd_image_do_log = ENABLE_HDD_IMAGE_LOG; @@ -77,885 +63,652 @@ int hdd_image_do_log = ENABLE_HDD_IMAGE_LOG; static void hdd_image_log(const char *fmt, ...) { - va_list ap; + va_list ap; - if (hdd_image_do_log) { - va_start(ap, fmt); - pclog_ex(fmt, ap); - va_end(ap); - } + if (hdd_image_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } } #else #define hdd_image_log(fmt, ...) #endif - int image_is_hdi(const wchar_t *s) { - int len; - wchar_t ext[5] = { 0, 0, 0, 0, 0 }; - char *ws = (char *) s; - len = wcslen(s); - if ((len < 4) || (s[0] == L'.')) - return 0; - memcpy(ext, ws + ((len - 4) << 1), 8); - if (! wcscasecmp(ext, L".HDI")) - return 1; - else - return 0; + int len; + wchar_t ext[5] = { 0, 0, 0, 0, 0 }; + char *ws = (char *) s; + len = wcslen(s); + if ((len < 4) || (s[0] == L'.')) + return 0; + memcpy(ext, ws + ((len - 4) << 1), 8); + if (! wcscasecmp(ext, L".HDI")) + return 1; + else + return 0; } int image_is_hdx(const wchar_t *s, int check_signature) { - int len; - FILE *f; - uint64_t filelen; - uint64_t signature; - char *ws = (char *) s; - wchar_t ext[5] = { 0, 0, 0, 0, 0 }; - len = wcslen(s); - if ((len < 4) || (s[0] == L'.')) - return 0; - memcpy(ext, ws + ((len - 4) << 1), 8); - if (wcscasecmp(ext, L".HDX") == 0) { - if (check_signature) { - f = plat_fopen((wchar_t *)s, L"rb"); - if (!f) - return 0; - if (fseeko64(f, 0, SEEK_END)) - fatal("image_is_hdx(): Error while seeking"); - filelen = ftello64(f); - if (fseeko64(f, 0, SEEK_SET)) - fatal("image_is_hdx(): Error while seeking"); - if (filelen < 44) { - if (f != NULL) - fclose(f); - return 0; - } - if (fread(&signature, 1, 8, f) != 8) - fatal("image_is_hdx(): Error reading signature\n"); - fclose(f); - if (signature == 0xD778A82044445459ll) + int len; + FILE *f; + uint64_t filelen; + uint64_t signature; + char *ws = (char *) s; + wchar_t ext[5] = { 0, 0, 0, 0, 0 }; + len = wcslen(s); + if ((len < 4) || (s[0] == L'.')) + return 0; + memcpy(ext, ws + ((len - 4) << 1), 8); + if (wcscasecmp(ext, L".HDX") == 0) { + if (check_signature) { + f = plat_fopen((wchar_t *)s, L"rb"); + if (!f) + return 0; + if (fseeko64(f, 0, SEEK_END)) + fatal("image_is_hdx(): Error while seeking"); + filelen = ftello64(f); + if (fseeko64(f, 0, SEEK_SET)) + fatal("image_is_hdx(): Error while seeking"); + if (filelen < 44) { + if (f != NULL) + fclose(f); + return 0; + } + if (fread(&signature, 1, 8, f) != 8) + fatal("image_is_hdx(): Error reading signature\n"); + fclose(f); + if (signature == 0xD778A82044445459ll) + return 1; + else + return 0; + } else return 1; - else - return 0; } else - return 1; - } else - return 0; + return 0; } int image_is_vhd(const wchar_t *s, int check_signature) { - int len; - FILE *f; - uint64_t filelen; - uint64_t signature; - char *ws = (char *) s; - wchar_t ext[5] = { 0, 0, 0, 0, 0 }; - len = wcslen(s); - if ((len < 4) || (s[0] == L'.')) - return 0; - memcpy(ext, ws + ((len - 4) << 1), 8); - if (wcscasecmp(ext, L".VHD") == 0) { - if (check_signature) { - f = plat_fopen((wchar_t *)s, L"rb"); - if (!f) - return 0; - fseeko64(f, 0, SEEK_END); - filelen = ftello64(f); - if (fseeko64(f, -512, SEEK_END) == -1) { + int len; + FILE* f; + char *ws = (char *) s; + wchar_t ext[5] = { 0, 0, 0, 0, 0 }; + len = wcslen(s); + if ((len < 4) || (s[0] == L'.')) + return 0; + memcpy(ext, ws + ((len - 4) << 1), 8); + if (wcscasecmp(ext, L".VHD") == 0) { + if (check_signature) { + f = plat_fopen((wchar_t*)s, L"rb"); + if (!f) + return 0; + + bool is_vhd = mvhd_file_is_vhd(f); fclose(f); - fatal("image_is_vhd(): Error seeking\n"); - } - if (filelen < 512) { - if (f != NULL) - fclose(f); - return 0; - } - if (fread(&signature, 1, 8, f) != 8) - fatal("image_is_vhd(): Error reading signature\n"); - fclose(f); - if (signature == 0x78697463656E6F63ll) + return is_vhd ? 1 : 0; + } else return 1; - else - return 0; } else - return 1; - } else - return 0; + return 0; } - -static uint64_t -be_to_u64(uint8_t *bytes, int start) -{ - uint64_t n = ((uint64_t) bytes[start + 7] << 0) | - ((uint64_t) bytes[start + 6] << 8) | - ((uint64_t) bytes[start + 5] << 16) | - ((uint64_t) bytes[start + 4] << 24) | - ((uint64_t) bytes[start + 3] << 32) | - ((uint64_t) bytes[start + 2] << 40) | - ((uint64_t) bytes[start + 1] << 48) | - ((uint64_t) bytes[start ] << 56); - return n; -} - - -static uint32_t -be_to_u32(uint8_t *bytes, int start) -{ - uint32_t n = ((uint32_t) bytes[start + 3] << 0) | - ((uint32_t) bytes[start + 2] << 8) | - ((uint32_t) bytes[start + 1] << 16) | - ((uint32_t) bytes[start ] << 24); - return n; -} - - -static uint16_t -be_to_u16(uint8_t *bytes, int start) -{ - uint16_t n = ((uint16_t) bytes[start + 1] << 0) | - ((uint16_t) bytes[start ] << 8); - return n; -} - - -static uint64_t -u64_to_be(uint64_t value, int is_be) -{ - uint64_t res = 0; - if (is_be) - res = value; - else { - uint64_t mask = 0xff00000000000000; - res = ((value & (mask >> 0)) >> 56) | - ((value & (mask >> 8)) >> 40) | - ((value & (mask >> 16)) >> 24) | - ((value & (mask >> 24)) >> 8) | - ((value & (mask >> 32)) << 8) | - ((value & (mask >> 40)) << 24) | - ((value & (mask >> 48)) << 40) | - ((value & (mask >> 56)) << 56); - } - return res; -} - - -static uint32_t -u32_to_be(uint32_t value, int is_be) -{ - uint32_t res = 0; - if (is_be) - res = value; - else { - uint32_t mask = 0xff000000; - res = ((value & (mask >> 0)) >> 24) | - ((value & (mask >> 8)) >> 8) | - ((value & (mask >> 16)) << 8) | - ((value & (mask >> 24)) << 24); - } - return res; -} - - -static uint16_t -u16_to_be(uint16_t value, int is_be) -{ - uint16_t res = 0; - if (is_be) - res = value; - else - res = (value >> 8) | (value << 8); - - return res; -} - - -static void -mk_guid(uint8_t *guid) -{ - int n; - - for (n = 0; n < 16; n++) - guid[n] = random_generate(); - - guid[6] &= 0x0F; - guid[6] |= 0x40; /* Type 4 */ - guid[8] &= 0x3F; - guid[8] |= 0x80; /* Variant 1 */ -} - - -static uint32_t -calc_vhd_timestamp() -{ - time_t start_time; - time_t curr_time; - double vhd_time; - start_time = 946684800; /* 1 Jan 2000 00:00 */ - curr_time = time(NULL); - vhd_time = difftime(curr_time, start_time); - - return (uint32_t)vhd_time; -} - - -void -vhd_footer_from_bytes(vhd_footer_t *vhd, uint8_t *bytes) -{ - memcpy(vhd->cookie, bytes + VHD_OFFSET_COOKIE, sizeof(vhd->cookie)); - vhd->features = be_to_u32(bytes, VHD_OFFSET_FEATURES); - vhd->version = be_to_u32(bytes, VHD_OFFSET_VERSION); - vhd->offset = be_to_u64(bytes, VHD_OFFSET_DATA_OFFSET); - vhd->timestamp = be_to_u32(bytes, VHD_OFFSET_TIMESTAMP); - memcpy(vhd->creator, bytes + VHD_OFFSET_CREATOR, sizeof(vhd->creator)); - vhd->creator_vers = be_to_u32(bytes, VHD_OFFSET_CREATOR_VERS); - memcpy(vhd->creator_host_os, bytes + VHD_OFFSET_CREATOR_HOST, sizeof(vhd->creator_host_os)); - vhd->orig_size = be_to_u64(bytes, VHD_OFFSET_ORIG_SIZE); - vhd->curr_size = be_to_u64(bytes, VHD_OFFSET_CURR_SIZE); - vhd->geom.cyl = be_to_u16(bytes, VHD_OFFSET_GEOM_CYL); - vhd->geom.heads = bytes[VHD_OFFSET_GEOM_HEAD]; - vhd->geom.spt = bytes[VHD_OFFSET_GEOM_SPT]; - vhd->type = be_to_u32(bytes, VHD_OFFSET_TYPE); - vhd->checksum = be_to_u32(bytes, VHD_OFFSET_CHECKSUM); - memcpy(vhd->uuid, bytes + VHD_OFFSET_UUID, sizeof(vhd->uuid)); /* TODO: handle UUID's properly */ - vhd->saved_state = bytes[VHD_OFFSET_SAVED_STATE]; - memcpy(vhd->reserved, bytes + VHD_OFFSET_RESERVED, sizeof(vhd->reserved)); -} - - -void -vhd_footer_to_bytes(uint8_t *bytes, vhd_footer_t *vhd) -{ - /* Quick endian check */ - int is_be = 0; - uint8_t e = 1; - uint8_t *ep = &e; - uint16_t u16; - uint32_t u32; - uint64_t u64; - - if (ep[0] == 0) - is_be = 1; - - memcpy(bytes + VHD_OFFSET_COOKIE, vhd->cookie, sizeof(vhd->cookie)); - u32 = u32_to_be(vhd->features, is_be); - memcpy(bytes + VHD_OFFSET_FEATURES, &u32, sizeof(vhd->features)); - u32 = u32_to_be(vhd->version, is_be); - memcpy(bytes + VHD_OFFSET_VERSION, &u32, sizeof(vhd->version)); - u64 = u64_to_be(vhd->offset, is_be); - memcpy(bytes + VHD_OFFSET_DATA_OFFSET, &u64, sizeof(vhd->offset)); - u32 = u32_to_be(vhd->timestamp, is_be); - memcpy(bytes + VHD_OFFSET_TIMESTAMP, &u32, sizeof(vhd->timestamp)); - memcpy(bytes + VHD_OFFSET_CREATOR, vhd->creator, sizeof(vhd->creator)); - u32 = u32_to_be(vhd->creator_vers, is_be); - memcpy(bytes + VHD_OFFSET_CREATOR_VERS, &u32, sizeof(vhd->creator_vers)); - memcpy(bytes + VHD_OFFSET_CREATOR_HOST, vhd->creator_host_os, sizeof(vhd->creator_host_os)); - u64 = u64_to_be(vhd->orig_size, is_be); - memcpy(bytes + VHD_OFFSET_ORIG_SIZE, &u64, sizeof(vhd->orig_size)); - u64 = u64_to_be(vhd->curr_size, is_be); - memcpy(bytes + VHD_OFFSET_CURR_SIZE, &u64, sizeof(vhd->curr_size)); - u16 = u16_to_be(vhd->geom.cyl, is_be); - memcpy(bytes + VHD_OFFSET_GEOM_CYL, &u16, sizeof(vhd->geom.cyl)); - memcpy(bytes + VHD_OFFSET_GEOM_HEAD, &(vhd->geom.heads), sizeof(vhd->geom.heads)); - memcpy(bytes + VHD_OFFSET_GEOM_SPT, &(vhd->geom.spt), sizeof(vhd->geom.spt)); - u32 = u32_to_be(vhd->type, is_be); - memcpy(bytes + VHD_OFFSET_TYPE, &u32, sizeof(vhd->type)); - u32 = u32_to_be(vhd->checksum, is_be); - memcpy(bytes + VHD_OFFSET_CHECKSUM, &u32, sizeof(vhd->checksum)); - memcpy(bytes + VHD_OFFSET_UUID, vhd->uuid, sizeof(vhd->uuid)); - memcpy(bytes + VHD_OFFSET_SAVED_STATE, &(vhd->saved_state), sizeof(vhd->saved_state)); - memcpy(bytes + VHD_OFFSET_RESERVED, vhd->reserved, sizeof(vhd->reserved)); -} - - -void -new_vhd_footer(vhd_footer_t **vhd) -{ - uint8_t cookie[8] = {'c', 'o', 'n', 'e', 'c', 't', 'i', 'x'}; - uint8_t creator[4] = {'8', '6', 'b', 'x'}; - uint8_t cr_host_os[4] = {'W', 'i', '2', 'k'}; - - if (*vhd == NULL) - *vhd = (vhd_footer_t *) malloc(sizeof(vhd_footer_t)); - - memcpy((*vhd)->cookie, cookie, 8); - (*vhd)->features = 0x00000002; - (*vhd)->version = 0x00010000; - (*vhd)->offset = 0xffffffffffffffff; /* fixed disk */ - (*vhd)->timestamp = calc_vhd_timestamp(); - memcpy((*vhd)->creator, creator, 4); - (*vhd)->creator_vers = 0x00010000; - memcpy((*vhd)->creator_host_os, cr_host_os, 4); - (*vhd)->type = 2; /* fixed disk */ - mk_guid((*vhd)->uuid); - (*vhd)->saved_state = 0; - memset((*vhd)->reserved, 0, 427); -} - - -void -generate_vhd_checksum(vhd_footer_t *vhd) -{ - uint32_t chk = 0; - int i; - for (i = 0; i < sizeof(vhd_footer_t); i++) { - /* We don't include the checksum field in the checksum */ - if ((i < VHD_OFFSET_CHECKSUM) || (i >= VHD_OFFSET_UUID)) - chk += ((uint8_t*)vhd)[i]; - } - vhd->checksum = ~chk; -} - - void hdd_image_calc_chs(uint32_t *c, uint32_t *h, uint32_t *s, uint32_t size) { - /* Calculate the geometry from size (in MB), using the algorithm provided in - "Virtual Hard Disk Image Format Specification, Appendix: CHS Calculation" */ - uint64_t ts = ((uint64_t) size) << 11LL; - uint32_t spt, heads, cyl, cth; - if (ts > 65535 * 16 * 255) - ts = 65535 * 16 * 255; + /* Calculate the geometry from size (in MB), using the algorithm provided in + "Virtual Hard Disk Image Format Specification, Appendix: CHS Calculation" */ + uint64_t ts = ((uint64_t) size) << 11LL; + uint32_t spt, heads, cyl, cth; + if (ts > 65535 * 16 * 255) + ts = 65535 * 16 * 255; - if (ts >= 65535 * 16 * 63) { - spt = 255; - heads = 16; - cth = (uint32_t) (ts / spt); - } else { - spt = 17; - cth = (uint32_t) (ts / spt); - heads = (cth +1023) / 1024; - if (heads < 4) - heads = 4; - if ((cth >= (heads * 1024)) || (heads > 16)) { - spt = 31; + if (ts >= 65535 * 16 * 63) { + spt = 255; heads = 16; cth = (uint32_t) (ts / spt); - } - if (cth >= (heads * 1024)) { - spt = 63; - heads = 16; + } else { + spt = 17; cth = (uint32_t) (ts / spt); + heads = (cth +1023) / 1024; + if (heads < 4) + heads = 4; + if ((cth >= (heads * 1024)) || (heads > 16)) { + spt = 31; + heads = 16; + cth = (uint32_t) (ts / spt); + } + if (cth >= (heads * 1024)) { + spt = 63; + heads = 16; + cth = (uint32_t) (ts / spt); + } } - } - cyl = cth / heads; - *c = cyl; - *h = heads; - *s = spt; + cyl = cth / heads; + *c = cyl; + *h = heads; + *s = spt; } static int prepare_new_hard_disk(uint8_t id, uint64_t full_size) { - uint64_t target_size = (full_size + hdd_images[id].base) - ftello64(hdd_images[id].file); + uint64_t target_size = (full_size + hdd_images[id].base) - ftello64(hdd_images[id].file); - uint32_t size; - uint32_t t, i; + uint32_t size; + uint32_t t, i; - t = (uint32_t) (target_size >> 20); /* Amount of 1 MB blocks. */ - size = (uint32_t) (target_size & 0xfffff); /* 1 MB mask. */ + t = (uint32_t) (target_size >> 20); /* Amount of 1 MB blocks. */ + size = (uint32_t) (target_size & 0xfffff); /* 1 MB mask. */ - empty_sector_1mb = (char *) malloc(1048576); - memset(empty_sector_1mb, 0, 1048576); + empty_sector_1mb = (char *) malloc(1048576); + memset(empty_sector_1mb, 0, 1048576); - /* Temporarily switch off suppression of seen messages so that the - progress gets displayed. */ - pclog_toggle_suppr(); - pclog("Writing image sectors: ["); + /* Temporarily switch off suppression of seen messages so that the + progress gets displayed. */ + pclog_toggle_suppr(); + pclog("Writing image sectors: ["); - /* First, write all the 1 MB blocks. */ - if (t > 0) { - for (i = 0; i < t; i++) { + /* First, write all the 1 MB blocks. */ + if (t > 0) { + for (i = 0; i < t; i++) { + fseek(hdd_images[id].file, 0, SEEK_END); + fwrite(empty_sector_1mb, 1, 1048576, hdd_images[id].file); + pclog("#"); + } + } + + /* Then, write the remainder. */ + if (size > 0) { fseek(hdd_images[id].file, 0, SEEK_END); - fwrite(empty_sector_1mb, 1, 1048576, hdd_images[id].file); + fwrite(empty_sector_1mb, 1, size, hdd_images[id].file); pclog("#"); } - } + pclog("]\n"); + /* Switch the suppression of seen messages back on. */ + pclog_toggle_suppr(); - /* Then, write the remainder. */ - if (size > 0) { - fseek(hdd_images[id].file, 0, SEEK_END); - fwrite(empty_sector_1mb, 1, size, hdd_images[id].file); - pclog("#"); - } - pclog("]\n"); - /* Switch the suppression of seen messages back on. */ - pclog_toggle_suppr(); + free(empty_sector_1mb); - free(empty_sector_1mb); + hdd_images[id].last_sector = (uint32_t) (full_size >> 9) - 1; - hdd_images[id].last_sector = (uint32_t) (full_size >> 9) - 1; + hdd_images[id].loaded = 1; - hdd_images[id].loaded = 1; - - return 1; + return 1; } void hdd_image_init(void) { - int i; + int i; - for (i = 0; i < HDD_NUM; i++) - memset(&hdd_images[i], 0, sizeof(hdd_image_t)); + for (i = 0; i < HDD_NUM; i++) + memset(&hdd_images[i], 0, sizeof(hdd_image_t)); } - -static void -hdd_image_gen_vft(int id, vhd_footer_t **vft, uint64_t full_size) -{ - /* Generate new footer. */ - new_vhd_footer(vft); - (*vft)->orig_size = (*vft)->curr_size = full_size; - (*vft)->geom.cyl = hdd[id].tracks; - (*vft)->geom.heads = hdd[id].hpc; - (*vft)->geom.spt = hdd[id].spt; - generate_vhd_checksum(*vft); - vhd_footer_to_bytes((uint8_t *) empty_sector, *vft); - fseeko64(hdd_images[id].file, 0, SEEK_END); - fwrite(empty_sector, 1, 512, hdd_images[id].file); - free(*vft); - *vft = NULL; - hdd_images[id].type = 3; -} - - int hdd_image_load(int id) { - uint32_t sector_size = 512; - uint32_t zero = 0; - uint64_t signature = 0xD778A82044445459ll; - uint64_t full_size = 0; - uint64_t spt = 0, hpc = 0, tracks = 0; - int c, ret; - uint64_t s = 0; - wchar_t *fn = hdd[id].fn; - int is_hdx[2] = { 0, 0 }; - int is_vhd[2] = { 0, 0 }; - vhd_footer_t *vft = NULL; + uint32_t sector_size = 512; + uint32_t zero = 0; + uint64_t signature = 0xD778A82044445459ll; + uint64_t full_size = 0; + uint64_t spt = 0, hpc = 0, tracks = 0; + int c, ret; + uint64_t s = 0; + wchar_t *fn = hdd[id].fn; + char fn_multibyte_buf[1200]; + int is_hdx[2] = { 0, 0 }; + int is_vhd[2] = { 0, 0 }; + int vhd_error = 0; - memset(empty_sector, 0, sizeof(empty_sector)); + memset(empty_sector, 0, sizeof(empty_sector)); - hdd_images[id].base = 0; + hdd_images[id].base = 0; - if (hdd_images[id].loaded) { - if (hdd_images[id].file) { - fclose(hdd_images[id].file); - hdd_images[id].file = NULL; + if (hdd_images[id].loaded) { + if (hdd_images[id].file) { + fclose(hdd_images[id].file); + hdd_images[id].file = NULL; + } + else if (hdd_images[id].vhd) { + mvhd_close(hdd_images[id].vhd); + hdd_images[id].vhd = NULL; + } + hdd_images[id].loaded = 0; } - hdd_images[id].loaded = 0; - } - is_hdx[0] = image_is_hdx(fn, 0); - is_hdx[1] = image_is_hdx(fn, 1); + is_hdx[0] = image_is_hdx(fn, 0); + is_hdx[1] = image_is_hdx(fn, 1); - is_vhd[0] = image_is_vhd(fn, 0); - is_vhd[1] = image_is_vhd(fn, 1); + is_vhd[0] = image_is_vhd(fn, 0); + is_vhd[1] = image_is_vhd(fn, 1); - hdd_images[id].pos = 0; + hdd_images[id].pos = 0; - /* Try to open existing hard disk image */ - if (fn[0] == '.') { - hdd_image_log("File name starts with .\n"); - memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); - return 0; - } - hdd_images[id].file = plat_fopen(fn, L"rb+"); - if (hdd_images[id].file == NULL) { - /* Failed to open existing hard disk image */ - if (errno == ENOENT) { - /* Failed because it does not exist, - so try to create new file */ - if (hdd[id].wp) { - hdd_image_log("A write-protected image must exist\n"); - memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); - return 0; - } - - hdd_images[id].file = plat_fopen(fn, L"wb+"); - if (hdd_images[id].file == NULL) { - hdd_image_log("Unable to open image\n"); - memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); - return 0; - } else { - if (image_is_hdi(fn)) { - full_size = ((uint64_t) hdd[id].spt) * - ((uint64_t) hdd[id].hpc) * - ((uint64_t) hdd[id].tracks) << 9LL; - hdd_images[id].base = 0x1000; - fwrite(&zero, 1, 4, hdd_images[id].file); - fwrite(&zero, 1, 4, hdd_images[id].file); - fwrite(&(hdd_images[id].base), 1, 4, hdd_images[id].file); - fwrite(&full_size, 1, 4, hdd_images[id].file); - fwrite(§or_size, 1, 4, hdd_images[id].file); - fwrite(&(hdd[id].spt), 1, 4, hdd_images[id].file); - fwrite(&(hdd[id].hpc), 1, 4, hdd_images[id].file); - fwrite(&(hdd[id].tracks), 1, 4, hdd_images[id].file); - for (c = 0; c < 0x3f8; c++) - fwrite(&zero, 1, 4, hdd_images[id].file); - hdd_images[id].type = 1; - } else if (is_hdx[0]) { - full_size = ((uint64_t) hdd[id].spt) * - ((uint64_t) hdd[id].hpc) * - ((uint64_t) hdd[id].tracks) << 9LL; - hdd_images[id].base = 0x28; - fwrite(&signature, 1, 8, hdd_images[id].file); - fwrite(&full_size, 1, 8, hdd_images[id].file); - fwrite(§or_size, 1, 4, hdd_images[id].file); - fwrite(&(hdd[id].spt), 1, 4, hdd_images[id].file); - fwrite(&(hdd[id].hpc), 1, 4, hdd_images[id].file); - fwrite(&(hdd[id].tracks), 1, 4, hdd_images[id].file); - fwrite(&zero, 1, 4, hdd_images[id].file); - fwrite(&zero, 1, 4, hdd_images[id].file); - hdd_images[id].type = 2; - } - else - hdd_images[id].type = 0; - hdd_images[id].last_sector = 0; - } - - s = full_size = ((uint64_t) hdd[id].spt) * - ((uint64_t) hdd[id].hpc) * - ((uint64_t) hdd[id].tracks) << 9LL; - - ret = prepare_new_hard_disk(id, full_size); - - if (is_vhd[0]) { - /* VHD image. */ - hdd_image_gen_vft(id, &vft, full_size); - } - - return ret; - } else { - /* Failed for another reason */ - hdd_image_log("Failed for another reason\n"); + /* Try to open existing hard disk image */ + if (fn[0] == '.') { + hdd_image_log("File name starts with .\n"); memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); return 0; } - } else { - if (image_is_hdi(fn)) { - if (fseeko64(hdd_images[id].file, 0x8, SEEK_SET) == -1) - fatal("hdd_image_load(): HDI: Error seeking to offset 0x8\n"); - if (fread(&(hdd_images[id].base), 1, 4, hdd_images[id].file) != 4) - fatal("hdd_image_load(): HDI: Error reading base offset\n"); - if (fseeko64(hdd_images[id].file, 0xC, SEEK_SET) == -1) - fatal("hdd_image_load(): HDI: Error seeking to offest 0xC\n"); - full_size = 0LL; - if (fread(&full_size, 1, 4, hdd_images[id].file) != 4) - fatal("hdd_image_load(): HDI: Error reading full size\n"); - if (fseeko64(hdd_images[id].file, 0x10, SEEK_SET) == -1) - fatal("hdd_image_load(): HDI: Error seeking to offset 0x10\n"); - if (fread(§or_size, 1, 4, hdd_images[id].file) != 4) - fatal("hdd_image_load(): HDI: Error reading sector size\n"); - if (sector_size != 512) { - /* Sector size is not 512 */ - hdd_image_log("HDI: Sector size is not 512\n"); - fclose(hdd_images[id].file); - hdd_images[id].file = NULL; + hdd_images[id].file = plat_fopen(fn, L"rb+"); + if (hdd_images[id].file == NULL) { + /* Failed to open existing hard disk image */ + if (errno == ENOENT) { + /* Failed because it does not exist, + so try to create new file */ + if (hdd[id].wp) { + hdd_image_log("A write-protected image must exist\n"); + memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); + return 0; + } + + hdd_images[id].file = plat_fopen(fn, L"wb+"); + if (hdd_images[id].file == NULL) { + hdd_image_log("Unable to open image\n"); + memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); + return 0; + } else { + if (image_is_hdi(fn)) { + full_size = ((uint64_t) hdd[id].spt) * + ((uint64_t) hdd[id].hpc) * + ((uint64_t) hdd[id].tracks) << 9LL; + hdd_images[id].base = 0x1000; + fwrite(&zero, 1, 4, hdd_images[id].file); + fwrite(&zero, 1, 4, hdd_images[id].file); + fwrite(&(hdd_images[id].base), 1, 4, hdd_images[id].file); + fwrite(&full_size, 1, 4, hdd_images[id].file); + fwrite(§or_size, 1, 4, hdd_images[id].file); + fwrite(&(hdd[id].spt), 1, 4, hdd_images[id].file); + fwrite(&(hdd[id].hpc), 1, 4, hdd_images[id].file); + fwrite(&(hdd[id].tracks), 1, 4, hdd_images[id].file); + for (c = 0; c < 0x3f8; c++) + fwrite(&zero, 1, 4, hdd_images[id].file); + hdd_images[id].type = HDD_IMAGE_HDI; + } else if (is_hdx[0]) { + full_size = ((uint64_t) hdd[id].spt) * + ((uint64_t) hdd[id].hpc) * + ((uint64_t) hdd[id].tracks) << 9LL; + hdd_images[id].base = 0x28; + fwrite(&signature, 1, 8, hdd_images[id].file); + fwrite(&full_size, 1, 8, hdd_images[id].file); + fwrite(§or_size, 1, 4, hdd_images[id].file); + fwrite(&(hdd[id].spt), 1, 4, hdd_images[id].file); + fwrite(&(hdd[id].hpc), 1, 4, hdd_images[id].file); + fwrite(&(hdd[id].tracks), 1, 4, hdd_images[id].file); + fwrite(&zero, 1, 4, hdd_images[id].file); + fwrite(&zero, 1, 4, hdd_images[id].file); + hdd_images[id].type = HDD_IMAGE_HDX; + } else if (is_vhd[0]) { + fclose(hdd_images[id].file); + MVHDGeom geometry; + geometry.cyl = hdd[id].tracks; + geometry.heads = hdd[id].hpc; + geometry.spt = hdd[id].spt; + full_size = ((uint64_t) hdd[id].spt) * + ((uint64_t) hdd[id].hpc) * + ((uint64_t) hdd[id].tracks) << 9LL; + hdd_images[id].last_sector = (full_size >> 9LL) - 1; + + wcstombs(fn_multibyte_buf, fn, sizeof fn_multibyte_buf); + hdd_images[id].vhd = mvhd_create_fixed(fn_multibyte_buf, geometry, &vhd_error, NULL); + if (hdd_images[id].vhd == NULL) + fatal("hdd_image_load(): VHD: Could not create VHD : %s\n", mvhd_strerr(vhd_error)); + + hdd_images[id].type = HDD_IMAGE_VHD; + return 1; + } else { + hdd_images[id].type = HDD_IMAGE_RAW; + } + hdd_images[id].last_sector = 0; + } + + s = full_size = ((uint64_t) hdd[id].spt) * + ((uint64_t) hdd[id].hpc) * + ((uint64_t) hdd[id].tracks) << 9LL; + + ret = prepare_new_hard_disk(id, full_size); + return ret; + } else { + /* Failed for another reason */ + hdd_image_log("Failed for another reason\n"); memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); return 0; } - if (fread(&spt, 1, 4, hdd_images[id].file) != 4) - fatal("hdd_image_load(): HDI: Error reading sectors per track\n"); - if (fread(&hpc, 1, 4, hdd_images[id].file) != 4) - fatal("hdd_image_load(): HDI: Error reading heads per cylinder\n"); - if (fread(&tracks, 1, 4, hdd_images[id].file) != 4) - fatal("hdd_image_load(): HDI: Error reading number of tracks\n"); - hdd[id].spt = spt; - hdd[id].hpc = hpc; - hdd[id].tracks = tracks; - hdd_images[id].type = 1; - } else if (is_hdx[1]) { - hdd_images[id].base = 0x28; - if (fseeko64(hdd_images[id].file, 8, SEEK_SET) == -1) - fatal("hdd_image_load(): HDX: Error seeking to offset 0x8\n"); - if (fread(&full_size, 1, 8, hdd_images[id].file) != 8) - fatal("hdd_image_load(): HDX: Error reading full size\n"); - if (fseeko64(hdd_images[id].file, 0x10, SEEK_SET) == -1) - fatal("hdd_image_load(): HDX: Error seeking to offset 0x10\n"); - if (fread(§or_size, 1, 4, hdd_images[id].file) != 4) - fatal("hdd_image_load(): HDX: Error reading sector size\n"); - if (sector_size != 512) { - /* Sector size is not 512 */ - hdd_image_log("HDX: Sector size is not 512\n"); + } else { + if (image_is_hdi(fn)) { + if (fseeko64(hdd_images[id].file, 0x8, SEEK_SET) == -1) + fatal("hdd_image_load(): HDI: Error seeking to offset 0x8\n"); + if (fread(&(hdd_images[id].base), 1, 4, hdd_images[id].file) != 4) + fatal("hdd_image_load(): HDI: Error reading base offset\n"); + if (fseeko64(hdd_images[id].file, 0xC, SEEK_SET) == -1) + fatal("hdd_image_load(): HDI: Error seeking to offest 0xC\n"); + full_size = 0LL; + if (fread(&full_size, 1, 4, hdd_images[id].file) != 4) + fatal("hdd_image_load(): HDI: Error reading full size\n"); + if (fseeko64(hdd_images[id].file, 0x10, SEEK_SET) == -1) + fatal("hdd_image_load(): HDI: Error seeking to offset 0x10\n"); + if (fread(§or_size, 1, 4, hdd_images[id].file) != 4) + fatal("hdd_image_load(): HDI: Error reading sector size\n"); + if (sector_size != 512) { + /* Sector size is not 512 */ + hdd_image_log("HDI: Sector size is not 512\n"); + fclose(hdd_images[id].file); + hdd_images[id].file = NULL; + memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); + return 0; + } + if (fread(&spt, 1, 4, hdd_images[id].file) != 4) + fatal("hdd_image_load(): HDI: Error reading sectors per track\n"); + if (fread(&hpc, 1, 4, hdd_images[id].file) != 4) + fatal("hdd_image_load(): HDI: Error reading heads per cylinder\n"); + if (fread(&tracks, 1, 4, hdd_images[id].file) != 4) + fatal("hdd_image_load(): HDI: Error reading number of tracks\n"); + hdd[id].spt = spt; + hdd[id].hpc = hpc; + hdd[id].tracks = tracks; + hdd_images[id].type = HDD_IMAGE_HDI; + } else if (is_hdx[1]) { + hdd_images[id].base = 0x28; + if (fseeko64(hdd_images[id].file, 8, SEEK_SET) == -1) + fatal("hdd_image_load(): HDX: Error seeking to offset 0x8\n"); + if (fread(&full_size, 1, 8, hdd_images[id].file) != 8) + fatal("hdd_image_load(): HDX: Error reading full size\n"); + if (fseeko64(hdd_images[id].file, 0x10, SEEK_SET) == -1) + fatal("hdd_image_load(): HDX: Error seeking to offset 0x10\n"); + if (fread(§or_size, 1, 4, hdd_images[id].file) != 4) + fatal("hdd_image_load(): HDX: Error reading sector size\n"); + if (sector_size != 512) { + /* Sector size is not 512 */ + hdd_image_log("HDX: Sector size is not 512\n"); + fclose(hdd_images[id].file); + hdd_images[id].file = NULL; + memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); + return 0; + } + if (fread(&spt, 1, 4, hdd_images[id].file) != 4) + fatal("hdd_image_load(): HDI: Error reading sectors per track\n"); + if (fread(&hpc, 1, 4, hdd_images[id].file) != 4) + fatal("hdd_image_load(): HDI: Error reading heads per cylinder\n"); + if (fread(&tracks, 1, 4, hdd_images[id].file) != 4) + fatal("hdd_image_load(): HDX: Error reading number of tracks\n"); + hdd[id].spt = spt; + hdd[id].hpc = hpc; + hdd[id].tracks = tracks; + hdd_images[id].type = HDD_IMAGE_HDX; + } else if (is_vhd[1]) { fclose(hdd_images[id].file); hdd_images[id].file = NULL; - memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); - return 0; + wcstombs(fn_multibyte_buf, fn, sizeof fn_multibyte_buf); + hdd_images[id].vhd = mvhd_open(fn_multibyte_buf, (bool)0, &vhd_error); + if (hdd_images[id].vhd == NULL) { + if (vhd_error == MVHD_ERR_FILE) + fatal("hdd_image_load(): VHD: Error opening VHD file '%s': %s\n", fn_multibyte_buf, strerror(mvhd_errno)); + else + fatal("hdd_image_load(): VHD: Error opening VHD file '%s': %s\n", fn_multibyte_buf, mvhd_strerr(vhd_error)); + } + else if (vhd_error == MVHD_ERR_TIMESTAMP) { + fatal("hdd_image_load(): VHD: Parent/child timestamp mismatch for VHD file '%s'\n", fn_multibyte_buf); + } + + hdd[id].tracks = hdd_images[id].vhd->footer.geom.cyl; + hdd[id].hpc = hdd_images[id].vhd->footer.geom.heads; + hdd[id].spt = hdd_images[id].vhd->footer.geom.spt; + full_size = ((uint64_t) hdd[id].spt) * + ((uint64_t) hdd[id].hpc) * + ((uint64_t) hdd[id].tracks) << 9LL; + hdd_images[id].type = HDD_IMAGE_VHD; + /* If we're here, this means there is a valid VHD footer in the + image, which means that by definition, all valid sectors + are there. */ + hdd_images[id].last_sector = (uint32_t) (full_size >> 9) - 1; + hdd_images[id].loaded = 1; + return 1; + } else { + full_size = ((uint64_t) hdd[id].spt) * + ((uint64_t) hdd[id].hpc) * + ((uint64_t) hdd[id].tracks) << 9LL; + hdd_images[id].type = HDD_IMAGE_RAW; } - if (fread(&spt, 1, 4, hdd_images[id].file) != 4) - fatal("hdd_image_load(): HDI: Error reading sectors per track\n"); - if (fread(&hpc, 1, 4, hdd_images[id].file) != 4) - fatal("hdd_image_load(): HDI: Error reading heads per cylinder\n"); - if (fread(&tracks, 1, 4, hdd_images[id].file) != 4) - fatal("hdd_image_load(): HDX: Error reading number of tracks\n"); - hdd[id].spt = spt; - hdd[id].hpc = hpc; - hdd[id].tracks = tracks; - hdd_images[id].type = 2; - } else if (is_vhd[1]) { - if (fseeko64(hdd_images[id].file, -512, SEEK_END) == -1) - fatal("hdd_image_load(): VHD: Error seeking to 512 bytes before the end of file\n"); - if (fread(empty_sector, 1, 512, hdd_images[id].file) != 512) - fatal("hdd_image_load(): HDX: Error reading the footer\n"); - new_vhd_footer(&vft); - vhd_footer_from_bytes(vft, (uint8_t *) empty_sector); - if (vft->type != 2) { - /* VHD is not fixed size */ - hdd_image_log("VHD: Image is not fixed size\n"); - free(vft); - vft = NULL; - fclose(hdd_images[id].file); - hdd_images[id].file = NULL; - memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); - return 0; - } - full_size = vft->orig_size; - hdd[id].tracks = vft->geom.cyl; - hdd[id].hpc = vft->geom.heads; - hdd[id].spt = vft->geom.spt; - free(vft); - vft = NULL; - hdd_images[id].type = 3; - /* If we're here, this means there is a valid VHD footer in the - image, which means that by definition, all valid sectors - are there. */ + } + + if (fseeko64(hdd_images[id].file, 0, SEEK_END) == -1) + fatal("hdd_image_load(): Error seeking to the end of file\n"); + s = ftello64(hdd_images[id].file); + if (s < (full_size + hdd_images[id].base)) + ret = prepare_new_hard_disk(id, full_size); + else { hdd_images[id].last_sector = (uint32_t) (full_size >> 9) - 1; hdd_images[id].loaded = 1; - return 1; - } else { - full_size = ((uint64_t) hdd[id].spt) * - ((uint64_t) hdd[id].hpc) * - ((uint64_t) hdd[id].tracks) << 9LL; - hdd_images[id].type = 0; - } - } + ret = 1; + } - if (fseeko64(hdd_images[id].file, 0, SEEK_END) == -1) - fatal("hdd_image_load(): Error seeking to the end of file\n"); - s = ftello64(hdd_images[id].file); - if (s < (full_size + hdd_images[id].base)) - ret = prepare_new_hard_disk(id, full_size); - else { - hdd_images[id].last_sector = (uint32_t) (full_size >> 9) - 1; - hdd_images[id].loaded = 1; - ret = 1; - } - - if (is_vhd[0]) { - if (fseeko64(hdd_images[id].file, 0, SEEK_END) == -1) - fatal("hdd_image_load(): VHD: Error seeking to the end of file\n"); - s = ftello64(hdd_images[id].file); - if (s == (full_size + hdd_images[id].base)) { - /* VHD image. */ - hdd_image_gen_vft(id, &vft, full_size); - } - } - - return ret; + return ret; } void hdd_image_seek(uint8_t id, uint32_t sector) { - off64_t addr = sector; - addr = (uint64_t)sector << 9LL; + off64_t addr = sector; + addr = (uint64_t)sector << 9LL; - hdd_images[id].pos = sector; - if (fseeko64(hdd_images[id].file, addr + hdd_images[id].base, SEEK_SET) == -1) - fatal("hdd_image_seek(): Error seeking\n"); + hdd_images[id].pos = sector; + if (hdd_images[id].type != HDD_IMAGE_VHD) { + if (fseeko64(hdd_images[id].file, addr + hdd_images[id].base, SEEK_SET) == -1) + fatal("hdd_image_seek(): Error seeking\n"); + } } void hdd_image_read(uint8_t id, uint32_t sector, uint32_t count, uint8_t *buffer) { - int i; + if (hdd_images[id].type == HDD_IMAGE_VHD) { + int non_transferred_sectors = mvhd_read_sectors(hdd_images[id].vhd, sector, count, buffer); + hdd_images[id].pos = sector + count - non_transferred_sectors - 1; + } else { + int i; - if (fseeko64(hdd_images[id].file, ((uint64_t)(sector) << 9LL) + hdd_images[id].base, SEEK_SET) == -1) { - fatal("Hard disk image %i: Read error during seek\n", id); - return; - } + if (fseeko64(hdd_images[id].file, ((uint64_t)(sector) << 9LL) + hdd_images[id].base, SEEK_SET) == -1) { + fatal("Hard disk image %i: Read error during seek\n", id); + return; + } - for (i = 0; i < count; i++) { - if (feof(hdd_images[id].file)) - break; + for (i = 0; i < count; i++) { + if (feof(hdd_images[id].file)) + break; - hdd_images[id].pos = sector + i; - fread(buffer + (i << 9), 1, 512, hdd_images[id].file); - } -} - - -uint32_t -hdd_sectors(uint8_t id) -{ - fseeko64(hdd_images[id].file, 0, SEEK_END); - return (uint32_t) ((ftello64(hdd_images[id].file) - hdd_images[id].base) >> 9); -} - - -int -hdd_image_read_ex(uint8_t id, uint32_t sector, uint32_t count, uint8_t *buffer) -{ - uint32_t transfer_sectors = count; - uint32_t sectors = hdd_sectors(id); - - if ((sectors - sector) < transfer_sectors) - transfer_sectors = sectors - sector; - - hdd_image_read(id, sector, transfer_sectors, buffer); - - if (count != transfer_sectors) - return 1; - return 0; -} - - -void -hdd_image_write(uint8_t id, uint32_t sector, uint32_t count, uint8_t *buffer) -{ - int i; - - if (fseeko64(hdd_images[id].file, ((uint64_t)(sector) << 9LL) + hdd_images[id].base, SEEK_SET) == -1) { - fatal("Hard disk image %i: Write error during seek\n", id); - return; - } - - for (i = 0; i < count; i++) { - if (feof(hdd_images[id].file)) - break; - - hdd_images[id].pos = sector + i; - fwrite(buffer + (i << 9), 512, 1, hdd_images[id].file); - } -} - - -int -hdd_image_write_ex(uint8_t id, uint32_t sector, uint32_t count, uint8_t *buffer) -{ - uint32_t transfer_sectors = count; - uint32_t sectors = hdd_sectors(id); - - if ((sectors - sector) < transfer_sectors) - transfer_sectors = sectors - sector; - - hdd_image_write(id, sector, transfer_sectors, buffer); - - if (count != transfer_sectors) - return 1; - return 0; -} - - -void -hdd_image_zero(uint8_t id, uint32_t sector, uint32_t count) -{ - uint32_t i = 0; - - memset(empty_sector, 0, 512); - - if (fseeko64(hdd_images[id].file, ((uint64_t)(sector) << 9LL) + hdd_images[id].base, SEEK_SET) == -1) { - fatal("Hard disk image %i: Zero error during seek\n", id); - return; - } - - for (i = 0; i < count; i++) { - if (feof(hdd_images[id].file)) - break; - - hdd_images[id].pos = sector + i; - fwrite(empty_sector, 512, 1, hdd_images[id].file); - } -} - - -int -hdd_image_zero_ex(uint8_t id, uint32_t sector, uint32_t count) -{ - uint32_t transfer_sectors = count; - uint32_t sectors = hdd_sectors(id); - - if ((sectors - sector) < transfer_sectors) - transfer_sectors = sectors - sector; - - hdd_image_zero(id, sector, transfer_sectors); - - if (count != transfer_sectors) - return 1; - return 0; + hdd_images[id].pos = sector + i; + fread(buffer + (i << 9), 1, 512, hdd_images[id].file); + } + } } uint32_t hdd_image_get_last_sector(uint8_t id) { - return hdd_images[id].last_sector; + return hdd_images[id].last_sector; +} + + +uint32_t +hdd_sectors(uint8_t id) +{ + return hdd_image_get_last_sector(id) - 1; +} + + +int +hdd_image_read_ex(uint8_t id, uint32_t sector, uint32_t count, uint8_t *buffer) +{ + uint32_t transfer_sectors = count; + uint32_t sectors = hdd_sectors(id); + + if ((sectors - sector) < transfer_sectors) + transfer_sectors = sectors - sector; + + hdd_image_read(id, sector, transfer_sectors, buffer); + + if (count != transfer_sectors) + return 1; + return 0; +} + + +void +hdd_image_write(uint8_t id, uint32_t sector, uint32_t count, uint8_t *buffer) +{ + if (hdd_images[id].type == HDD_IMAGE_VHD) { + int non_transferred_sectors = mvhd_write_sectors(hdd_images[id].vhd, sector, count, buffer); + hdd_images[id].pos = sector + count - non_transferred_sectors - 1; + } else { + int i; + + if (fseeko64(hdd_images[id].file, ((uint64_t)(sector) << 9LL) + hdd_images[id].base, SEEK_SET) == -1) { + fatal("Hard disk image %i: Write error during seek\n", id); + return; + } + + for (i = 0; i < count; i++) { + if (feof(hdd_images[id].file)) + break; + + hdd_images[id].pos = sector + i; + fwrite(buffer + (i << 9), 512, 1, hdd_images[id].file); + } + } +} + + +int +hdd_image_write_ex(uint8_t id, uint32_t sector, uint32_t count, uint8_t *buffer) +{ + uint32_t transfer_sectors = count; + uint32_t sectors = hdd_sectors(id); + + if ((sectors - sector) < transfer_sectors) + transfer_sectors = sectors - sector; + + hdd_image_write(id, sector, transfer_sectors, buffer); + + if (count != transfer_sectors) + return 1; + return 0; +} + + +void +hdd_image_zero(uint8_t id, uint32_t sector, uint32_t count) +{ + if (hdd_images[id].type == HDD_IMAGE_VHD) { + int non_transferred_sectors = mvhd_format_sectors(hdd_images[id].vhd, sector, count); + hdd_images[id].pos = sector + count - non_transferred_sectors - 1; + } else { + uint32_t i = 0; + + memset(empty_sector, 0, 512); + + if (fseeko64(hdd_images[id].file, ((uint64_t)(sector) << 9LL) + hdd_images[id].base, SEEK_SET) == -1) { + fatal("Hard disk image %i: Zero error during seek\n", id); + return; + } + + for (i = 0; i < count; i++) { + if (feof(hdd_images[id].file)) + break; + + hdd_images[id].pos = sector + i; + fwrite(empty_sector, 512, 1, hdd_images[id].file); + } + } +} + + +int +hdd_image_zero_ex(uint8_t id, uint32_t sector, uint32_t count) +{ + uint32_t transfer_sectors = count; + uint32_t sectors = hdd_sectors(id); + + if ((sectors - sector) < transfer_sectors) + transfer_sectors = sectors - sector; + + hdd_image_zero(id, sector, transfer_sectors); + + if (count != transfer_sectors) + return 1; + return 0; } uint32_t hdd_image_get_pos(uint8_t id) { - return hdd_images[id].pos; + return hdd_images[id].pos; } uint8_t hdd_image_get_type(uint8_t id) { - return hdd_images[id].type; + return hdd_images[id].type; } void hdd_image_unload(uint8_t id, int fn_preserve) { - if (wcslen(hdd[id].fn) == 0) - return; + if (wcslen(hdd[id].fn) == 0) + return; - if (hdd_images[id].loaded) { - if (hdd_images[id].file != NULL) { - fclose(hdd_images[id].file); - hdd_images[id].file = NULL; + if (hdd_images[id].loaded) { + if (hdd_images[id].file != NULL) { + fclose(hdd_images[id].file); + hdd_images[id].file = NULL; + } else if (hdd_images[id].vhd != NULL) { + mvhd_close(hdd_images[id].vhd); + hdd_images[id].vhd = NULL; + } + hdd_images[id].loaded = 0; } - hdd_images[id].loaded = 0; - } - hdd_images[id].last_sector = -1; + hdd_images[id].last_sector = -1; - memset(hdd[id].prev_fn, 0, sizeof(hdd[id].prev_fn)); - if (fn_preserve) - wcscpy(hdd[id].prev_fn, hdd[id].fn); - memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); + memset(hdd[id].prev_fn, 0, sizeof(hdd[id].prev_fn)); + if (fn_preserve) + wcscpy(hdd[id].prev_fn, hdd[id].fn); + memset(hdd[id].fn, 0, sizeof(hdd[id].fn)); } void hdd_image_close(uint8_t id) { - hdd_image_log("hdd_image_close(%i)\n", id); + hdd_image_log("hdd_image_close(%i)\n", id); - if (!hdd_images[id].loaded) - return; + if (!hdd_images[id].loaded) + return; - if (hdd_images[id].file != NULL) { - fclose(hdd_images[id].file); - hdd_images[id].file = NULL; - } - memset(&hdd_images[id], 0, sizeof(hdd_image_t)); - hdd_images[id].loaded = 0; + if (hdd_images[id].file != NULL) { + fclose(hdd_images[id].file); + hdd_images[id].file = NULL; + } else if (hdd_images[id].vhd != NULL) { + mvhd_close(hdd_images[id].vhd); + hdd_images[id].vhd = NULL; + } + + memset(&hdd_images[id], 0, sizeof(hdd_image_t)); + hdd_images[id].loaded = 0; } diff --git a/src/disk/minivhd/CREDITS.md b/src/disk/minivhd/CREDITS.md new file mode 100644 index 000000000..c494d4e43 --- /dev/null +++ b/src/disk/minivhd/CREDITS.md @@ -0,0 +1,12 @@ +# Credits +MiniVHD Copyright (c) 2019 Sherman Perry + +MiniVHD was made possible with the help of the following projects + +### libxml2 +**Project Home:** http://www.xmlsoft.org/ +**License:** MIT (see src/libxml2_encoding.c for details) + +### cwalk +**Project Home:** https://likle.github.io/cwalk/ +**Licence:** MIT (https://github.com/likle/cwalk/blob/master/LICENSE.md) diff --git a/src/disk/minivhd/LICENSE b/src/disk/minivhd/LICENSE new file mode 100644 index 000000000..2997be44a --- /dev/null +++ b/src/disk/minivhd/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Sherman Perry + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/src/disk/minivhd/cwalk.c b/src/disk/minivhd/cwalk.c new file mode 100644 index 000000000..f0c48427c --- /dev/null +++ b/src/disk/minivhd/cwalk.c @@ -0,0 +1,1424 @@ +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 +#endif +#include +#include +#include +#include +#include +#include "cwalk.h" +/** + * We try to default to a different path style depending on the operating + * system. So this should detect whether we should use windows or unix paths. + */ +#if defined(WIN32) || defined(_WIN32) || \ + defined(__WIN32) && !defined(__CYGWIN__) +static enum cwk_path_style path_style = CWK_STYLE_WINDOWS; +#else +static enum cwk_path_style path_style = CWK_STYLE_UNIX; +#endif + +/** + * This is a list of separators used in different styles. Windows can read + * multiple separators, but it generally outputs just a backslash. The output + * will always use the first character for the output. + */ +static const char *separators[] = {[CWK_STYLE_WINDOWS] = "\\/", + [CWK_STYLE_UNIX] = "/"}; + +/** + * A joined path represents multiple path strings which are concatenated, but + * not (necessarily) stored in contiguous memory. The joined path allows to + * iterate over the segments as if it was one piece of path. + */ +struct cwk_segment_joined +{ + struct cwk_segment segment; + const char **paths; + size_t path_index; +}; + +static size_t cwk_path_output_sized(char *buffer, size_t buffer_size, + size_t position, const char *str, size_t length) +{ + size_t amount_written; + + // First we determine the amount which we can write to the buffer. There are + // three cases. In the first case we have enough to store the whole string in + // it. In the second one we can only store a part of it, and in the third we + // have no space left. + if (buffer_size > position + length) { + amount_written = length; + } else if (buffer_size > position) { + amount_written = buffer_size - position; + } else { + amount_written = 0; + } + + // If we actually want to write out something we will do that here. We will + // always append a '\0', this way we are guaranteed to have a valid string at + // all times. + if (amount_written > 0) { + memmove(&buffer[position], str, amount_written); + } + + // Return the theoretical length which would have been written when everything + // would have fit in the buffer. + return length; +} + +static size_t cwk_path_output_current(char *buffer, size_t buffer_size, + size_t position) +{ + // We output a "current" directory, which is a single character. This + // character is currently not style dependant. + return cwk_path_output_sized(buffer, buffer_size, position, ".", 1); +} + +static size_t cwk_path_output_back(char *buffer, size_t buffer_size, + size_t position) +{ + // We output a "back" directory, which ahs two characters. This + // character is currently not style dependant. + return cwk_path_output_sized(buffer, buffer_size, position, "..", 2); +} + +static size_t cwk_path_output_separator(char *buffer, size_t buffer_size, + size_t position) +{ + // We output a separator, which is a single character. + return cwk_path_output_sized(buffer, buffer_size, position, + separators[path_style], 1); +} + +static size_t cwk_path_output_dot(char *buffer, size_t buffer_size, + size_t position) +{ + // We output a dot, which is a single character. This is used for extensions. + return cwk_path_output_sized(buffer, buffer_size, position, ".", 1); +} + +static size_t cwk_path_output(char *buffer, size_t buffer_size, size_t position, + const char *str) +{ + size_t length; + + // This just does a sized output internally, but first measuring the + // null-terminated string. + length = strlen(str); + return cwk_path_output_sized(buffer, buffer_size, position, str, length); +} + +static void cwk_path_terminate_output(char *buffer, size_t buffer_size, + size_t pos) +{ + if (buffer_size > 0) { + if (pos >= buffer_size) { + buffer[buffer_size - 1] = '\0'; + } else { + buffer[pos] = '\0'; + } + } +} + +static bool cwk_path_is_string_equal(const char *first, const char *second, + size_t n) +{ + // If the path style is UNIX, we will compare case sensitively. This can be + // done easily using strncmp. + if (path_style == CWK_STYLE_UNIX) { + return strncmp(first, second, n) == 0; + } + + // However, if this is windows we will have to compare case insensitively. + // Since there is no standard method to do that we will have to do it on our + // own. + while (*first && *second && n > 0) { + // We can consider the string to be not equal if the two lowercase + // characters are not equal. + if (tolower(*first++) != tolower(*second++)) { + return false; + } + + --n; + } + + // We can consider the string to be equal if we either reached n == 0 or both + // cursors point to a null character. + return n == 0 || (*first == '\0' && *second == '\0'); +} + +static const char *cwk_path_find_next_stop(const char *c) +{ + // We just move forward until we find a '\0' or a separator, which will be our + // next "stop". + while (*c != '\0' && !cwk_path_is_separator(c)) { + ++c; + } + + // Return the pointer of the next stop. + return c; +} + +static const char *cwk_path_find_previous_stop(const char *begin, const char *c) +{ + // We just move back until we find a separator or reach the beginning of the + // path, which will be our previous "stop". + while (c > begin && !cwk_path_is_separator(c)) { + --c; + } + + // Return the pointer to the previous stop. We have to return the first + // character after the separator, not on the separator itself. + if (cwk_path_is_separator(c)) { + return c + 1; + } else { + return c; + } +} + +static bool cwk_path_get_first_segment_without_root(const char *path, + const char *segments, struct cwk_segment *segment) +{ + // Let's remember the path. We will move the path pointer afterwards, that's + // why this has to be done first. + segment->path = path; + segment->segments = segments; + + // Now let's check whether this is an empty string. An empty string has no + // segment it could use. + if (*segments == '\0') { + return false; + } + + // If the string starts with separators, we will jump over those. If there is + // only a slash and a '\0' after it, we can't determine the first segment + // since there is none. + while (cwk_path_is_separator(segments)) { + ++segments; + if (*segments == '\0') { + return false; + } + } + + // So this is the beginning of our segment. + segment->begin = segments; + + // Now let's determine the end of the segment, which we do by moving the path + // pointer further until we find a separator. + segments = cwk_path_find_next_stop(segments); + + // And finally, calculate the size of the segment by subtracting the position + // from the end. + segment->size = segments - segment->begin; + segment->end = segments; + + // Tell the caller that we found a segment. + return true; +} + +static bool cwk_path_get_last_segment_without_root(const char *path, + struct cwk_segment *segment) +{ + // Now this is fairly similar to the normal algorithm, however, it will assume + // that there is no root in the path. So we grab the first segment at this + // position, assuming there is no root. + if (!cwk_path_get_first_segment_without_root(path, path, segment)) { + return false; + } + + // Now we find our last segment. The segment struct of the caller + // will contain the last segment, since the function we call here will not + // change the segment struct when it reaches the end. + while (cwk_path_get_next_segment(segment)) { + // We just loop until there is no other segment left. + } + + return true; +} + +static bool cwk_path_get_first_segment_joined(const char **paths, + struct cwk_segment_joined *sj) +{ + bool result; + + // Prepare the first segment. We position the joined segment on the first path + // and assign the path array to the struct. + sj->path_index = 0; + sj->paths = paths; + + // We loop through all paths until we find one which has a segment. The result + // is stored in a variable, so we can let the caller know whether we found one + // or not. + result = false; + while (paths[sj->path_index] != NULL && + (result = cwk_path_get_first_segment(paths[sj->path_index], + &sj->segment)) == false) { + ++sj->path_index; + } + + return result; +} + +static bool cwk_path_get_next_segment_joined(struct cwk_segment_joined *sj) +{ + bool result; + + if (sj->paths[sj->path_index] == NULL) { + // We reached already the end of all paths, so there is no other segment + // left. + return false; + } else if (cwk_path_get_next_segment(&sj->segment)) { + // There was another segment on the current path, so we are good to + // continue. + return true; + } + + // We try to move to the next path which has a segment available. We must at + // least move one further since the current path reached the end. + result = false; + + do { + ++sj->path_index; + + // And we obviously have to stop this loop if there are no more paths left. + if (sj->paths[sj->path_index] == NULL) { + break; + } + + // Grab the first segment of the next path and determine whether this path + // has anything useful in it. There is one more thing we have to consider + // here - for the first time we do this we want to skip the root, but + // afterwards we will consider that to be part of the segments. + result = cwk_path_get_first_segment_without_root(sj->paths[sj->path_index], + sj->paths[sj->path_index], &sj->segment); + + } while (!result); + + // Finally, report the result back to the caller. + return result; +} + +static bool cwk_path_get_previous_segment_joined(struct cwk_segment_joined *sj) +{ + bool result; + + if (*sj->paths == NULL) { + // It's possible that there is no initialized segment available in the + // struct since there are no paths. In that case we can return false, since + // there is no previous segment. + return false; + } else if (cwk_path_get_previous_segment(&sj->segment)) { + // Now we try to get the previous segment from the current path. If we can + // do that successfully, we can let the caller know that we found one. + return true; + } + + result = false; + + do { + // We are done once we reached index 0. In that case there are no more + // segments left. + if (sj->path_index == 0) { + break; + } + + // There is another path which we have to inspect. So we decrease the path + // index. + --sj->path_index; + + // If this is the first path we will have to consider that this path might + // include a root, otherwise we just treat is as a segment. + if (sj->path_index == 0) { + result = cwk_path_get_last_segment(sj->paths[sj->path_index], + &sj->segment); + } else { + result = cwk_path_get_last_segment_without_root(sj->paths[sj->path_index], + &sj->segment); + } + + } while (!result); + + return result; +} + +static bool cwk_path_segment_back_will_be_removed(struct cwk_segment_joined *sj) +{ + enum cwk_segment_type type; + int counter; + + // We are handling back segments here. We must verify how many back segments + // and how many normal segments come before this one to decide whether we keep + // or remove it. + + // The counter determines how many normal segments are our current segment, + // which will popped off before us. If the counter goes above zero it means + // that our segment will be popped as well. + counter = 0; + + // We loop over all previous segments until we either reach the beginning, + // which means our segment will not be dropped or the counter goes above zero. + while (cwk_path_get_previous_segment_joined(sj)) { + + // Now grab the type. The type determines whether we will increase or + // decrease the counter. We don't handle a CWK_CURRENT frame here since it + // has no influence. + type = cwk_path_get_segment_type(&sj->segment); + if (type == CWK_NORMAL) { + // This is a normal segment. The normal segment will increase the counter + // since it neutralizes one back segment. If we go above zero we can + // return immediately. + ++counter; + if (counter > 0) { + return true; + } + } else if (type == CWK_BACK) { + // A CWK_BACK segment will reduce the counter by one. We can not remove a + // back segment as long we are not above zero since we don't have the + // opposite normal segment which we would remove. + --counter; + } + } + + // We never got a count larger than zero, so we will keep this segment alive. + return false; +} + +static bool cwk_path_segment_normal_will_be_removed( + struct cwk_segment_joined *sj) +{ + enum cwk_segment_type type; + int counter; + + // The counter determines how many segments are above our current segment, + // which will popped off before us. If the counter goes below zero it means + // that our segment will be popped as well. + counter = 0; + + // We loop over all following segments until we either reach the end, which + // means our segment will not be dropped or the counter goes below zero. + while (cwk_path_get_next_segment_joined(sj)) { + + // First, grab the type. The type determines whether we will increase or + // decrease the counter. We don't handle a CWK_CURRENT frame here since it + // has no influence. + type = cwk_path_get_segment_type(&sj->segment); + if (type == CWK_NORMAL) { + // This is a normal segment. The normal segment will increase the counter + // since it will be removed by a "../" before us. + ++counter; + } else if (type == CWK_BACK) { + // A CWK_BACK segment will reduce the counter by one. If we are below zero + // we can return immediately. + --counter; + if (counter < 0) { + return true; + } + } + } + + // We never got a negative count, so we will keep this segment alive. + return false; +} + +static bool +cwk_path_segment_will_be_removed(const struct cwk_segment_joined *sj, + bool absolute) +{ + enum cwk_segment_type type; + struct cwk_segment_joined sjc; + + // We copy the joined path so we don't need to modify it. + sjc = *sj; + + // First we check whether this is a CWK_CURRENT or CWK_BACK segment, since + // those will always be dropped. + type = cwk_path_get_segment_type(&sj->segment); + if (type == CWK_CURRENT) { + return true; + } else if (type == CWK_BACK && absolute) { + return true; + } else if (type == CWK_BACK) { + return cwk_path_segment_back_will_be_removed(&sjc); + } else { + return cwk_path_segment_normal_will_be_removed(&sjc); + } +} + +static bool +cwk_path_segment_joined_skip_invisible(struct cwk_segment_joined *sj, + bool absolute) +{ + while (cwk_path_segment_will_be_removed(sj, absolute)) { + if (!cwk_path_get_next_segment_joined(sj)) { + return false; + } + } + + return true; +} + +static void cwk_path_get_root_windows(const char *path, size_t *length) +{ + const char *c; + bool is_device_path; + + // A device path is a path which starts with "\\." or "\\?". A device path can + // be a UNC path as well, in which case it will take up one more segment. + is_device_path = false; + + // We can not determine the root if this is an empty string. So we set the + // root to NULL and the length to zero and cancel the whole thing. + c = path; + *length = 0; + if (!*c) { + return; + } + + // Now we have to verify whether this is a windows network path (UNC), which + // we will consider our root. + if (cwk_path_is_separator(c)) { + ++c; + + // Check whether the path starts with a single back slash, which means this + // is not a network path - just a normal path starting with a backslash. + if (!cwk_path_is_separator(c)) { + // Okay, this is not a network path but we still use the backslash as a + // root. + ++(*length); + return; + } + + // Yes, this is a network or device path. Skip the previous separator. Now + // we need to determine whether this is a device path. We might advance one + // character here if the server name starts with a '?' or a '.', but that's + // fine since we will search for a separator afterwards anyway. + ++c; + is_device_path = (*c == '?' || *c == '.') && cwk_path_is_separator(++c); + if (is_device_path) { + // That's a device path, and the root must be either "\\.\" or "\\?\" + // which is 4 characters long. (at least that's how Windows + // GetFullPathName behaves.) + *length = 4; + return; + } + + // We will grab anything up to the next stop. The next top might be a '\0' + // or another separator. That will be the server name. + c = cwk_path_find_next_stop(c); + + // If this is a separator and not the end of a string we wil have to include + // it. However, if this is a '\0' we must not skip it. + while (cwk_path_is_separator(c)) { + ++c; + } + + // We are now skipping the shared folder name, which will end after the + // next stop. + c = cwk_path_find_next_stop(c); + + // Then there might be a separator at the end. We will include that as well, + // it will mark the path as absolute. + if (cwk_path_is_separator(c)) { + ++c; + } + + // Finally, calculate the size of the root. + *length = c - path; + return; + } + + // Move to the next and check whether this is a colon. + if (*++c == ':') { + *length = 2; + + // Now check whether this is a backslash (or slash). If it is not, we could + // assume that the next character is a '\0' if it is a valid path. However, + // we will not assume that - since ':' is not valid in a path it must be a + // mistake by the caller than. We will try to understand it anyway. + if (cwk_path_is_separator(++c)) { + *length = 3; + } + } +} + +static void cwk_path_get_root_unix(const char *path, size_t *length) +{ + // The slash of the unix path represents the root. There is no root if there + // is no slash. + if (cwk_path_is_separator(path)) { + *length = 1; + } else { + *length = 0; + } +} + +static bool cwk_path_is_root_absolute(const char *path, size_t length) +{ + // This is definitely not absolute if there is no root. + if (length == 0) { + return false; + } + + // If there is a separator at the end of the root, we can safely consider this + // to be an absolute path. + return cwk_path_is_separator(&path[length - 1]); +} + +static size_t cwk_path_join_and_normalize_multiple(const char **paths, + char *buffer, size_t buffer_size) +{ + size_t pos; + bool absolute, has_segment_output; + struct cwk_segment_joined sj; + + // We initialize the position after the root, which should get us started. + cwk_path_get_root(paths[0], &pos); + + // Determine whether the path is absolute or not. We need that to determine + // later on whether we can remove superfluous "../" or not. + absolute = cwk_path_is_root_absolute(paths[0], pos); + + // First copy the root to the output. We will not modify the root. + cwk_path_output_sized(buffer, buffer_size, 0, paths[0], pos); + + // So we just grab the first segment. If there is no segment we will always + // output a "/", since we currently only support absolute paths here. + if (!cwk_path_get_first_segment_joined(paths, &sj)) { + goto done; + } + + // Let's assume that we don't have any segment output for now. We will toggle + // this flag once there is some output. + has_segment_output = false; + + do { + // Check whether we have to drop this segment because of resolving a + // relative path or because it is a CWK_CURRENT segment. + if (cwk_path_segment_will_be_removed(&sj, absolute)) { + continue; + } + + // Remember that we have segment output, so we can handle the trailing slash + // later on. This is necessary since we might have segments but they are all + // removed. + has_segment_output = true; + + // Write out the segment but keep in mind that we need to follow the + // buffer size limitations. That's why we use the path output functions + // here. + pos += cwk_path_output_sized(buffer, buffer_size, pos, sj.segment.begin, + sj.segment.size); + pos += cwk_path_output_separator(buffer, buffer_size, pos); + } while (cwk_path_get_next_segment_joined(&sj)); + + // Remove the trailing slash, but only if we have segment output. We don't + // want to remove anything from the root. + if (has_segment_output) { + --pos; + } else if (pos == 0) { + // This may happen if the path is absolute and all segments have been + // removed. We can not have an empty output - and empty output means we stay + // in the current directory. So we will output a ".". + assert(absolute == false); + pos += cwk_path_output_current(buffer, buffer_size, pos); + } + + // We must append a '\0' in any case, unless the buffer size is zero. If the + // buffer size is zero, which means we can not. +done: + cwk_path_terminate_output(buffer, buffer_size, pos); + + // And finally let our caller know about the total size of the normalized + // path. + return pos; +} + +size_t cwk_path_get_absolute(const char *base, const char *path, char *buffer, + size_t buffer_size) +{ + size_t i; + const char *paths[4]; + + // The basename should be an absolute path if the caller is using the API + // correctly. However, he might not and in that case we will append a fake + // root at the beginning. + if (cwk_path_is_absolute(base)) { + i = 0; + } else { + paths[0] = "/"; + i = 1; + } + + if (cwk_path_is_absolute(path)) { + // If the submitted path is not relative the base path becomes irrelevant. + // We will only normalize the submitted path instead. + paths[i++] = path; + paths[i] = NULL; + } else { + // Otherwise we append the relative path to the base path and normalize it. + // The result will be a new absolute path. + paths[i++] = base; + paths[i++] = path; + paths[i] = NULL; + } + + // Finally join everything together and normalize it. + return cwk_path_join_and_normalize_multiple(paths, buffer, buffer_size); +} + +static void cwk_path_skip_segments_until_diverge(struct cwk_segment_joined *bsj, + struct cwk_segment_joined *osj, bool absolute, bool *base_available, + bool *other_available) +{ + // Now looping over all segments until they start to diverge. A path may + // diverge if two segments are not equal or if one path reaches the end. + do { + + // Check whether there is anything available after we skip everything which + // is invisible. We do that for both paths, since we want to let the caller + // know which path has some trailing segments after they diverge. + *base_available = cwk_path_segment_joined_skip_invisible(bsj, absolute); + *other_available = cwk_path_segment_joined_skip_invisible(osj, absolute); + + // We are done if one or both of those paths reached the end. They either + // diverge or both reached the end - but in both cases we can not continue + // here. + if (!*base_available || !*other_available) { + break; + } + + // Compare the content of both segments. We are done if they are not equal, + // since they diverge. + if (!cwk_path_is_string_equal(bsj->segment.begin, osj->segment.begin, + bsj->segment.size)) { + break; + } + + // We keep going until one of those segments reached the end. The next + // segment might be invisible, but we will check for that in the beginning + // of the loop once again. + *base_available = cwk_path_get_next_segment_joined(bsj); + *other_available = cwk_path_get_next_segment_joined(osj); + } while (*base_available && *other_available); +} + +size_t cwk_path_get_relative(const char *base_directory, const char *path, + char *buffer, size_t buffer_size) +{ + size_t pos, base_root_length, path_root_length; + bool absolute, base_available, other_available, has_output; + const char *base_paths[2], *other_paths[2]; + struct cwk_segment_joined bsj, osj; + + pos = 0; + + // First we compare the roots of those two paths. If the roots are not equal + // we can't continue, since there is no way to get a relative path from + // different roots. + cwk_path_get_root(base_directory, &base_root_length); + cwk_path_get_root(path, &path_root_length); + if (!cwk_path_is_string_equal(base_directory, path, base_root_length)) { + return pos; + } + + // Verify whether this is an absolute path. We need to know that since we can + // remove all back-segments if it is. + absolute = cwk_path_is_root_absolute(base_directory, base_root_length); + + // Initialize our joined segments. This will allow us to use the internal + // functions to skip until diverge and invisible. We only have one path in + // them though. + base_paths[0] = base_directory; + base_paths[1] = NULL; + other_paths[0] = path; + other_paths[1] = NULL; + cwk_path_get_first_segment_joined(base_paths, &bsj); + cwk_path_get_first_segment_joined(other_paths, &osj); + + // Okay, now we skip until the segments diverge. We don't have anything to do + // with the segments which are equal. + cwk_path_skip_segments_until_diverge(&bsj, &osj, absolute, &base_available, + &other_available); + + // Assume there is no output until we have got some. We will need this + // information later on to remove trailing slashes or alternatively output a + // current-segment. + has_output = false; + + // So if we still have some segments left in the base path we will now output + // a back segment for all of them. + if (base_available) { + do { + // Skip any invisible segment. We don't care about those and we don't need + // to navigate back because of them. + if (!cwk_path_segment_joined_skip_invisible(&bsj, absolute)) { + break; + } + + // Toggle the flag if we have output. We need to remember that, since we + // want to remove the trailing slash. + has_output = true; + + // Output the back segment and a separator. No need to worry about the + // superfluous segment since it will be removed later on. + pos += cwk_path_output_back(buffer, buffer_size, pos); + pos += cwk_path_output_separator(buffer, buffer_size, pos); + } while (cwk_path_get_next_segment_joined(&bsj)); + } + + // And if we have some segments available of the target path we will output + // all of those. + if (other_available) { + do { + // Again, skip any invisible segments since we don't need to navigate into + // them. + if (!cwk_path_segment_joined_skip_invisible(&osj, absolute)) { + break; + } + + // Toggle the flag if we have output. We need to remember that, since we + // want to remove the trailing slash. + has_output = true; + + // Output the current segment and a separator. No need to worry about the + // superfluous segment since it will be removed later on. + pos += cwk_path_output_sized(buffer, buffer_size, pos, osj.segment.begin, + osj.segment.size); + pos += cwk_path_output_separator(buffer, buffer_size, pos); + } while (cwk_path_get_next_segment_joined(&osj)); + } + + // If we have some output by now we will have to remove the trailing slash. We + // simply do that by moving back one character. The terminate output function + // will then place the '\0' on this position. Otherwise, if there is no + // output, we will have to output a "current directory", since the target path + // points to the base path. + if (has_output) { + --pos; + } else { + pos += cwk_path_output_current(buffer, buffer_size, pos); + } + + // Finally, we can terminate the output - which means we place a '\0' at the + // current position or at the end of the buffer. + cwk_path_terminate_output(buffer, buffer_size, pos); + + return pos; +} + +size_t cwk_path_join(const char *path_a, const char *path_b, char *buffer, + size_t buffer_size) +{ + const char *paths[3]; + + // This is simple. We will just create an array with the two paths which we + // wish to join. + paths[0] = path_a; + paths[1] = path_b; + paths[2] = NULL; + + // And then call the join and normalize function which will do the hard work + // for us. + return cwk_path_join_and_normalize_multiple(paths, buffer, buffer_size); +} + +size_t cwk_path_join_multiple(const char **paths, char *buffer, + size_t buffer_size) +{ + // We can just call the internal join and normalize function for this one, + // since it will handle everything. + return cwk_path_join_and_normalize_multiple(paths, buffer, buffer_size); +} + +void cwk_path_get_root(const char *path, size_t *length) +{ + // We use a different implementation here based on the configuration of the + // library. + if (path_style == CWK_STYLE_WINDOWS) { + cwk_path_get_root_windows(path, length); + } else { + cwk_path_get_root_unix(path, length); + } +} + +size_t cwk_path_change_root(const char *path, const char *new_root, + char *buffer, size_t buffer_size) +{ + const char *tail; + size_t root_length, path_length, tail_length, new_root_length, new_path_size; + + // First we need to determine the actual size of the root which we will + // change. + cwk_path_get_root(path, &root_length); + + // Now we determine the sizes of the new root and the path. We need that to + // determine the size of the part after the root (the tail). + new_root_length = strlen(new_root); + path_length = strlen(path); + + // Okay, now we calculate the position of the tail and the length of it. + tail = path + root_length; + tail_length = path_length - root_length; + + // We first output the tail and then the new root, that's because the source + // path and the buffer may be overlapping. This way the root will not + // overwrite the tail. + cwk_path_output_sized(buffer, buffer_size, new_root_length, tail, + tail_length); + cwk_path_output_sized(buffer, buffer_size, 0, new_root, new_root_length); + + // Finally we calculate the size o the new path and terminate the output with + // a '\0'. + new_path_size = tail_length + new_root_length; + cwk_path_terminate_output(buffer, buffer_size, new_path_size); + + return new_path_size; +} + +bool cwk_path_is_absolute(const char *path) +{ + size_t length; + + // We grab the root of the path. This root does not include the first + // separator of a path. + cwk_path_get_root(path, &length); + + // Now we can determine whether the root is absolute or not. + return cwk_path_is_root_absolute(path, length); +} + +bool cwk_path_is_relative(const char *path) +{ + // The path is relative if it is not absolute. + return !cwk_path_is_absolute(path); +} + +void cwk_path_get_basename(const char *path, const char **basename, + size_t *length) +{ + struct cwk_segment segment; + + // We get the last segment of the path. The last segment will contain the + // basename if there is any. If there are no segments we will set the basename + // to NULL and the length to 0. + if (!cwk_path_get_last_segment(path, &segment)) { + *basename = NULL; + *length = 0; + return; + } + + // Now we can just output the segment contents, since that's our basename. + // There might be trailing separators after the basename, but the size does + // not include those. + *basename = segment.begin; + *length = segment.size; +} + +size_t cwk_path_change_basename(const char *path, const char *new_basename, + char *buffer, size_t buffer_size) +{ + struct cwk_segment segment; + size_t pos, root_size, new_basename_size; + + // First we try to get the last segment. We may only have a root without any + // segments, in which case we will create one. + if (!cwk_path_get_last_segment(path, &segment)) { + + // So there is no segment in this path. First we grab the root and output + // that. We are not going to modify the root in any way. + cwk_path_get_root(path, &root_size); + pos = cwk_path_output_sized(buffer, buffer_size, 0, path, root_size); + + // We have to trim the separators from the beginning of the new basename. + // This is quite easy to do. + while (cwk_path_is_separator(new_basename)) { + ++new_basename; + } + + // Now we measure the length of the new basename, this is a two step + // process. First we find the '\0' character at the end of the string. + new_basename_size = 0; + while (new_basename[new_basename_size]) { + ++new_basename_size; + } + + // And then we trim the separators at the end of the basename until we reach + // the first valid character. + while (new_basename_size > 0 && + cwk_path_is_separator(&new_basename[new_basename_size - 1])) { + --new_basename_size; + } + + // Now we will output the new basename after the root. + pos += cwk_path_output_sized(buffer, buffer_size, pos, new_basename, + new_basename_size); + + // And finally terminate the output and return the total size of the path. + cwk_path_terminate_output(buffer, buffer_size, pos); + return pos; + } + + // If there is a last segment we can just forward this call, which is fairly + // easy. + return cwk_path_change_segment(&segment, new_basename, buffer, buffer_size); +} + +void cwk_path_get_dirname(const char *path, size_t *length) +{ + struct cwk_segment segment; + + // We get the last segment of the path. The last segment will contain the + // basename if there is any. If there are no segments we will set the length + // to 0. + if (!cwk_path_get_last_segment(path, &segment)) { + *length = 0; + return; + } + + // We can now return the length from the beginning of the string up to the + // beginning of the last segment. + *length = segment.begin - path; +} + +bool cwk_path_get_extension(const char *path, const char **extension, + size_t *length) +{ + struct cwk_segment segment; + const char *c; + + // We get the last segment of the path. The last segment will contain the + // extension if there is any. + if (!cwk_path_get_last_segment(path, &segment)) { + return false; + } + + // Now we search for a dot within the segment. If there is a dot, we consider + // the rest of the segment the extension. We do this from the end towards the + // beginning, since we want to find the last dot. + for (c = segment.end; c >= segment.begin; --c) { + if (*c == '.') { + // Okay, we found an extension. We can stop looking now. + *extension = c; + *length = segment.end - c; + return true; + } + } + + // We couldn't find any extension. + return false; +} + +bool cwk_path_has_extension(const char *path) +{ + const char *extension; + size_t length; + + // We just wrap the get_extension call which will then do the work for us. + return cwk_path_get_extension(path, &extension, &length); +} + +size_t cwk_path_change_extension(const char *path, const char *new_extension, + char *buffer, size_t buffer_size) +{ + struct cwk_segment segment; + const char *c, *old_extension; + size_t pos, root_size, trail_size, new_extension_size; + + // First we try to get the last segment. We may only have a root without any + // segments, in which case we will create one. + if (!cwk_path_get_last_segment(path, &segment)) { + + // So there is no segment in this path. First we grab the root and output + // that. We are not going to modify the root in any way. If there is no + // root, this will end up with a root size 0, and nothing will be written. + cwk_path_get_root(path, &root_size); + pos = cwk_path_output_sized(buffer, buffer_size, 0, path, root_size); + + // Add a dot if the submitted value doesn't have any. + if (*new_extension != '.') { + pos += cwk_path_output_dot(buffer, buffer_size, pos); + } + + // And finally terminate the output and return the total size of the path. + pos += cwk_path_output(buffer, buffer_size, pos, new_extension); + cwk_path_terminate_output(buffer, buffer_size, pos); + return pos; + } + + // Now we seek the old extension in the last segment, which we will replace + // with the new one. If there is no old extension, it will point to the end of + // the segment. + old_extension = segment.end; + for (c = segment.begin; c < segment.end; ++c) { + if (*c == '.') { + old_extension = c; + } + } + + pos = cwk_path_output_sized(buffer, buffer_size, 0, segment.path, + old_extension - segment.path); + + // If the new extension starts with a dot, we will skip that dot. We always + // output exactly one dot before the extension. If the extension contains + // multiple dots, we will output those as part of the extension. + if (*new_extension == '.') { + ++new_extension; + } + + // We calculate the size of the new extension, including the dot, in order to + // output the trail - which is any part of the path coming after the + // extension. We must output this first, since the buffer may overlap with the + // submitted path - and it would be overridden by longer extensions. + new_extension_size = strlen(new_extension) + 1; + trail_size = cwk_path_output(buffer, buffer_size, pos + new_extension_size, + segment.end); + + // Finally we output the dot and the new extension. The new extension itself + // doesn't contain the dot anymore, so we must output that first. + pos += cwk_path_output_dot(buffer, buffer_size, pos); + pos += cwk_path_output(buffer, buffer_size, pos, new_extension); + + // Now we terminate the output with a null-terminating character, but before + // we do that we must add the size of the trail to the position which we + // output before. + pos += trail_size; + cwk_path_terminate_output(buffer, buffer_size, pos); + + // And the position is our output size now. + return pos; +} + +size_t cwk_path_normalize(const char *path, char *buffer, size_t buffer_size) +{ + const char *paths[2]; + + // Now we initialize the paths which we will normalize. Since this function + // only supports submitting a single path, we will only add that one. + paths[0] = path; + paths[1] = NULL; + + return cwk_path_join_and_normalize_multiple(paths, buffer, buffer_size); +} + +size_t cwk_path_get_intersection(const char *path_base, const char *path_other) +{ + bool absolute; + size_t base_root_length, other_root_length; + const char *end; + const char *paths_base[2], *paths_other[2]; + struct cwk_segment_joined base, other; + + // We first compare the two roots. We just return zero if they are not equal. + // This will also happen to return zero if the paths are mixed relative and + // absolute. + cwk_path_get_root(path_base, &base_root_length); + cwk_path_get_root(path_other, &other_root_length); + if (!cwk_path_is_string_equal(path_base, path_other, base_root_length)) { + return 0; + } + + // Configure our paths. We just have a single path in here for now. + paths_base[0] = path_base; + paths_base[1] = NULL; + paths_other[0] = path_other; + paths_other[1] = NULL; + + // So we get the first segment of both paths. If one of those paths don't have + // any segment, we will return 0. + if (!cwk_path_get_first_segment_joined(paths_base, &base) || + !cwk_path_get_first_segment_joined(paths_other, &other)) { + return base_root_length; + } + + // We now determine whether the path is absolute or not. This is required + // because if will ignore removed segments, and this behaves differently if + // the path is absolute. However, we only need to check the base path because + // we are guaranteed that both paths are either relative or absolute. + absolute = cwk_path_is_root_absolute(path_base, base_root_length); + + // We must keep track of the end of the previous segment. Initially, this is + // set to the beginning of the path. This means that 0 is returned if the + // first segment is not equal. + end = path_base + base_root_length; + + // Now we loop over both segments until one of them reaches the end or their + // contents are not equal. + do { + // We skip all segments which will be removed in each path, since we want to + // know about the true path. + if (!cwk_path_segment_joined_skip_invisible(&base, absolute) || + !cwk_path_segment_joined_skip_invisible(&other, absolute)) { + break; + } + + if (!cwk_path_is_string_equal(base.segment.begin, other.segment.begin, + base.segment.size)) { + // So the content of those two segments are not equal. We will return the + // size up to the beginning. + return end - path_base; + } + + // Remember the end of the previous segment before we go to the next one. + end = base.segment.end; + } while (cwk_path_get_next_segment_joined(&base) && + cwk_path_get_next_segment_joined(&other)); + + // Now we calculate the length up to the last point where our paths pointed to + // the same place. + return end - path_base; +} + +bool cwk_path_get_first_segment(const char *path, struct cwk_segment *segment) +{ + size_t length; + const char *segments; + + // We skip the root since that's not part of the first segment. The root is + // treated as a separate entity. + cwk_path_get_root(path, &length); + segments = path + length; + + // Now, after we skipped the root we can continue and find the actual segment + // content. + return cwk_path_get_first_segment_without_root(path, segments, segment); +} + +bool cwk_path_get_last_segment(const char *path, struct cwk_segment *segment) +{ + // We first grab the first segment. This might be our last segment as well, + // but we don't know yet. There is no last segment if there is no first + // segment, so we return false in that case. + if (!cwk_path_get_first_segment(path, segment)) { + return false; + } + + // Now we find our last segment. The segment struct of the caller + // will contain the last segment, since the function we call here will not + // change the segment struct when it reaches the end. + while (cwk_path_get_next_segment(segment)) { + // We just loop until there is no other segment left. + } + + return true; +} + +bool cwk_path_get_next_segment(struct cwk_segment *segment) +{ + const char *c; + + // First we jump to the end of the previous segment. The first character must + // be either a '\0' or a separator. + c = segment->begin + segment->size; + if (*c == '\0') { + return false; + } + + // Now we skip all separator until we reach something else. We are not yet + // guaranteed to have a segment, since the string could just end afterwards. + assert(cwk_path_is_separator(c)); + do { + ++c; + } while (cwk_path_is_separator(c)); + + // If the string ends here, we can safely assume that there is no other + // segment after this one. + if (*c == '\0') { + return false; + } + + // Now we are safe to assume there is a segment. We store the beginning of + // this segment in the segment struct of the caller. + segment->begin = c; + + // And now determine the size of this segment, and store it in the struct of + // the caller as well. + c = cwk_path_find_next_stop(c); + segment->end = c; + segment->size = c - segment->begin; + + // Tell the caller that we found a segment. + return true; +} + +bool cwk_path_get_previous_segment(struct cwk_segment *segment) +{ + const char *c; + + // The current position might point to the first character of the path, which + // means there are no previous segments available. + c = segment->begin; + if (c <= segment->segments) { + return false; + } + + // We move towards the beginning of the path until we either reached the + // beginning or the character is no separator anymore. + do { + --c; + if (c <= segment->segments) { + // So we reached the beginning here and there is no segment. So we return + // false and don't change the segment structure submitted by the caller. + return false; + } + } while (cwk_path_is_separator(c)); + + // We are guaranteed now that there is another segment, since we moved before + // the previous separator and did not reach the segment path beginning. + segment->end = c + 1; + segment->begin = cwk_path_find_previous_stop(segment->segments, c); + segment->size = segment->end - segment->begin; + + return true; +} + +enum cwk_segment_type cwk_path_get_segment_type( + const struct cwk_segment *segment) +{ + // We just make a string comparison with the segment contents and return the + // appropriate type. + if (strncmp(segment->begin, ".", segment->size) == 0) { + return CWK_CURRENT; + } else if (strncmp(segment->begin, "..", segment->size) == 0) { + return CWK_BACK; + } + + return CWK_NORMAL; +} + +bool cwk_path_is_separator(const char *str) +{ + const char *c; + + // We loop over all characters in the read symbols. + c = separators[path_style]; + while (*c) { + if (*c == *str) { + return true; + } + + ++c; + } + + return false; +} + +size_t cwk_path_change_segment(struct cwk_segment *segment, const char *value, + char *buffer, size_t buffer_size) +{ + size_t pos, value_size, tail_size; + + // First we have to output the head, which is the whole string up to the + // beginning of the segment. This part of the path will just stay the same. + pos = cwk_path_output_sized(buffer, buffer_size, 0, segment->path, + segment->begin - segment->path); + + // In order to trip the submitted value, we will skip any separator at the + // beginning of it and behave as if it was never there. + while (cwk_path_is_separator(value)) { + ++value; + } + + // Now we determine the length of the value. In order to do that we first + // locate the '\0'. + value_size = 0; + while (value[value_size]) { + ++value_size; + } + + // Since we trim separators at the beginning and in the end of the value we + // have to subtract from the size until there are either no more characters + // left or the last character is no separator. + while (value_size > 0 && cwk_path_is_separator(&value[value_size - 1])) { + --value_size; + } + + // We also have to determine the tail size, which is the part of the string + // following the current segment. This part will not change. + tail_size = strlen(segment->end); + + // Now we output the tail. We have to do that, because if the buffer and the + // source are overlapping we would override the tail if the value is + // increasing in length. + cwk_path_output_sized(buffer, buffer_size, pos + value_size, segment->end, + tail_size); + + // Finally we can output the value in the middle of the head and the tail, + // where we have enough space to fit the whole trimmed value. + pos += cwk_path_output_sized(buffer, buffer_size, pos, value, value_size); + + // Now we add the tail size to the current position and terminate the output - + // basically, ensure that there is a '\0' at the end of the buffer. + pos += tail_size; + cwk_path_terminate_output(buffer, buffer_size, pos); + + // And now tell the caller how long the whole path would be. + return pos; +} + +enum cwk_path_style cwk_path_guess_style(const char *path) +{ + const char *c; + size_t root_length; + struct cwk_segment segment; + + // First we determine the root. Only windows roots can be longer than a single + // slash, so if we can determine that it starts with something like "C:", we + // know that this is a windows path. + cwk_path_get_root_windows(path, &root_length); + if (root_length > 1) { + return CWK_STYLE_WINDOWS; + } + + // Next we check for slashes. Windows uses backslashes, while unix uses + // forward slashes. Windows actually supports both, but our best guess is to + // assume windows with backslashes and unix with forward slashes. + for (c = path; *c; ++c) { + if (*c == *separators[CWK_STYLE_UNIX]) { + return CWK_STYLE_UNIX; + } else if (*c == *separators[CWK_STYLE_WINDOWS]) { + return CWK_STYLE_WINDOWS; + } + } + + // This path does not have any slashes. We grab the last segment (which + // actually must be the first one), and determine whether the segment starts + // with a dot. A dot is a hidden folder or file in the UNIX world, in that + // case we assume the path to have UNIX style. + if (!cwk_path_get_last_segment(path, &segment)) { + // We couldn't find any segments, so we default to a UNIX path style since + // there is no way to make any assumptions. + return CWK_STYLE_UNIX; + } + + if (*segment.begin == '.') { + return CWK_STYLE_UNIX; + } + + // And finally we check whether the last segment contains a dot. If it + // contains a dot, that might be an extension. Windows is more likely to have + // file names with extensions, so our guess would be windows. + for (c = segment.begin; *c; ++c) { + if (*c == '.') { + return CWK_STYLE_WINDOWS; + } + } + + // All our checks failed, so we will return a default value which is currently + // UNIX. + return CWK_STYLE_UNIX; +} + +void cwk_path_set_style(enum cwk_path_style style) +{ + // We can just set the global path style variable and then the behaviour for + // all functions will change accordingly. + assert(style == CWK_STYLE_UNIX || style == CWK_STYLE_WINDOWS); + path_style = style; +} + +enum cwk_path_style cwk_path_get_style(void) +{ + // Simply return the path style which we store in a global variable. + return path_style; +} diff --git a/src/disk/minivhd/cwalk.h b/src/disk/minivhd/cwalk.h new file mode 100644 index 000000000..baa5d432d --- /dev/null +++ b/src/disk/minivhd/cwalk.h @@ -0,0 +1,457 @@ +#pragma once + +#ifndef CWK_LIBRARY_H +#define CWK_LIBRARY_H + +#include +#include + +/** + * A segment represents a single component of a path. For instance, on linux a + * path might look like this "/var/log/", which consists of two segments "var" + * and "log". + */ +struct cwk_segment +{ + const char *path; + const char *segments; + const char *begin; + const char *end; + size_t size; +}; + +/** + * The segment type can be used to identify whether a segment is a special + * segment or not. + * + * CWK_NORMAL - normal folder or file segment + * CWK_CURRENT - "./" current folder segment + * CWK_BACK - "../" relative back navigation segment + */ +enum cwk_segment_type +{ + CWK_NORMAL, + CWK_CURRENT, + CWK_BACK +}; + +/** + * @brief Determines the style which is used for the path parsing and + * generation. + */ +enum cwk_path_style +{ + CWK_STYLE_WINDOWS, + CWK_STYLE_UNIX +}; + +/** + * @brief Generates an absolute path based on a base. + * + * This function generates an absolute path based on a base path and another + * path. It is guaranteed to return an absolute path. If the second submitted + * path is absolute, it will override the base path. The result will be written + * to a buffer, which might be truncated if the buffer is not large enough to + * hold the full path. However, the truncated result will always be + * null-terminated. The returned value is the amount of characters which the + * resulting path would take if it was not truncated (excluding the + * null-terminating character). + * + * @param base The base path on which the relative path will be applied. + * @param path The relative path which will be applied on the base path. + * @param buffer The buffer where the result will be written to. + * @param buffer_size The size of the result buffer. + * @return Returns the total amount of characters of the new absolute path. + */ +size_t cwk_path_get_absolute(const char *base, const char *path, char *buffer, + size_t buffer_size); + +/** + * @brief Generates a relative path based on a base. + * + * This function generates a relative path based on a base path and another + * path. It determines how to get to the submitted path, starting from the base + * directory. The result will be written to a buffer, which might be truncated + * if the buffer is not large enough to hold the full path. However, the + * truncated result will always be null-terminated. The returned value is the + * amount of characters which the resulting path would take if it was not + * truncated (excluding the null-terminating character). + * + * @param base_directory The base path from which the relative path will start. + * @param path The target path where the relative path will point to. + * @param buffer The buffer where the result will be written to. + * @param buffer_size The size of the result buffer. + * @return Returns the total amount of characters of the full path. + */ +size_t cwk_path_get_relative(const char *base_directory, const char *path, + char *buffer, size_t buffer_size); + +/** + * @brief Joins two paths together. + * + * This function generates a new path by combining the two submitted paths. It + * will remove double separators, and unlike cwk_path_get_absolute it permits + * the use of two relative paths to combine. The result will be written to a + * buffer, which might be truncated if the buffer is not large enough to hold + * the full path. However, the truncated result will always be null-terminated. + * The returned value is the amount of characters which the resulting path would + * take if it was not truncated (excluding the null-terminating character). + * + * @param path_a The first path which comes first. + * @param path_b The second path which comes after the first. + * @param buffer The buffer where the result will be written to. + * @param buffer_size The size of the result buffer. + * @return Returns the total amount of characters of the full, combined path. + */ +size_t cwk_path_join(const char *path_a, const char *path_b, char *buffer, + size_t buffer_size); + +/** + * @brief Joins multiple paths together. + * + * This function generates a new path by joining multiple paths together. It + * will remove double separators, and unlike cwk_path_get_absolute it permits + * the use of multiple relative paths to combine. The last path of the submitted + * string array must be set to NULL. The result will be written to a buffer, + * which might be truncated if the buffer is not large enough to hold the full + * path. However, the truncated result will always be null-terminated. The + * returned value is the amount of characters which the resulting path would + * take if it was not truncated (excluding the null-terminating character). + * + * @param paths An array of paths which will be joined. + * @param buffer The buffer where the result will be written to. + * @param buffer_size The size of the result buffer. + * @return Returns the total amount of characters of the full, combined path. + */ +size_t cwk_path_join_multiple(const char **paths, char *buffer, + size_t buffer_size); + +/** + * @brief Determines the root of a path. + * + * This function determines the root of a path by finding it's length. The root + * always starts at the submitted path. If the path has no root, the length will + * be set to zero. + * + * @param path The path which will be inspected. + * @param length The output of the root length. + */ +void cwk_path_get_root(const char *path, size_t *length); + +/** + * @brief Changes the root of a path. + * + * This function changes the root of a path. It does not normalize the result. + * The result will be written to a buffer, which might be truncated if the + * buffer is not large enough to hold the full path. However, the truncated + * result will always be null-terminated. The returned value is the amount of + * characters which the resulting path would take if it was not truncated + * (excluding the null-terminating character). + * + * @param path The original path which will get a new root. + * @param new_root The new root which will be placed in the path. + * @param buffer The output buffer where the result is written to. + * @param buffer_size The size of the output buffer where the result is written + * to. + * @return Returns the total amount of characters of the new path. + */ +size_t cwk_path_change_root(const char *path, const char *new_root, + char *buffer, size_t buffer_size); + +/** + * @brief Determine whether the path is absolute or not. + * + * This function checks whether the path is an absolute path or not. A path is + * considered to be absolute if the root ends with a separator. + * + * @param path The path which will be checked. + * @return Returns true if the path is absolute or false otherwise. + */ +bool cwk_path_is_absolute(const char *path); + +/** + * @brief Determine whether the path is relative or not. + * + * This function checks whether the path is a relative path or not. A path is + * considered to be relative if the root does not end with a separator. + * + * @param path The path which will be checked. + * @return Returns true if the path is relative or false otherwise. + */ +bool cwk_path_is_relative(const char *path); + +/** + * @brief Gets the basename of a file path. + * + * This function gets the basename of a file path. A pointer to the beginning of + * the basename will be returned through the basename parameter. This pointer + * will be positioned on the first letter after the separator. The length of the + * file path will be returned through the length parameter. The length will be + * set to zero and the basename to NULL if there is no basename available. + * + * @param path The path which will be inspected. + * @param basename The output of the basename pointer. + * @param length The output of the length of the basename. + */ +void cwk_path_get_basename(const char *path, const char **basename, + size_t *length); + +/** + * @brief Changes the basename of a file path. + * + * This function changes the basename of a file path. This function will not + * write out more than the specified buffer can contain. However, the generated + * string is always null-terminated - even if not the whole path is written out. + * The function returns the total number of characters the complete buffer would + * have, even if it was not written out completely. The path may be the same + * memory address as the buffer. + * + * @param path The original path which will be used for the modified path. + * @param new_basename The new basename which will replace the old one. + * @param buffer The buffer where the changed path will be written to. + * @param buffer_size The size of the result buffer where the changed path is + * written to. + * @return Returns the size which the complete new path would have if it was not + * truncated. + */ +size_t cwk_path_change_basename(const char *path, const char *new_basename, + char *buffer, size_t buffer_size); + +/** + * @brief Gets the dirname of a file path. + * + * This function determines the dirname of a file path and returns the length up + * to which character is considered to be part of it. If no dirname is found, + * the length will be set to zero. The beginning of the dirname is always equal + * to the submitted path pointer. + * + * @param path The path which will be inspected. + * @param length The length of the dirname. + */ +void cwk_path_get_dirname(const char *path, size_t *length); + +/** + * @brief Gets the extension of a file path. + * + * This function extracts the extension portion of a file path. A pointer to + * the beginning of the extension will be returned through the extension + * parameter if an extension is found and true is returned. This pointer will be + * positioned on the dot. The length of the extension name will be returned + * through the length parameter. If no extension is found both parameters won't + * be touched and false will be returned. + * + * @param path The path which will be inspected. + * @param extension The output of the extension pointer. + * @param length The output of the length of the extension. + * @return Returns true if an extension is found or false otherwise. + */ +bool cwk_path_get_extension(const char *path, const char **extension, + size_t *length); + +/** + * @brief Determines whether the file path has an extension. + * + * This function determines whether the submitted file path has an extension. + * This will evaluate to true if the last segment of the path contains a dot. + * + * @param path The path which will be inspected. + * @return Returns true if the path has an extension or false otherwise. + */ +bool cwk_path_has_extension(const char *path); + +/** + * @brief Changes the extension of a file path. + * + * This function changes the extension of a file name. The function will append + * an extension if the basename does not have an extension, or use the extension + * as a basename if the path does not have a basename. This function will not + * write out more than the specified buffer can contain. However, the generated + * string is always null-terminated - even if not the whole path is written out. + * The function returns the total number of characters the complete buffer would + * have, even if it was not written out completely. The path may be the same + * memory address as the buffer. + * + * @param path The path which will be used to make the change. + * @param new_extension The extension which will be placed within the new path. + * @param buffer The output buffer where the result will be written to. + * @param buffer_size The size of the output buffer where the result will be + * written to. + * @return Returns the total size which the output would have if it was not + * truncated. + */ +size_t cwk_path_change_extension(const char *path, const char *new_extension, + char *buffer, size_t buffer_size); + +/** + * @brief Creates a normalized version of the path. + * + * This function creates a normalized version of the path within the specified + * buffer. This function will not write out more than the specified buffer can + * contain. However, the generated string is always null-terminated - even if + * not the whole path is written out. The function returns the total number of + * characters the complete buffer would have, even if it was not written out + * completely. The path may be the same memory address as the buffer. + * + * The following will be true for the normalized path: + * 1) "../" will be resolved. + * 2) "./" will be removed. + * 3) double separators will be fixed with a single separator. + * 4) separator suffixes will be removed. + * + * @param path The path which will be normalized. + * @param buffer The buffer where the new path is written to. + * @param buffer_size The size of the buffer. + * @return The size which the complete normalized path has if it was not + * truncated. + */ +size_t cwk_path_normalize(const char *path, char *buffer, size_t buffer_size); + +/** + * @brief Finds common portions in two paths. + * + * This function finds common portions in two paths and returns the number + * characters from the beginning of the base path which are equal to the other + * path. + * + * @param path_base The base path which will be compared with the other path. + * @param path_other The other path which will compared with the base path. + * @return Returns the number of characters which are common in the base path. + */ +size_t cwk_path_get_intersection(const char *path_base, const char *path_other); + +/** + * @brief Gets the first segment of a path. + * + * This function finds the first segment of a path. The position of the segment + * is set to the first character after the separator, and the length counts all + * characters until the next separator (excluding the separator). + * + * @param path The path which will be inspected. + * @param segment The segment which will be extracted. + * @return Returns true if there is a segment or false if there is none. + */ +bool cwk_path_get_first_segment(const char *path, struct cwk_segment *segment); + +/** + * @brief Gets the last segment of the path. + * + * This function gets the last segment of a path. This function may return false + * if the path doesn't contain any segments, in which case the submitted segment + * parameter is not modified. The position of the segment is set to the first + * character after the separator, and the length counts all characters until the + * end of the path (excluding the separator). + * + * @param path The path which will be inspected. + * @param segment The segment which will be extracted. + * @return Returns true if there is a segment or false if there is none. + */ +bool cwk_path_get_last_segment(const char *path, struct cwk_segment *segment); + +/** + * @brief Advances to the next segment. + * + * This function advances the current segment to the next segment. If there are + * no more segments left, the submitted segment structure will stay unchanged + * and false is returned. + * + * @param segment The current segment which will be advanced to the next one. + * @return Returns true if another segment was found or false otherwise. + */ +bool cwk_path_get_next_segment(struct cwk_segment *segment); + +/** + * @brief Moves to the previous segment. + * + * This function moves the current segment to the previous segment. If the + * current segment is the first one, the submitted segment structure will stay + * unchanged and false is returned. + * + * @param segment The current segment which will be moved to the previous one. + * @return Returns true if there is a segment before this one or false + * otherwise. + */ +bool cwk_path_get_previous_segment(struct cwk_segment *segment); + +/** + * @brief Gets the type of the submitted path segment. + * + * This function inspects the contents of the segment and determines the type of + * it. Currently, there are three types CWK_NORMAL, CWK_CURRENT and CWK_BACK. A + * CWK_NORMAL segment is a normal folder or file entry. A CWK_CURRENT is a "./" + * and a CWK_BACK a "../" segment. + * + * @param segment The segment which will be inspected. + * @return Returns the type of the segment. + */ +enum cwk_segment_type cwk_path_get_segment_type( + const struct cwk_segment *segment); + +/** + * @brief Changes the content of a segment. + * + * This function overrides the content of a segment to the submitted value and + * outputs the whole new path to the submitted buffer. The result might require + * less or more space than before if the new value length differs from the + * original length. The output is truncated if the new path is larger than the + * submitted buffer size, but it is always null-terminated. The source of the + * segment and the submitted buffer may be the same. + * + * @param segment The segment which will be modifier. + * @param value The new content of the segment. + * @param buffer The buffer where the modified path will be written to. + * @param buffer_size The size of the output buffer. + * @return Returns the total size which would have been written if the output + * was not truncated. + */ +size_t cwk_path_change_segment(struct cwk_segment *segment, const char *value, + char *buffer, size_t buffer_size); + +/** + * @brief Checks whether the submitted pointer points to a separator. + * + * This function simply checks whether the submitted pointer points to a + * separator, which has to be null-terminated (but not necessarily after the + * separator). The function will return true if it is a separator, or false + * otherwise. + * + * @param symbol A pointer to a string. + * @return Returns true if it is a separator, or false otherwise. + */ +bool cwk_path_is_separator(const char *str); + +/** + * @brief Guesses the path style. + * + * This function guesses the path style based on a submitted path-string. The + * guessing will look at the root and the type of slashes contained in the path + * and return the style which is more likely used in the path. + * + * @param path The path which will be inspected. + * @return Returns the style which is most likely used for the path. + */ +enum cwk_path_style cwk_path_guess_style(const char *path); + +/** + * @brief Configures which path style is used. + * + * This function configures which path style is used. The following styles are + * currently supported. + * + * CWK_STYLE_WINDOWS: Use backslashes as a separator and volume for the root. + * CWK_STYLE_UNIX: Use slashes as a separator and a slash for the root. + * + * @param style The style which will be used from now on. + */ +void cwk_path_set_style(enum cwk_path_style style); + +/** + * @brief Gets the path style configuration. + * + * This function gets the style configuration which is currently used for the + * paths. This configuration determines how paths are parsed and generated. + * + * @return Returns the current path style configuration. + */ +enum cwk_path_style cwk_path_get_style(void); + +#endif diff --git a/src/disk/minivhd/libxml2_encoding.c b/src/disk/minivhd/libxml2_encoding.c new file mode 100644 index 000000000..cb881a89b --- /dev/null +++ b/src/disk/minivhd/libxml2_encoding.c @@ -0,0 +1,447 @@ +/* + * encoding.c : implements the encoding conversion functions needed for XML + * + * Related specs: + * rfc2044 (UTF-8 and UTF-16) F. Yergeau Alis Technologies + * rfc2781 UTF-16, an encoding of ISO 10646, P. Hoffman, F. Yergeau + * [ISO-10646] UTF-8 and UTF-16 in Annexes + * [ISO-8859-1] ISO Latin-1 characters codes. + * [UNICODE] The Unicode Consortium, "The Unicode Standard -- + * Worldwide Character Encoding -- Version 1.0", Addison- + * Wesley, Volume 1, 1991, Volume 2, 1992. UTF-8 is + * described in Unicode Technical Report #4. + * [US-ASCII] Coded Character Set--7-bit American Standard Code for + * Information Interchange, ANSI X3.4-1986. + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + * + * Original code for IsoLatin1 and UTF-16 by "Martin J. Duerst" + * + * Adapted and abridged for MiniVHD by Sherman Perry + */ +#include + +static int xmlLittleEndian = 1; + +/* Note: extracted from original 'void xmlInitCharEncodingHandlers(void)' function */ +void xmlEncodingInit(void) +{ + unsigned short int tst = 0x1234; + unsigned char *ptr = (unsigned char *) &tst; + + if (*ptr == 0x12) xmlLittleEndian = 0; + else if (*ptr == 0x34) xmlLittleEndian = 1; +} + +/** + * UTF16LEToUTF8: + * @out: a pointer to an array of bytes to store the result + * @outlen: the length of @out + * @inb: a pointer to an array of UTF-16LE passwd as a byte array + * @inlenb: the length of @in in UTF-16LE chars + * + * Take a block of UTF-16LE ushorts in and try to convert it to an UTF-8 + * block of chars out. This function assumes the endian property + * is the same between the native type of this machine and the + * inputed one. + * + * Returns the number of bytes written, or -1 if lack of space, or -2 + * if the transcoding fails (if *in is not a valid utf16 string) + * The value of *inlen after return is the number of octets consumed + * if the return value is positive, else unpredictable. + */ +int UTF16LEToUTF8(unsigned char* out, int *outlen, + const unsigned char* inb, int *inlenb) +{ + unsigned char* outstart = out; + const unsigned char* processed = inb; + unsigned char* outend = out + *outlen; + unsigned short* in = (unsigned short*) inb; + unsigned short* inend; + unsigned int c, d, inlen; + unsigned char *tmp; + int bits; + + if ((*inlenb % 2) == 1) + (*inlenb)--; + inlen = *inlenb / 2; + inend = in + inlen; + while ((in < inend) && (out - outstart + 5 < *outlen)) { + if (xmlLittleEndian) { + c= *in++; + } else { + tmp = (unsigned char *) in; + c = *tmp++; + c = c | (((unsigned int)*tmp) << 8); + in++; + } + if ((c & 0xFC00) == 0xD800) { /* surrogates */ + if (in >= inend) { /* (in > inend) shouldn't happens */ + break; + } + if (xmlLittleEndian) { + d = *in++; + } else { + tmp = (unsigned char *) in; + d = *tmp++; + d = d | (((unsigned int)*tmp) << 8); + in++; + } + if ((d & 0xFC00) == 0xDC00) { + c &= 0x03FF; + c <<= 10; + c |= d & 0x03FF; + c += 0x10000; + } + else { + *outlen = out - outstart; + *inlenb = processed - inb; + return(-2); + } + } + + /* assertion: c is a single UTF-4 value */ + if (out >= outend) + break; + if (c < 0x80) { *out++= c; bits= -6; } + else if (c < 0x800) { *out++= ((c >> 6) & 0x1F) | 0xC0; bits= 0; } + else if (c < 0x10000) { *out++= ((c >> 12) & 0x0F) | 0xE0; bits= 6; } + else { *out++= ((c >> 18) & 0x07) | 0xF0; bits= 12; } + + for ( ; bits >= 0; bits-= 6) { + if (out >= outend) + break; + *out++= ((c >> bits) & 0x3F) | 0x80; + } + processed = (const unsigned char*) in; + } + *outlen = out - outstart; + *inlenb = processed - inb; + return(*outlen); +} + +/** + * UTF8ToUTF16LE: + * @outb: a pointer to an array of bytes to store the result + * @outlen: the length of @outb + * @in: a pointer to an array of UTF-8 chars + * @inlen: the length of @in + * + * Take a block of UTF-8 chars in and try to convert it to an UTF-16LE + * block of chars out. + * + * Returns the number of bytes written, or -1 if lack of space, or -2 + * if the transcoding failed. + */ +int UTF8ToUTF16LE(unsigned char* outb, int *outlen, + const unsigned char* in, int *inlen) +{ + unsigned short* out = (unsigned short*) outb; + const unsigned char* processed = in; + const unsigned char *const instart = in; + unsigned short* outstart= out; + unsigned short* outend; + const unsigned char* inend; + unsigned int c, d; + int trailing; + unsigned char *tmp; + unsigned short tmp1, tmp2; + + /* UTF16LE encoding has no BOM */ + if ((out == NULL) || (outlen == NULL) || (inlen == NULL)) return(-1); + if (in == NULL) { + *outlen = 0; + *inlen = 0; + return(0); + } + inend= in + *inlen; + outend = out + (*outlen / 2); + while (in < inend) { + d= *in++; + if (d < 0x80) { c= d; trailing= 0; } + else if (d < 0xC0) { + /* trailing byte in leading position */ + *outlen = (out - outstart) * 2; + *inlen = processed - instart; + return(-2); + } else if (d < 0xE0) { c= d & 0x1F; trailing= 1; } + else if (d < 0xF0) { c= d & 0x0F; trailing= 2; } + else if (d < 0xF8) { c= d & 0x07; trailing= 3; } + else { + /* no chance for this in UTF-16 */ + *outlen = (out - outstart) * 2; + *inlen = processed - instart; + return(-2); + } + + if (inend - in < trailing) { + break; + } + + for ( ; trailing; trailing--) { + if ((in >= inend) || (((d= *in++) & 0xC0) != 0x80)) + break; + c <<= 6; + c |= d & 0x3F; + } + + /* assertion: c is a single UTF-4 value */ + if (c < 0x10000) { + if (out >= outend) + break; + if (xmlLittleEndian) { + *out++ = c; + } else { + tmp = (unsigned char *) out; + *tmp = c ; + *(tmp + 1) = c >> 8 ; + out++; + } + } + else if (c < 0x110000) { + if (out+1 >= outend) + break; + c -= 0x10000; + if (xmlLittleEndian) { + *out++ = 0xD800 | (c >> 10); + *out++ = 0xDC00 | (c & 0x03FF); + } else { + tmp1 = 0xD800 | (c >> 10); + tmp = (unsigned char *) out; + *tmp = (unsigned char) tmp1; + *(tmp + 1) = tmp1 >> 8; + out++; + + tmp2 = 0xDC00 | (c & 0x03FF); + tmp = (unsigned char *) out; + *tmp = (unsigned char) tmp2; + *(tmp + 1) = tmp2 >> 8; + out++; + } + } + else + break; + processed = in; + } + *outlen = (out - outstart) * 2; + *inlen = processed - instart; + return(*outlen); +} + +/** + * UTF16BEToUTF8: + * @out: a pointer to an array of bytes to store the result + * @outlen: the length of @out + * @inb: a pointer to an array of UTF-16 passed as a byte array + * @inlenb: the length of @in in UTF-16 chars + * + * Take a block of UTF-16 ushorts in and try to convert it to an UTF-8 + * block of chars out. This function assumes the endian property + * is the same between the native type of this machine and the + * inputed one. + * + * Returns the number of bytes written, or -1 if lack of space, or -2 + * if the transcoding fails (if *in is not a valid utf16 string) + * The value of *inlen after return is the number of octets consumed + * if the return value is positive, else unpredictable. + */ +int UTF16BEToUTF8(unsigned char* out, int *outlen, + const unsigned char* inb, int *inlenb) +{ + unsigned char* outstart = out; + const unsigned char* processed = inb; + unsigned char* outend = out + *outlen; + unsigned short* in = (unsigned short*) inb; + unsigned short* inend; + unsigned int c, d, inlen; + unsigned char *tmp; + int bits; + + if ((*inlenb % 2) == 1) + (*inlenb)--; + inlen = *inlenb / 2; + inend= in + inlen; + while (in < inend) { + if (xmlLittleEndian) { + tmp = (unsigned char *) in; + c = *tmp++; + c = c << 8; + c = c | (unsigned int) *tmp; + in++; + } else { + c= *in++; + } + if ((c & 0xFC00) == 0xD800) { /* surrogates */ + if (in >= inend) { /* (in > inend) shouldn't happens */ + *outlen = out - outstart; + *inlenb = processed - inb; + return(-2); + } + if (xmlLittleEndian) { + tmp = (unsigned char *) in; + d = *tmp++; + d = d << 8; + d = d | (unsigned int) *tmp; + in++; + } else { + d= *in++; + } + if ((d & 0xFC00) == 0xDC00) { + c &= 0x03FF; + c <<= 10; + c |= d & 0x03FF; + c += 0x10000; + } + else { + *outlen = out - outstart; + *inlenb = processed - inb; + return(-2); + } + } + + /* assertion: c is a single UTF-4 value */ + if (out >= outend) + break; + if (c < 0x80) { *out++= c; bits= -6; } + else if (c < 0x800) { *out++= ((c >> 6) & 0x1F) | 0xC0; bits= 0; } + else if (c < 0x10000) { *out++= ((c >> 12) & 0x0F) | 0xE0; bits= 6; } + else { *out++= ((c >> 18) & 0x07) | 0xF0; bits= 12; } + + for ( ; bits >= 0; bits-= 6) { + if (out >= outend) + break; + *out++= ((c >> bits) & 0x3F) | 0x80; + } + processed = (const unsigned char*) in; + } + *outlen = out - outstart; + *inlenb = processed - inb; + return(*outlen); +} + +/** + * UTF8ToUTF16BE: + * @outb: a pointer to an array of bytes to store the result + * @outlen: the length of @outb + * @in: a pointer to an array of UTF-8 chars + * @inlen: the length of @in + * + * Take a block of UTF-8 chars in and try to convert it to an UTF-16BE + * block of chars out. + * + * Returns the number of byte written, or -1 by lack of space, or -2 + * if the transcoding failed. + */ +int UTF8ToUTF16BE(unsigned char* outb, int *outlen, + const unsigned char* in, int *inlen) +{ + unsigned short* out = (unsigned short*) outb; + const unsigned char* processed = in; + const unsigned char *const instart = in; + unsigned short* outstart= out; + unsigned short* outend; + const unsigned char* inend; + unsigned int c, d; + int trailing; + unsigned char *tmp; + unsigned short tmp1, tmp2; + + /* UTF-16BE has no BOM */ + if ((outb == NULL) || (outlen == NULL) || (inlen == NULL)) return(-1); + if (in == NULL) { + *outlen = 0; + *inlen = 0; + return(0); + } + inend= in + *inlen; + outend = out + (*outlen / 2); + while (in < inend) { + d= *in++; + if (d < 0x80) { c= d; trailing= 0; } + else if (d < 0xC0) { + /* trailing byte in leading position */ + *outlen = out - outstart; + *inlen = processed - instart; + return(-2); + } else if (d < 0xE0) { c= d & 0x1F; trailing= 1; } + else if (d < 0xF0) { c= d & 0x0F; trailing= 2; } + else if (d < 0xF8) { c= d & 0x07; trailing= 3; } + else { + /* no chance for this in UTF-16 */ + *outlen = out - outstart; + *inlen = processed - instart; + return(-2); + } + + if (inend - in < trailing) { + break; + } + + for ( ; trailing; trailing--) { + if ((in >= inend) || (((d= *in++) & 0xC0) != 0x80)) break; + c <<= 6; + c |= d & 0x3F; + } + + /* assertion: c is a single UTF-4 value */ + if (c < 0x10000) { + if (out >= outend) break; + if (xmlLittleEndian) { + tmp = (unsigned char *) out; + *tmp = c >> 8; + *(tmp + 1) = c; + out++; + } else { + *out++ = c; + } + } + else if (c < 0x110000) { + if (out+1 >= outend) break; + c -= 0x10000; + if (xmlLittleEndian) { + tmp1 = 0xD800 | (c >> 10); + tmp = (unsigned char *) out; + *tmp = tmp1 >> 8; + *(tmp + 1) = (unsigned char) tmp1; + out++; + + tmp2 = 0xDC00 | (c & 0x03FF); + tmp = (unsigned char *) out; + *tmp = tmp2 >> 8; + *(tmp + 1) = (unsigned char) tmp2; + out++; + } else { + *out++ = 0xD800 | (c >> 10); + *out++ = 0xDC00 | (c & 0x03FF); + } + } + else + break; + processed = in; + } + *outlen = (out - outstart) * 2; + *inlen = processed - instart; + return(*outlen); +} + +/* This file is licenced under the MIT licence as follows: + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is fur- +nished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT- +NESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. */ \ No newline at end of file diff --git a/src/disk/minivhd/libxml2_encoding.h b/src/disk/minivhd/libxml2_encoding.h new file mode 100644 index 000000000..831aea4af --- /dev/null +++ b/src/disk/minivhd/libxml2_encoding.h @@ -0,0 +1,12 @@ +#ifndef LIBXML2_ENCODING_H +#define LIBXML2_ENCODING_H + +#include +typedef uint16_t mvhd_utf16; + +void xmlEncodingInit(void); +int UTF16LEToUTF8(unsigned char* out, int *outlen, const unsigned char* inb, int *inlenb); +int UTF8ToUTF16LE(unsigned char* outb, int *outlen, const unsigned char* in, int *inlen); +int UTF16BEToUTF8(unsigned char* out, int *outlen, const unsigned char* inb, int *inlenb); +int UTF8ToUTF16BE(unsigned char* outb, int *outlen, const unsigned char* in, int *inlen); +#endif \ No newline at end of file diff --git a/src/disk/minivhd/minivhd.h b/src/disk/minivhd/minivhd.h new file mode 100644 index 000000000..0d925f71a --- /dev/null +++ b/src/disk/minivhd/minivhd.h @@ -0,0 +1,269 @@ +#ifndef MINIVHD_H +#define MINIVHD_H + +#include +#include +#include + +extern int mvhd_errno; + +typedef enum MVHDError { + MVHD_ERR_MEM = -128, + MVHD_ERR_FILE, + MVHD_ERR_NOT_VHD, + MVHD_ERR_TYPE, + MVHD_ERR_FOOTER_CHECKSUM, + MVHD_ERR_SPARSE_CHECKSUM, + MVHD_ERR_UTF_TRANSCODING_FAILED, + MVHD_ERR_UTF_SIZE, + MVHD_ERR_PATH_REL, + MVHD_ERR_PATH_LEN, + MVHD_ERR_PAR_NOT_FOUND, + MVHD_ERR_INVALID_PAR_UUID, + MVHD_ERR_INVALID_GEOM, + MVHD_ERR_INVALID_SIZE, + MVHD_ERR_INVALID_BLOCK_SIZE, + MVHD_ERR_INVALID_PARAMS, + MVHD_ERR_CONV_SIZE, + MVHD_ERR_TIMESTAMP +} MVHDError; + +typedef enum MVHDType { + MVHD_TYPE_FIXED = 2, + MVHD_TYPE_DYNAMIC = 3, + MVHD_TYPE_DIFF = 4 +} MVHDType; + +typedef enum MVHDBlockSize { + MVHD_BLOCK_DEFAULT = 0, /**< 2 MB blocks */ + MVHD_BLOCK_SMALL = 1024, /**< 512 KB blocks */ + MVHD_BLOCK_LARGE = 4096 /**< 2 MB blocks */ +} MVHDBlockSize; + +typedef struct MVHDGeom { + uint16_t cyl; + uint8_t heads; + uint8_t spt; +} MVHDGeom; + +typedef void (*mvhd_progress_callback)(uint32_t current_sector, uint32_t total_sectors); + +typedef struct MVHDCreationOptions { + int type; /** MVHD_TYPE_FIXED, MVHD_TYPE_DYNAMIC, or MVHD_TYPE_DIFF */ + char* path; /** Absolute path of the new VHD file */ + char* parent_path; /** For MVHD_TYPE_DIFF, this is the absolute path of the VHD's parent. For non-diff VHDs, this should be NULL. */ + uint64_t size_in_bytes; /** Total size of the VHD's virtual disk in bytes. Must be a multiple of 512. If 0, the size is auto-calculated from the geometry field. Ignored for MVHD_TYPE_DIFF. */ + MVHDGeom geometry; /** The geometry of the VHD. If set to 0, the geometry is auto-calculated from the size_in_bytes field. */ + uint32_t block_size_in_sectors; /** MVHD_BLOCK_LARGE or MVHD_BLOCK_SMALL, or 0 for the default value. The number of sectors per block. */ + mvhd_progress_callback progress_callback; /** Optional; if not NULL, gets called to indicate progress on the creation operation. Only applies to MVHD_TYPE_FIXED. */ +} MVHDCreationOptions; + +typedef struct MVHDMeta MVHDMeta; + +/** + * \brief Output a string from a MiniVHD error number + * + * \param [in] err is the error number to return string from + * + * \return Error string + */ +const char* mvhd_strerr(MVHDError err); + +/** + * \brief A simple test to see if a given file is a VHD + * + * \param [in] f file to test + * + * \retval true if f is a VHD + * \retval false if f is not a VHD + */ +bool mvhd_file_is_vhd(FILE* f); + +/** + * \brief Open a VHD image for reading and/or writing + * + * The returned pointer contains all required values and structures (and files) to + * read and write to a VHD file. + * + * Remember to call mvhd_close() when you are finished. + * + * \param [in] Absolute path to VHD file. Relative path will cause issues when opening + * a differencing VHD file + * \param [in] readonly set this to true to open the VHD in a read only manner + * \param [out] err will be set if the VHD fails to open. Value could be one of + * MVHD_ERR_MEM, MVHD_ERR_FILE, MVHD_ERR_NOT_VHD, MVHD_ERR_FOOTER_CHECKSUM, MVHD_ERR_SPARSE_CHECKSUM, + * MVHD_ERR_TYPE, MVHD_ERR_TIMESTAMP + * If MVHD_ERR_FILE is set, mvhd_errno will be set to the appropriate system errno value + * + * \return MVHDMeta pointer. If NULL, check err. err may also be set to MVHD_ERR_TIMESTAMP if + * opening a differencing VHD. + */ +MVHDMeta* mvhd_open(const char* path, bool readonly, int* err); + +/** + * \brief Update the parent modified timestamp in the VHD file + * + * Differencing VHD's use a parent last modified timestamp to try and detect if the + * parent has been modified after the child has been created. However, this is rather + * fragile and can be broken by moving/copying the parent. Also, MS DiskPart does not + * set this timestamp in the child :( + * + * Be careful when using this function that you don't update the timestamp after the + * parent actually has been modified. + * + * \param [in] vhdm Differencing VHD to update. + * \param [out] err will be set if the timestamp could not be updated + * + * \return non-zero on error, 0 on success + */ +int mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err); + +/** + * \brief Create a fixed VHD image + * + * \param [in] path is the absolute path to the image to create + * \param [in] geom is the HDD geometry of the image to create. Determines final image size + * \param [out] err indicates what error occurred, if any + * \param [out] progress_callback optional; if not NULL, gets called to indicate progress on the creation operation + * + * \retval NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct + */ +MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_progress_callback progress_callback); + +/** + * \brief Create sparse (dynamic) VHD image. + * + * \param [in] path is the absolute path to the VHD file to create + * \param [in] geom is the HDD geometry of the image to create. Determines final image size + * \param [out] err indicates what error occurred, if any + * + * \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct + */ +MVHDMeta* mvhd_create_sparse(const char* path, MVHDGeom geom, int* err); + +/** + * \brief Create differencing VHD imagee. + * + * \param [in] path is the absolute path to the VHD file to create + * \param [in] par_path is the absolute path to a parent image. If NULL, a sparse image is created, otherwise create a differencing image + * \param [out] err indicates what error occurred, if any + * + * \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct + */ +MVHDMeta* mvhd_create_diff(const char* path, const char* par_path, int* err); + +/** + * \brief Create a VHD using the provided options + * + * Use mvhd_create_ex if you want more control over the VHD's options. For quick creation, you can use mvhd_create_fixed, mvhd_create_sparse, or mvhd_create_diff. + * + * \param [in] options the VHD creation options. + * \param [out] err indicates what error occurred, if any + * + * \retval NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct + */ +MVHDMeta* mvhd_create_ex(MVHDCreationOptions options, int* err); + +/** + * \brief Safely close a VHD image + * + * \param [in] vhdm MiniVHD data structure to close + */ +void mvhd_close(MVHDMeta* vhdm); + +/** + * \brief Calculate hard disk geometry from a provided size + * + * The VHD format uses Cylinder, Heads, Sectors per Track (CHS) when accessing the disk. + * The size of the disk can be determined from C * H * S * sector_size. + * + * Note, maximum geometry size (in bytes) is 65535 * 16 * 255 * 512, which is 127GB. + * However, the maximum VHD size is 2040GB. For VHDs larger than 127GB, the geometry size will be + * smaller than the actual VHD size. + * + * This function determines the appropriate CHS geometry from a provided size in bytes. + * The calculations used are those provided in "Appendix: CHS Calculation" from the document + * "Virtual Hard Disk Image Format Specification" provided by Microsoft. + * + * \param [in] size the desired VHD image size, in bytes + * + * \return MVHDGeom the calculated geometry. This can be used in the appropriate create functions. + */ +MVHDGeom mvhd_calculate_geometry(uint64_t size); + +/** + * \brief Convert a raw disk image to a fixed VHD image + * + * \param [in] utf8_raw_path is the path of the raw image to convert + * \param [in] utf8_vhd_path is the path of the VHD to create + * \param [out] err indicates what error occurred, if any + * + * \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct + */ +MVHDMeta* mvhd_convert_to_vhd_fixed(const char* utf8_raw_path, const char* utf8_vhd_path, int* err); + +/** + * \brief Convert a raw disk image to a sparse VHD image + * + * \param [in] utf8_raw_path is the path of the raw image to convert + * \param [in] utf8_vhd_path is the path of the VHD to create + * \param [out] err indicates what error occurred, if any + * + * \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct + */ +MVHDMeta* mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8_vhd_path, int* err); + +/** + * \brief Convert a VHD image to a raw disk image + * + * \param [in] utf8_vhd_path is the path of the VHD to convert + * \param [in] utf8_raw_path is the path of the raw image to create + * \param [out] err indicates what error occurred, if any + * + * \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns the raw disk image FILE pointer + */ +FILE* mvhd_convert_to_raw(const char* utf8_vhd_path, const char* utf8_raw_path, int *err); + +/** + * \brief Read sectors from VHD file + * + * Read num_sectors, beginning at offset from the VHD file into a buffer + * + * \param [in] vhdm MiniVHD data structure + * \param [in] offset the sector offset from which to start reading from + * \param [in] num_sectors the number of sectors to read + * \param [out] out_buff the buffer to write sector data to + * + * \return the number of sectors that were not read, or zero + */ +int mvhd_read_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff); + +/** + * \brief Write sectors to VHD file + * + * Write num_sectors, beginning at offset from a buffer VHD file into the VHD file + * + * \param [in] vhdm MiniVHD data structure + * \param [in] offset the sector offset from which to start writing to + * \param [in] num_sectors the number of sectors to write + * \param [in] in_buffer the buffer to write sector data to + * + * \return the number of sectors that were not written, or zero + */ +int mvhd_write_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff); + +/** + * \brief Write zeroed sectors to VHD file + * + * Write num_sectors, beginning at offset, of zero data into the VHD file. + * We reuse the existing write functions, with a preallocated zero buffer as + * our source buffer. + * + * \param [in] vhdm MiniVHD data structure + * \param [in] offset the sector offset from which to start writing to + * \param [in] num_sectors the number of sectors to write + * + * \return the number of sectors that were not written, or zero + */ +int mvhd_format_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors); +#endif \ No newline at end of file diff --git a/src/disk/minivhd/minivhd_convert.c b/src/disk/minivhd/minivhd_convert.c new file mode 100644 index 000000000..1de6f4613 --- /dev/null +++ b/src/disk/minivhd/minivhd_convert.c @@ -0,0 +1,108 @@ +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 +#endif +#include +#include +#include +#include +#include +#include "minivhd_create.h" +#include "minivhd_internal.h" +#include "minivhd_util.h" +#include "minivhd.h" + +static FILE* mvhd_open_existing_raw_img(const char* utf8_raw_path, MVHDGeom* geom, int* err); + +static FILE* mvhd_open_existing_raw_img(const char* utf8_raw_path, MVHDGeom* geom, int* err) { + FILE *raw_img = mvhd_fopen(utf8_raw_path, "rb", err); + if (raw_img == NULL) { + *err = MVHD_ERR_FILE; + return NULL; + } + if (geom == NULL) { + *err = MVHD_ERR_INVALID_GEOM; + return NULL; + } + mvhd_fseeko64(raw_img, 0, SEEK_END); + uint64_t size_bytes = (uint64_t)mvhd_ftello64(raw_img); + MVHDGeom new_geom = mvhd_calculate_geometry(size_bytes); + if (mvhd_calc_size_bytes(&new_geom) != size_bytes) { + *err = MVHD_ERR_CONV_SIZE; + return NULL; + } + geom->cyl = new_geom.cyl; + geom->heads = new_geom.heads; + geom->spt = new_geom.spt; + mvhd_fseeko64(raw_img, 0, SEEK_SET); + return raw_img; +} + +MVHDMeta* mvhd_convert_to_vhd_fixed(const char* utf8_raw_path, const char* utf8_vhd_path, int* err) { + MVHDGeom geom; + FILE *raw_img = mvhd_open_existing_raw_img(utf8_raw_path, &geom, err); + if (raw_img == NULL) { + return NULL; + } + uint64_t size_in_bytes = mvhd_calc_size_bytes(&geom); + MVHDMeta *vhdm = mvhd_create_fixed_raw(utf8_vhd_path, raw_img, size_in_bytes, &geom, err, NULL); + if (vhdm == NULL) { + return NULL; + } + return vhdm; +} +MVHDMeta* mvhd_convert_to_vhd_sparse(const char* utf8_raw_path, const char* utf8_vhd_path, int* err) { + MVHDGeom geom; + MVHDMeta *vhdm = NULL; + FILE *raw_img = mvhd_open_existing_raw_img(utf8_raw_path, &geom, err); + if (raw_img == NULL) { + return NULL; + } + vhdm = mvhd_create_sparse(utf8_vhd_path, geom, err); + if (vhdm == NULL) { + goto end; + } + uint8_t buff[4096] = {0}; // 8 sectors + uint8_t empty_buff[4096] = {0}; + int total_sectors = mvhd_calc_size_sectors(&geom); + int copy_sect = 0; + for (int i = 0; i < total_sectors; i += 8) { + copy_sect = 8; + if ((i + 8) >= total_sectors) { + copy_sect = total_sectors - i; + memset(buff, 0, sizeof buff); + } + fread(buff, MVHD_SECTOR_SIZE, copy_sect, raw_img); + /* Only write data if there's data to write, to take advantage of the sparse VHD format */ + if (memcmp(buff, empty_buff, sizeof buff) != 0) { + mvhd_write_sectors(vhdm, i, copy_sect, buff); + } + } +end: + fclose(raw_img); + return vhdm; +} +FILE* mvhd_convert_to_raw(const char* utf8_vhd_path, const char* utf8_raw_path, int *err) { + FILE *raw_img = mvhd_fopen(utf8_raw_path, "wb", err); + if (raw_img == NULL) { + return NULL; + } + MVHDMeta *vhdm = mvhd_open(utf8_vhd_path, true, err); + if (vhdm == NULL) { + fclose(raw_img); + return NULL; + } + uint8_t buff[4096] = {0}; // 8 sectors + int total_sectors = mvhd_calc_size_sectors((MVHDGeom*)&vhdm->footer.geom); + int copy_sect = 0; + for (int i = 0; i < total_sectors; i += 8) { + copy_sect = 8; + if ((i + 8) >= total_sectors) { + copy_sect = total_sectors - i; + } + mvhd_read_sectors(vhdm, i, copy_sect, buff); + fwrite(buff, MVHD_SECTOR_SIZE, copy_sect, raw_img); + } + mvhd_close(vhdm); + mvhd_fseeko64(raw_img, 0, SEEK_SET); + return raw_img; +} \ No newline at end of file diff --git a/src/disk/minivhd/minivhd_create.c b/src/disk/minivhd/minivhd_create.c new file mode 100644 index 000000000..c47c7d82d --- /dev/null +++ b/src/disk/minivhd/minivhd_create.c @@ -0,0 +1,485 @@ +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 +#endif +#include +#include +#include +#include +#include +#include "cwalk.h" +#include "libxml2_encoding.h" +#include "minivhd_internal.h" +#include "minivhd_util.h" +#include "minivhd_struct_rw.h" +#include "minivhd_io.h" +#include "minivhd_create.h" +#include "minivhd.h" + +static void mvhd_gen_footer(MVHDFooter* footer, uint64_t size_in_bytes, MVHDGeom* geom, MVHDType type, uint64_t sparse_header_off); +static void mvhd_gen_sparse_header(MVHDSparseHeader* header, uint32_t num_blks, uint64_t bat_offset, uint32_t block_size_in_sectors); +static int mvhd_gen_par_loc(MVHDSparseHeader* header, + const char* child_path, + const char* par_path, + uint64_t start_offset, + mvhd_utf16* w2ku_path_buff, + mvhd_utf16* w2ru_path_buff, + MVHDError* err); +static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path, uint64_t size_in_bytes, MVHDGeom* geom, uint32_t block_size_in_sectors, int* err); + +/** + * \brief Populate a VHD footer + * + * \param [in] footer to populate + * \param [in] size_in_bytes is the total size of the virtual hard disk in bytes + * \param [in] geom to use + * \param [in] type of HVD that is being created + * \param [in] sparse_header_off, an absolute file offset to the sparse header. Not used for fixed VHD images + */ +static void mvhd_gen_footer(MVHDFooter* footer, uint64_t size_in_bytes, MVHDGeom* geom, MVHDType type, uint64_t sparse_header_off) { + memcpy(footer->cookie, "conectix", sizeof footer->cookie); + footer->features = 0x00000002; + footer->fi_fmt_vers = 0x00010000; + footer->data_offset = (type == MVHD_TYPE_DIFF || type == MVHD_TYPE_DYNAMIC) ? sparse_header_off : 0xffffffffffffffff; + footer->timestamp = vhd_calc_timestamp(); + memcpy(footer->cr_app, "mvhd", sizeof footer->cr_app); + footer->cr_vers = 0x000e0000; + memcpy(footer->cr_host_os, "Wi2k", sizeof footer->cr_host_os); + footer->orig_sz = footer->curr_sz = size_in_bytes; + footer->geom.cyl = geom->cyl; + footer->geom.heads = geom->heads; + footer->geom.spt = geom->spt; + footer->disk_type = type; + mvhd_generate_uuid(footer->uuid); + footer->checksum = mvhd_gen_footer_checksum(footer); +} + +/** + * \brief Populate a VHD sparse header + * + * \param [in] header for sparse and differencing images + * \param [in] num_blks is the number of data blocks that the image contains + * \param [in] bat_offset is the absolute file offset for start of the Block Allocation Table + * \param [in] block_size_in_sectors is the block size in sectors. + */ +static void mvhd_gen_sparse_header(MVHDSparseHeader* header, uint32_t num_blks, uint64_t bat_offset, uint32_t block_size_in_sectors) { + memcpy(header->cookie, "cxsparse", sizeof header->cookie); + header->data_offset = 0xffffffffffffffff; + header->bat_offset = bat_offset; + header->head_vers = 0x00010000; + header->max_bat_ent = num_blks; + header->block_sz = block_size_in_sectors * (uint32_t)MVHD_SECTOR_SIZE; + header->checksum = mvhd_gen_sparse_checksum(header); +} + +/** + * \brief Generate parent locators for differencing VHD images + * + * \param [in] header the sparse header to populate with parent locator entries + * \param [in] child_path is the full path to the VHD being created + * \param [in] par_path is the full path to the parent image + * \param [in] start_offset is the absolute file offset from where to start storing the entries themselves. Must be sector aligned. + * \param [out] w2ku_path_buff is a buffer containing the full path to the parent, encoded as UTF16-LE + * \param [out] w2ru_path_buff is a buffer containing the relative path to the parent, encoded as UTF16-LE + * \param [out] err indicates what error occurred, if any + * + * \retval 0 if success + * \retval < 0 if an error occurrs. Check value of *err for actual error + */ +static int mvhd_gen_par_loc(MVHDSparseHeader* header, + const char* child_path, + const char* par_path, + uint64_t start_offset, + mvhd_utf16* w2ku_path_buff, + mvhd_utf16* w2ru_path_buff, + MVHDError* err) { + /* Get our paths to store in the differencing VHD. We want both the absolute path to the parent, + as well as the relative path from the child VHD */ + int rv = 0; + char* par_filename; + size_t par_fn_len; + char rel_path[MVHD_MAX_PATH_BYTES] = {0}; + char child_dir[MVHD_MAX_PATH_BYTES] = {0}; + size_t child_dir_len; + if (strlen(child_path) < sizeof child_dir) { + strcpy(child_dir, child_path); + } else { + *err = MVHD_ERR_PATH_LEN; + rv = -1; + goto end; + } + cwk_path_get_basename(par_path, (const char**)&par_filename, &par_fn_len); + cwk_path_get_dirname(child_dir, &child_dir_len); + child_dir[child_dir_len] = '\0'; + size_t rel_len = cwk_path_get_relative(child_dir, par_path, rel_path, sizeof rel_path); + if (rel_len > sizeof rel_path) { + *err = MVHD_ERR_PATH_LEN; + rv = -1; + goto end; + } + /* We have our paths, now store the parent filename directly in the sparse header. */ + int outlen = sizeof header->par_utf16_name; + int utf_ret; + utf_ret = UTF8ToUTF16BE((unsigned char*)header->par_utf16_name, &outlen, (const unsigned char*)par_filename, (int*)&par_fn_len); + if (utf_ret < 0) { + mvhd_set_encoding_err(utf_ret, (int*)err); + rv = -1; + goto end; + } + + /* And encode the paths to UTF16-LE */ + size_t par_path_len = strlen(par_path); + outlen = sizeof *w2ku_path_buff * MVHD_MAX_PATH_CHARS; + utf_ret = UTF8ToUTF16LE((unsigned char*)w2ku_path_buff, &outlen, (const unsigned char*)par_path, (int*)&par_path_len); + if (utf_ret < 0) { + mvhd_set_encoding_err(utf_ret, (int*)err); + rv = -1; + goto end; + } + int w2ku_len = utf_ret; + outlen = sizeof *w2ru_path_buff * MVHD_MAX_PATH_CHARS; + utf_ret = UTF8ToUTF16LE((unsigned char*)w2ru_path_buff, &outlen, (const unsigned char*)rel_path, (int*)&rel_len); + if (utf_ret < 0) { + mvhd_set_encoding_err(utf_ret, (int*)err); + rv = -1; + goto end; + } + int w2ru_len = utf_ret; + /** + * Finally populate the parent locaters in the sparse header. + * This is the information needed to find the paths saved elsewhere + * in the VHD image + */ + + /* Note about the plat_data_space field: The VHD spec says this field stores the number of sectors needed to store the locator path. + * However, Hyper-V and VPC store the number of bytes, not the number of sectors, and will refuse to open VHDs which have the + * number of sectors in this field. + * See https://stackoverflow.com/questions/40760181/mistake-in-virtual-hard-disk-image-format-specification + */ + header->par_loc_entry[0].plat_code = MVHD_DIF_LOC_W2KU; + header->par_loc_entry[0].plat_data_len = (uint32_t)w2ku_len; + header->par_loc_entry[0].plat_data_offset = (uint64_t)start_offset; + header->par_loc_entry[0].plat_data_space = ((header->par_loc_entry[0].plat_data_len / MVHD_SECTOR_SIZE) + 1) * MVHD_SECTOR_SIZE; + header->par_loc_entry[1].plat_code = MVHD_DIF_LOC_W2RU; + header->par_loc_entry[1].plat_data_len = (uint32_t)w2ru_len; + header->par_loc_entry[1].plat_data_offset = (uint64_t)start_offset + ((uint64_t)header->par_loc_entry[0].plat_data_space); + header->par_loc_entry[1].plat_data_space = ((header->par_loc_entry[1].plat_data_len / MVHD_SECTOR_SIZE) + 1) * MVHD_SECTOR_SIZE; + goto end; + +end: + return rv; +} + +MVHDMeta* mvhd_create_fixed(const char* path, MVHDGeom geom, int* err, mvhd_progress_callback progress_callback) { + uint64_t size_in_bytes = mvhd_calc_size_bytes(&geom); + return mvhd_create_fixed_raw(path, NULL, size_in_bytes, &geom, err, progress_callback); +} + +/** + * \brief internal function that implements public mvhd_create_fixed() functionality + * + * Contains one more parameter than the public function, to allow using an existing + * raw disk image as the data source for the new fixed VHD. + * + * \param [in] raw_image file handle to a raw disk image to populate VHD + */ +MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, uint64_t size_in_bytes, MVHDGeom* geom, int* err, mvhd_progress_callback progress_callback) { + uint8_t img_data[MVHD_SECTOR_SIZE] = {0}; + uint8_t footer_buff[MVHD_FOOTER_SIZE] = {0}; + MVHDMeta* vhdm = calloc(1, sizeof *vhdm); + if (vhdm == NULL) { + *err = MVHD_ERR_MEM; + goto end; + } + if (geom == NULL || (geom->cyl == 0 || geom->heads == 0 || geom->spt == 0)) { + *err = MVHD_ERR_INVALID_GEOM; + goto cleanup_vhdm; + } + FILE* f = mvhd_fopen(path, "wb+", err); + if (f == NULL) { + goto cleanup_vhdm; + } + mvhd_fseeko64(f, 0, SEEK_SET); + uint32_t size_sectors = (uint32_t)(size_in_bytes / MVHD_SECTOR_SIZE); + uint32_t s; + if (progress_callback) + progress_callback(0, size_sectors); + if (raw_img != NULL) { + mvhd_fseeko64(raw_img, 0, SEEK_END); + uint64_t raw_size = (uint64_t)mvhd_ftello64(raw_img); + MVHDGeom raw_geom = mvhd_calculate_geometry(raw_size); + if (mvhd_calc_size_bytes(&raw_geom) != raw_size) { + *err = MVHD_ERR_CONV_SIZE; + goto cleanup_vhdm; + } + mvhd_gen_footer(&vhdm->footer, raw_size, geom, MVHD_TYPE_FIXED, 0); + mvhd_fseeko64(raw_img, 0, SEEK_SET); + for (s = 0; s < size_sectors; s++) { + fread(img_data, sizeof img_data, 1, raw_img); + fwrite(img_data, sizeof img_data, 1, f); + if (progress_callback) + progress_callback(s + 1, size_sectors); + } + } else { + mvhd_gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_FIXED, 0); + for (s = 0; s < size_sectors; s++) { + fwrite(img_data, sizeof img_data, 1, f); + if (progress_callback) + progress_callback(s + 1, size_sectors); + } + } + mvhd_footer_to_buffer(&vhdm->footer, footer_buff); + fwrite(footer_buff, sizeof footer_buff, 1, f); + fclose(f); + f = NULL; + free(vhdm); + vhdm = mvhd_open(path, false, err); + goto end; + +cleanup_vhdm: + free(vhdm); + vhdm = NULL; +end: + return vhdm; +} + +/** + * \brief Create sparse or differencing VHD image. + * + * \param [in] path is the absolute path to the VHD file to create + * \param [in] par_path is the absolute path to a parent image. If NULL, a sparse image is created, otherwise create a differencing image + * \param [in] size_in_bytes is the total size in bytes of the virtual hard disk image + * \param [in] geom is the HDD geometry of the image to create. Determines final image size + * \param [in] block_size_in_sectors is the block size in sectors + * \param [out] err indicates what error occurred, if any + * + * \return NULL if an error occurrs. Check value of *err for actual error. Otherwise returns pointer to a MVHDMeta struct + */ +static MVHDMeta* mvhd_create_sparse_diff(const char* path, const char* par_path, uint64_t size_in_bytes, MVHDGeom* geom, uint32_t block_size_in_sectors, int* err) { + uint8_t footer_buff[MVHD_FOOTER_SIZE] = {0}; + uint8_t sparse_buff[MVHD_SPARSE_SIZE] = {0}; + uint8_t bat_sect[MVHD_SECTOR_SIZE]; + MVHDGeom par_geom = {0}; + memset(bat_sect, 0xffffffff, sizeof bat_sect); + MVHDMeta* vhdm = NULL; + MVHDMeta* par_vhdm = NULL; + mvhd_utf16* w2ku_path_buff = NULL; + mvhd_utf16* w2ru_path_buff = NULL; + uint32_t par_mod_timestamp = 0; + if (par_path != NULL) { + par_mod_timestamp = mvhd_file_mod_timestamp(par_path, err); + if (*err != 0) { + goto end; + } + par_vhdm = mvhd_open(par_path, true, err); + if (par_vhdm == NULL) { + goto end; + } + } + vhdm = calloc(1, sizeof *vhdm); + if (vhdm == NULL) { + *err = MVHD_ERR_MEM; + goto cleanup_par_vhdm; + } + if (par_vhdm != NULL) { + /* We use the geometry from the parent VHD, not what was passed in */ + par_geom.cyl = par_vhdm->footer.geom.cyl; + par_geom.heads = par_vhdm->footer.geom.heads; + par_geom.spt = par_vhdm->footer.geom.spt; + geom = &par_geom; + size_in_bytes = par_vhdm->footer.curr_sz; + } else if (geom == NULL || (geom->cyl == 0 || geom->heads == 0 || geom->spt == 0)) { + *err = MVHD_ERR_INVALID_GEOM; + goto cleanup_vhdm; + } + + FILE* f = mvhd_fopen(path, "wb+", err); + if (f == NULL) { + goto cleanup_vhdm; + } + mvhd_fseeko64(f, 0, SEEK_SET); + /* Note, the sparse header follows the footer copy at the beginning of the file */ + if (par_path == NULL) { + mvhd_gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_DYNAMIC, MVHD_FOOTER_SIZE); + } else { + mvhd_gen_footer(&vhdm->footer, size_in_bytes, geom, MVHD_TYPE_DIFF, MVHD_FOOTER_SIZE); + } + mvhd_footer_to_buffer(&vhdm->footer, footer_buff); + /* As mentioned, start with a copy of the footer */ + fwrite(footer_buff, sizeof footer_buff, 1, f); + /** + * Calculate the number of (2MB or 512KB) data blocks required to store the entire + * contents of the disk image, followed by the number of sectors the + * BAT occupies in the image. Note, the BAT is sector aligned, and is padded + * to the next sector boundary + * */ + uint32_t size_in_sectors = (uint32_t)(size_in_bytes / MVHD_SECTOR_SIZE); + uint32_t num_blks = size_in_sectors / block_size_in_sectors; + if (size_in_sectors % block_size_in_sectors != 0) { + num_blks += 1; + } + uint32_t num_bat_sect = num_blks / MVHD_BAT_ENT_PER_SECT; + if (num_blks % MVHD_BAT_ENT_PER_SECT != 0) { + num_bat_sect += 1; + } + /* Storing the BAT directly following the footer and header */ + uint64_t bat_offset = MVHD_FOOTER_SIZE + MVHD_SPARSE_SIZE; + uint64_t par_loc_offset = 0; + + /** + * If creating a differencing VHD, populate the sparse header with additional + * data about the parent image, and where to find it, and it's last modified timestamp + * */ + if (par_vhdm != NULL) { + /** + * Create output buffers to encode paths into. + * The paths are not stored directly in the sparse header, hence the need to + * store them in buffers to be written to the VHD image later + */ + w2ku_path_buff = calloc(MVHD_MAX_PATH_CHARS, sizeof * w2ku_path_buff); + if (w2ku_path_buff == NULL) { + *err = MVHD_ERR_MEM; + goto end; + } + w2ru_path_buff = calloc(MVHD_MAX_PATH_CHARS, sizeof * w2ru_path_buff); + if (w2ru_path_buff == NULL) { + *err = MVHD_ERR_MEM; + goto end; + } + memcpy(vhdm->sparse.par_uuid, par_vhdm->footer.uuid, sizeof vhdm->sparse.par_uuid); + par_loc_offset = bat_offset + ((uint64_t)num_bat_sect * MVHD_SECTOR_SIZE) + (5 * MVHD_SECTOR_SIZE); + if (mvhd_gen_par_loc(&vhdm->sparse, path, par_path, par_loc_offset, w2ku_path_buff, w2ru_path_buff, (MVHDError*)err) < 0) { + goto cleanup_vhdm; + } + vhdm->sparse.par_timestamp = par_mod_timestamp; + } + mvhd_gen_sparse_header(&vhdm->sparse, num_blks, bat_offset, block_size_in_sectors); + mvhd_header_to_buffer(&vhdm->sparse, sparse_buff); + fwrite(sparse_buff, sizeof sparse_buff, 1, f); + /* The BAT sectors need to be filled with 0xffffffff */ + for (uint32_t i = 0; i < num_bat_sect; i++) { + fwrite(bat_sect, sizeof bat_sect, 1, f); + } + mvhd_write_empty_sectors(f, 5); + /** + * If creating a differencing VHD, the paths to the parent image need to be written + * tp the file. Both absolute and relative paths are written + * */ + if (par_vhdm != NULL) { + uint64_t curr_pos = (uint64_t)mvhd_ftello64(f); + /* Double check my sums... */ + assert(curr_pos == par_loc_offset); + /* Fill the space required for location data with zero */ + uint8_t empty_sect[MVHD_SECTOR_SIZE] = {0}; + for (int i = 0; i < 2; i++) { + for (uint32_t j = 0; j < (vhdm->sparse.par_loc_entry[i].plat_data_space / MVHD_SECTOR_SIZE); j++) { + fwrite(empty_sect, sizeof empty_sect, 1, f); + } + } + /* Now write the location entries */ + mvhd_fseeko64(f, vhdm->sparse.par_loc_entry[0].plat_data_offset, SEEK_SET); + fwrite(w2ku_path_buff, vhdm->sparse.par_loc_entry[0].plat_data_len, 1, f); + mvhd_fseeko64(f, vhdm->sparse.par_loc_entry[1].plat_data_offset, SEEK_SET); + fwrite(w2ru_path_buff, vhdm->sparse.par_loc_entry[1].plat_data_len, 1, f); + /* and reset the file position to continue */ + mvhd_fseeko64(f, vhdm->sparse.par_loc_entry[1].plat_data_offset + vhdm->sparse.par_loc_entry[1].plat_data_space, SEEK_SET); + mvhd_write_empty_sectors(f, 5); + } + /* And finish with the footer */ + fwrite(footer_buff, sizeof footer_buff, 1, f); + fclose(f); + f = NULL; + free(vhdm); + vhdm = mvhd_open(path, false, err); + goto end; + +cleanup_vhdm: + free(vhdm); + vhdm = NULL; +cleanup_par_vhdm: + if (par_vhdm != NULL) { + mvhd_close(par_vhdm); + } +end: + free(w2ku_path_buff); + free(w2ru_path_buff); + return vhdm; +} + +MVHDMeta* mvhd_create_sparse(const char* path, MVHDGeom geom, int* err) { + uint64_t size_in_bytes = mvhd_calc_size_bytes(&geom); + return mvhd_create_sparse_diff(path, NULL, size_in_bytes, &geom, MVHD_BLOCK_LARGE, err); +} + +MVHDMeta* mvhd_create_diff(const char* path, const char* par_path, int* err) { + return mvhd_create_sparse_diff(path, par_path, 0, NULL, MVHD_BLOCK_LARGE, err); +} + +MVHDMeta* mvhd_create_ex(MVHDCreationOptions options, int* err) { + uint32_t geom_sector_size; + switch (options.type) + { + case MVHD_TYPE_FIXED: + case MVHD_TYPE_DYNAMIC: + geom_sector_size = mvhd_calc_size_sectors(&(options.geometry)); + if ((options.size_in_bytes > 0 && (options.size_in_bytes % MVHD_SECTOR_SIZE) > 0) + || (options.size_in_bytes > MVHD_MAX_SIZE_IN_BYTES) + || (options.size_in_bytes == 0 && geom_sector_size == 0)) + { + *err = MVHD_ERR_INVALID_SIZE; + return NULL; + } + + if (options.size_in_bytes > 0 && ((uint64_t)geom_sector_size * MVHD_SECTOR_SIZE) > options.size_in_bytes) + { + *err = MVHD_ERR_INVALID_GEOM; + return NULL; + } + + if (options.size_in_bytes == 0) + options.size_in_bytes = (uint64_t)geom_sector_size * MVHD_SECTOR_SIZE; + + if (geom_sector_size == 0) + options.geometry = mvhd_calculate_geometry(options.size_in_bytes); + break; + case MVHD_TYPE_DIFF: + if (options.parent_path == NULL) + { + *err = MVHD_ERR_FILE; + return NULL; + } + break; + default: + *err = MVHD_ERR_TYPE; + return NULL; + } + + if (options.path == NULL) + { + *err = MVHD_ERR_FILE; + return NULL; + } + + if (options.type != MVHD_TYPE_FIXED) + { + if (options.block_size_in_sectors == MVHD_BLOCK_DEFAULT) + options.block_size_in_sectors = MVHD_BLOCK_LARGE; + + if (options.block_size_in_sectors != MVHD_BLOCK_LARGE && options.block_size_in_sectors != MVHD_BLOCK_SMALL) + { + *err = MVHD_ERR_INVALID_BLOCK_SIZE; + return NULL; + } + } + + switch (options.type) + { + case MVHD_TYPE_FIXED: + return mvhd_create_fixed_raw(options.path, NULL, options.size_in_bytes, &(options.geometry), err, options.progress_callback); + case MVHD_TYPE_DYNAMIC: + return mvhd_create_sparse_diff(options.path, NULL, options.size_in_bytes, &(options.geometry), options.block_size_in_sectors, err); + case MVHD_TYPE_DIFF: + return mvhd_create_sparse_diff(options.path, options.parent_path, 0, NULL, options.block_size_in_sectors, err); + } + + return NULL; /* Make the compiler happy */ +} \ No newline at end of file diff --git a/src/disk/minivhd/minivhd_create.h b/src/disk/minivhd/minivhd_create.h new file mode 100644 index 000000000..9840d19ff --- /dev/null +++ b/src/disk/minivhd/minivhd_create.h @@ -0,0 +1,8 @@ +#ifndef MINIVHD_CREATE_H +#define MINIVHD_CREATE_H +#include +#include "minivhd.h" + +MVHDMeta* mvhd_create_fixed_raw(const char* path, FILE* raw_img, uint64_t size_in_bytes, MVHDGeom* geom, int* err, mvhd_progress_callback progress_callback); + +#endif \ No newline at end of file diff --git a/src/disk/minivhd/minivhd_internal.h b/src/disk/minivhd/minivhd_internal.h new file mode 100644 index 000000000..ea75284f8 --- /dev/null +++ b/src/disk/minivhd/minivhd_internal.h @@ -0,0 +1,96 @@ +#ifndef MINIVHD_INTERNAL_H +#define MINIVHD_INTERNAL_H +#include +#include +#include + +#define MVHD_FOOTER_SIZE 512 +#define MVHD_SPARSE_SIZE 1024 + +#define MVHD_SECTOR_SIZE 512 +#define MVHD_BAT_ENT_PER_SECT 128 + +#define MVHD_MAX_SIZE_IN_BYTES 0x1fe00000000 + +#define MVHD_SPARSE_BLK 0xffffffff +/* For simplicity, we don't handle paths longer than this + * Note, this is the max path in characters, as that is what + * Windows uses + */ +#define MVHD_MAX_PATH_CHARS 260 +#define MVHD_MAX_PATH_BYTES 1040 + +#define MVHD_DIF_LOC_W2RU 0x57327275 +#define MVHD_DIF_LOC_W2KU 0x57326B75 + +typedef struct MVHDSectorBitmap { + uint8_t* curr_bitmap; + int sector_count; + int curr_block; +} MVHDSectorBitmap; + +typedef struct MVHDFooter { + uint8_t cookie[8]; + uint32_t features; + uint32_t fi_fmt_vers; + uint64_t data_offset; + uint32_t timestamp; + uint8_t cr_app[4]; + uint32_t cr_vers; + uint8_t cr_host_os[4]; + uint64_t orig_sz; + uint64_t curr_sz; + struct { + uint16_t cyl; + uint8_t heads; + uint8_t spt; + } geom; + uint32_t disk_type; + uint32_t checksum; + uint8_t uuid[16]; + uint8_t saved_st; + uint8_t reserved[427]; +} MVHDFooter; + +typedef struct MVHDSparseHeader { + uint8_t cookie[8]; + uint64_t data_offset; + uint64_t bat_offset; + uint32_t head_vers; + uint32_t max_bat_ent; + uint32_t block_sz; + uint32_t checksum; + uint8_t par_uuid[16]; + uint32_t par_timestamp; + uint32_t reserved_1; + uint8_t par_utf16_name[512]; + struct { + uint32_t plat_code; + uint32_t plat_data_space; + uint32_t plat_data_len; + uint32_t reserved; + uint64_t plat_data_offset; + } par_loc_entry[8]; + uint8_t reserved_2[256]; +} MVHDSparseHeader; + +typedef struct MVHDMeta MVHDMeta; +struct MVHDMeta { + FILE* f; + bool readonly; + char filename[MVHD_MAX_PATH_BYTES]; + struct MVHDMeta* parent; + MVHDFooter footer; + MVHDSparseHeader sparse; + uint32_t* block_offset; + int sect_per_block; + MVHDSectorBitmap bitmap; + int (*read_sectors)(MVHDMeta*, uint32_t, int, void*); + int (*write_sectors)(MVHDMeta*, uint32_t, int, void*); + struct { + uint8_t* zero_data; + int sector_count; + } format_buffer; +}; + +#endif \ No newline at end of file diff --git a/src/disk/minivhd/minivhd_io.c b/src/disk/minivhd/minivhd_io.c new file mode 100644 index 000000000..4169d66f1 --- /dev/null +++ b/src/disk/minivhd/minivhd_io.c @@ -0,0 +1,279 @@ +/** + * \file + * \brief Sector reading and writing implementations + */ + +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 +#endif +#include +#include +#include "minivhd_internal.h" +#include "minivhd_util.h" + +/* The following bit array macros adapted from + http://www.mathcs.emory.edu/~cheung/Courses/255/Syllabus/1-C-intro/bit-array.html */ + +#define VHD_SETBIT(A,k) ( A[(k/8)] |= (0x80 >> (k%8)) ) +#define VHD_CLEARBIT(A,k) ( A[(k/8)] &= ~(0x80 >> (k%8)) ) +#define VHD_TESTBIT(A,k) ( A[(k/8)] & (0x80 >> (k%8)) ) + +static inline void mvhd_check_sectors(uint32_t offset, int num_sectors, uint32_t total_sectors, int* transfer_sect, int* trunc_sect); +static void mvhd_read_sect_bitmap(MVHDMeta* vhdm, int blk); +static void mvhd_write_bat_entry(MVHDMeta* vhdm, int blk); +static void mvhd_create_block(MVHDMeta* vhdm, int blk); +static void mvhd_write_curr_sect_bitmap(MVHDMeta* vhdm); + +/** + * \brief Check that we will not be overflowing buffers + * + * \param [in] offset The offset from which we are beginning from + * \param [in] num_sectors The number of sectors which we desire to read/write + * \param [in] total_sectors The total number of sectors available + * \param [out] transfer_sect The number of sectors to actually write. + * This may be lower than num_sectors if offset + num_sectors >= total_sectors + * \param [out] trunc_sectors The number of sectors truncated if transfer_sectors < num_sectors + */ +static inline void mvhd_check_sectors(uint32_t offset, int num_sectors, uint32_t total_sectors, int* transfer_sect, int* trunc_sect) { + *transfer_sect = num_sectors; + *trunc_sect = 0; + if ((total_sectors - offset) < (uint32_t)*transfer_sect) { + *transfer_sect = total_sectors - offset; + *trunc_sect = num_sectors - *transfer_sect; + } +} + +void mvhd_write_empty_sectors(FILE* f, int sector_count) { + uint8_t zero_bytes[MVHD_SECTOR_SIZE] = {0}; + for (int i = 0; i < sector_count; i++) { + fwrite(zero_bytes, sizeof zero_bytes, 1, f); + } +} + +/** + * \brief Read the sector bitmap for a block. + * + * If the block is sparse, the sector bitmap in memory will be + * zeroed. Otherwise, the sector bitmap is read from the VHD file. + * + * \param [in] vhdm MiniVHD data structure + * \param [in] blk The block for which to read the sector bitmap from + */ +static void mvhd_read_sect_bitmap(MVHDMeta* vhdm, int blk) { + if (vhdm->block_offset[blk] != MVHD_SPARSE_BLK) { + mvhd_fseeko64(vhdm->f, (uint64_t)vhdm->block_offset[blk] * MVHD_SECTOR_SIZE, SEEK_SET); + fread(vhdm->bitmap.curr_bitmap, vhdm->bitmap.sector_count * MVHD_SECTOR_SIZE, 1, vhdm->f); + } else { + memset(vhdm->bitmap.curr_bitmap, 0, vhdm->bitmap.sector_count * MVHD_SECTOR_SIZE); + } + vhdm->bitmap.curr_block = blk; +} + +/** + * \brief Write the current sector bitmap in memory to file + * + * \param [in] vhdm MiniVHD data structure + */ +static void mvhd_write_curr_sect_bitmap(MVHDMeta* vhdm) { + if (vhdm->bitmap.curr_block >= 0) { + int64_t abs_offset = (int64_t)vhdm->block_offset[vhdm->bitmap.curr_block] * MVHD_SECTOR_SIZE; + mvhd_fseeko64(vhdm->f, abs_offset, SEEK_SET); + fwrite(vhdm->bitmap.curr_bitmap, MVHD_SECTOR_SIZE, vhdm->bitmap.sector_count, vhdm->f); + } +} + +/** + * \brief Write block offset from memory into file + * + * \param [in] vhdm MiniVHD data structure + * \param [in] blk The block for which to write the offset for + */ +static void mvhd_write_bat_entry(MVHDMeta* vhdm, int blk) { + uint64_t table_offset = vhdm->sparse.bat_offset + ((uint64_t)blk * sizeof *vhdm->block_offset); + uint32_t offset = mvhd_to_be32(vhdm->block_offset[blk]); + mvhd_fseeko64(vhdm->f, table_offset, SEEK_SET); + fwrite(&offset, sizeof offset, 1, vhdm->f); +} + +/** + * \brief Create an empty block in a sparse or differencing VHD image + * + * VHD images store data in blocks, which are typically 4096 sectors in size + * (~2MB). These blocks may be stored on disk in any order. Blocks are created + * on demand when required. + * + * This function creates new, empty blocks, by replacing the footer at the end of the file + * and then re-inserting the footer at the new file end. The BAT table entry for the + * new block is updated with the new offset. + * + * \param [in] vhdm MiniVHD data structure + * \param [in] blk The block number to create + */ +static void mvhd_create_block(MVHDMeta* vhdm, int blk) { + uint8_t footer[MVHD_FOOTER_SIZE]; + /* Seek to where the footer SHOULD be */ + mvhd_fseeko64(vhdm->f, -MVHD_FOOTER_SIZE, SEEK_END); + fread(footer, sizeof footer, 1, vhdm->f); + mvhd_fseeko64(vhdm->f, -MVHD_FOOTER_SIZE, SEEK_END); + if (!mvhd_is_conectix_str(footer)) { + /* Oh dear. We use the header instead, since something has gone wrong at the footer */ + mvhd_fseeko64(vhdm->f, 0, SEEK_SET); + fread(footer, sizeof footer, 1, vhdm->f); + mvhd_fseeko64(vhdm->f, 0, SEEK_END); + } + int64_t abs_offset = mvhd_ftello64(vhdm->f); + if (abs_offset % MVHD_SECTOR_SIZE != 0) { + /* Yikes! We're supposed to be on a sector boundary. Add some padding */ + int64_t padding_amount = (int64_t)MVHD_SECTOR_SIZE - (abs_offset % MVHD_SECTOR_SIZE); + uint8_t zero_byte = 0; + for (int i = 0; i < padding_amount; i++) { + fwrite(&zero_byte, sizeof zero_byte, 1, vhdm->f); + } + abs_offset += padding_amount; + } + uint32_t sect_offset = (uint32_t)(abs_offset / MVHD_SECTOR_SIZE); + int blk_size_sectors = vhdm->sparse.block_sz / MVHD_SECTOR_SIZE; + mvhd_write_empty_sectors(vhdm->f, vhdm->bitmap.sector_count + blk_size_sectors); + /* Add a bit of padding. That's what Windows appears to do, although it's not strictly necessary... */ + mvhd_write_empty_sectors(vhdm->f, 5); + /* And we finish with the footer */ + fwrite(footer, sizeof footer, 1, vhdm->f); + /* We no longer have a sparse block. Update that BAT! */ + vhdm->block_offset[blk] = sect_offset; + mvhd_write_bat_entry(vhdm, blk); +} + +int mvhd_fixed_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) { + int64_t addr; + int transfer_sectors, truncated_sectors; + uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE); + mvhd_check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors); + addr = (int64_t)offset * MVHD_SECTOR_SIZE; + mvhd_fseeko64(vhdm->f, addr, SEEK_SET); + fread(out_buff, transfer_sectors*MVHD_SECTOR_SIZE, 1, vhdm->f); + return truncated_sectors; +} + +int mvhd_sparse_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) { + int transfer_sectors, truncated_sectors; + uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE); + mvhd_check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors); + uint8_t* buff = (uint8_t*)out_buff; + int64_t addr; + uint32_t s, ls; + int blk, prev_blk, sib; + ls = offset + transfer_sectors; + prev_blk = -1; + for (s = offset; s < ls; s++) { + blk = s / vhdm->sect_per_block; + sib = s % vhdm->sect_per_block; + if (blk != prev_blk) { + prev_blk = blk; + if (vhdm->bitmap.curr_block != blk) { + mvhd_read_sect_bitmap(vhdm, blk); + mvhd_fseeko64(vhdm->f, (uint64_t)sib * MVHD_SECTOR_SIZE, SEEK_CUR); + } else { + addr = ((int64_t)vhdm->block_offset[blk] + vhdm->bitmap.sector_count + sib) * MVHD_SECTOR_SIZE; + mvhd_fseeko64(vhdm->f, addr, SEEK_SET); + } + } + if (VHD_TESTBIT(vhdm->bitmap.curr_bitmap, sib)) { + fread(buff, MVHD_SECTOR_SIZE, 1, vhdm->f); + } else { + memset(buff, 0, MVHD_SECTOR_SIZE); + mvhd_fseeko64(vhdm->f, MVHD_SECTOR_SIZE, SEEK_CUR); + } + buff += MVHD_SECTOR_SIZE; + } + return truncated_sectors; +} + +int mvhd_diff_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) { + int transfer_sectors, truncated_sectors; + uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE); + mvhd_check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors); + uint8_t* buff = (uint8_t*)out_buff; + MVHDMeta* curr_vhdm = vhdm; + uint32_t s, ls; + int blk, sib; + ls = offset + transfer_sectors; + for (s = offset; s < ls; s++) { + while (curr_vhdm->footer.disk_type == MVHD_TYPE_DIFF) { + blk = s / curr_vhdm->sect_per_block; + sib = s % curr_vhdm->sect_per_block; + if (curr_vhdm->bitmap.curr_block != blk) { + mvhd_read_sect_bitmap(curr_vhdm, blk); + } + if (!VHD_TESTBIT(curr_vhdm->bitmap.curr_bitmap, sib)) { + curr_vhdm = curr_vhdm->parent; + } else { break; } + } + /* We handle actual sector reading using the fixed or sparse functions, + as a differencing VHD is also a sparse VHD */ + if (curr_vhdm->footer.disk_type == MVHD_TYPE_DIFF || curr_vhdm->footer.disk_type == MVHD_TYPE_DYNAMIC) { + mvhd_sparse_read(curr_vhdm, s, 1, buff); + } else { + mvhd_fixed_read(curr_vhdm, s, 1, buff); + } + curr_vhdm = vhdm; + buff += MVHD_SECTOR_SIZE; + } + return truncated_sectors; +} + +int mvhd_fixed_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff) { + int64_t addr; + int transfer_sectors, truncated_sectors; + uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE); + mvhd_check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors); + addr = (int64_t)offset * MVHD_SECTOR_SIZE; + mvhd_fseeko64(vhdm->f, addr, SEEK_SET); + fwrite(in_buff, transfer_sectors*MVHD_SECTOR_SIZE, 1, vhdm->f); + return truncated_sectors; +} + +int mvhd_sparse_diff_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff) { + int transfer_sectors, truncated_sectors; + uint32_t total_sectors = (uint32_t)(vhdm->footer.curr_sz / MVHD_SECTOR_SIZE); + mvhd_check_sectors(offset, num_sectors, total_sectors, &transfer_sectors, &truncated_sectors); + uint8_t* buff = (uint8_t*)in_buff; + int64_t addr; + uint32_t s, ls; + int blk, prev_blk, sib; + ls = offset + transfer_sectors; + prev_blk = -1; + for (s = offset; s < ls; s++) { + blk = s / vhdm->sect_per_block; + sib = s % vhdm->sect_per_block; + if (vhdm->bitmap.curr_block != blk && prev_blk >= 0) { + /* Write the sector bitmap for the previous block, before we replace it. */ + mvhd_write_curr_sect_bitmap(vhdm); + } + if (vhdm->block_offset[blk] == MVHD_SPARSE_BLK) { + /* "read" the sector bitmap first, before creating a new block, as the bitmap will be + zero either way */ + mvhd_read_sect_bitmap(vhdm, blk); + mvhd_create_block(vhdm, blk); + } + if (blk != prev_blk) { + if (vhdm->bitmap.curr_block != blk) { + mvhd_read_sect_bitmap(vhdm, blk); + mvhd_fseeko64(vhdm->f, (uint64_t)sib * MVHD_SECTOR_SIZE, SEEK_CUR); + } else { + addr = ((int64_t)vhdm->block_offset[blk] + vhdm->bitmap.sector_count + sib) * MVHD_SECTOR_SIZE; + mvhd_fseeko64(vhdm->f, addr, SEEK_SET); + } + prev_blk = blk; + } + fwrite(buff, MVHD_SECTOR_SIZE, 1, vhdm->f); + VHD_SETBIT(vhdm->bitmap.curr_bitmap, sib); + buff += MVHD_SECTOR_SIZE; + } + /* And write the sector bitmap for the last block we visited to disk */ + mvhd_write_curr_sect_bitmap(vhdm); + return truncated_sectors; +} + +int mvhd_noop_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff) { + return 0; +} diff --git a/src/disk/minivhd/minivhd_io.h b/src/disk/minivhd/minivhd_io.h new file mode 100644 index 000000000..cdbfa6d77 --- /dev/null +++ b/src/disk/minivhd/minivhd_io.h @@ -0,0 +1,132 @@ +#ifndef MINIVHD_IO_H +#define MINIVHD_IO_H +#include "minivhd.h" + +/** + * \brief Write zero filled sectors to file. + * + * Note, the caller should set the file position before calling this + * function for correct operation. + * + * \param [in] f File to write sectors to + * \param [in] sector_count The number of sectors to write + */ +void mvhd_write_empty_sectors(FILE* f, int sector_count); + +/** + * \brief Read a fixed VHD image + * + * Fixed VHD images are essentially raw image files with a footer tacked on + * the end. They are therefore straightforward to write + * + * \param [in] vhdm MiniVHD data structure + * \param [in] offset Sector offset to read from + * \param [in] num_sectors The desired number of sectors to read + * \param [out] out_buff An output buffer to store read sectors. Must be + * large enough to hold num_sectors worth of sectors. + * + * \retval 0 num_sectors were read from file + * \retval >0 < num_sectors were read from file + */ +int mvhd_fixed_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff); + +/** + * \brief Read a sparse VHD image + * + * Sparse, or dynamic images are VHD images that grow as data is written to them. + * + * This function implements the logic to read sectors from the file, taking into + * account the fact that blocks may be stored on disk in any order, and that the + * read could cross block boundaries. + * + * \param [in] vhdm MiniVHD data structure + * \param [in] offset Sector offset to read from + * \param [in] num_sectors The desired number of sectors to read + * \param [out] out_buff An output buffer to store read sectors. Must be + * large enough to hold num_sectors worth of sectors. + * + * \retval 0 num_sectors were read from file + * \retval >0 < num_sectors were read from file + */ +int mvhd_sparse_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff); + +/** + * \brief Read a differencing VHD image + * + * Differencing images are a variant of a sparse image. They contain the grow-on-demand + * properties of sparse images, but also reference a parent image. Data is read from the + * child image only if it is newer than the data stored in the parent image. + * + * This function implements the logic to read sectors from the child, or a parent image. + * Differencing images may have a differencing image as a parent, creating a chain of images. + * There is no theoretical chain length limit, although I do not consider long chains to be + * advisable. Verifying the parent-child relationship is not very robust. + * + * \param [in] vhdm MiniVHD data structure + * \param [in] offset Sector offset to read from + * \param [in] num_sectors The desired number of sectors to read + * \param [out] out_buff An output buffer to store read sectors. Must be + * large enough to hold num_sectors worth of sectors. + * + * \retval 0 num_sectors were read from file + * \retval >0 < num_sectors were read from file + */ +int mvhd_diff_read(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff); + +/** + * \brief Write to a fixed VHD image + * + * Fixed VHD images are essentially raw image files with a footer tacked on + * the end. They are therefore straightforward to write + * + * \param [in] vhdm MiniVHD data structure + * \param [in] offset Sector offset to write to + * \param [in] num_sectors The desired number of sectors to write + * \param [in] in_buff A source buffer to write sectors from. Must be + * large enough to hold num_sectors worth of sectors. + * + * \retval 0 num_sectors were written to file + * \retval >0 < num_sectors were written to file + */ +int mvhd_fixed_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff); + +/** + * \brief Write to a sparse or differencing VHD image + * + * Sparse, or dynamic images are VHD images that grow as data is written to them. + * + * Differencing images are a variant of a sparse image. They contain the grow-on-demand + * properties of sparse images, but also reference a parent image. Data is always written + * to the child image. This makes writing to differencing images essentially identical to + * writing to sparse images, hence they use the same function. + * + * This function implements the logic to write sectors to the file, taking into + * account the fact that blocks may be stored on disk in any order, and that the + * write operation could cross block boundaries. + * + * \param [in] vhdm MiniVHD data structure + * \param [in] offset Sector offset to write to + * \param [in] num_sectors The desired number of sectors to write + * \param [in] in_buff A source buffer to write sectors from. Must be + * large enough to hold num_sectors worth of sectors. + * + * \retval 0 num_sectors were written to file + * \retval >0 < num_sectors were written to file + */ +int mvhd_sparse_diff_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff); + +/** + * \brief A no-op function to "write" to read-only VHD images + * + * \param [in] vhdm MiniVHD data structure + * \param [in] offset Sector offset to write to + * \param [in] num_sectors The desired number of sectors to write + * \param [in] in_buff A source buffer to write sectors from. Must be + * large enough to hold num_sectors worth of sectors. + * + * \retval 0 num_sectors were written to file + * \retval >0 < num_sectors were written to file + */ +int mvhd_noop_write(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff); + +#endif \ No newline at end of file diff --git a/src/disk/minivhd/minivhd_manage.c b/src/disk/minivhd/minivhd_manage.c new file mode 100644 index 000000000..e9473c0e3 --- /dev/null +++ b/src/disk/minivhd/minivhd_manage.c @@ -0,0 +1,535 @@ +/** + * \file + * \brief VHD management functions (open, close, read write etc) + */ +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 +#endif +#include +#include +#include +#include +#include +#include "cwalk.h" +#include "libxml2_encoding.h" +#include "minivhd_internal.h" +#include "minivhd_io.h" +#include "minivhd_util.h" +#include "minivhd_struct_rw.h" +#include "minivhd.h" + +int mvhd_errno = 0; +static char tmp_open_path[MVHD_MAX_PATH_BYTES] = {0}; +struct MVHDPaths { + char dir_path[MVHD_MAX_PATH_BYTES]; + char file_name[MVHD_MAX_PATH_BYTES]; + char w2ku_path[MVHD_MAX_PATH_BYTES]; + char w2ru_path[MVHD_MAX_PATH_BYTES]; + char joined_path[MVHD_MAX_PATH_BYTES]; + uint16_t tmp_src_path[MVHD_MAX_PATH_CHARS]; +}; + +static void mvhd_read_footer(MVHDMeta* vhdm); +static void mvhd_read_sparse_header(MVHDMeta* vhdm); +static bool mvhd_footer_checksum_valid(MVHDMeta* vhdm); +static bool mvhd_sparse_checksum_valid(MVHDMeta* vhdm); +static int mvhd_read_bat(MVHDMeta *vhdm, MVHDError* err); +static void mvhd_calc_sparse_values(MVHDMeta* vhdm); +static int mvhd_init_sector_bitmap(MVHDMeta* vhdm, MVHDError* err); + +/** + * \brief Populate data stuctures with content from a VHD footer + * + * \param [in] vhdm MiniVHD data structure + */ +static void mvhd_read_footer(MVHDMeta* vhdm) { + uint8_t buffer[MVHD_FOOTER_SIZE]; + mvhd_fseeko64(vhdm->f, -MVHD_FOOTER_SIZE, SEEK_END); + fread(buffer, sizeof buffer, 1, vhdm->f); + mvhd_buffer_to_footer(&vhdm->footer, buffer); +} + +/** + * \brief Populate data stuctures with content from a VHD sparse header + * + * \param [in] vhdm MiniVHD data structure + */ +static void mvhd_read_sparse_header(MVHDMeta* vhdm) { + uint8_t buffer[MVHD_SPARSE_SIZE]; + mvhd_fseeko64(vhdm->f, vhdm->footer.data_offset, SEEK_SET); + fread(buffer, sizeof buffer, 1, vhdm->f); + mvhd_buffer_to_header(&vhdm->sparse, buffer); +} + +/** + * \brief Validate VHD footer checksum + * + * This works by generating a checksum from the footer, and comparing it against the stored checksum. + * + * \param [in] vhdm MiniVHD data structure + */ +static bool mvhd_footer_checksum_valid(MVHDMeta* vhdm) { + return vhdm->footer.checksum == mvhd_gen_footer_checksum(&vhdm->footer); +} + +/** + * \brief Validate VHD sparse header checksum + * + * This works by generating a checksum from the sparse header, and comparing it against the stored checksum. + * + * \param [in] vhdm MiniVHD data structure + */ +static bool mvhd_sparse_checksum_valid(MVHDMeta* vhdm) { + return vhdm->sparse.checksum == mvhd_gen_sparse_checksum(&vhdm->sparse); +} + +/** + * \brief Read BAT into MiniVHD data structure + * + * The Block Allocation Table (BAT) is the structure in a sparse and differencing VHD which stores + * the 4-byte sector offsets for each data block. This function allocates enough memory to contain + * the entire BAT, and then reads the contents of the BAT into the buffer. + * + * \param [in] vhdm MiniVHD data structure + * \param [out] err this is populated with MVHD_ERR_MEM if the calloc fails + * + * \retval -1 if an error occurrs. Check value of err in this case + * \retval 0 if the function call succeeds + */ +static int mvhd_read_bat(MVHDMeta *vhdm, MVHDError* err) { + vhdm->block_offset = calloc(vhdm->sparse.max_bat_ent, sizeof *vhdm->block_offset); + if (vhdm->block_offset == NULL) { + *err = MVHD_ERR_MEM; + return -1; + } + mvhd_fseeko64(vhdm->f, vhdm->sparse.bat_offset, SEEK_SET); + for (uint32_t i = 0; i < vhdm->sparse.max_bat_ent; i++) { + fread(&vhdm->block_offset[i], sizeof *vhdm->block_offset, 1, vhdm->f); + vhdm->block_offset[i] = mvhd_from_be32(vhdm->block_offset[i]); + } + return 0; +} + +/** + * \brief Perform a one-time calculation of some sparse VHD values + * + * \param [in] vhdm MiniVHD data structure + */ +static void mvhd_calc_sparse_values(MVHDMeta* vhdm) { + vhdm->sect_per_block = vhdm->sparse.block_sz / MVHD_SECTOR_SIZE; + int bm_bytes = vhdm->sect_per_block / 8; + vhdm->bitmap.sector_count = bm_bytes / MVHD_SECTOR_SIZE; + if (bm_bytes % MVHD_SECTOR_SIZE > 0) { + vhdm->bitmap.sector_count++; + } +} + +/** + * \brief Allocate memory for a sector bitmap. + * + * Each data block is preceded by a sector bitmap. Each bit indicates whether the corresponding sector + * is considered 'clean' or 'dirty' (for sparse VHD images), or whether to read from the parent or current + * image (for differencing images). + * + * \param [in] vhdm MiniVHD data structure + * \param [out] err this is populated with MVHD_ERR_MEM if the calloc fails + * + * \retval -1 if an error occurrs. Check value of err in this case + * \retval 0 if the function call succeeds + */ +static int mvhd_init_sector_bitmap(MVHDMeta* vhdm, MVHDError* err) { + vhdm->bitmap.curr_bitmap = calloc(vhdm->bitmap.sector_count, MVHD_SECTOR_SIZE); + if (vhdm->bitmap.curr_bitmap == NULL) { + *err = MVHD_ERR_MEM; + return -1; + } + vhdm->bitmap.curr_block = -1; + return 0; +} + +/** + * \brief Check if the path for a given platform code exists + * + * From the available paths, both relative and absolute, construct a full path + * and attempt to open a file at that path. + * + * Note, this function makes no attempt to verify that the path is the correct + * VHD image, or even a VHD image at all. + * + * \param [in] paths a struct containing all available paths to work with + * \param [in] the platform code to try and obtain a path for. Setting this to zero + * will try using the directory of the child image + * + * \retval true if a file is found + * \retval false if a file is not found + */ +static bool mvhd_parent_path_exists(struct MVHDPaths* paths, uint32_t plat_code) { + memset(paths->joined_path, 0, sizeof paths->joined_path); + FILE* f; + int cwk_ret, ferr; + enum cwk_path_style style = cwk_path_guess_style((const char*)paths->dir_path); + cwk_path_set_style(style); + cwk_ret = 1; + if (plat_code == MVHD_DIF_LOC_W2RU && *paths->w2ru_path) { + cwk_ret = cwk_path_join((const char*)paths->dir_path, (const char*)paths->w2ru_path, paths->joined_path, sizeof paths->joined_path); + } else if (plat_code == MVHD_DIF_LOC_W2KU && *paths->w2ku_path) { + memcpy(paths->joined_path, paths->w2ku_path, (sizeof paths->joined_path) - 1); + cwk_ret = 0; + } else if (plat_code == 0) { + cwk_ret = cwk_path_join((const char*)paths->dir_path, (const char*)paths->file_name, paths->joined_path, sizeof paths->joined_path); + } + if (cwk_ret > MVHD_MAX_PATH_BYTES) { + return false; + } + f = mvhd_fopen((const char*)paths->joined_path, "rb", &ferr); + if (f != NULL) { + /* We found a file at the requested path! */ + memcpy(tmp_open_path, paths->joined_path, (sizeof paths->joined_path) - 1); + tmp_open_path[sizeof tmp_open_path - 1] = '\0'; + fclose(f); + return true; + } else { + return false; + } +} + +/** + * \brief attempt to obtain a file path to a file that may be a valid VHD image + * + * Differential VHD images store both a UTF-16BE file name (or path), and up to + * eight "parent locator" entries. Using this information, this function tries to + * find a parent image. + * + * This function does not verify if the path returned is a valid parent image. + * + * \param [in] vhdm current MiniVHD data structure + * \param [out] err any errors that may occurr. Check this if NULL is returned + * + * \return a pointer to the global string `tmp_open_path`, or NULL if a path could + * not be found, or some error occurred + */ +static char* mvhd_get_diff_parent_path(MVHDMeta* vhdm, int* err) { + int utf_outlen, utf_inlen, utf_ret; + char* par_fp = NULL; + /* We can't resolve relative paths if we don't have an absolute + path to work with */ + if (!cwk_path_is_absolute((const char*)vhdm->filename)) { + *err = MVHD_ERR_PATH_REL; + goto end; + } + struct MVHDPaths* paths = calloc(1, sizeof *paths); + if (paths == NULL) { + *err = MVHD_ERR_MEM; + goto end; + } + size_t dirlen; + cwk_path_get_dirname((const char*)vhdm->filename, &dirlen); + if (dirlen >= sizeof paths->dir_path) { + *err = MVHD_ERR_PATH_LEN; + goto paths_cleanup; + } + memcpy(paths->dir_path, vhdm->filename, dirlen); + /* Get the filename field from the sparse header. */ + utf_outlen = (int)sizeof paths->file_name; + utf_inlen = (int)sizeof vhdm->sparse.par_utf16_name; + utf_ret = UTF16BEToUTF8((unsigned char*)paths->file_name, &utf_outlen, (const unsigned char*)vhdm->sparse.par_utf16_name, &utf_inlen); + if (utf_ret < 0) { + mvhd_set_encoding_err(utf_ret, err); + goto paths_cleanup; + } + /* Now read the parent locator entries, both relative and absolute, if they exist */ + unsigned char* loc_path; + for (int i = 0; i < 8; i++) { + utf_outlen = MVHD_MAX_PATH_BYTES - 1; + if (vhdm->sparse.par_loc_entry[i].plat_code == MVHD_DIF_LOC_W2RU) { + loc_path = (unsigned char*)paths->w2ru_path; + } else if (vhdm->sparse.par_loc_entry[i].plat_code == MVHD_DIF_LOC_W2KU) { + loc_path = (unsigned char*)paths->w2ku_path; + } else { + continue; + } + utf_inlen = vhdm->sparse.par_loc_entry[i].plat_data_len; + if (utf_inlen > MVHD_MAX_PATH_BYTES) { + *err = MVHD_ERR_PATH_LEN; + goto paths_cleanup; + } + mvhd_fseeko64(vhdm->f, vhdm->sparse.par_loc_entry[i].plat_data_offset, SEEK_SET); + fread(paths->tmp_src_path, sizeof (uint8_t), utf_inlen, vhdm->f); + /* Note, the W2*u parent locators are UTF-16LE, unlike the filename field previously obtained, + which is UTF-16BE */ + utf_ret = UTF16LEToUTF8(loc_path, &utf_outlen, (const unsigned char*)paths->tmp_src_path, &utf_inlen); + if (utf_ret < 0) { + mvhd_set_encoding_err(utf_ret, err); + goto paths_cleanup; + } + } + /* We have paths in UTF-8. We should have enough info to try and find the parent VHD */ + /* Does the relative path exist? */ + if (mvhd_parent_path_exists(paths, MVHD_DIF_LOC_W2RU)) { + par_fp = tmp_open_path; + goto paths_cleanup; + } + /* What about trying the child directory? */ + if (mvhd_parent_path_exists(paths, 0)) { + par_fp = tmp_open_path; + goto paths_cleanup; + } + /* Well, all else fails, try the stored absolute path, if it exists */ + if (mvhd_parent_path_exists(paths, MVHD_DIF_LOC_W2KU)) { + par_fp = tmp_open_path; + goto paths_cleanup; + } + /* If we reach this point, we could not find a path with a valid file */ + par_fp = NULL; + *err = MVHD_ERR_PAR_NOT_FOUND; + +paths_cleanup: + free(paths); + paths = NULL; +end: + return par_fp; +} + +/** + * \brief Attach the read/write function pointers to read/write functions + * + * Depending on the VHD type, different sector reading and writing functions are used. + * The functions are called via function pointers stored in the vhdm struct. + * + * \param [in] vhdm MiniVHD data structure + */ +static void mvhd_assign_io_funcs(MVHDMeta* vhdm) { + switch (vhdm->footer.disk_type) { + case MVHD_TYPE_FIXED: + vhdm->read_sectors = mvhd_fixed_read; + vhdm->write_sectors = mvhd_fixed_write; + break; + case MVHD_TYPE_DYNAMIC: + vhdm->read_sectors = mvhd_sparse_read; + vhdm->write_sectors = mvhd_sparse_diff_write; + break; + case MVHD_TYPE_DIFF: + vhdm->read_sectors = mvhd_diff_read; + vhdm->write_sectors = mvhd_sparse_diff_write; + break; + } + if (vhdm->readonly) { + vhdm->write_sectors = mvhd_noop_write; + } +} + +bool mvhd_file_is_vhd(FILE* f) { + if (f) { + uint8_t con_str[8]; + mvhd_fseeko64(f, -MVHD_FOOTER_SIZE, SEEK_END); + fread(con_str, sizeof con_str, 1, f); + return mvhd_is_conectix_str(con_str); + } else { + return false; + } +} + +MVHDGeom mvhd_calculate_geometry(uint64_t size) { + MVHDGeom chs; + uint32_t ts = (uint32_t)(size / MVHD_SECTOR_SIZE); + uint32_t spt, heads, cyl, cth; + if (ts > 65535 * 16 * 255) { + ts = 65535 * 16 * 255; + } + if (ts >= 65535 * 16 * 63) { + spt = 255; + heads = 16; + cth = ts / spt; + } else { + spt = 17; + cth = ts / spt; + heads = (cth + 1023) / 1024; + if (heads < 4) { + heads = 4; + } + if (cth >= (heads * 1024) || heads > 16) { + spt = 31; + heads = 16; + cth = ts / spt; + } + if (cth >= (heads * 1024)) { + spt = 63; + heads = 16; + cth = ts / spt; + } + } + cyl = cth / heads; + chs.heads = heads; + chs.spt = spt; + chs.cyl = cyl; + return chs; +} + +MVHDMeta* mvhd_open(const char* path, bool readonly, int* err) { + MVHDError open_err; + MVHDMeta *vhdm = calloc(sizeof *vhdm, 1); + if (vhdm == NULL) { + *err = MVHD_ERR_MEM; + goto end; + } + if (strlen(path) >= sizeof vhdm->filename) { + *err = MVHD_ERR_PATH_LEN; + goto cleanup_vhdm; + } + //This is safe, as we've just checked for potential overflow above + strcpy(vhdm->filename, path); + vhdm->f = readonly ? mvhd_fopen((const char*)vhdm->filename, "rb", err) : mvhd_fopen((const char*)vhdm->filename, "rb+", err); + if (vhdm->f == NULL) { + /* note, mvhd_fopen sets err for us */ + goto cleanup_vhdm; + } + vhdm->readonly = readonly; + if (!mvhd_file_is_vhd(vhdm->f)) { + *err = MVHD_ERR_NOT_VHD; + goto cleanup_file; + } + mvhd_read_footer(vhdm); + if (!mvhd_footer_checksum_valid(vhdm)) { + *err = MVHD_ERR_FOOTER_CHECKSUM; + goto cleanup_file; + } + if (vhdm->footer.disk_type == MVHD_TYPE_DIFF || vhdm->footer.disk_type == MVHD_TYPE_DYNAMIC) { + mvhd_read_sparse_header(vhdm); + if (!mvhd_sparse_checksum_valid(vhdm)) { + *err = MVHD_ERR_SPARSE_CHECKSUM; + goto cleanup_file; + } + if (mvhd_read_bat(vhdm, &open_err) == -1) { + *err = open_err; + goto cleanup_file; + } + mvhd_calc_sparse_values(vhdm); + if (mvhd_init_sector_bitmap(vhdm, &open_err) == -1) { + *err = open_err; + goto cleanup_bat; + } + + } else if (vhdm->footer.disk_type != MVHD_TYPE_FIXED) { + *err = MVHD_ERR_TYPE; + goto cleanup_bitmap; + } + mvhd_assign_io_funcs(vhdm); + vhdm->format_buffer.zero_data = calloc(64, MVHD_SECTOR_SIZE); + if (vhdm->format_buffer.zero_data == NULL) { + *err = MVHD_ERR_MEM; + goto cleanup_bitmap; + } + vhdm->format_buffer.sector_count = 64; + if (vhdm->footer.disk_type == MVHD_TYPE_DIFF) { + char* par_path = mvhd_get_diff_parent_path(vhdm, err); + if (par_path == NULL) { + goto cleanup_format_buff; + } + uint32_t par_mod_ts = mvhd_file_mod_timestamp(par_path, err); + if (*err != 0) { + goto cleanup_format_buff; + } + if (vhdm->sparse.par_timestamp != par_mod_ts) { + /* The last-modified timestamp is to fragile to make this a fatal error. + Instead, we inform the caller of the potential problem. */ + *err = MVHD_ERR_TIMESTAMP; + } + vhdm->parent = mvhd_open(par_path, true, err); + if (vhdm->parent == NULL) { + goto cleanup_format_buff; + } + if (memcmp(vhdm->sparse.par_uuid, vhdm->parent->footer.uuid, sizeof vhdm->sparse.par_uuid) != 0) { + *err = MVHD_ERR_INVALID_PAR_UUID; + goto cleanup_format_buff; + } + } + /* If we've reached this point, we are good to go, so skip the cleanup steps */ + goto end; +cleanup_format_buff: + free(vhdm->format_buffer.zero_data); + vhdm->format_buffer.zero_data = NULL; +cleanup_bitmap: + free(vhdm->bitmap.curr_bitmap); + vhdm->bitmap.curr_bitmap = NULL; +cleanup_bat: + free(vhdm->block_offset); + vhdm->block_offset = NULL; +cleanup_file: + fclose(vhdm->f); + vhdm->f = NULL; +cleanup_vhdm: + free(vhdm); + vhdm = NULL; +end: + return vhdm; +} + +void mvhd_close(MVHDMeta* vhdm) { + if (vhdm != NULL) { + if (vhdm->parent != NULL) { + mvhd_close(vhdm->parent); + } + fclose(vhdm->f); + if (vhdm->block_offset != NULL) { + free(vhdm->block_offset); + vhdm->block_offset = NULL; + } + if (vhdm->bitmap.curr_bitmap != NULL) { + free(vhdm->bitmap.curr_bitmap); + vhdm->bitmap.curr_bitmap = NULL; + } + if (vhdm->format_buffer.zero_data != NULL) { + free(vhdm->format_buffer.zero_data); + vhdm->format_buffer.zero_data = NULL; + } + free(vhdm); + vhdm = NULL; + } +} + +int mvhd_diff_update_par_timestamp(MVHDMeta* vhdm, int* err) { + uint8_t sparse_buff[1024]; + if (vhdm == NULL || err == NULL) { + *err = MVHD_ERR_INVALID_PARAMS; + return -1; + } + if (vhdm->footer.disk_type != MVHD_TYPE_DIFF) { + *err = MVHD_ERR_TYPE; + return -1; + } + char* par_path = mvhd_get_diff_parent_path(vhdm, err); + if (par_path == NULL) { + return -1; + } + uint32_t par_mod_ts = mvhd_file_mod_timestamp(par_path, err); + if (*err != 0) { + return -1; + } + /* Update the timestamp and sparse header checksum */ + vhdm->sparse.par_timestamp = par_mod_ts; + vhdm->sparse.checksum = mvhd_gen_sparse_checksum(&vhdm->sparse); + /* Generate and write the updated sparse header */ + mvhd_header_to_buffer(&vhdm->sparse, sparse_buff); + mvhd_fseeko64(vhdm->f, (int64_t)vhdm->footer.data_offset, SEEK_SET); + fwrite(sparse_buff, sizeof sparse_buff, 1, vhdm->f); + return 0; +} + +int mvhd_read_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* out_buff) { + return vhdm->read_sectors(vhdm, offset, num_sectors, out_buff); +} + +int mvhd_write_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors, void* in_buff) { + return vhdm->write_sectors(vhdm, offset, num_sectors, in_buff); +} + +int mvhd_format_sectors(MVHDMeta* vhdm, uint32_t offset, int num_sectors) { + int num_full = num_sectors / vhdm->format_buffer.sector_count; + int remain = num_sectors % vhdm->format_buffer.sector_count; + for (int i = 0; i < num_full; i++) { + vhdm->write_sectors(vhdm, offset, vhdm->format_buffer.sector_count, vhdm->format_buffer.zero_data); + offset += vhdm->format_buffer.sector_count; + } + vhdm->write_sectors(vhdm, offset, remain, vhdm->format_buffer.zero_data); + return 0; +} \ No newline at end of file diff --git a/src/disk/minivhd/minivhd_struct_rw.c b/src/disk/minivhd/minivhd_struct_rw.c new file mode 100644 index 000000000..c77fa600a --- /dev/null +++ b/src/disk/minivhd/minivhd_struct_rw.c @@ -0,0 +1,167 @@ +/** + * \file + * \brief Header and footer serialize/deserialize functions + */ +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 +#endif +#include +#include +#include +#include +#include +#include "minivhd_util.h" +#include "minivhd_internal.h" + +/* Read data from footer into the struct members, swapping endian where necessary + Note: order matters here! We must read each field in the order the struct is in. + Doing this may be less elegant than performing a memcpy to a packed struct, but + it avoids potential data alignment issues, and the endian swapping allows us to + use the fields directly. */ + +static void mvhd_next_buffer_to_struct(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer); +static void mvhd_next_struct_to_buffer(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer); + +/** + * \brief Get the next field from a buffer and store it in a struct member, converting endian if necessary + * + * \param [out] struct_memb struct member to save the field to + * \param [in] memb_size the size of struct_memb, in bytes + * \param [in] req_endian is the field a value that requires endian conversion (eg: uint16, uint32) + * \param [in] buffer the buffer from which fields are read from. Will be advanced at the end of the function call + */ +static void mvhd_next_buffer_to_struct(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer) { + memcpy(struct_memb, *buffer, memb_size); + if (req_endian) { + switch (memb_size) { + case 2: + *(uint16_t*)(struct_memb) = mvhd_from_be16(*(uint16_t*)(struct_memb)); + break; + case 4: + *(uint32_t*)(struct_memb) = mvhd_from_be32(*(uint32_t*)(struct_memb)); + break; + case 8: + *(uint64_t*)(struct_memb) = mvhd_from_be64(*(uint64_t*)(struct_memb)); + break; + } + } + *buffer += memb_size; +} + +/** + * \brief Save a struct member into a buffer, converting endian if necessary + * + * \param [in] struct_memb struct member read from + * \param [in] memb_size the size of struct_memb, in bytes + * \param [in] req_endian is the field a value that requires endian conversion (eg: uint16, uint32) + * \param [out] buffer the buffer from which struct member is saved to. Will be advanced at the end of the function call + */ +static void mvhd_next_struct_to_buffer(void* struct_memb, size_t memb_size, bool req_endian, uint8_t** buffer) { + uint8_t *buf_ptr = *buffer; + memcpy(buf_ptr, struct_memb, memb_size); + if (req_endian) { + switch (memb_size) { + case 2: + *((uint16_t*)buf_ptr) = mvhd_to_be16(*(uint16_t*)(struct_memb)); + break; + case 4: + *((uint32_t*)buf_ptr) = mvhd_to_be32(*(uint32_t*)(struct_memb)); + break; + case 8: + *((uint64_t*)buf_ptr) = mvhd_to_be64(*(uint64_t*)(struct_memb)); + break; + } + } + buf_ptr += memb_size; + *buffer = buf_ptr; +} + +void mvhd_buffer_to_footer(MVHDFooter* footer, uint8_t* buffer) { + uint8_t* buff_ptr = buffer; + mvhd_next_buffer_to_struct(&footer->cookie, sizeof footer->cookie, false, &buff_ptr); + mvhd_next_buffer_to_struct(&footer->features, sizeof footer->features, true, &buff_ptr); + mvhd_next_buffer_to_struct(&footer->fi_fmt_vers, sizeof footer->fi_fmt_vers, true, &buff_ptr); + mvhd_next_buffer_to_struct(&footer->data_offset, sizeof footer->data_offset, true, &buff_ptr); + mvhd_next_buffer_to_struct(&footer->timestamp, sizeof footer->timestamp, true, &buff_ptr); + mvhd_next_buffer_to_struct(&footer->cr_app, sizeof footer->cr_app, false, &buff_ptr); + mvhd_next_buffer_to_struct(&footer->cr_vers, sizeof footer->cr_vers, true, &buff_ptr); + mvhd_next_buffer_to_struct(&footer->cr_host_os, sizeof footer->cr_host_os, false, &buff_ptr); + mvhd_next_buffer_to_struct(&footer->orig_sz, sizeof footer->orig_sz, true, &buff_ptr); + mvhd_next_buffer_to_struct(&footer->curr_sz, sizeof footer->curr_sz, true, &buff_ptr); + mvhd_next_buffer_to_struct(&footer->geom.cyl, sizeof footer->geom.cyl, true, &buff_ptr); + mvhd_next_buffer_to_struct(&footer->geom.heads, sizeof footer->geom.heads, false, &buff_ptr); + mvhd_next_buffer_to_struct(&footer->geom.spt, sizeof footer->geom.spt, false, &buff_ptr); + mvhd_next_buffer_to_struct(&footer->disk_type, sizeof footer->disk_type, true, &buff_ptr); + mvhd_next_buffer_to_struct(&footer->checksum, sizeof footer->checksum, true, &buff_ptr); + mvhd_next_buffer_to_struct(&footer->uuid, sizeof footer->uuid, false, &buff_ptr); + mvhd_next_buffer_to_struct(&footer->saved_st, sizeof footer->saved_st, false, &buff_ptr); + mvhd_next_buffer_to_struct(&footer->reserved, sizeof footer->reserved, false, &buff_ptr); +} + +void mvhd_footer_to_buffer(MVHDFooter* footer, uint8_t* buffer) { + uint8_t* buff_ptr = buffer; + mvhd_next_struct_to_buffer(&footer->cookie, sizeof footer->cookie, false, &buff_ptr); + mvhd_next_struct_to_buffer(&footer->features, sizeof footer->features, true, &buff_ptr); + mvhd_next_struct_to_buffer(&footer->fi_fmt_vers, sizeof footer->fi_fmt_vers, true, &buff_ptr); + mvhd_next_struct_to_buffer(&footer->data_offset, sizeof footer->data_offset, true, &buff_ptr); + mvhd_next_struct_to_buffer(&footer->timestamp, sizeof footer->timestamp, true, &buff_ptr); + mvhd_next_struct_to_buffer(&footer->cr_app, sizeof footer->cr_app, false, &buff_ptr); + mvhd_next_struct_to_buffer(&footer->cr_vers, sizeof footer->cr_vers, true, &buff_ptr); + mvhd_next_struct_to_buffer(&footer->cr_host_os, sizeof footer->cr_host_os, false, &buff_ptr); + mvhd_next_struct_to_buffer(&footer->orig_sz, sizeof footer->orig_sz, true, &buff_ptr); + mvhd_next_struct_to_buffer(&footer->curr_sz, sizeof footer->curr_sz, true, &buff_ptr); + mvhd_next_struct_to_buffer(&footer->geom.cyl, sizeof footer->geom.cyl, true, &buff_ptr); + mvhd_next_struct_to_buffer(&footer->geom.heads, sizeof footer->geom.heads, false, &buff_ptr); + mvhd_next_struct_to_buffer(&footer->geom.spt, sizeof footer->geom.spt, false, &buff_ptr); + mvhd_next_struct_to_buffer(&footer->disk_type, sizeof footer->disk_type, true, &buff_ptr); + mvhd_next_struct_to_buffer(&footer->checksum, sizeof footer->checksum, true, &buff_ptr); + mvhd_next_struct_to_buffer(&footer->uuid, sizeof footer->uuid, false, &buff_ptr); + mvhd_next_struct_to_buffer(&footer->saved_st, sizeof footer->saved_st, false, &buff_ptr); + mvhd_next_struct_to_buffer(&footer->reserved, sizeof footer->reserved, false, &buff_ptr); +} + +void mvhd_buffer_to_header(MVHDSparseHeader* header, uint8_t* buffer) { + uint8_t* buff_ptr = buffer; + mvhd_next_buffer_to_struct(&header->cookie, sizeof header->cookie, false, &buff_ptr); + mvhd_next_buffer_to_struct(&header->data_offset, sizeof header->data_offset, true, &buff_ptr); + mvhd_next_buffer_to_struct(&header->bat_offset, sizeof header->bat_offset, true, &buff_ptr); + mvhd_next_buffer_to_struct(&header->head_vers, sizeof header->head_vers, true, &buff_ptr); + mvhd_next_buffer_to_struct(&header->max_bat_ent, sizeof header->max_bat_ent, true, &buff_ptr); + mvhd_next_buffer_to_struct(&header->block_sz, sizeof header->block_sz, true, &buff_ptr); + mvhd_next_buffer_to_struct(&header->checksum, sizeof header->checksum, true, &buff_ptr); + mvhd_next_buffer_to_struct(&header->par_uuid, sizeof header->par_uuid, false, &buff_ptr); + mvhd_next_buffer_to_struct(&header->par_timestamp, sizeof header->par_timestamp, true, &buff_ptr); + mvhd_next_buffer_to_struct(&header->reserved_1, sizeof header->reserved_1, true, &buff_ptr); + mvhd_next_buffer_to_struct(&header->par_utf16_name, sizeof header->par_utf16_name, false, &buff_ptr); + for (int i = 0; i < 8; i++) { + mvhd_next_buffer_to_struct(&header->par_loc_entry[i].plat_code, sizeof header->par_loc_entry[i].plat_code, true, &buff_ptr); + mvhd_next_buffer_to_struct(&header->par_loc_entry[i].plat_data_space, sizeof header->par_loc_entry[i].plat_data_space, true, &buff_ptr); + mvhd_next_buffer_to_struct(&header->par_loc_entry[i].plat_data_len, sizeof header->par_loc_entry[i].plat_data_len, true, &buff_ptr); + mvhd_next_buffer_to_struct(&header->par_loc_entry[i].reserved, sizeof header->par_loc_entry[i].reserved, true, &buff_ptr); + mvhd_next_buffer_to_struct(&header->par_loc_entry[i].plat_data_offset, sizeof header->par_loc_entry[i].plat_data_offset, true, &buff_ptr); + } + mvhd_next_buffer_to_struct(&header->reserved_2, sizeof header->reserved_2, false, &buff_ptr); +} + +void mvhd_header_to_buffer(MVHDSparseHeader* header, uint8_t* buffer) { + uint8_t* buff_ptr = buffer; + mvhd_next_struct_to_buffer(&header->cookie, sizeof header->cookie, false, &buff_ptr); + mvhd_next_struct_to_buffer(&header->data_offset, sizeof header->data_offset, true, &buff_ptr); + mvhd_next_struct_to_buffer(&header->bat_offset, sizeof header->bat_offset, true, &buff_ptr); + mvhd_next_struct_to_buffer(&header->head_vers, sizeof header->head_vers, true, &buff_ptr); + mvhd_next_struct_to_buffer(&header->max_bat_ent, sizeof header->max_bat_ent, true, &buff_ptr); + mvhd_next_struct_to_buffer(&header->block_sz, sizeof header->block_sz, true, &buff_ptr); + mvhd_next_struct_to_buffer(&header->checksum, sizeof header->checksum, true, &buff_ptr); + mvhd_next_struct_to_buffer(&header->par_uuid, sizeof header->par_uuid, false, &buff_ptr); + mvhd_next_struct_to_buffer(&header->par_timestamp, sizeof header->par_timestamp, true, &buff_ptr); + mvhd_next_struct_to_buffer(&header->reserved_1, sizeof header->reserved_1, true, &buff_ptr); + mvhd_next_struct_to_buffer(&header->par_utf16_name, sizeof header->par_utf16_name, false, &buff_ptr); + for (int i = 0; i < 8; i++) { + mvhd_next_struct_to_buffer(&header->par_loc_entry[i].plat_code, sizeof header->par_loc_entry[i].plat_code, true, &buff_ptr); + mvhd_next_struct_to_buffer(&header->par_loc_entry[i].plat_data_space, sizeof header->par_loc_entry[i].plat_data_space, true, &buff_ptr); + mvhd_next_struct_to_buffer(&header->par_loc_entry[i].plat_data_len, sizeof header->par_loc_entry[i].plat_data_len, true, &buff_ptr); + mvhd_next_struct_to_buffer(&header->par_loc_entry[i].reserved, sizeof header->par_loc_entry[i].reserved, true, &buff_ptr); + mvhd_next_struct_to_buffer(&header->par_loc_entry[i].plat_data_offset, sizeof header->par_loc_entry[i].plat_data_offset, true, &buff_ptr); + } + mvhd_next_struct_to_buffer(&header->reserved_2, sizeof header->reserved_2, false, &buff_ptr); +} \ No newline at end of file diff --git a/src/disk/minivhd/minivhd_struct_rw.h b/src/disk/minivhd/minivhd_struct_rw.h new file mode 100644 index 000000000..ee49bb696 --- /dev/null +++ b/src/disk/minivhd/minivhd_struct_rw.h @@ -0,0 +1,38 @@ +#ifndef MINIVHD_STRUCT_RW_H +#define MINIVHD_STRUCT_RW_H + +#include "minivhd_internal.h" + +/** + * \brief Save the contents of a VHD footer from a buffer to a struct + * + * \param [out] footer save contents of buffer into footer + * \param [in] buffer VHD footer in raw bytes + */ +void mvhd_buffer_to_footer(MVHDFooter* footer, uint8_t* buffer); + +/** + * \brief Save the contents of a VHD sparse header from a buffer to a struct + * + * \param [out] header save contents of buffer into header + * \param [in] buffer VHD header in raw bytes + */ +void mvhd_buffer_to_header(MVHDSparseHeader* header, uint8_t* buffer); + +/** + * \brief Save the contents of a VHD footer struct to a buffer + * + * \param [in] footer save contents of struct into buffer + * \param [out] buffer VHD footer in raw bytes + */ +void mvhd_footer_to_buffer(MVHDFooter* footer, uint8_t* buffer); + +/** + * \brief Save the contents of a VHD sparse header struct to a buffer + * + * \param [in] header save contents of struct into buffer + * \param [out] buffer VHD sparse header in raw bytes + */ +void mvhd_header_to_buffer(MVHDSparseHeader* header, uint8_t* buffer); + +#endif \ No newline at end of file diff --git a/src/disk/minivhd/minivhd_util.c b/src/disk/minivhd/minivhd_util.c new file mode 100644 index 000000000..5bfc59915 --- /dev/null +++ b/src/disk/minivhd/minivhd_util.c @@ -0,0 +1,329 @@ +/** + * \file + * \brief Utility functions + */ +#ifndef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include "libxml2_encoding.h" +#include "minivhd_internal.h" +#include "minivhd_util.h" + +const char MVHD_CONECTIX_COOKIE[] = "conectix"; +const char MVHD_CREATOR[] = "pcem"; +const char MVHD_CREATOR_HOST_OS[] = "Wi2k"; +const char MVHD_CXSPARSE_COOKIE[] = "cxsparse"; + +uint16_t mvhd_from_be16(uint16_t val) { + uint8_t *tmp = (uint8_t*)&val; + uint16_t ret = 0; + ret |= (uint16_t)tmp[0] << 8; + ret |= (uint16_t)tmp[1] << 0; + return ret; +} +uint32_t mvhd_from_be32(uint32_t val) { + uint8_t *tmp = (uint8_t*)&val; + uint32_t ret = 0; + ret = (uint32_t)tmp[0] << 24; + ret |= (uint32_t)tmp[1] << 16; + ret |= (uint32_t)tmp[2] << 8; + ret |= (uint32_t)tmp[3] << 0; + return ret; +} +uint64_t mvhd_from_be64(uint64_t val) { + uint8_t *tmp = (uint8_t*)&val; + uint64_t ret = 0; + ret = (uint64_t)tmp[0] << 56; + ret |= (uint64_t)tmp[1] << 48; + ret |= (uint64_t)tmp[2] << 40; + ret |= (uint64_t)tmp[3] << 32; + ret |= (uint64_t)tmp[4] << 24; + ret |= (uint64_t)tmp[5] << 16; + ret |= (uint64_t)tmp[6] << 8; + ret |= (uint64_t)tmp[7] << 0; + return ret; +} +uint16_t mvhd_to_be16(uint16_t val) { + uint16_t ret = 0; + uint8_t *tmp = (uint8_t*)&ret; + tmp[0] = (val & 0xff00) >> 8; + tmp[1] = (val & 0x00ff) >> 0; + return ret; +} +uint32_t mvhd_to_be32(uint32_t val) { + uint32_t ret = 0; + uint8_t *tmp = (uint8_t*)&ret; + tmp[0] = (val & 0xff000000) >> 24; + tmp[1] = (val & 0x00ff0000) >> 16; + tmp[2] = (val & 0x0000ff00) >> 8; + tmp[3] = (val & 0x000000ff) >> 0; + return ret; +} +uint64_t mvhd_to_be64(uint64_t val) { + uint64_t ret = 0; + uint8_t *tmp = (uint8_t*)&ret; + tmp[0] = (uint8_t)((val & 0xff00000000000000) >> 56); + tmp[1] = (uint8_t)((val & 0x00ff000000000000) >> 48); + tmp[2] = (uint8_t)((val & 0x0000ff0000000000) >> 40); + tmp[3] = (uint8_t)((val & 0x000000ff00000000) >> 32); + tmp[4] = (uint8_t)((val & 0x00000000ff000000) >> 24); + tmp[5] = (uint8_t)((val & 0x0000000000ff0000) >> 16); + tmp[6] = (uint8_t)((val & 0x000000000000ff00) >> 8); + tmp[7] = (uint8_t)((val & 0x00000000000000ff) >> 0); + return ret; +} + +bool mvhd_is_conectix_str(const void* buffer) { + if (strncmp(buffer, MVHD_CONECTIX_COOKIE, strlen(MVHD_CONECTIX_COOKIE)) == 0) { + return true; + } else { + return false; + } +} + +void mvhd_generate_uuid(uint8_t* uuid) +{ + /* We aren't doing crypto here, so using system time as seed should be good enough */ + srand((unsigned int)time(0)); + for (int n = 0; n < 16; n++) { + uuid[n] = rand(); + } + uuid[6] &= 0x0F; + uuid[6] |= 0x40; /* Type 4 */ + uuid[8] &= 0x3F; + uuid[8] |= 0x80; /* Variant 1 */ +} + +uint32_t vhd_calc_timestamp(void) +{ + time_t start_time; + time_t curr_time; + double vhd_time; + start_time = MVHD_START_TS; /* 1 Jan 2000 00:00 */ + curr_time = time(NULL); + vhd_time = difftime(curr_time, start_time); + return (uint32_t)vhd_time; +} + +uint32_t mvhd_epoch_to_vhd_ts(time_t ts) { + time_t start_time = MVHD_START_TS; + if (ts < start_time) { + return start_time; + } + double vhd_time = difftime(ts, start_time); + return (uint32_t)vhd_time; +} + +time_t vhd_get_created_time(MVHDMeta *vhdm) +{ + time_t vhd_time = (time_t)vhdm->footer.timestamp; + time_t vhd_time_unix = MVHD_START_TS + vhd_time; + return vhd_time_unix; +} + +FILE* mvhd_fopen(const char* path, const char* mode, int* err) { + FILE* f = NULL; +#ifdef _WIN32 + size_t path_len = strlen(path); + size_t mode_len = strlen(mode); + mvhd_utf16 new_path[260] = {0}; + int new_path_len = (sizeof new_path) - 2; + mvhd_utf16 mode_str[5] = {0}; + int new_mode_len = (sizeof mode_str) - 2; + int path_res = UTF8ToUTF16LE((unsigned char*)new_path, &new_path_len, (const unsigned char*)path, (int*)&path_len); + int mode_res = UTF8ToUTF16LE((unsigned char*)mode_str, &new_mode_len, (const unsigned char*)mode, (int*)&mode_len); + if (path_res > 0 && mode_res > 0) { + f = _wfopen(new_path, mode_str); + if (f == NULL) { + mvhd_errno = errno; + *err = MVHD_ERR_FILE; + } + } else { + if (path_res == -1 || mode_res == -1) { + *err = MVHD_ERR_UTF_SIZE; + } else if (path_res == -2 || mode_res == -2) { + *err = MVHD_ERR_UTF_TRANSCODING_FAILED; + } + } +#else + f = fopen(path, mode); + if (f == NULL) { + mvhd_errno = errno; + *err = MVHD_ERR_FILE; + } +#endif + return f; +} + +void mvhd_set_encoding_err(int encoding_retval, int* err) { + if (encoding_retval == -1) { + *err = MVHD_ERR_UTF_SIZE; + } else if (encoding_retval == -2) { + *err = MVHD_ERR_UTF_TRANSCODING_FAILED; + } +} + +uint64_t mvhd_calc_size_bytes(MVHDGeom *geom) { + uint64_t img_size = (uint64_t)geom->cyl * (uint64_t)geom->heads * (uint64_t)geom->spt * (uint64_t)MVHD_SECTOR_SIZE; + return img_size; +} + +uint32_t mvhd_calc_size_sectors(MVHDGeom *geom) { + uint32_t sector_size = (uint32_t)geom->cyl * (uint32_t)geom->heads * (uint32_t)geom->spt; + return sector_size; +} + +MVHDGeom mvhd_get_geometry(MVHDMeta* vhdm) { + MVHDGeom geometry = { .cyl = vhdm->footer.geom.cyl, .heads = vhdm->footer.geom.heads, .spt = vhdm->footer.geom.spt }; + return geometry; +} + +uint32_t mvhd_gen_footer_checksum(MVHDFooter* footer) { + uint32_t new_chk = 0; + uint32_t orig_chk = footer->checksum; + footer->checksum = 0; + uint8_t* footer_bytes = (uint8_t*)footer; + for (size_t i = 0; i < sizeof *footer; i++) { + new_chk += footer_bytes[i]; + } + footer->checksum = orig_chk; + return ~new_chk; +} + +uint32_t mvhd_gen_sparse_checksum(MVHDSparseHeader* header) { + uint32_t new_chk = 0; + uint32_t orig_chk = header->checksum; + header->checksum = 0; + uint8_t* sparse_bytes = (uint8_t*)header; + for (size_t i = 0; i < sizeof *header; i++) { + new_chk += sparse_bytes[i]; + } + header->checksum = orig_chk; + return ~new_chk; +} + +const char* mvhd_strerr(MVHDError err) { + switch (err) { + case MVHD_ERR_MEM: + return "memory allocation error"; + case MVHD_ERR_FILE: + return "file error"; + case MVHD_ERR_NOT_VHD: + return "file is not a VHD image"; + case MVHD_ERR_TYPE: + return "unsupported VHD image type"; + case MVHD_ERR_FOOTER_CHECKSUM: + return "invalid VHD footer checksum"; + case MVHD_ERR_SPARSE_CHECKSUM: + return "invalid VHD sparse header checksum"; + case MVHD_ERR_UTF_TRANSCODING_FAILED: + return "error converting path encoding"; + case MVHD_ERR_UTF_SIZE: + return "buffer size mismatch when converting path encoding"; + case MVHD_ERR_PATH_REL: + return "relative path detected where absolute path expected"; + case MVHD_ERR_PATH_LEN: + return "path length exceeds MVHD_MAX_PATH"; + case MVHD_ERR_PAR_NOT_FOUND: + return "parent VHD image not found"; + case MVHD_ERR_INVALID_PAR_UUID: + return "UUID mismatch between child and parent VHD"; + case MVHD_ERR_INVALID_GEOM: + return "invalid geometry detected"; + case MVHD_ERR_INVALID_SIZE: + return "invalid size"; + case MVHD_ERR_INVALID_BLOCK_SIZE: + return "invalid block size"; + case MVHD_ERR_INVALID_PARAMS: + return "invalid parameters passed to function"; + case MVHD_ERR_CONV_SIZE: + return "error converting image. Size mismatch detechted"; + default: + return "unknown error"; + } +} + +int64_t mvhd_ftello64(FILE* stream) +{ +#ifdef _MSC_VER + return _ftelli64(stream); +#elif defined(__MINGW32__) + return ftello64(stream); +#else /* This should work with linux (with _FILE_OFFSET_BITS), and hopefully OS X and BSD */ + return ftello(stream); +#endif +} + +int mvhd_fseeko64(FILE* stream, int64_t offset, int origin) +{ +#ifdef _MSC_VER + return _fseeki64(stream, offset, origin); +#elif defined(__MINGW32__) + return fseeko64(stream, offset, origin); +#else /* This should work with linux (with _FILE_OFFSET_BITS), and hopefully OS X and BSD */ + return fseeko(stream, offset, origin); +#endif +} + +uint32_t mvhd_crc32_for_byte(uint32_t r) { + for (int j = 0; j < 8; ++j) + r = (r & 1 ? 0 : (uint32_t)0xEDB88320L) ^ r >> 1; + return r ^ (uint32_t)0xFF000000L; +} + +uint32_t mvhd_crc32(const void* data, size_t n_bytes) { + static uint32_t table[0x100]; + if (!*table) + for (size_t i = 0; i < 0x100; ++i) + table[i] = mvhd_crc32_for_byte(i); + + uint32_t crc = 0; + for (size_t i = 0; i < n_bytes; ++i) + crc = table[(uint8_t)crc ^ ((uint8_t*)data)[i]] ^ crc >> 8; + + return crc; +} + +uint32_t mvhd_file_mod_timestamp(const char* path, int *err) { + *err = 0; +#ifdef _WIN32 + struct _stat file_stat; + size_t path_len = strlen(path); + mvhd_utf16 new_path[260] = {0}; + int new_path_len = (sizeof new_path) - 2; + int path_res = UTF8ToUTF16LE((unsigned char*)new_path, &new_path_len, (const unsigned char*)path, (int*)&path_len); + if (path_res > 0) { + int stat_res = _wstat(new_path, &file_stat); + if (stat_res != 0) { + mvhd_errno = errno; + *err = MVHD_ERR_FILE; + return 0; + } + return mvhd_epoch_to_vhd_ts(file_stat.st_mtime); + } else { + if (path_res == -1) { + *err = MVHD_ERR_UTF_SIZE; + } else if (path_res == -2) { + *err = MVHD_ERR_UTF_TRANSCODING_FAILED; + } + return 0; + } +#else + struct stat file_stat; + int stat_res = stat(path, &file_stat); + if (stat_res != 0) { + mvhd_errno = errno; + *err = MVHD_ERR_FILE; + return 0; + } + return mvhd_epoch_to_vhd_ts(file_stat.st_mtime); +#endif +} diff --git a/src/disk/minivhd/minivhd_util.h b/src/disk/minivhd/minivhd_util.h new file mode 100644 index 000000000..df6841009 --- /dev/null +++ b/src/disk/minivhd/minivhd_util.h @@ -0,0 +1,136 @@ +#ifndef MINIVHD_UTIL_H +#define MINIVHD_UTIL_H + +#include +#include +#include +#include "minivhd_internal.h" +#include "minivhd.h" +#define MVHD_START_TS 946684800 + +/** + * Functions to deal with endian issues + */ +uint16_t mvhd_from_be16(uint16_t val); +uint32_t mvhd_from_be32(uint32_t val); +uint64_t mvhd_from_be64(uint64_t val); +uint16_t mvhd_to_be16(uint16_t val); +uint32_t mvhd_to_be32(uint32_t val); +uint64_t mvhd_to_be64(uint64_t val); + +/** + * \brief Check if provided buffer begins with the string "conectix" + * + * \param [in] buffer The buffer to compare. Must be at least 8 bytes in length + * + * \return true if the buffer begins with "conectix" + * \return false if the buffer does not begin with "conectix" + */ +bool mvhd_is_conectix_str(const void* buffer); + +/** + * \brief Generate a raw 16 byte UUID + * + * \param [out] uuid A 16 byte buffer in which the generated UUID will be stored to + */ +void mvhd_generate_uuid(uint8_t *uuid); + +/** + * \brief Calculate a VHD formatted timestamp from the current time + */ +uint32_t vhd_calc_timestamp(void); + +/** + * \brief Convert an epoch timestamp to a VHD timestamp + * + * \param [in] ts epoch timestamp to convert. + * + * \return The adjusted timestamp, or 0 if the input timestamp is + * earlier that 1 Janurary 2000 + */ +uint32_t mvhd_epoch_to_vhd_ts(time_t ts); + +/** + * \brief Return the created time from a VHD image + * + * \param [in] vhdm Pointer to the MiniVHD metadata structure + * + * \return The created time, as a Unix timestamp + */ +time_t vhd_get_created_time(MVHDMeta *vhdm); + +/** + * \brief Cross platform, unicode filepath opening + * + * This function accounts for the fact that fopen() handles file paths differently compared to other + * operating systems. Windows version of fopen() will not handle multi byte encoded text like UTF-8. + * + * Unicode filepath support on Windows requires using the _wfopen() function, which expects UTF-16LE + * encoded path and modestring. + * + * \param [in] path The filepath to open as a UTF-8 string + * \param [in] mode The mode string to use (eg: "rb+"") + * \param [out] err The error value, if an error occurrs + * + * \return a FILE pointer if successful, NULL otherwise. If NULL, check the value of err + */ +FILE* mvhd_fopen(const char* path, const char* mode, int* err); + +void mvhd_set_encoding_err(int encoding_retval, int* err); +uint64_t mvhd_calc_size_bytes(MVHDGeom *geom); +uint32_t mvhd_calc_size_sectors(MVHDGeom *geom); +MVHDGeom mvhd_get_geometry(MVHDMeta* vhdm); + +/** + * \brief Generate VHD footer checksum + * + * \param [in] vhdm MiniVHD data structure + */ +uint32_t mvhd_gen_footer_checksum(MVHDFooter* footer); + +/** + * \brief Generate VHD sparse header checksum + * + * \param [in] vhdm MiniVHD data structure + */ +uint32_t mvhd_gen_sparse_checksum(MVHDSparseHeader* header); + +/** + * \brief Get current position in file stream + * + * This is a portable version of the POSIX ftello64(). * + */ +int64_t mvhd_ftello64(FILE* stream); + +/** + * \brief Reposition the file stream's position + * + * This is a portable version of the POSIX fseeko64(). * + */ +int mvhd_fseeko64(FILE* stream, int64_t offset, int origin); + +/** + * \brief Calculate the CRC32 of a data buffer. + * + * This function can be used for verifying data integrity. + * + * \param [in] data The data buffer + * \param [in] n_bytes The size of the data buffer in bytes + * + * \return The CRC32 of the data buffer + */ +uint32_t mvhd_crc32(const void* data, size_t n_bytes); + +/** + * \brief Calculate the file modification timestamp. + * + * This function is primarily to help protect differencing VHD's + * + * \param [in] path the UTF-8 file path + * \param [out] err The error value, if an error occurrs + * + * \return The file modified timestamp, in VHD compatible timestamp. + * 'err' will be set to non-zero on error + */ +uint32_t mvhd_file_mod_timestamp(const char* path, int *err); +#endif diff --git a/src/floppy/fdc.c b/src/floppy/fdc.c index 82191881c..a48307b2f 100644 --- a/src/floppy/fdc.c +++ b/src/floppy/fdc.c @@ -735,7 +735,7 @@ fdc_write(uint16_t addr, uint8_t val, void *priv) fdc_log("Write FDC %04X %02X\n", addr, val); - sub_cycles(ISA_CYCLES(8)); + cycles -= ISA_CYCLES(8); switch (addr&7) { case 0: @@ -1261,7 +1261,7 @@ fdc_read(uint16_t addr, void *priv) uint8_t ret; int drive; - sub_cycles(ISA_CYCLES(8)); + cycles -= ISA_CYCLES(8); switch (addr & 7) { case 0: /* STA */ diff --git a/src/floppy/fdd.c b/src/floppy/fdd.c index 7e1b21452..ece63c55e 100644 --- a/src/floppy/fdd.c +++ b/src/floppy/fdd.c @@ -552,7 +552,7 @@ fdd_hole(int drive) } -uint64_t +static __inline uint64_t fdd_byteperiod(int drive) { if (!fdd_get_turbo(drive) && drives[drive].byteperiod) diff --git a/src/game/gameport.c b/src/game/gameport.c index d1fc35492..883af2388 100644 --- a/src/game/gameport.c +++ b/src/game/gameport.c @@ -186,7 +186,7 @@ gameport_write(uint16_t addr, uint8_t val, void *priv) p->joystick->write(p->joystick_dat); - sub_cycles(ISA_CYCLES(8)); + cycles -= ISA_CYCLES(8); } @@ -198,7 +198,7 @@ gameport_read(uint16_t addr, void *priv) ret = p->state | p->joystick->read(p->joystick_dat); - sub_cycles(ISA_CYCLES(8)); + cycles -= ISA_CYCLES(8); return(ret); } diff --git a/src/include/86box/86box.h b/src/include/86box/86box.h index b8118d4f0..297c60c99 100644 --- a/src/include/86box/86box.h +++ b/src/include/86box/86box.h @@ -111,7 +111,8 @@ extern int network_card; /* (C) net interface num */ extern char network_host[522]; /* (C) host network intf */ extern int hdd_format_type; /* (C) hard disk file format */ extern int confirm_reset, /* (C) enable reset confirmation */ - confirm_exit; /* (C) enable exit confirmation */ + confirm_exit, /* (C) enable exit confirmation */ + confirm_save; /* (C) enable save confirmation */ #ifdef USE_DISCORD extern int enable_discord; /* (C) enable Discord integration */ #endif @@ -151,6 +152,7 @@ extern void pclog_toggle_suppr(void); extern void pclog(const char *fmt, ...); extern void fatal(const char *fmt, ...); extern void set_screen_size(int x, int y); +extern void reset_screen_size(void); extern void set_screen_size_natural(void); #if 0 extern void pc_reload(wchar_t *fn); @@ -176,6 +178,7 @@ extern uint16_t get_last_addr(void); should be in cpu.c but I put it here to avoid having to include cpu.c everywhere. */ extern void sub_cycles(int c); +extern void resub_cycles(int old_cycles); extern double isa_timing; extern int io_delay; diff --git a/src/include/86box/acpi.h b/src/include/86box/acpi.h index 0e5e688ef..6cc98cefb 100644 --- a/src/include/86box/acpi.h +++ b/src/include/86box/acpi.h @@ -89,6 +89,8 @@ typedef struct /* Global variables. */ +extern int acpi_rtc_status; + extern const device_t acpi_intel_device; extern const device_t acpi_smc_device; extern const device_t acpi_via_device; diff --git a/src/include/86box/hdd.h b/src/include/86box/hdd.h index 4da0e190f..24a66eae2 100644 --- a/src/include/86box/hdd.h +++ b/src/include/86box/hdd.h @@ -106,32 +106,6 @@ typedef struct { extern hard_disk_t hdd[HDD_NUM]; extern unsigned int hdd_table[128][3]; - -typedef struct vhd_footer_t -{ - uint8_t cookie[8]; - uint32_t features; - uint32_t version; - uint64_t offset; - uint32_t timestamp; - uint8_t creator[4]; - uint32_t creator_vers; - uint8_t creator_host_os[4]; - uint64_t orig_size; - uint64_t curr_size; - struct { - uint16_t cyl; - uint8_t heads; - uint8_t spt; - } geom; - uint32_t type; - uint32_t checksum; - uint8_t uuid[16]; - uint8_t saved_state; - uint8_t reserved[427]; -} vhd_footer_t; - - extern int hdd_init(void); extern int hdd_string_to_bus(char *str, int cdrom); extern char *hdd_bus_to_string(int bus, int cdrom); @@ -153,11 +127,6 @@ extern void hdd_image_unload(uint8_t id, int fn_preserve); extern void hdd_image_close(uint8_t id); extern void hdd_image_calc_chs(uint32_t *c, uint32_t *h, uint32_t *s, uint32_t size); -extern void vhd_footer_from_bytes(vhd_footer_t *vhd, uint8_t *bytes); -extern void vhd_footer_to_bytes(uint8_t *bytes, vhd_footer_t *vhd); -extern void new_vhd_footer(vhd_footer_t **vhd); -extern void generate_vhd_checksum(vhd_footer_t *vhd); - extern int image_is_hdi(const wchar_t *s); extern int image_is_hdx(const wchar_t *s, int check_signature); extern int image_is_vhd(const wchar_t *s, int check_signature); diff --git a/src/include/86box/language.h b/src/include/86box/language.h index a16f2f0ac..5ae5bb169 100644 --- a/src/include/86box/language.h +++ b/src/include/86box/language.h @@ -24,7 +24,7 @@ #define IDS_STRINGS 2048 // "86Box" #define IDS_2049 2049 // "Error" #define IDS_2050 2050 // "Fatal error" -#define IDS_2051 2051 // "Are you sure you want to save..." +#define IDS_2051 2051 // "" #define IDS_2052 2052 // "Press CTRL+ALT+PAGE DOWN..." #define IDS_2053 2053 // "Speed" #define IDS_2054 2054 // "ZIP %i (%03i): %ls" @@ -44,11 +44,11 @@ #define IDS_2068 2068 // "Sound" #define IDS_2069 2069 // "Network" #define IDS_2070 2070 // "Ports (COM & LPT)" -#define IDS_2071 2071 // "Other peripherals" +#define IDS_2071 2071 // "Storage controllers" #define IDS_2072 2072 // "Hard disks" #define IDS_2073 2073 // "Floppy and CD-ROM drives" #define IDS_2074 2074 // "Other removable devices" -#define IDS_2075 2075 // "CD-ROM images (*.ISO;*.CU.." +#define IDS_2075 2075 // "Other peripherals" #define IDS_2076 2076 // "Surface-based images (*.8.." #define IDS_2077 2077 // "Click to capture mouse" #define IDS_2078 2078 // "Press F12-F8 to release mouse" @@ -94,9 +94,9 @@ #define IDS_2118 2118 // "Internal controller" #define IDS_2119 2119 // "Exit" #define IDS_2120 2120 // "No ROMs found" -#define IDS_2121 2121 // "Save changes\nThis will hard..." -#define IDS_2122 2122 // "Discard changes\nAll changes..." -#define IDS_2123 2123 // "Cancel\nGo back to the..." +#define IDS_2121 2121 // "Do you want to save the settings?" +#define IDS_2122 2122 // "This will hard reset the virtual..." +#define IDS_2123 2123 // "Save" #define IDS_2124 2124 // "About 86Box" #define IDS_2125 2125 // "86Box v" EMU_VERSION #define IDS_2126 2126 // "An emulator of old computers..." @@ -109,10 +109,12 @@ #define IDS_2133 2133 // LIB_NAME_FLUIDSYNTH " is required..." #define IDS_2134 2134 // "Entering fullscreen mode" #define IDS_2135 2135 // "Don't show this message again" -#define IDS_2136 2136 // "Don't Exit" +#define IDS_2136 2136 // "Don't exit" #define IDS_2137 2137 // "Reset" -#define IDS_2138 2138 // "Don't Reset" +#define IDS_2138 2138 // "Don't reset" #define IDS_2139 2139 // "MO images (*.IM?)\0*.IM?..." +#define IDS_2140 2140 // "CD-ROM images (*.ISO;*.CU.." +#define IDS_2141 2141 // "%hs Device Configuration" #define IDS_4096 4096 // "Hard disk (%s)" #define IDS_4097 4097 // "%01i:%01i" @@ -139,7 +141,20 @@ #define IDS_4118 4118 // "The selected file will be..." #define IDS_4119 4119 // "Unsupported disk image" #define IDS_4120 4120 // "Overwrite" -#define IDS_4121 4121 // "Don't Overwrite" +#define IDS_4121 4121 // "Don't overwrite" +#define IDS_4122 4122 // "Raw image (.img)" +#define IDS_4123 4123 // "HDI image (.hdi)" +#define IDS_4124 4124 // "HDX image (.hdx)" +#define IDS_4125 4125 // "Fixed-size VHD (.vhd)" +#define IDS_4126 4126 // "Dynamic-size VHD (.vhd)" +#define IDS_4127 4127 // "Differencing VHD (.vhd)" +#define IDS_4128 4128 // "Large blocks (2 MB)" +#define IDS_4129 4129 // "Small blocks (512 KB)" +#define IDS_4130 4130 // "VHD files (*.VHD)\0*.VHD\0All..." +#define IDS_4131 4131 // "Select the parent VHD" +#define IDS_4132 4132 // "This could mean that the parent..." +#define IDS_4133 4133 // "Parent and child disk timestamps..." +#define IDS_4134 4134 // "Could not fix VHD timestamp." #define IDS_4352 4352 // "MFM/RLL" #define IDS_4353 4353 // "XT IDE" @@ -207,9 +222,9 @@ #define IDS_LANG_ENUS IDS_7168 -#define STR_NUM_2048 92 +#define STR_NUM_2048 94 #define STR_NUM_3072 11 -#define STR_NUM_4096 18 +#define STR_NUM_4096 39 #define STR_NUM_4352 6 #define STR_NUM_4608 6 #define STR_NUM_5120 1 diff --git a/src/include/86box/machine.h b/src/include/86box/machine.h index 4f9bc5100..59b52b5e1 100644 --- a/src/include/86box/machine.h +++ b/src/include/86box/machine.h @@ -64,7 +64,7 @@ /* Combined flags. */ #define MACHINE_VIDEO_FIXED 0x00003000 /* sys has fixed int video */ /* Feature flags for internal storage controllers. */ -#define MACHINE_HDC 0x0FFC0000 /* sys has int HDC */ +#define MACHINE_HDC 0x07FC0000 /* sys has int HDC */ #define MACHINE_MFM 0x00100000 /* sys has int MFM/RLL */ #define MACHINE_XTA 0x00200000 /* sys has int XTA */ #define MACHINE_ESDI 0x00400000 /* sys has int ESDI */ diff --git a/src/include/86box/mem.h b/src/include/86box/mem.h index a5bae6f89..b6718762b 100644 --- a/src/include/86box/mem.h +++ b/src/include/86box/mem.h @@ -236,31 +236,16 @@ extern uint16_t read_mem_w(uint32_t addr); extern void write_mem_b(uint32_t addr, uint8_t val); extern void write_mem_w(uint32_t addr, uint16_t val); -#ifndef USE_NEW_DYNAREC -#define readmemb(a) ((readlookup2[(a)>>12]==-1)?readmembl(a):*(uint8_t *)(readlookup2[(a) >> 12] + (a))) -#define readmemw(s,a) ((readlookup2[(uint32_t)((s)+(a))>>12]==-1 || (s)==0xFFFFFFFF || (((s)+(a)) & 1))?readmemwl(s,a):*(uint16_t *)(readlookup2[(uint32_t)((s)+(a))>>12]+(uint32_t)((s)+(a)))) -#define readmeml(s,a) ((readlookup2[(uint32_t)((s)+(a))>>12]==-1 || (s)==0xFFFFFFFF || (((s)+(a)) & 3))?readmemll(s,a):*(uint32_t *)(readlookup2[(uint32_t)((s)+(a))>>12]+(uint32_t)((s)+(a)))) +extern uint8_t readmembl(uint32_t addr); +extern void writemembl(uint32_t addr, uint8_t val); +extern void rwmembl(uint32_t raddr, uint32_t waddr, uint8_t val); -extern uint8_t readmembl(uint32_t addr); -extern void writemembl(uint32_t addr, uint8_t val); -extern uint8_t readmemb386l(uint32_t seg, uint32_t addr); -extern void writememb386l(uint32_t seg, uint32_t addr, uint8_t val); -extern uint16_t readmemwl(uint32_t seg, uint32_t addr); -extern void writememwl(uint32_t seg, uint32_t addr, uint16_t val); -extern uint32_t readmemll(uint32_t seg, uint32_t addr); -extern void writememll(uint32_t seg, uint32_t addr, uint32_t val); -extern uint64_t readmemql(uint32_t seg, uint32_t addr); -extern void writememql(uint32_t seg, uint32_t addr, uint64_t val); -#else -extern uint8_t readmembl(uint32_t addr); -extern void writemembl(uint32_t addr, uint8_t val); extern uint16_t readmemwl(uint32_t addr); extern void writememwl(uint32_t addr, uint16_t val); extern uint32_t readmemll(uint32_t addr); extern void writememll(uint32_t addr, uint32_t val); extern uint64_t readmemql(uint32_t addr); extern void writememql(uint32_t addr, uint64_t val); -#endif extern uint8_t *getpccache(uint32_t a); extern uint64_t mmutranslatereal(uint32_t addr, int rw); diff --git a/src/include/86box/resource.h b/src/include/86box/resource.h index b0422d18b..24ede722a 100644 --- a/src/include/86box/resource.h +++ b/src/include/86box/resource.h @@ -35,11 +35,12 @@ #define DLG_CFG_SOUND 114 /* sub-dialog of config */ #define DLG_CFG_NETWORK 115 /* sub-dialog of config */ #define DLG_CFG_PORTS 116 /* sub-dialog of config */ -#define DLG_CFG_PERIPHERALS 117 /* sub-dialog of config */ +#define DLG_CFG_STORAGE 117 /* sub-dialog of config */ #define DLG_CFG_HARD_DISKS 118 /* sub-dialog of config */ #define DLG_CFG_HARD_DISKS_ADD 119 /* sub-dialog of config */ #define DLG_CFG_FLOPPY_AND_CDROM_DRIVES 120 /* sub-dialog of config */ #define DLG_CFG_OTHER_REMOVABLE_DEVICES 121 /* sub-dialog of config */ +#define DLG_CFG_PERIPHERALS 122 /* sub-dialog of config */ /* Static text label IDs. */ #define IDT_1700 1700 /* Language: */ @@ -100,11 +101,13 @@ #define IDT_1766 1766 /* Board #4: */ #define IDT_1767 1767 /* ISA RTC: */ #define IDT_1768 1768 /* Ext FD Controller: */ -#define IDT_1769 1769 /* MO drives: */ -#define IDT_1770 1770 /* Bus: */ -#define IDT_1771 1771 /* ID: */ -#define IDT_1772 1772 /* Channel */ -#define IDT_1773 1773 /* Type: */ +#define IDT_1769 1769 /* MO drives: */ +#define IDT_1770 1770 /* Bus: */ +#define IDT_1771 1771 /* ID: */ +#define IDT_1772 1772 /* Channel */ +#define IDT_1773 1773 /* Type: */ +#define IDT_1774 1774 /* Image Format: */ +#define IDT_1775 1775 /* Block Size: */ /* @@ -137,114 +140,117 @@ #define IDC_MEMSPIN 1019 #define IDC_TEXT_MB IDT_1705 -#define IDC_VIDEO 1030 /* video config */ -#define IDC_COMBO_VIDEO 1031 -#define IDC_CHECK_VOODOO 1032 -#define IDC_BUTTON_VOODOO 1033 +#define IDC_VIDEO 1020 /* video config */ +#define IDC_COMBO_VIDEO 1021 +#define IDC_CHECK_VOODOO 1022 +#define IDC_BUTTON_VOODOO 1023 -#define IDC_INPUT 1050 /* input config */ -#define IDC_COMBO_MOUSE 1051 -#define IDC_COMBO_JOYSTICK 1052 -#define IDC_COMBO_JOY 1053 -#define IDC_CONFIGURE_MOUSE 1054 +#define IDC_INPUT 1030 /* input config */ +#define IDC_COMBO_MOUSE 1031 +#define IDC_COMBO_JOYSTICK 1032 +#define IDC_COMBO_JOY 1033 +#define IDC_CONFIGURE_MOUSE 1034 -#define IDC_SOUND 1070 /* sound config */ -#define IDC_COMBO_SOUND 1071 -#define IDC_CHECK_SSI 1072 -#define IDC_CHECK_CMS 1073 -#define IDC_CHECK_GUS 1074 -#define IDC_COMBO_MIDI 1075 -#define IDC_CHECK_MPU401 1076 -#define IDC_CONFIGURE_MPU401 1077 -#define IDC_CHECK_FLOAT 1078 -#define IDC_CONFIGURE_GUS 1079 -#define IDC_COMBO_MIDI_IN 1080 +#define IDC_SOUND 1040 /* sound config */ +#define IDC_COMBO_SOUND 1041 +#define IDC_CHECK_SSI 1042 +#define IDC_CHECK_CMS 1043 +#define IDC_CHECK_GUS 1044 +#define IDC_COMBO_MIDI 1045 +#define IDC_CHECK_MPU401 1046 +#define IDC_CONFIGURE_MPU401 1047 +#define IDC_CHECK_FLOAT 1048 +#define IDC_CONFIGURE_GUS 1049 +#define IDC_COMBO_MIDI_IN 1050 -#define IDC_COMBO_NET_TYPE 1090 /* network config */ -#define IDC_COMBO_PCAP 1091 -#define IDC_COMBO_NET 1092 +#define IDC_COMBO_NET_TYPE 1060 /* network config */ +#define IDC_COMBO_PCAP 1061 +#define IDC_COMBO_NET 1062 -#define IDC_COMBO_LPT1 1110 /* ports config */ -#define IDC_COMBO_LPT2 1111 -#define IDC_COMBO_LPT3 1112 -#define IDC_CHECK_SERIAL1 1113 -#define IDC_CHECK_SERIAL2 1114 -#define IDC_CHECK_SERIAL3 1115 -#define IDC_CHECK_SERIAL4 1116 -#define IDC_CHECK_PARALLEL1 1117 -#define IDC_CHECK_PARALLEL2 1118 -#define IDC_CHECK_PARALLEL3 1119 +#define IDC_COMBO_LPT1 1070 /* ports config */ +#define IDC_COMBO_LPT2 1071 +#define IDC_COMBO_LPT3 1072 +#define IDC_CHECK_SERIAL1 1073 +#define IDC_CHECK_SERIAL2 1074 +#define IDC_CHECK_SERIAL3 1075 +#define IDC_CHECK_SERIAL4 1076 +#define IDC_CHECK_PARALLEL1 1077 +#define IDC_CHECK_PARALLEL2 1078 +#define IDC_CHECK_PARALLEL3 1079 -#define IDC_OTHER_PERIPH 1120 /* other periph config */ -#define IDC_COMBO_SCSI 1121 -#define IDC_CONFIGURE_SCSI 1122 -#define IDC_COMBO_HDC 1123 -#define IDC_CONFIGURE_HDC 1124 -#define IDC_CHECK_IDE_TER 1125 -#define IDC_BUTTON_IDE_TER 1126 -#define IDC_CHECK_IDE_QUA 1127 -#define IDC_BUTTON_IDE_QUA 1128 -#define IDC_CHECK_BUGGER 1129 -#define IDC_CHECK_POSTCARD 1130 -#define IDC_COMBO_ISARTC 1131 -#define IDC_CONFIGURE_ISARTC 1132 -#define IDC_COMBO_FDC 1133 -#define IDC_CONFIGURE_FDC 1134 -#define IDC_GROUP_ISAMEM 1140 -#define IDC_COMBO_ISAMEM_1 1141 -#define IDC_COMBO_ISAMEM_2 1142 -#define IDC_COMBO_ISAMEM_3 1143 -#define IDC_COMBO_ISAMEM_4 1144 -#define IDC_CONFIGURE_ISAMEM_1 1145 -#define IDC_CONFIGURE_ISAMEM_2 1146 -#define IDC_CONFIGURE_ISAMEM_3 1147 -#define IDC_CONFIGURE_ISAMEM_4 1148 +#define IDC_OTHER_PERIPH 1080 /* storage controllers config */ +#define IDC_COMBO_SCSI 1081 +#define IDC_CONFIGURE_SCSI 1082 +#define IDC_COMBO_HDC 1083 +#define IDC_CONFIGURE_HDC 1084 +#define IDC_CHECK_IDE_TER 1085 +#define IDC_BUTTON_IDE_TER 1086 +#define IDC_CHECK_IDE_QUA 1087 +#define IDC_BUTTON_IDE_QUA 1088 -#define IDC_HARD_DISKS 1150 /* hard disks config */ -#define IDC_LIST_HARD_DISKS 1151 -#define IDC_BUTTON_HDD_ADD_NEW 1152 -#define IDC_BUTTON_HDD_ADD 1153 -#define IDC_BUTTON_HDD_REMOVE 1154 -#define IDC_COMBO_HD_BUS 1155 -#define IDC_COMBO_HD_CHANNEL 1156 -#define IDC_COMBO_HD_ID 1157 -#define IDC_COMBO_HD_LUN 1158 -#define IDC_COMBO_HD_CHANNEL_IDE 1159 +#define IDC_HARD_DISKS 1090 /* hard disks config */ +#define IDC_LIST_HARD_DISKS 1091 +#define IDC_BUTTON_HDD_ADD_NEW 1092 +#define IDC_BUTTON_HDD_ADD 1093 +#define IDC_BUTTON_HDD_REMOVE 1094 +#define IDC_COMBO_HD_BUS 1095 +#define IDC_COMBO_HD_CHANNEL 1096 +#define IDC_COMBO_HD_ID 1097 +#define IDC_COMBO_HD_LUN 1098 +#define IDC_COMBO_HD_CHANNEL_IDE 1099 -#define IDC_EDIT_HD_FILE_NAME 1160 /* add hard disk dialog */ -#define IDC_EDIT_HD_SPT 1161 -#define IDC_EDIT_HD_HPC 1162 -#define IDC_EDIT_HD_CYL 1163 -#define IDC_EDIT_HD_SIZE 1164 -#define IDC_COMBO_HD_TYPE 1165 -#define IDC_PBAR_IMG_CREATE 1166 +#define IDC_EDIT_HD_FILE_NAME 1100 /* add hard disk dialog */ +#define IDC_EDIT_HD_SPT 1101 +#define IDC_EDIT_HD_HPC 1102 +#define IDC_EDIT_HD_CYL 1103 +#define IDC_EDIT_HD_SIZE 1104 +#define IDC_COMBO_HD_TYPE 1105 +#define IDC_PBAR_IMG_CREATE 1106 +#define IDC_COMBO_HD_IMG_FORMAT 1107 +#define IDC_COMBO_HD_BLOCK_SIZE 1108 -#define IDC_REMOV_DEVICES 1170 /* floppy and cd-rom drives config */ -#define IDC_LIST_FLOPPY_DRIVES 1171 -#define IDC_COMBO_FD_TYPE 1172 -#define IDC_CHECKTURBO 1173 -#define IDC_CHECKBPB 1174 -#define IDC_LIST_CDROM_DRIVES 1175 -#define IDC_COMBO_CD_BUS 1176 -#define IDC_COMBO_CD_ID 1177 -#define IDC_COMBO_CD_LUN 1178 -#define IDC_COMBO_CD_CHANNEL_IDE 1179 +#define IDC_REMOV_DEVICES 1110 /* floppy and cd-rom drives config */ +#define IDC_LIST_FLOPPY_DRIVES 1111 +#define IDC_COMBO_FD_TYPE 1112 +#define IDC_CHECKTURBO 1113 +#define IDC_CHECKBPB 1114 +#define IDC_LIST_CDROM_DRIVES 1115 +#define IDC_COMBO_CD_BUS 1116 +#define IDC_COMBO_CD_ID 1117 +#define IDC_COMBO_CD_LUN 1118 +#define IDC_COMBO_CD_CHANNEL_IDE 1119 -#define IDC_LIST_ZIP_DRIVES 1180 /* other removable devices config */ -#define IDC_COMBO_ZIP_BUS 1181 -#define IDC_COMBO_ZIP_ID 1182 -#define IDC_COMBO_ZIP_LUN 1183 -#define IDC_COMBO_ZIP_CHANNEL_IDE 1184 -#define IDC_CHECK250 1185 -#define IDC_COMBO_CD_SPEED 1186 -#define IDC_LIST_MO_DRIVES 1187 -#define IDC_COMBO_MO_BUS 1188 -#define IDC_COMBO_MO_ID 1189 -#define IDC_COMBO_MO_LUN 1190 -#define IDC_COMBO_MO_CHANNEL_IDE 1191 -#define IDC_COMBO_MO_TYPE 1192 +#define IDC_LIST_ZIP_DRIVES 1120 /* other removable devices config */ +#define IDC_COMBO_ZIP_BUS 1121 +#define IDC_COMBO_ZIP_ID 1122 +#define IDC_COMBO_ZIP_LUN 1123 +#define IDC_COMBO_ZIP_CHANNEL_IDE 1124 +#define IDC_CHECK250 1125 +#define IDC_COMBO_CD_SPEED 1126 +#define IDC_LIST_MO_DRIVES 1127 +#define IDC_COMBO_MO_BUS 1128 +#define IDC_COMBO_MO_ID 1129 +#define IDC_COMBO_MO_LUN 1130 +#define IDC_COMBO_MO_CHANNEL_IDE 1131 +#define IDC_COMBO_MO_TYPE 1132 -#define IDC_SLIDER_GAIN 1193 /* sound gain dialog */ +#define IDC_CHECK_BUGGER 1140 /* other periph config */ +#define IDC_CHECK_POSTCARD 1141 +#define IDC_COMBO_ISARTC 1142 +#define IDC_CONFIGURE_ISARTC 1143 +#define IDC_COMBO_FDC 1144 +#define IDC_CONFIGURE_FDC 1145 +#define IDC_GROUP_ISAMEM 1146 +#define IDC_COMBO_ISAMEM_1 1147 +#define IDC_COMBO_ISAMEM_2 1148 +#define IDC_COMBO_ISAMEM_3 1149 +#define IDC_COMBO_ISAMEM_4 1150 +#define IDC_CONFIGURE_ISAMEM_1 1151 +#define IDC_CONFIGURE_ISAMEM_2 1152 +#define IDC_CONFIGURE_ISAMEM_3 1153 +#define IDC_CONFIGURE_ISAMEM_4 1154 + +#define IDC_SLIDER_GAIN 1160 /* sound gain dialog */ #define IDC_EDIT_FILE_NAME 1200 /* new floppy image dialog */ #define IDC_COMBO_DISK_SIZE 1201 @@ -289,8 +295,9 @@ #define IDM_VID_REMEMBER 40041 #define IDM_VID_SDL_SW 40050 #define IDM_VID_SDL_HW 40051 +#define IDM_VID_SDL_OPENGL 40052 #ifdef USE_VNC -#define IDM_VID_VNC 40052 +#define IDM_VID_VNC 40053 #endif #define IDM_VID_SCALE_1X 40055 #define IDM_VID_SCALE_2X 40056 diff --git a/src/include/86box/rom.h b/src/include/86box/rom.h index ee9d9e95f..d06864ca0 100644 --- a/src/include/86box/rom.h +++ b/src/include/86box/rom.h @@ -49,6 +49,8 @@ extern FILE *rom_fopen(wchar_t *fn, wchar_t *mode); extern int rom_getfile(wchar_t *fn, wchar_t *s, int size); extern int rom_present(wchar_t *fn); +extern int rom_load_linear_oddeven(wchar_t *fn, uint32_t addr, int sz, + int off, uint8_t *ptr); extern int rom_load_linear(wchar_t *fn, uint32_t addr, int sz, int off, uint8_t *ptr); extern int rom_load_interleaved(wchar_t *fnl, wchar_t *fnh, uint32_t addr, @@ -68,6 +70,8 @@ extern int bios_load_linear_combined2(wchar_t *fn1, wchar_t *fn2, extern int rom_init(rom_t *rom, wchar_t *fn, uint32_t address, int size, int mask, int file_offset, uint32_t flags); +extern int rom_init_oddeven(rom_t *rom, wchar_t *fn, uint32_t address, int size, + int mask, int file_offset, uint32_t flags); extern int rom_init_interleaved(rom_t *rom, wchar_t *fn_low, wchar_t *fn_high, uint32_t address, int size, int mask, int file_offset, diff --git a/src/include/86box/snd_ad1848.h b/src/include/86box/snd_ad1848.h index 82a7721f3..b6782c139 100644 --- a/src/include/86box/snd_ad1848.h +++ b/src/include/86box/snd_ad1848.h @@ -15,7 +15,7 @@ typedef struct ad1848_t int16_t out_l, out_r; - uint32_t cd_vol_l, cd_vol_r; + double cd_vol_l, cd_vol_r; int enable; diff --git a/src/include/86box/ui.h b/src/include/86box/ui.h index f497fb857..15950c6e6 100644 --- a/src/include/86box/ui.h +++ b/src/include/86box/ui.h @@ -34,7 +34,10 @@ extern "C" { #define MBX_ERROR 2 #define MBX_QUESTION 3 #define MBX_QUESTION_YN 4 -#define MBX_FATAL 0x20 +#define MBX_QUESTION_OK 8 +#define MBX_QMARK 0x10 +#define MBX_WARNING 0x20 +#define MBX_FATAL 0x40 #define MBX_ANSI 0x80 #define MBX_LINKS 0x100 #define MBX_DONTASK 0x200 diff --git a/src/include/86box/vid_svga.h b/src/include/86box/vid_svga.h index 9e4277789..ca7ae4c21 100644 --- a/src/include/86box/vid_svga.h +++ b/src/include/86box/vid_svga.h @@ -20,7 +20,8 @@ #define FLAG_EXTRA_BANKS 1 #define FLAG_ADDR_BY8 2 -#define FLAG_LATCH8 4 +#define FLAG_EXT_WRITE 4 +#define FLAG_LATCH8 8 typedef struct { diff --git a/src/include/86box/vid_voodoo_codegen_x86-64.h b/src/include/86box/vid_voodoo_codegen_x86-64.h index b5e3ed6d1..bbc7d2537 100644 --- a/src/include/86box/vid_voodoo_codegen_x86-64.h +++ b/src/include/86box/vid_voodoo_codegen_x86-64.h @@ -1733,10 +1733,44 @@ static inline void voodoo_generate(uint8_t *code_block, voodoo_t *voodoo, voodoo if ((params->fbzMode & FBZ_CHROMAKEY)) { - addbyte(0x66); /*MOVD EAX, XMM0*/ - addbyte(0x0f); - addbyte(0x7e); - addbyte(0xc0); + switch (_rgb_sel) + { + case CC_LOCALSELECT_ITER_RGB: + addbyte(0xf3); /*MOVDQU XMM0, ib*/ /* ir, ig and ib must be in same dqword!*/ + addbyte(0x0f); + addbyte(0x6f); + addbyte(0x87); + addlong(offsetof(voodoo_state_t, ib)); + addbyte(0x66); /*PSRAD XMM0, 12*/ + addbyte(0x0f); + addbyte(0x72); + addbyte(0xe0); + addbyte(12); + addbyte(0x66); /*PACKSSDW XMM0, XMM0*/ + addbyte(0x0f); + addbyte(0x6b); + addbyte(0xc0); + addbyte(0x66); /*PACKUSWB XMM0, XMM0*/ + addbyte(0x0f); + addbyte(0x67); + addbyte(0xc0); + addbyte(0x66); /*MOVD EAX, XMM0*/ + addbyte(0x0f); + addbyte(0x7e); + addbyte(0xc0); + break; + case CC_LOCALSELECT_COLOR1: + addbyte(0x8b); /*MOV EAX, params->color1[RSI]*/ + addbyte(0x86); + addlong(offsetof(voodoo_params_t, color1)); + break; + case CC_LOCALSELECT_TEX: + addbyte(0x66); /*MOVD EAX, XMM0*/ + addbyte(0x0f); + addbyte(0x7e); + addbyte(0xc0); + break; + } addbyte(0x8b); /*MOV EBX, params->chromaKey[ESI]*/ addbyte(0x9e); addlong(offsetof(voodoo_params_t, chromaKey)); diff --git a/src/include/86box/vid_voodoo_codegen_x86.h b/src/include/86box/vid_voodoo_codegen_x86.h index a9f2c5533..6e8c891c1 100644 --- a/src/include/86box/vid_voodoo_codegen_x86.h +++ b/src/include/86box/vid_voodoo_codegen_x86.h @@ -1668,10 +1668,44 @@ static inline void voodoo_generate(uint8_t *code_block, voodoo_t *voodoo, voodoo if ((params->fbzMode & FBZ_CHROMAKEY)) { - addbyte(0x66); /*MOVD EAX, XMM0*/ - addbyte(0x0f); - addbyte(0x7e); - addbyte(0xc0); + switch (_rgb_sel) + { + case CC_LOCALSELECT_ITER_RGB: + addbyte(0xf3); /*MOVDQU XMM0, ib*/ /* ir, ig and ib must be in same dqword!*/ + addbyte(0x0f); + addbyte(0x6f); + addbyte(0x87); + addlong(offsetof(voodoo_state_t, ib)); + addbyte(0x66); /*PSRAD XMM0, 12*/ + addbyte(0x0f); + addbyte(0x72); + addbyte(0xe0); + addbyte(12); + addbyte(0x66); /*PACKSSDW XMM0, XMM0*/ + addbyte(0x0f); + addbyte(0x6b); + addbyte(0xc0); + addbyte(0x66); /*PACKUSWB XMM0, XMM0*/ + addbyte(0x0f); + addbyte(0x67); + addbyte(0xc0); + addbyte(0x66); /*MOVD EAX, XMM0*/ + addbyte(0x0f); + addbyte(0x7e); + addbyte(0xc0); + break; + case CC_LOCALSELECT_COLOR1: + addbyte(0x8b); /*MOV EAX, params->color1[ESI]*/ + addbyte(0x86); + addlong(offsetof(voodoo_params_t, color1)); + break; + case CC_LOCALSELECT_TEX: + addbyte(0x66); /*MOVD EAX, XMM0*/ + addbyte(0x0f); + addbyte(0x7e); + addbyte(0xc0); + break; + } addbyte(0x8b); /*MOV EBX, params->chromaKey[ESI]*/ addbyte(0x9e); addlong(offsetof(voodoo_params_t, chromaKey)); diff --git a/src/include/86box/vid_voodoo_common.h b/src/include/86box/vid_voodoo_common.h index 073d2ca3d..85b97c7d2 100644 --- a/src/include/86box/vid_voodoo_common.h +++ b/src/include/86box/vid_voodoo_common.h @@ -314,7 +314,8 @@ typedef struct voodoo_t volatile int params_read_idx[4], params_write_idx; uint32_t cmdfifo_base, cmdfifo_end, cmdfifo_size; - int cmdfifo_rp; + int cmdfifo_rp, cmdfifo_ret_addr; + int cmdfifo_in_sub; volatile int cmdfifo_depth_rd, cmdfifo_depth_wr; volatile int cmdfifo_enabled; uint32_t cmdfifo_amin, cmdfifo_amax; @@ -384,6 +385,8 @@ typedef struct voodoo_t uint32_t dstFormat; uint32_t dstSize; uint32_t dstXY; + uint32_t lineStipple; + uint32_t lineStyle; uint32_t rop; uint32_t srcBaseAddr; uint32_t srcFormat; @@ -431,6 +434,9 @@ typedef struct voodoo_t int src_stride_src, src_stride_dest; int src_bpp; + + int line_pix_pos, line_bit_pos; + int line_rep_cnt, line_bit_mask_size; } banshee_blt; struct diff --git a/src/include/86box/video.h b/src/include/86box/video.h index 8997d3857..6640827ce 100644 --- a/src/include/86box/video.h +++ b/src/include/86box/video.h @@ -214,10 +214,8 @@ extern const device_t gd5401_isa_device; extern const device_t gd5402_isa_device; extern const device_t gd5402_onboard_device; extern const device_t gd5420_isa_device; -#if defined(DEV_BRANCH) && defined(USE_CL5422) extern const device_t gd5422_isa_device; extern const device_t gd5424_vlb_device; -#endif extern const device_t gd5426_vlb_device; extern const device_t gd5426_onboard_device; extern const device_t gd5428_isa_device; diff --git a/src/include/86box/win.h b/src/include/86box/win.h index a181dad06..dd83af427 100644 --- a/src/include/86box/win.h +++ b/src/include/86box/win.h @@ -55,6 +55,8 @@ DECLARE_HANDLE(DPI_AWARENESS_CONTEXT); #define SB_CLASS_NAME L"86BoxStatusBar" #define SB_MENU_NAME L"StatusBarMenu" #define FS_CLASS_NAME L"86BoxFullScreen" +#define SDL_CLASS_NAME L"86BoxSDLWnd" +#define SDL_SUB_CLASS_NAME L"86BoxSDLSubWnd" #define FLOPPY_SUBMENU_NAME L"FloppySubmenu" #define CDROM_SUBMENU_NAME L"CdromSubmenu" @@ -83,9 +85,9 @@ DECLARE_HANDLE(DPI_AWARENESS_CONTEXT); #define WM_HAS_SHUTDOWN 0x8897 #ifdef USE_VNC -#define RENDERERS_NUM 3 +#define RENDERERS_NUM 4 #else -#define RENDERERS_NUM 2 +#define RENDERERS_NUM 3 #endif @@ -100,6 +102,7 @@ extern HANDLE ghMutex; extern LCID lang_id; extern HICON hIcon[256]; extern RECT oldclip; +extern int sbar_height; // extern int status_is_open; @@ -109,6 +112,7 @@ extern WCHAR wopenfilestring[512]; extern uint8_t filterindex; +extern void ResizeWindowByClientArea(HWND hwnd, int width, int height); extern void InitCrashDump(void); extern HICON LoadIconEx(PCTSTR pszIconName); @@ -155,7 +159,6 @@ extern int hard_disk_was_added(void); /* Platform UI support functions. */ extern int ui_init(int nCmdShow); -extern void plat_set_input(HWND h); /* Functions in win_about.c: */ @@ -177,10 +180,11 @@ extern void NewFloppyDialogCreate(HWND hwnd, int id, int part); #define SETTINGS_PAGE_SOUND 3 #define SETTINGS_PAGE_NETWORK 4 #define SETTINGS_PAGE_PORTS 5 -#define SETTINGS_PAGE_PERIPHERALS 6 +#define SETTINGS_PAGE_STORAGE 6 #define SETTINGS_PAGE_HARD_DISKS 7 #define SETTINGS_PAGE_FLOPPY_AND_CDROM_DRIVES 8 #define SETTINGS_PAGE_OTHER_REMOVABLE_DEVICES 9 +#define SETTINGS_PAGE_PERIPHERALS 10 extern void win_settings_open(HWND hwnd); extern void win_settings_open_ex(HWND hwnd, int category); @@ -193,11 +197,12 @@ extern int MediaMenuHandler(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPara /* Functions in win_dialog.c: */ -extern int file_dlg_w(HWND hwnd, WCHAR *f, WCHAR *fn, int save); -extern int file_dlg(HWND hwnd, WCHAR *f, char *fn, int save); -extern int file_dlg_mb(HWND hwnd, char *f, char *fn, int save); -extern int file_dlg_w_st(HWND hwnd, int i, WCHAR *fn, int save); -extern int file_dlg_st(HWND hwnd, int i, char *fn, int save); +/* Pass NULL in the title param to use the default title. */ +extern int file_dlg_w(HWND hwnd, WCHAR *f, WCHAR *fn, WCHAR *title, int save); +extern int file_dlg(HWND hwnd, WCHAR *f, char *fn, char *title, int save); +extern int file_dlg_mb(HWND hwnd, char *f, char *fn, char *title, int save); +extern int file_dlg_w_st(HWND hwnd, int i, WCHAR *fn, char *title, int save); +extern int file_dlg_st(HWND hwnd, int i, char *fn, char *title, int save); extern wchar_t *BrowseFolder(wchar_t *saved_path, wchar_t *title); diff --git a/src/include/86box/win_sdl.h b/src/include/86box/win_sdl.h index 97ee42090..cc61142be 100644 --- a/src/include/86box/win_sdl.h +++ b/src/include/86box/win_sdl.h @@ -53,12 +53,11 @@ extern void sdl_close(void); extern int sdl_inits(HWND h); extern int sdl_inith(HWND h); -extern int sdl_inits_fs(HWND h); -extern int sdl_inith_fs(HWND h); +extern int sdl_initho(HWND h); extern int sdl_pause(void); extern void sdl_resize(int x, int y); extern void sdl_enable(int enable); -extern void sdl_reinit_texture(); +extern void sdl_set_fs(int fs); #endif /*WIN_SDL_H*/ diff --git a/src/io.c b/src/io.c index daea584c4..0930da37d 100644 --- a/src/io.c +++ b/src/io.c @@ -307,7 +307,7 @@ inb(uint16_t port) amstrad_latch = AMSTRAD_SW9; if (!found) - sub_cycles(io_delay); + cycles -= io_delay; io_log("[%04X:%08X] (%i, %i, %04i) in b(%04X) = %02X\n", CS, cpu_state.pc, in_smm, found, qfound, port, ret); @@ -334,7 +334,7 @@ outb(uint16_t port, uint8_t val) } if (!found) { - sub_cycles(io_delay); + cycles -= io_delay; #ifdef USE_DYNAREC if (cpu_use_dynarec && ((port == 0xeb) || (port == 0xed))) update_tsc(); @@ -392,7 +392,7 @@ inw(uint16_t port) amstrad_latch = AMSTRAD_SW9; if (!found) - sub_cycles(io_delay); + cycles -= io_delay; io_log("[%04X:%08X] (%i, %i, %04i) in w(%04X) = %04X\n", CS, cpu_state.pc, in_smm, found, qfound, port, ret); @@ -433,7 +433,7 @@ outw(uint16_t port, uint16_t val) } if (!found) { - sub_cycles(io_delay); + cycles -= io_delay; #ifdef USE_DYNAREC if (cpu_use_dynarec && ((port == 0xeb) || (port == 0xed))) update_tsc(); @@ -510,7 +510,7 @@ inl(uint16_t port) amstrad_latch = AMSTRAD_SW9; if (!found) - sub_cycles(io_delay); + cycles -= io_delay; io_log("[%04X:%08X] (%i, %i, %04i) in l(%04X) = %08X\n", CS, cpu_state.pc, in_smm, found, qfound, port, ret); @@ -566,7 +566,7 @@ outl(uint16_t port, uint32_t val) } if (!found) { - sub_cycles(io_delay); + cycles -= io_delay; #ifdef USE_DYNAREC if (cpu_use_dynarec && ((port == 0xeb) || (port == 0xed))) update_tsc(); diff --git a/src/machine/m_amstrad.c b/src/machine/m_amstrad.c index bc22e11f9..87de506ef 100644 --- a/src/machine/m_amstrad.c +++ b/src/machine/m_amstrad.c @@ -301,7 +301,7 @@ vid_write_1512(uint32_t addr, uint8_t val, void *priv) amsvid_t *vid = (amsvid_t *)priv; egawrites++; - sub_cycles(12); + cycles -= 12; addr &= 0x3fff; if ((vid->cgamode & 0x12) == 0x12) { @@ -320,7 +320,7 @@ vid_read_1512(uint32_t addr, void *priv) amsvid_t *vid = (amsvid_t *)priv; egareads++; - sub_cycles(12); + cycles -= 12; addr &= 0x3fff; if ((vid->cgamode & 0x12) == 0x12) diff --git a/src/machine/m_at_386dx_486.c b/src/machine/m_at_386dx_486.c index 2f4a11871..0a14926bf 100644 --- a/src/machine/m_at_386dx_486.c +++ b/src/machine/m_at_386dx_486.c @@ -730,8 +730,8 @@ machine_at_4sa2_init(const machine_t *model) device_add(&sis_85c496_device); pci_register_slot(0x0B, PCI_CARD_NORMAL, 1, 2, 3, 4); pci_register_slot(0x0D, PCI_CARD_NORMAL, 2, 3, 4, 1); - pci_register_slot(0x0E, PCI_CARD_NORMAL, 3, 4, 1, 2); - pci_register_slot(0x07, PCI_CARD_NORMAL, 4, 1, 2, 3); + pci_register_slot(0x0F, PCI_CARD_NORMAL, 3, 4, 1, 2); + pci_register_slot(0x11, PCI_CARD_NORMAL, 4, 1, 2, 3); device_add(&pc87332_device); device_add(&keyboard_ps2_pci_device); diff --git a/src/machine/m_at_t3100e_vid.c b/src/machine/m_at_t3100e_vid.c index 1f27b7392..945b6b296 100644 --- a/src/machine/m_at_t3100e_vid.c +++ b/src/machine/m_at_t3100e_vid.c @@ -204,7 +204,7 @@ void t3100e_write(uint32_t addr, uint8_t val, void *p) egawrites++; t3100e->vram[addr & 0x7fff] = val; - sub_cycles(4); + cycles -= 4; } @@ -213,7 +213,7 @@ uint8_t t3100e_read(uint32_t addr, void *p) { t3100e_t *t3100e = (t3100e_t *)p; egareads++; - sub_cycles(4); + cycles -= 4; return t3100e->vram[addr & 0x7fff]; } diff --git a/src/machine/m_xt_t1000_vid.c b/src/machine/m_xt_t1000_vid.c index 15a9f699c..2f291ab63 100644 --- a/src/machine/m_xt_t1000_vid.c +++ b/src/machine/m_xt_t1000_vid.c @@ -194,14 +194,14 @@ static void t1000_write(uint32_t addr, uint8_t val, void *p) egawrites++; t1000->vram[addr & 0x3fff] = val; - sub_cycles(4); + cycles -= 4; } static uint8_t t1000_read(uint32_t addr, void *p) { t1000_t *t1000 = (t1000_t *)p; egareads++; - sub_cycles(4); + cycles -= 4; return t1000->vram[addr & 0x3fff]; } diff --git a/src/mem/mem.c b/src/mem/mem.c index 629325aad..4c9d6a8c2 100644 --- a/src/mem/mem.c +++ b/src/mem/mem.c @@ -274,7 +274,7 @@ mem_flush_write_page(uint32_t addr, uint32_t virt) #define rammap(x) ((uint32_t *)(_mem_exec[(x) >> MEM_GRANULARITY_BITS]))[((x) >> 2) & MEM_GRANULARITY_QMASK] #define rammap64(x) ((uint64_t *)(_mem_exec[(x) >> MEM_GRANULARITY_BITS]))[((x) >> 3) & MEM_GRANULARITY_PMASK] -static uint64_t +static __inline uint64_t mmutranslatereal_normal(uint32_t addr, int rw) { uint32_t temp,temp2,temp3; @@ -297,7 +297,7 @@ mmutranslatereal_normal(uint32_t addr, int rw) if ((temp & 0x80) && (cr4 & CR4_PSE)) { /*4MB page*/ - if (((CPL == 3) && !(temp & 4) && !cpl_override) || (rw && !(temp & 2) && (((CPL == 3) && !cpl_override) || (cr0 & WP_FLAG)))) { + if (((CPL == 3) && !(temp & 4) && !cpl_override) || (rw && !(temp & 2) && (((CPL == 3) && !cpl_override) || (is486 && (cr0 & WP_FLAG))))) { cr2 = addr; temp &= 1; if (CPL == 3) @@ -318,7 +318,7 @@ mmutranslatereal_normal(uint32_t addr, int rw) temp = rammap((temp & ~0xfff) + ((addr >> 10) & 0xffc)); temp3 = temp & temp2; - if (!(temp&1) || ((CPL == 3) && !(temp3 & 4) && !cpl_override) || (rw && !(temp3 & 2) && (((CPL == 3) && !cpl_override) || (cr0 & WP_FLAG)))) { + if (!(temp&1) || ((CPL == 3) && !(temp3 & 4) && !cpl_override) || (rw && !(temp3 & 2) && (((CPL == 3) && !cpl_override) || (is486 && (cr0 & WP_FLAG))))) { cr2 = addr; temp &= 1; if (CPL == 3) temp |= 4; @@ -336,7 +336,7 @@ mmutranslatereal_normal(uint32_t addr, int rw) } -static uint64_t +static __inline uint64_t mmutranslatereal_pae(uint32_t addr, int rw) { uint64_t temp,temp2,temp3,temp4; @@ -429,7 +429,7 @@ mmutranslatereal32(uint32_t addr, int rw) } -static uint64_t +static __inline uint64_t mmutranslate_noabrt_normal(uint32_t addr, int rw) { uint32_t temp,temp2,temp3; @@ -462,7 +462,7 @@ mmutranslate_noabrt_normal(uint32_t addr, int rw) } -static uint64_t +static __inline uint64_t mmutranslate_noabrt_pae(uint32_t addr, int rw) { uint64_t temp,temp2,temp3,temp4; @@ -572,7 +572,7 @@ addreadlookup(uint32_t virt, uint32_t phys) readlookup[readlnext++] = virt >> 12; readlnext &= (cachesize-1); - sub_cycles(9); + cycles -= 9; } @@ -625,7 +625,7 @@ addwritelookup(uint32_t virt, uint32_t phys) writelookup[writelnext++] = virt >> 12; writelnext &= (cachesize - 1); - sub_cycles(9); + cycles -= 9; } @@ -665,15 +665,19 @@ uint8_t read_mem_b(uint32_t addr) { mem_mapping_t *map; + uint8_t ret = 0xff; + int old_cycles = cycles; mem_logical_addr = addr; addr &= rammask; map = read_mapping[addr >> MEM_GRANULARITY_BITS]; if (map && map->read_b) - return map->read_b(addr, map->p); + ret = map->read_b(addr, map->p); - return 0xff; + resub_cycles(old_cycles); + + return ret; } @@ -681,22 +685,26 @@ uint16_t read_mem_w(uint32_t addr) { mem_mapping_t *map; + uint16_t ret = 0xffff; + int old_cycles = cycles; mem_logical_addr = addr; addr &= rammask; if (addr & 1) - return read_mem_b(addr) | (read_mem_b(addr + 1) << 8); + ret = read_mem_b(addr) | (read_mem_b(addr + 1) << 8); + else { + map = read_mapping[addr >> MEM_GRANULARITY_BITS]; - map = read_mapping[addr >> MEM_GRANULARITY_BITS]; + if (map && map->read_w) + ret = map->read_w(addr, map->p); + else if (map && map->read_b) + ret = map->read_b(addr, map->p) | (map->read_b(addr + 1, map->p) << 8); + } - if (map && map->read_w) - return map->read_w(addr, map->p); + resub_cycles(old_cycles); - if (map && map->read_b) - return map->read_b(addr, map->p) | (map->read_b(addr + 1, map->p) << 8); - - return 0xffff; + return ret; } @@ -704,6 +712,7 @@ void write_mem_b(uint32_t addr, uint8_t val) { mem_mapping_t *map; + int old_cycles = cycles; mem_logical_addr = addr; addr &= rammask; @@ -711,6 +720,8 @@ write_mem_b(uint32_t addr, uint8_t val) map = write_mapping[addr >> MEM_GRANULARITY_BITS]; if (map && map->write_b) map->write_b(addr, val, map->p); + + resub_cycles(old_cycles); } @@ -718,6 +729,7 @@ void write_mem_w(uint32_t addr, uint16_t val) { mem_mapping_t *map; + int old_cycles = cycles; mem_logical_addr = addr; addr &= rammask; @@ -725,18 +737,19 @@ write_mem_w(uint32_t addr, uint16_t val) if (addr & 1) { write_mem_b(addr, val); write_mem_b(addr + 1, val >> 8); - return; - } - - map = write_mapping[addr >> MEM_GRANULARITY_BITS]; - if (map) { - if (map->write_w) - map->write_w(addr, val, map->p); - else if (map->write_b) { - map->write_b(addr, val, map->p); - map->write_b(addr + 1, val >> 8, map->p); + } else { + map = write_mapping[addr >> MEM_GRANULARITY_BITS]; + if (map) { + if (map->write_w) + map->write_w(addr, val, map->p); + else if (map->write_b) { + map->write_b(addr, val, map->p); + map->write_b(addr + 1, val >> 8, map->p); + } } } + + resub_cycles(old_cycles); } @@ -792,6 +805,55 @@ writemembl(uint32_t addr, uint8_t val) } +void +rwmembl(uint32_t raddr, uint32_t waddr, uint8_t val) +{ + uint64_t raddr64 = (uint64_t) raddr; + uint64_t waddr64 = (uint64_t) waddr; + mem_mapping_t *rmap, *wmap; + uint8_t temp = 0xff; + + mem_logical_addr = raddr; + + if (cr0 >> 31) { + raddr64 = mmutranslate_read(raddr); + if (raddr64 == 0xffffffffffffffffULL) + goto do_writebl; + if (raddr64 > 0xffffffffULL) + goto do_writebl; + } + raddr = (uint32_t) (raddr64 & rammask); + + rmap = read_mapping[raddr >> MEM_GRANULARITY_BITS]; + if (rmap && rmap->read_b) + temp = rmap->read_b(raddr, rmap->p); + +do_writebl: + if (cpu_state.abrt) + return; + + mem_logical_addr = waddr; + + if (page_lookup[waddr >> 12] && page_lookup[waddr >> 12]->write_b) { + page_lookup[waddr >> 12]->write_b(waddr, temp, page_lookup[waddr >> 12]); + return; + } + + if (cr0 >> 31) { + waddr64 = mmutranslate_write(waddr); + if (waddr64 == 0xffffffffffffffffULL) + return; + if (waddr64 > 0xffffffffULL) + return; + } + waddr = (uint32_t) (waddr64 & rammask); + + wmap = write_mapping[waddr >> MEM_GRANULARITY_BITS]; + if (wmap && wmap->write_b) + wmap->write_b(waddr, temp, wmap->p); +} + + #ifdef USE_NEW_DYNAREC uint16_t readmemwl(uint32_t addr) @@ -803,7 +865,7 @@ readmemwl(uint32_t addr) if (addr64 & 1) { if (!cpu_cyrix_alignment || (addr64 & 7) == 7) - sub_cycles(timing_misaligned); + cycles -= timing_misaligned; if ((addr64 & 0xfff) > 0xffe) { if (cr0 >> 31) { if (mmutranslate_read(addr) == 0xffffffffffffffffULL) @@ -847,7 +909,7 @@ writememwl(uint32_t addr, uint16_t val) if (addr & 1) { if (!cpu_cyrix_alignment || (addr & 7) == 7) - sub_cycles(timing_misaligned); + cycles -= timing_misaligned; if ((addr & 0xFFF) > 0xFFE) { if (cr0 >> 31) { if (mmutranslate_write(addr) == 0xffffffff) @@ -900,7 +962,7 @@ readmemll(uint32_t addr) if (addr & 3) { if (!cpu_cyrix_alignment || (addr & 7) > 4) - sub_cycles(timing_misaligned); + cycles -= timing_misaligned; if ((addr & 0xfff) > 0xffc) { if (cr0>>31) { if (mmutranslate_read(addr) == 0xffffffffffffffffULL) @@ -950,7 +1012,7 @@ writememll(uint32_t addr, uint32_t val) if (addr & 3) { if (!cpu_cyrix_alignment || (addr & 7) > 4) - sub_cycles(timing_misaligned); + cycles -= timing_misaligned; if ((addr & 0xFFF) > 0xFFC) { if (cr0>>31) { if (mmutranslate_write(addr) == 0xffffffffffffffffULL) @@ -1006,7 +1068,7 @@ readmemql(uint32_t addr) mem_logical_addr = addr; if (addr & 7) { - sub_cycles(timing_misaligned); + cycles -= timing_misaligned; if ((addr & 0xFFF) > 0xFF8) { if (cr0>>31) { if (mmutranslate_read(addr) == 0xffffffffffffffffULL) @@ -1046,7 +1108,7 @@ writememql(uint32_t addr, uint64_t val) mem_logical_addr = addr; if (addr & 7) { - sub_cycles(timing_misaligned); + cycles -= timing_misaligned; if ((addr & 0xFFF) > 0xFF8) { if (cr0>>31) { if (mmutranslate_write(addr) == 0xffffffffffffffffULL) @@ -1100,30 +1162,16 @@ writememql(uint32_t addr, uint64_t val) } } #else -uint8_t -readmemb386l(uint32_t seg, uint32_t addr) -{ - return readmembl(addr + seg); -} - - -void -writememb386l(uint32_t seg, uint32_t addr, uint8_t val) -{ - writemembl(addr + seg, val); -} - - uint16_t -readmemwl(uint32_t seg, uint32_t addr) +readmemwl(uint32_t addr) { uint64_t addr64 = (uint64_t) addr; mem_mapping_t *map; - uint32_t addr2 = mem_logical_addr = seg + addr; + uint32_t addr2 = mem_logical_addr = addr; if (addr2 & 1) { if (!cpu_cyrix_alignment || (addr2 & 7) == 7) - sub_cycles(timing_misaligned); + cycles -= timing_misaligned; if ((addr2 & 0xfff) > 0xffe) { if (cr0 >> 31) { if (mmutranslate_read(addr2) == 0xffffffffffffffffULL) @@ -1131,8 +1179,7 @@ readmemwl(uint32_t seg, uint32_t addr) if (mmutranslate_read(addr2+1) == 0xffffffffffffffffULL) return 0xffff; } - if (is386) return readmemb386l(seg,addr)|(((uint16_t) readmemb386l(seg,addr+1))<<8); - else return readmembl(seg+addr)|(((uint16_t) readmembl(seg+addr+1))<<8); + return readmembl(addr)|(((uint16_t) readmembl(addr+1))<<8); } else if (readlookup2[addr2 >> 12] != (uintptr_t) LOOKUP_INV) return *(uint16_t *)(readlookup2[addr2 >> 12] + addr2); } @@ -1154,12 +1201,8 @@ readmemwl(uint32_t seg, uint32_t addr) return map->read_w(addr2, map->p); if (map && map->read_b) { - if (AT) - return map->read_b(addr2, map->p) | - ((uint16_t) (map->read_b(addr2 + 1, map->p)) << 8); - else - return map->read_b(addr2, map->p) | - ((uint16_t) (map->read_b(seg + ((addr + 1) & 0xffff), map->p)) << 8); + return map->read_b(addr2, map->p) | + ((uint16_t) (map->read_b(addr2 + 1, map->p)) << 8); } return 0xffff; @@ -1167,27 +1210,22 @@ readmemwl(uint32_t seg, uint32_t addr) void -writememwl(uint32_t seg, uint32_t addr, uint16_t val) +writememwl(uint32_t addr, uint16_t val) { uint64_t addr64 = (uint64_t) addr; mem_mapping_t *map; - uint32_t addr2 = mem_logical_addr = seg + addr; + uint32_t addr2 = mem_logical_addr = addr; if (addr2 & 1) { if (!cpu_cyrix_alignment || (addr2 & 7) == 7) - sub_cycles(timing_misaligned); + cycles -= timing_misaligned; if ((addr2 & 0xFFF) > 0xffe) { if (cr0 >> 31) { if (mmutranslate_write(addr2) == 0xffffffffffffffffULL) return; if (mmutranslate_write(addr2+1) == 0xffffffffffffffffULL) return; } - if (is386) { - writememb386l(seg,addr,val); - writememb386l(seg,addr+1,val>>8); - } else { - writemembl(seg+addr,val); - writemembl(seg+addr+1,val>>8); - } + writemembl(addr,val); + writemembl(addr+1,val>>8); return; } else if (writelookup2[addr2 >> 12] != (uintptr_t) LOOKUP_INV) { *(uint16_t *)(writelookup2[addr2 >> 12] + addr2) = val; @@ -1227,21 +1265,21 @@ writememwl(uint32_t seg, uint32_t addr, uint16_t val) uint32_t -readmemll(uint32_t seg, uint32_t addr) +readmemll(uint32_t addr) { uint64_t addr64 = (uint64_t) addr; mem_mapping_t *map; - uint32_t addr2 = mem_logical_addr = seg + addr; + uint32_t addr2 = mem_logical_addr = addr; if (addr2 & 3) { if (!cpu_cyrix_alignment || (addr2 & 7) > 4) - sub_cycles(timing_misaligned); + cycles -= timing_misaligned; if ((addr2 & 0xfff) > 0xffc) { if (cr0 >> 31) { if (mmutranslate_read(addr2) == 0xffffffffffffffffULL) return 0xffffffff; if (mmutranslate_read(addr2+3) == 0xffffffffffffffffULL) return 0xffffffff; } - return readmemwl(seg,addr)|(readmemwl(seg,addr+2)<<16); + return readmemwl(addr)|(readmemwl(addr+2)<<16); } else if (readlookup2[addr2 >> 12] != (uintptr_t) LOOKUP_INV) return *(uint32_t *)(readlookup2[addr2 >> 12] + addr2); } @@ -1277,22 +1315,22 @@ readmemll(uint32_t seg, uint32_t addr) void -writememll(uint32_t seg, uint32_t addr, uint32_t val) +writememll(uint32_t addr, uint32_t val) { uint64_t addr64 = (uint64_t) addr; mem_mapping_t *map; - uint32_t addr2 = mem_logical_addr = seg + addr; + uint32_t addr2 = mem_logical_addr = addr; if (addr2 & 3) { if (!cpu_cyrix_alignment || (addr2 & 7) > 4) - sub_cycles(timing_misaligned); + cycles -= timing_misaligned; if ((addr2 & 0xfff) > 0xffc) { if (cr0 >> 31) { if (mmutranslate_write(addr2) == 0xffffffffffffffffULL) return; if (mmutranslate_write(addr2+3) == 0xffffffffffffffffULL) return; } - writememwl(seg,addr,val); - writememwl(seg,addr+2,val>>16); + writememwl(addr,val); + writememwl(addr+2,val>>16); return; } else if (writelookup2[addr2 >> 12] != (uintptr_t) LOOKUP_INV) { *(uint32_t *)(writelookup2[addr2 >> 12] + addr2) = val; @@ -1338,20 +1376,20 @@ writememll(uint32_t seg, uint32_t addr, uint32_t val) uint64_t -readmemql(uint32_t seg, uint32_t addr) +readmemql(uint32_t addr) { uint64_t addr64 = (uint64_t) addr; mem_mapping_t *map; - uint32_t addr2 = mem_logical_addr = seg + addr; + uint32_t addr2 = mem_logical_addr = addr; if (addr2 & 7) { - sub_cycles(timing_misaligned); + cycles -= timing_misaligned; if ((addr2 & 0xfff) > 0xff8) { if (cr0 >> 31) { if (mmutranslate_read(addr2) == 0xffffffffffffffffULL) return 0xffffffffffffffffULL; if (mmutranslate_read(addr2+7) == 0xffffffffffffffffULL) return 0xffffffffffffffffULL; } - return readmemll(seg,addr)|((uint64_t)readmemll(seg,addr+4)<<32); + return readmemll(addr)|((uint64_t)readmemll(addr+4)<<32); } else if (readlookup2[addr2 >> 12] != (uintptr_t) LOOKUP_INV) return *(uint64_t *)(readlookup2[addr2 >> 12] + addr2); } @@ -1371,26 +1409,26 @@ readmemql(uint32_t seg, uint32_t addr) if (map && map->read_l) return map->read_l(addr2, map->p) | ((uint64_t)map->read_l(addr2 + 4, map->p) << 32); - return readmemll(seg,addr) | ((uint64_t)readmemll(seg,addr+4)<<32); + return readmemll(addr) | ((uint64_t)readmemll(addr+4)<<32); } void -writememql(uint32_t seg, uint32_t addr, uint64_t val) +writememql(uint32_t addr, uint64_t val) { uint64_t addr64 = (uint64_t) addr; mem_mapping_t *map; - uint32_t addr2 = mem_logical_addr = seg + addr; + uint32_t addr2 = mem_logical_addr = addr; if (addr2 & 7) { - sub_cycles(timing_misaligned); + cycles -= timing_misaligned; if ((addr2 & 0xfff) > 0xff8) { if (cr0 >> 31) { if (mmutranslate_write(addr2) == 0xffffffffffffffffULL) return; if (mmutranslate_write(addr2+7) == 0xffffffffffffffffULL) return; } - writememll(seg, addr, val); - writememll(seg, addr+4, val >> 32); + writememll(addr, val); + writememll(addr+4, val >> 32); return; } else if (writelookup2[addr2 >> 12] != (uintptr_t) LOOKUP_INV) { *(uint64_t *)(writelookup2[addr2 >> 12] + addr2) = val; diff --git a/src/mem/rom.c b/src/mem/rom.c index fc827539b..8e4eda437 100644 --- a/src/mem/rom.c +++ b/src/mem/rom.c @@ -162,6 +162,42 @@ rom_readl(uint32_t addr, void *priv) } +int +rom_load_linear_oddeven(wchar_t *fn, uint32_t addr, int sz, int off, uint8_t *ptr) +{ + FILE *f = rom_fopen(fn, L"rb"); + int i; + + if (f == NULL) { + rom_log("ROM: image '%ls' not found\n", fn); + return(0); + } + + /* Make sure we only look at the base-256K offset. */ + if (addr >= 0x40000) + addr = 0; + else + addr &= 0x03ffff; + + if (ptr != NULL) { + if (fseek(f, off, SEEK_SET) == -1) + fatal("rom_load_linear(): Error seeking to the beginning of the file\n"); + for (i = 0; i < (sz >> 1); i++) { + if (fread(ptr + (addr + (i << 1)), 1, 1, f) != 1) + fatal("rom_load_linear(): Error reading even data\n"); + } + for (i = 0; i < (sz >> 1); i++) { + if (fread(ptr + (addr + (i << 1) + 1), 1, 1, f) != 1) + fatal("rom_load_linear(): Error reading od data\n"); + } + } + + (void)fclose(f); + + return(1); +} + + /* Load a ROM BIOS from its chips, interleaved mode. */ int rom_load_linear(wchar_t *fn, uint32_t addr, int sz, int off, uint8_t *ptr) @@ -507,6 +543,36 @@ rom_init(rom_t *rom, wchar_t *fn, uint32_t addr, int sz, int mask, int off, uint } +int +rom_init_oddeven(rom_t *rom, wchar_t *fn, uint32_t addr, int sz, int mask, int off, uint32_t flags) +{ + rom_log("rom_init(%08X, %08X, %08X, %08X, %08X, %08X, %08X)\n", rom, fn, addr, sz, mask, off, flags); + + /* Allocate a buffer for the image. */ + rom->rom = malloc(sz); + memset(rom->rom, 0xff, sz); + + /* Load the image file into the buffer. */ + if (! rom_load_linear_oddeven(fn, addr, sz, off, rom->rom)) { + /* Nope.. clean up. */ + free(rom->rom); + rom->rom = NULL; + return(-1); + } + + rom->sz = sz; + rom->mask = mask; + + mem_mapping_add(&rom->mapping, + addr, sz, + rom_read, rom_readw, rom_readl, + mem_write_null, mem_write_nullw, mem_write_nulll, + rom->rom, flags | MEM_MAPPING_ROM, rom); + + return(0); +} + + int rom_init_interleaved(rom_t *rom, wchar_t *fnl, wchar_t *fnh, uint32_t addr, int sz, int mask, int off, uint32_t flags) { diff --git a/src/nvr_at.c b/src/nvr_at.c index 1b5612593..e7a3f7f4e 100644 --- a/src/nvr_at.c +++ b/src/nvr_at.c @@ -638,7 +638,7 @@ nvr_write(uint16_t addr, uint8_t val, void *priv) local_t *local = (local_t *)nvr->data; uint8_t addr_id = (addr & 0x0e) >> 1; - sub_cycles(ISA_CYCLES(8)); + cycles -= ISA_CYCLES(8); if (local->bank[addr_id] == 0xff) return; @@ -673,7 +673,7 @@ nvr_read(uint16_t addr, void *priv) uint8_t addr_id = (addr & 0x0e) >> 1; uint16_t i, checksum = 0x0000; - sub_cycles(ISA_CYCLES(8)); + cycles -= ISA_CYCLES(8); if (/* (addr & 1) && */(local->bank[addr_id] == 0xff)) return 0xff; diff --git a/src/pc.c b/src/pc.c index 9276ad255..31586a4b3 100644 --- a/src/pc.c +++ b/src/pc.c @@ -141,7 +141,8 @@ int cpu_use_dynarec = 0, /* (C) cpu uses/needs Dyna */ fpu_type = 0; /* (C) fpu type */ int time_sync = 0; /* (C) enable time sync */ int confirm_reset = 1, /* (C) enable reset confirmation */ - confirm_exit = 1; /* (C) enable exit confirmation */ + confirm_exit = 1, /* (C) enable exit confirmation */ + confirm_save = 1; /* (C) enable save confirmation */ #ifdef USE_DISCORD int enable_discord = 0; /* (C) enable Discord integration */ #endif @@ -1131,6 +1132,13 @@ set_screen_size(int x, int y) } +void +reset_screen_size(void) +{ + set_screen_size(unscaled_size_x, efscrnsz_y); +} + + void set_screen_size_natural(void) { diff --git a/src/pic.c b/src/pic.c index 8b0727070..847310d03 100644 --- a/src/pic.c +++ b/src/pic.c @@ -31,6 +31,10 @@ #include <86box/pic.h> #include <86box/timer.h> #include <86box/pit.h> +#include <86box/device.h> +#include <86box/apm.h> +#include <86box/nvr.h> +#include <86box/acpi.h> enum @@ -142,7 +146,7 @@ pic_cascade_mode(pic_t *dev) } -static uint8_t +static __inline uint8_t pic_slave_on(pic_t *dev, int channel) { pic_log("pic_slave_on(%i): %i, %02X, %02X\n", channel, pic_cascade_mode(dev), dev->icw4 & 0x0c, dev->icw3 & (1 << channel)); @@ -152,7 +156,7 @@ pic_slave_on(pic_t *dev, int channel) } -static int +static __inline int find_best_interrupt(pic_t *dev) { uint8_t b, s; @@ -184,7 +188,7 @@ find_best_interrupt(pic_t *dev) } -void +static __inline void pic_update_pending(void) { int is_at = IS_AT(machine); @@ -321,7 +325,7 @@ pic_action(pic_t *dev, uint8_t irq, uint8_t eoi, uint8_t rotate) /* Automatic non-specific EOI. */ -static void +static __inline void pic_auto_non_specific_eoi(pic_t *dev) { uint8_t irq; @@ -520,6 +524,9 @@ picint_common(uint16_t num, int level, int set) return; } + if (num & 0x0100) + acpi_rtc_status = !!set; + if (set) { if (num & 0xff00) { if (level) @@ -650,8 +657,13 @@ picinterrupt() pit_ctr_set_gate(&pit2->counters[0], 0); /* Two ACK's - do them in a loop to avoid potential compiler misoptimizations. */ - for (i = 0; i < 2; i++) - ret = pic_irq_ack(); + for (i = 0; i < 2; i++) { + ret = pic_irq_ack_read(&pic, pic.ack_bytes); + pic.ack_bytes = (pic.ack_bytes + 1) % (pic_i86_mode(&pic) ? 2 : 3); + + if (pic.ack_bytes == 0) + pic_update_pending(); + } } return ret; diff --git a/src/pit.c b/src/pit.c index 03fd80c93..2e10a7b38 100644 --- a/src/pit.c +++ b/src/pit.c @@ -358,7 +358,7 @@ ctr_load(ctr_t *ctr) } -static void +static __inline void ctr_latch_status(ctr_t *ctr) { ctr->read_status = (ctr->ctrl & 0x3f) | (ctr->out ? 0x80 : 0) | (ctr->null_count ? 0x40 : 0); @@ -366,7 +366,7 @@ ctr_latch_status(ctr_t *ctr) } -static void +static __inline void ctr_latch_count(ctr_t *ctr) { int count = (ctr->latch || (ctr->state == 1)) ? ctr->l : ctr->count; @@ -459,8 +459,8 @@ pit_ctr_set_gate(ctr_t *ctr, int gate) } -void -pit_ctr_set_clock(ctr_t *ctr, int clock) +static __inline void +pit_ctr_set_clock_common(ctr_t *ctr, int clock) { int old = ctr->clock; @@ -491,6 +491,13 @@ pit_ctr_set_clock(ctr_t *ctr, int clock) } +void +pit_ctr_set_clock(ctr_t *ctr, int clock) +{ + pit_ctr_set_clock_common(ctr, clock); +} + + void pit_ctr_set_using_timer(ctr_t *ctr, int using_timer) { @@ -509,7 +516,7 @@ pit_timer_over(void *p) dev->clock ^= 1; for (i = 0; i < 3; i++) - pit_ctr_set_clock(&dev->counters[i], dev->clock); + pit_ctr_set_clock_common(&dev->counters[i], dev->clock); timer_advance_u64(&dev->callback_timer, PITCONST >> 1ULL); } diff --git a/src/scsi/scsi_pcscsi.c b/src/scsi/scsi_pcscsi.c index 22c78818b..d55528c6e 100644 --- a/src/scsi/scsi_pcscsi.c +++ b/src/scsi/scsi_pcscsi.c @@ -595,7 +595,6 @@ handle_satn_stop(void *priv) dev->rregs[ESP_RSEQ] = SEQ_CD; esp_log("ESP SCSI Command len = %d, raising IRQ\n", dev->cmdlen); esp_raise_irq(dev); - timer_on_auto(&dev->timer, 10.0); } } @@ -729,6 +728,7 @@ esp_reg_write(esp_t *dev, uint32_t saddr, uint32_t val) dev->rregs[ESP_RINTR] = INTR_FC; dev->rregs[ESP_RSEQ] = 0; dev->rregs[ESP_RFLAGS] = 0; + timer_on_auto(&dev->timer, 10.0); break; case CMD_RESET: esp_pci_soft_reset(dev); diff --git a/src/sound/snd_ad1848.c b/src/sound/snd_ad1848.c index 6f2656ff8..be11e09ca 100644 --- a/src/sound/snd_ad1848.c +++ b/src/sound/snd_ad1848.c @@ -16,7 +16,7 @@ #define CS4231 0x80 static int ad1848_vols_6bits[64]; -static uint32_t ad1848_vols_5bits_aux_gain[32]; +static double ad1848_vols_5bits_aux_gain[32]; void ad1848_setirq(ad1848_t *ad1848, int irq) @@ -224,11 +224,11 @@ static void ad1848_poll(void *p) static void ad1848_filter_cd_audio(int channel, double *buffer, void *p) { ad1848_t *ad1848 = (ad1848_t *)p; - int32_t c; - uint32_t volume = channel ? ad1848->cd_vol_r : ad1848->cd_vol_l; + double c; + double volume = channel ? ad1848->cd_vol_r : ad1848->cd_vol_l; - c = (((int32_t) *buffer) * volume) >> 16; - *buffer = (double) c; + c = ((*buffer) * volume) / 65536.0; + *buffer = c; } void ad1848_init(ad1848_t *ad1848, int type) @@ -292,12 +292,13 @@ void ad1848_init(ad1848_t *ad1848, int type) attenuation = pow(10, attenuation / 10); - ad1848_vols_5bits_aux_gain[c] = (int)(attenuation * 65536); + ad1848_vols_5bits_aux_gain[c] = (attenuation * 65536); } ad1848->type = type; timer_add(&ad1848->timer_count, ad1848_poll, ad1848, 0); - sound_set_cd_audio_filter(ad1848_filter_cd_audio, ad1848); + if (ad1848->type != AD1848_TYPE_DEFAULT) + sound_set_cd_audio_filter(ad1848_filter_cd_audio, ad1848); } diff --git a/src/sound/snd_opl.c b/src/sound/snd_opl.c index 0547c19c2..f8326b053 100644 --- a/src/sound/snd_opl.c +++ b/src/sound/snd_opl.c @@ -228,7 +228,7 @@ opl2_read(uint16_t port, void *priv) opl_t *dev = (opl_t *)priv; if (dev->flags & FLAG_CYCLES) - sub_cycles((int) (isa_timing * 8)); + cycles -= ((int) (isa_timing * 8)); opl2_update(dev); @@ -277,7 +277,7 @@ opl3_read(uint16_t port, void *priv) opl_t *dev = (opl_t *)priv; if (dev->flags & FLAG_CYCLES) - sub_cycles((int)(isa_timing * 8)); + cycles -= ((int)(isa_timing * 8)); opl3_update(dev); diff --git a/src/sound/snd_sb.c b/src/sound/snd_sb.c index 1565bb8f2..353d25987 100644 --- a/src/sound/snd_sb.c +++ b/src/sound/snd_sb.c @@ -1207,7 +1207,7 @@ sb_pro_v1_opl_read(uint16_t port, void *priv) { sb_t *sb = (sb_t *)priv; - sub_cycles((int)(isa_timing * 8)); + cycles -= ((int) (isa_timing * 8)); (void)opl2_read(port, &sb->opl2); // read, but ignore return(opl2_read(port, &sb->opl)); diff --git a/src/usb.c b/src/usb.c index fec6797ae..9692068b2 100644 --- a/src/usb.c +++ b/src/usb.c @@ -102,8 +102,10 @@ uhci_reg_writew(uint16_t addr, uint16_t val, void *p) switch (addr) { case 0x00: - if ((regs[0x00] & 0x0001) && !(val & 0x0001)) - regs[0x02] |= 0x20; + if ((val & 0x0001) && !(regs[0x00] & 0x0001)) + regs[0x01] &= ~0x20; + else if (!(val & 0x0001)) + regs[0x01] |= 0x20; regs[0x00] = (val & 0x00ff); break; case 0x06: diff --git a/src/video/vid_ati28800.c b/src/video/vid_ati28800.c index b1a1c8222..5dcca426a 100644 --- a/src/video/vid_ati28800.c +++ b/src/video/vid_ati28800.c @@ -401,7 +401,7 @@ ati28800_recalctimings(svga_t *svga) case 0x09: svga->clock = (cpuclock * (double)(1ull << 32)) / 32000000.0; break; case 0x0A: svga->clock = (cpuclock * (double)(1ull << 32)) / 37500000.0; break; case 0x0B: svga->clock = (cpuclock * (double)(1ull << 32)) / 39000000.0; break; - case 0x0C: svga->clock = (cpuclock * (double)(1ull << 32)) / 40000000.0; break; + case 0x0C: svga->clock = (cpuclock * (double)(1ull << 32)) / 50350000.0; break; case 0x0D: svga->clock = (cpuclock * (double)(1ull << 32)) / 56644000.0; break; case 0x0E: svga->clock = (cpuclock * (double)(1ull << 32)) / 75000000.0; break; case 0x0F: svga->clock = (cpuclock * (double)(1ull << 32)) / 65000000.0; break; @@ -423,14 +423,6 @@ ati28800_recalctimings(svga_t *svga) svga->rowoffset <<= 1; } - if (svga->crtc[0x17] & 4) { - svga->vtotal <<= 1; - svga->dispend <<= 1; - svga->vsyncstart <<= 1; - svga->split <<= 1; - svga->vblankstart <<= 1; - } - if (!svga->scrblank && (ati28800->regs[0xb0] & 0x20)) { /* Extended 256 colour modes */ switch (svga->bpp) { case 8: diff --git a/src/video/vid_cga.c b/src/video/vid_cga.c index e7a5ffd61..60a662490 100644 --- a/src/video/vid_cga.c +++ b/src/video/vid_cga.c @@ -123,7 +123,7 @@ cga_waitstates(void *p) int ws; ws = ws_array[cycles & 0xf]; - sub_cycles(ws); + cycles -= ws; } diff --git a/src/video/vid_cl54xx.c b/src/video/vid_cl54xx.c index bb808e1dc..96a4e13a1 100644 --- a/src/video/vid_cl54xx.c +++ b/src/video/vid_cl54xx.c @@ -11,11 +11,13 @@ * * * - * Authors: TheCollector1995, - * Miran Grca, + * Authors: Miran Grca, + * tonioni, + * TheCollector1995, * - * Copyright 2016-2020 TheCollector1995. * Copyright 2016-2020 Miran Grca. + * Copyright 2020 tonioni. + * Copyright 2016-2020 TheCollector1995. */ #include #include @@ -42,11 +44,7 @@ #define BIOS_GD5402_PATH L"roms/video/cirruslogic/avga2.rom" #define BIOS_GD5402_ONBOARD_PATH L"roms/machines/cbm_sl386sx25/Commodore386SX-25_AVGA2.bin" #define BIOS_GD5420_PATH L"roms/video/cirruslogic/5420.vbi" - -#if defined(DEV_BRANCH) && defined(USE_CL5422) #define BIOS_GD5422_PATH L"roms/video/cirruslogic/cl5422.bin" -#endif - #define BIOS_GD5426_PATH L"roms/video/cirruslogic/Diamond SpeedStar PRO VLB v3.04.bin" #define BIOS_GD5428_ISA_PATH L"roms/video/cirruslogic/5428.bin" #define BIOS_GD5428_PATH L"roms/video/cirruslogic/vlbusjapan.BIN" @@ -93,7 +91,7 @@ #define CIRRUS_CURSOR_HIDDENPEL 0x02 #define CIRRUS_CURSOR_LARGE 0x04 /* 64x64 if set, 32x32 if clear */ -// sequencer 0x17 +/* sequencer 0x17 */ #define CIRRUS_BUSTYPE_VLBFAST 0x10 #define CIRRUS_BUSTYPE_PCI 0x20 #define CIRRUS_BUSTYPE_VLBSLOW 0x30 @@ -102,7 +100,7 @@ #define CIRRUS_MMIO_USE_PCIADDR 0x40 /* 0xb8000 if cleared. */ #define CIRRUS_MEMSIZEEXT_DOUBLE 0x80 -// control 0x0b +/* control 0x0b */ #define CIRRUS_BANKING_DUAL 0x01 #define CIRRUS_BANKING_GRANULARITY_16K 0x20 /* set:16k, clear:4k */ @@ -119,7 +117,7 @@ #define CIRRUS_BLTMODE_PIXELWIDTH24 0x20 #define CIRRUS_BLTMODE_PIXELWIDTH32 0x30 -// control 0x31 +/* control 0x31 */ #define CIRRUS_BLT_BUSY 0x01 #define CIRRUS_BLT_START 0x02 #define CIRRUS_BLT_RESET 0x04 @@ -128,7 +126,7 @@ #define CIRRUS_BLT_APERTURE2 0x40 #define CIRRUS_BLT_AUTOSTART 0x80 -// control 0x33 +/* control 0x33 */ #define CIRRUS_BLTMODEEXT_BACKGROUNDONLY 0x08 #define CIRRUS_BLTMODEEXT_SOLIDFILL 0x04 #define CIRRUS_BLTMODEEXT_COLOREXPINV 0x02 @@ -150,6 +148,7 @@ typedef struct gd54xx_t mem_mapping_t mmio_mapping; mem_mapping_t linear_mapping; mem_mapping_t aperture2_mapping; + mem_mapping_t vgablt_mapping; svga_t svga; @@ -191,11 +190,24 @@ typedef struct gd54xx_t int unlock_special; } blt; - int pci, vlb, mca; - int countminusone; + struct { + int mode; + uint16_t stride, r1sz, r1adjust, r2sz, + r2adjust, r2sdz, wvs, wve, + hzoom, vzoom; + uint8_t occlusion, colorkeycomparemask, + colorkeycompare; + int region1size, region2size, + colorkeymode; + uint32_t ck; + } overlay; + + int pci, vlb, mca, countminusone; + int vblank_irq, vportsync; uint8_t pci_regs[256]; - uint8_t int_line, unlocked; + uint8_t int_line, unlocked, status, extensions; + uint8_t crtcreg_mask; uint8_t fc; /* Feature Connector */ @@ -204,7 +216,7 @@ typedef struct gd54xx_t uint8_t pos_regs[8]; svga_t *mb_vga; - uint32_t lfb_base; + uint32_t lfb_base, vgablt_base; int mmio_vram_overlap; @@ -247,6 +259,266 @@ static void gd54xx_start_blit(uint32_t cpu_dat, uint32_t count, gd54xx_t *gd54xx, svga_t *svga); +#define CLAMP(x) do \ + { \ + if ((x) & ~0xff) \ + x = ((x) < 0) ? 0 : 0xff; \ + } \ + while (0) + +#define DECODE_YCbCr() \ + do \ + { \ + int c; \ + \ + for (c = 0; c < 2; c++) \ + { \ + uint8_t y1, y2; \ + int8_t Cr, Cb; \ + int dR, dG, dB; \ + \ + y1 = src[0]; \ + Cr = src[1] - 0x80; \ + y2 = src[2]; \ + Cb = src[3] - 0x80; \ + src += 4; \ + \ + dR = (359*Cr) >> 8; \ + dG = (88*Cb + 183*Cr) >> 8; \ + dB = (453*Cb) >> 8; \ + \ + r[x_write] = y1 + dR; \ + CLAMP(r[x_write]); \ + g[x_write] = y1 - dG; \ + CLAMP(g[x_write]); \ + b[x_write] = y1 + dB; \ + CLAMP(b[x_write]); \ + \ + r[x_write+1] = y2 + dR; \ + CLAMP(r[x_write+1]); \ + g[x_write+1] = y2 - dG; \ + CLAMP(g[x_write+1]); \ + b[x_write+1] = y2 + dB; \ + CLAMP(b[x_write+1]); \ + \ + x_write = (x_write + 2) & 7; \ + } \ + } while (0) + +/*Both YUV formats are untested*/ +#define DECODE_YUV211() \ + do \ + { \ + uint8_t y1, y2, y3, y4; \ + int8_t U, V; \ + int dR, dG, dB; \ + \ + U = src[0] - 0x80; \ + y1 = (298 * (src[1] - 16)) >> 8; \ + y2 = (298 * (src[2] - 16)) >> 8; \ + V = src[3] - 0x80; \ + y3 = (298 * (src[4] - 16)) >> 8; \ + y4 = (298 * (src[5] - 16)) >> 8; \ + src += 6; \ + \ + dR = (309*V) >> 8; \ + dG = (100*U + 208*V) >> 8; \ + dB = (516*U) >> 8; \ + \ + r[x_write] = y1 + dR; \ + CLAMP(r[x_write]); \ + g[x_write] = y1 - dG; \ + CLAMP(g[x_write]); \ + b[x_write] = y1 + dB; \ + CLAMP(b[x_write]); \ + \ + r[x_write+1] = y2 + dR; \ + CLAMP(r[x_write+1]); \ + g[x_write+1] = y2 - dG; \ + CLAMP(g[x_write+1]); \ + b[x_write+1] = y2 + dB; \ + CLAMP(b[x_write+1]); \ + \ + r[x_write+2] = y3 + dR; \ + CLAMP(r[x_write+2]); \ + g[x_write+2] = y3 - dG; \ + CLAMP(g[x_write+2]); \ + b[x_write+2] = y3 + dB; \ + CLAMP(b[x_write+2]); \ + \ + r[x_write+3] = y4 + dR; \ + CLAMP(r[x_write+3]); \ + g[x_write+3] = y4 - dG; \ + CLAMP(g[x_write+3]); \ + b[x_write+3] = y4 + dB; \ + CLAMP(b[x_write+3]); \ + \ + x_write = (x_write + 4) & 7; \ + } while (0) + +#define DECODE_YUV422() \ + do \ + { \ + int c; \ + \ + for (c = 0; c < 2; c++) \ + { \ + uint8_t y1, y2; \ + int8_t U, V; \ + int dR, dG, dB; \ + \ + U = src[0] - 0x80; \ + y1 = (298 * (src[1] - 16)) >> 8; \ + V = src[2] - 0x80; \ + y2 = (298 * (src[3] - 16)) >> 8; \ + src += 4; \ + \ + dR = (309*V) >> 8; \ + dG = (100*U + 208*V) >> 8; \ + dB = (516*U) >> 8; \ + \ + r[x_write] = y1 + dR; \ + CLAMP(r[x_write]); \ + g[x_write] = y1 - dG; \ + CLAMP(g[x_write]); \ + b[x_write] = y1 + dB; \ + CLAMP(b[x_write]); \ + \ + r[x_write+1] = y2 + dR; \ + CLAMP(r[x_write+1]); \ + g[x_write+1] = y2 - dG; \ + CLAMP(g[x_write+1]); \ + b[x_write+1] = y2 + dB; \ + CLAMP(b[x_write+1]); \ + \ + x_write = (x_write + 2) & 7; \ + } \ + } while (0) + +#define DECODE_RGB555() \ + do \ + { \ + int c; \ + \ + for (c = 0; c < 4; c++) \ + { \ + uint16_t dat; \ + \ + dat = *(uint16_t *)src; \ + src += 2; \ + \ + r[x_write + c] = ((dat & 0x001f) << 3) | ((dat & 0x001f) >> 2); \ + g[x_write + c] = ((dat & 0x03e0) >> 2) | ((dat & 0x03e0) >> 7); \ + b[x_write + c] = ((dat & 0x7c00) >> 7) | ((dat & 0x7c00) >> 12); \ + } \ + x_write = (x_write + 4) & 7; \ + } while (0) + +#define DECODE_RGB565() \ + do \ + { \ + int c; \ + \ + for (c = 0; c < 4; c++) \ + { \ + uint16_t dat; \ + \ + dat = *(uint16_t *)src; \ + src += 2; \ + \ + r[x_write + c] = ((dat & 0x001f) << 3) | ((dat & 0x001f) >> 2); \ + g[x_write + c] = ((dat & 0x07e0) >> 3) | ((dat & 0x07e0) >> 9); \ + b[x_write + c] = ((dat & 0xf800) >> 8) | ((dat & 0xf800) >> 13); \ + } \ + x_write = (x_write + 4) & 7; \ + } while (0) + +#define DECODE_CLUT() \ + do \ + { \ + int c; \ + \ + for (c = 0; c < 4; c++) \ + { \ + uint8_t dat; \ + \ + dat = *(uint8_t *)src; \ + src++; \ + \ + r[x_write + c] = svga->pallook[dat] >> 0; \ + g[x_write + c] = svga->pallook[dat] >> 8; \ + b[x_write + c] = svga->pallook[dat] >> 16; \ + } \ + x_write = (x_write + 4) & 7; \ + } while (0) + + + +#define OVERLAY_SAMPLE() \ + do \ + { \ + switch (gd54xx->overlay.mode) \ + { \ + case 0: \ + DECODE_YUV422(); \ + break; \ + case 2: \ + DECODE_CLUT(); \ + break; \ + case 3: \ + DECODE_YUV211(); \ + break; \ + case 4: \ + DECODE_RGB555(); \ + break; \ + case 5: \ + DECODE_RGB565(); \ + break; \ + } \ + } while (0) + + +static int +gd54xx_interrupt_enabled(gd54xx_t *gd54xx) +{ + return !gd54xx->pci || (gd54xx->svga.gdcreg[0x17] & 0x04); +} + + +static int +gd54xx_vga_vsync_enabled(gd54xx_t *gd54xx) +{ + if (!(gd54xx->svga.crtc[0x11] & 0x20) && (gd54xx->svga.crtc[0x11] & 0x10) && + gd54xx_interrupt_enabled(gd54xx)) + return 1; + return 0; +} + + +static void +gd54xx_update_irqs(gd54xx_t *gd54xx) +{ + if (!gd54xx->pci) + return; + + if ((gd54xx->vblank_irq > 0) && gd54xx_vga_vsync_enabled(gd54xx)) + pci_set_irq(gd54xx->card, PCI_INTA); + else + pci_clear_irq(gd54xx->card, PCI_INTA); +} + + +static void +gd54xx_vblank_start(svga_t *svga) +{ + gd54xx_t *gd54xx = (gd54xx_t*) svga->p; + if (gd54xx->vblank_irq >= 0) { + gd54xx->vblank_irq = 1; + gd54xx_update_irqs(gd54xx); + } +} + + /* Returns 1 if the card is a 5422+ */ static int gd54xx_is_5422(svga_t *svga) @@ -258,6 +530,92 @@ gd54xx_is_5422(svga_t *svga) } +static void +gd54xx_overlay_draw(svga_t *svga, int displine) +{ + gd54xx_t *gd54xx = (gd54xx_t *) svga->p; + int shift = (svga->crtc[0x27] >= CIRRUS_ID_CLGD5446) ? 2 : 0; + int h_acc = svga->overlay_latch.h_acc; + int r[8], g[8], b[8]; + int x_read = 4, x_write = 4; + int x; + uint32_t *p; + uint8_t *src = &svga->vram[(svga->overlay_latch.addr << shift) & svga->vram_mask]; + int bpp = svga->bpp; + int bytesperpix = (bpp + 7) / 8; + uint8_t *src2 = &svga->vram[(svga->ma - (svga->hdisp * bytesperpix)) & svga->vram_display_mask]; + int w = gd54xx->overlay.r2sdz; + int occl, ckval; + + if (gd54xx->overlay.mode == 2) + w *= 4; + else + w *= 2; + + p = &((uint32_t *)buffer32->line[displine])[gd54xx->overlay.region1size + svga->x_add]; + src2 += gd54xx->overlay.region1size * bytesperpix; + + OVERLAY_SAMPLE(); + + for (x = 0; (x < gd54xx->overlay.region2size) && + ((x + gd54xx->overlay.region1size) < svga->hdisp); x++) { + if (gd54xx->overlay.occlusion) { + occl = 1; + ckval = gd54xx->overlay.ck; + if (bytesperpix == 1) { + if (*src2 == ckval) + occl = 0; + } else if (bytesperpix == 2) { + if (*((uint16_t*)src2) == ckval) + occl = 0; + } else + occl = 0; + if (!occl) + *p++ = r[x_read] | (g[x_read] << 8) | (b[x_read] << 16); + src2 += bytesperpix; + } else + *p++ = r[x_read] | (g[x_read] << 8) | (b[x_read] << 16); + + h_acc += gd54xx->overlay.hzoom; + if (h_acc >= 256) { + if ((x_read ^ (x_read + 1)) & ~3) + OVERLAY_SAMPLE(); + x_read = (x_read + 1) & 7; + + h_acc -= 256; + } + } + + svga->overlay_latch.v_acc += gd54xx->overlay.vzoom; + if (svga->overlay_latch.v_acc >= 256) { + svga->overlay_latch.v_acc -= 256; + svga->overlay_latch.addr += svga->overlay.pitch << 1; + } +} + + +static void +gd54xx_update_overlay(gd54xx_t *gd54xx) +{ + svga_t *svga = &gd54xx->svga; + int bpp = svga->bpp; + + svga->overlay.ysize = gd54xx->overlay.wve - gd54xx->overlay.wvs + 1; + gd54xx->overlay.region1size = 32 * gd54xx->overlay.r1sz / bpp + (gd54xx->overlay.r1adjust * 8 / bpp); + gd54xx->overlay.region2size = 32 * gd54xx->overlay.r2sz / bpp + (gd54xx->overlay.r2adjust * 8 / bpp); + + gd54xx->overlay.occlusion = (svga->crtc[0x3e] & 0x80) != 0 && svga->bpp <= 16; + + /* Mask and chroma key ignored. */ + if (gd54xx->overlay.colorkeymode == 0) + gd54xx->overlay.ck = gd54xx->overlay.colorkeycompare; + else if (gd54xx->overlay.colorkeymode == 1) + gd54xx->overlay.ck = gd54xx->overlay.colorkeycompare | (gd54xx->overlay.colorkeycomparemask << 8); + else + gd54xx->overlay.occlusion = 0; +} + + /* Returns 1 if the card supports the 8-bpp/16-bpp transparency color or mask. */ static int gd54xx_has_transp(svga_t *svga, int mask) @@ -535,11 +893,22 @@ gd54xx_out(uint16_t addr, uint8_t val, void *p) svga->adv_flags = FLAG_EXTRA_BANKS; if (svga->gdcreg[0xb] & 0x02) svga->adv_flags |= FLAG_ADDR_BY8; + if (svga->gdcreg[0xb] & 0x04) + svga->adv_flags |= FLAG_EXT_WRITE; if (svga->gdcreg[0xb] & 0x08) svga->adv_flags |= FLAG_LATCH8; gd54xx_recalc_banking(gd54xx); break; + case 0x0c: + gd54xx->overlay.colorkeycompare = val; + gd54xx_update_overlay(gd54xx); + break; + case 0x0d: + gd54xx->overlay.colorkeycomparemask = val; + gd54xx_update_overlay(gd54xx); + break; + case 0x10: gd543x_mmio_write(0xb8001, val, gd54xx); break; @@ -642,7 +1011,7 @@ gd54xx_out(uint16_t addr, uint8_t val, void *p) } return; case 0x3d4: - svga->crtcreg = val & 0x3f; + svga->crtcreg = val & gd54xx->crtcreg_mask; return; case 0x3d5: if (((svga->crtcreg == 0x19) || (svga->crtcreg == 0x1a) || @@ -657,7 +1026,104 @@ gd54xx_out(uint16_t addr, uint8_t val, void *p) old = svga->crtc[svga->crtcreg]; svga->crtc[svga->crtcreg] = val; + if (svga->crtcreg == 0x11) { + if (!(val & 0x10)) { + if (gd54xx->vblank_irq > 0) + gd54xx->vblank_irq = -1; + } else if (gd54xx->vblank_irq < 0) + gd54xx->vblank_irq = 0; + gd54xx_update_irqs(gd54xx); + if ((val & ~0x30) == (old & ~0x30)) + old = val; + } + if (old != val) { + /* Overlay registers */ + switch (svga->crtcreg) { + case 0x1d: + if (((old >> 3) & 7) != ((val >> 3) & 7)) { + gd54xx->overlay.colorkeymode = (val >> 3) & 7; + gd54xx_update_overlay(gd54xx); + } + break; + case 0x31: + gd54xx->overlay.hzoom = val == 0 ? 256 : val; + gd54xx_update_overlay(gd54xx); + break; + case 0x32: + gd54xx->overlay.vzoom = val == 0 ? 256 : val; + gd54xx_update_overlay(gd54xx); + break; + case 0x33: + gd54xx->overlay.r1sz &= ~0xff; + gd54xx->overlay.r1sz |= val; + gd54xx_update_overlay(gd54xx); + break; + case 0x34: + gd54xx->overlay.r2sz &= ~0xff; + gd54xx->overlay.r2sz |= val; + gd54xx_update_overlay(gd54xx); + break; + case 0x35: + gd54xx->overlay.r2sdz &= ~0xff; + gd54xx->overlay.r2sdz |= val; + gd54xx_update_overlay(gd54xx); + break; + case 0x36: + gd54xx->overlay.r1sz &= 0xff; + gd54xx->overlay.r1sz |= (val << 8) & 0x300; + gd54xx->overlay.r2sz &= 0xff; + gd54xx->overlay.r2sz |= (val << 6) & 0x300; + gd54xx->overlay.r2sdz &= 0xff; + gd54xx->overlay.r2sdz |= (val << 4) & 0x300; + gd54xx_update_overlay(gd54xx); + break; + case 0x37: + gd54xx->overlay.wvs &= ~0xff; + gd54xx->overlay.wvs |= val; + svga->overlay.y = gd54xx->overlay.wvs; + break; + case 0x38: + gd54xx->overlay.wve &= ~0xff; + gd54xx->overlay.wve |= val; + gd54xx_update_overlay(gd54xx); + break; + case 0x39: + gd54xx->overlay.wvs &= 0xff; + gd54xx->overlay.wvs |= (val << 8) & 0x300; + gd54xx->overlay.wve &= 0xff; + gd54xx->overlay.wve |= (val << 6) & 0x300; + gd54xx_update_overlay(gd54xx); + break; + case 0x3a: + svga->overlay.addr &= ~0xff; + svga->overlay.addr |= val; + gd54xx_update_overlay(gd54xx); + break; + case 0x3b: + svga->overlay.addr &= ~0xff00; + svga->overlay.addr |= val << 8; + gd54xx_update_overlay(gd54xx); + break; + case 0x3c: + svga->overlay.addr &= ~0x0f0000; + svga->overlay.addr |= (val << 16) & 0x0f0000; + svga->overlay.pitch &= ~0x100; + svga->overlay.pitch |= (val & 0x20) << 3; + gd54xx_update_overlay(gd54xx); + break; + case 0x3d: + svga->overlay.pitch &= ~0xff; + svga->overlay.pitch |= val; + gd54xx_update_overlay(gd54xx); + break; + case 0x3e: + gd54xx->overlay.mode = (val >> 1) & 7; + svga->overlay.ena = (val & 1) != 0; + gd54xx_update_overlay(gd54xx); + break; + } + if (svga->crtcreg < 0xe || svga->crtcreg > 0x10) { svga->fullchange = changeframecount; svga_recalctimings(svga); @@ -681,6 +1147,11 @@ gd54xx_in(uint16_t addr, void *p) addr ^= 0x60; switch (addr) { + case 0x3c2: + ret = svga_in(addr, svga); + ret |= gd54xx->vblank_irq > 0 ? 0x80 : 0x00; + break; + case 0x3c4: if (svga->seqregs[6] == 0x12) { ret = svga->seqaddr; @@ -913,6 +1384,12 @@ gd54xx_in(uint16_t addr, void *p) case 0x39: ret = gd543x_mmio_read(0xb8021, gd54xx); break; + + case 0x3f: + if (svga->crtc[0x27] == CIRRUS_ID_CLGD5446) + gd54xx->vportsync = !gd54xx->vportsync; + ret = gd54xx->vportsync ? 0x80 : 0x00; + break; } } else { if ((svga->gdcaddr < 2) && !gd54xx->unlocked) @@ -1058,7 +1535,9 @@ gd543x_recalc_mapping(gd54xx_t *gd54xx) } } else if (gd54xx->pci) { base = gd54xx->lfb_base; - if (svga->crtc[0x27] >= CIRRUS_ID_CLGD5436) + /* if (svga->crtc[0x27] == CIRRUS_ID_CLGD5480) + size = 32 * 1024 * 1024; + else */ if (svga->crtc[0x27] >= CIRRUS_ID_CLGD5436) size = 16 * 1024 * 1024; else size = 4 * 1024 * 1024; @@ -1082,9 +1561,12 @@ gd543x_recalc_mapping(gd54xx_t *gd54xx) if ((svga->crtc[0x27] >= CIRRUS_ID_CLGD5436) && (gd54xx->blt.status & CIRRUS_BLT_APERTURE2) && ((gd54xx->blt.mode & (CIRRUS_BLTMODE_COLOREXPAND | CIRRUS_BLTMODE_MEMSYSSRC)) == - (CIRRUS_BLTMODE_COLOREXPAND | CIRRUS_BLTMODE_MEMSYSSRC))) - mem_mapping_set_addr(&gd54xx->aperture2_mapping, 0xbc000, 0x04000); - else + (CIRRUS_BLTMODE_COLOREXPAND | CIRRUS_BLTMODE_MEMSYSSRC))) { + if (svga->crtc[0x27] == CIRRUS_ID_CLGD5480) + mem_mapping_set_addr(&gd54xx->aperture2_mapping, gd54xx->lfb_base + (16777216), 16777216); + else + mem_mapping_set_addr(&gd54xx->aperture2_mapping, 0xbc000, 0x04000); + } else mem_mapping_disable(&gd54xx->aperture2_mapping); } } @@ -1095,15 +1577,19 @@ gd54xx_recalctimings(svga_t *svga) { gd54xx_t *gd54xx = (gd54xx_t *)svga->p; uint8_t clocksel, rdmask; + uint8_t linedbl = svga->dispend * 9 / 10 >= svga->hdisp; svga->rowoffset = (svga->crtc[0x13]) | ((svga->crtc[0x1b] & 0x10) << 4); svga->interlace = (svga->crtc[0x1a] & 0x01); svga->map8 = svga->pallook; - if (svga->seqregs[7] & CIRRUS_SR7_BPP_SVGA) - svga->render = svga_render_8bpp_highres; - else if (svga->gdcreg[5] & 0x40) + if (svga->seqregs[7] & CIRRUS_SR7_BPP_SVGA) { + if (linedbl) + svga->render = svga_render_8bpp_lowres; + else + svga->render = svga_render_8bpp_highres; + } else if (svga->gdcreg[5] & 0x40) svga->render = svga_render_8bpp_lowres; svga->ma_latch |= ((svga->crtc[0x1b] & 0x01) << 16) | ((svga->crtc[0x1b] & 0xc) << 15); @@ -1120,39 +1606,61 @@ gd54xx_recalctimings(svga_t *svga) switch (gd54xx->ramdac.ctrl & rdmask) { case 0: svga->bpp = 15; - if (gd54xx->ramdac.ctrl & 0x10) - svga->render = svga_render_15bpp_mix_highres; - else - svga->render = svga_render_15bpp_highres; + if (linedbl) { + if (gd54xx->ramdac.ctrl & 0x10) + svga->render = svga_render_15bpp_mix_lowres; + else + svga->render = svga_render_15bpp_lowres; + } else { + if (gd54xx->ramdac.ctrl & 0x10) + svga->render = svga_render_15bpp_mix_highres; + else + svga->render = svga_render_15bpp_highres; + } break; case 1: svga->bpp = 16; - svga->render = svga_render_16bpp_highres; + if (linedbl) + svga->render = svga_render_16bpp_lowres; + else + svga->render = svga_render_16bpp_highres; break; case 5: if (gd54xx_is_5434(svga) && (svga->seqregs[7] & CIRRUS_SR7_BPP_32)) { svga->bpp = 32; - svga->render = svga_render_32bpp_highres; + if (linedbl) + svga->render = svga_render_32bpp_lowres; + else + svga->render = svga_render_32bpp_highres; if (svga->crtc[0x27] < CIRRUS_ID_CLGD5436) svga->rowoffset *= 2; } else { svga->bpp = 24; - svga->render = svga_render_24bpp_highres; + if (linedbl) + svga->render = svga_render_24bpp_lowres; + else + svga->render = svga_render_24bpp_highres; } break; case 8: svga->bpp = 8; svga->map8 = video_8togs; - svga->render = svga_render_8bpp_highres; + if (linedbl) + svga->render = svga_render_8bpp_lowres; + else + svga->render = svga_render_8bpp_highres; break; case 9: svga->bpp = 8; svga->map8 = video_8to32; - svga->render = svga_render_8bpp_highres; + if (linedbl) + svga->render = svga_render_8bpp_lowres; + else + svga->render = svga_render_8bpp_highres; break; case 0xf: @@ -1160,41 +1668,63 @@ gd54xx_recalctimings(svga_t *svga) case CIRRUS_SR7_BPP_32: if (svga->crtc[0x27] >= CIRRUS_ID_CLGD5430) { svga->bpp = 32; - svga->render = svga_render_32bpp_highres; + if (linedbl) + svga->render = svga_render_32bpp_lowres; + else + svga->render = svga_render_32bpp_highres; svga->rowoffset *= 2; } break; case CIRRUS_SR7_BPP_24: svga->bpp = 24; - svga->render = svga_render_24bpp_highres; + if (linedbl) + svga->render = svga_render_24bpp_lowres; + else + svga->render = svga_render_24bpp_highres; break; case CIRRUS_SR7_BPP_16: if ((svga->crtc[0x27] >= CIRRUS_ID_CLGD5428) || (svga->crtc[0x27] == CIRRUS_ID_CLGD5426)) { svga->bpp = 16; - svga->render = svga_render_16bpp_highres; + if (linedbl) + svga->render = svga_render_16bpp_lowres; + else + svga->render = svga_render_16bpp_highres; } break; case CIRRUS_SR7_BPP_16_DOUBLEVCLK: svga->bpp = 16; - svga->render = svga_render_16bpp_highres; + if (linedbl) + svga->render = svga_render_16bpp_lowres; + else + svga->render = svga_render_16bpp_highres; break; case CIRRUS_SR7_BPP_8: svga->bpp = 8; - svga->render = svga_render_8bpp_highres; + if (linedbl) + svga->render = svga_render_8bpp_lowres; + else + svga->render = svga_render_8bpp_highres; break; } break; } } else { svga->bpp = 15; - if (gd54xx->ramdac.ctrl & 0x10) - svga->render = svga_render_15bpp_mix_highres; - else - svga->render = svga_render_15bpp_highres; + if (linedbl) { + if (gd54xx->ramdac.ctrl & 0x10) + svga->render = svga_render_15bpp_mix_lowres; + else + svga->render = svga_render_15bpp_lowres; + } else { + if (gd54xx->ramdac.ctrl & 0x10) + svga->render = svga_render_15bpp_mix_highres; + else + svga->render = svga_render_15bpp_highres; + } } } @@ -1235,16 +1765,19 @@ void gd54xx_hwcursor_draw(svga_t *svga, int displine) int pitch = (svga->hwcursor.xsize == 64) ? 16 : 4; uint32_t bgcol = gd54xx->extpallook[0x00]; uint32_t fgcol = gd54xx->extpallook[0x0f]; + uint8_t linedbl = svga->dispend * 9 / 10 >= svga->hdisp; + + offset <<= linedbl; if (svga->interlace && svga->hwcursor_oddeven) svga->hwcursor_latch.addr += pitch; for (x = 0; x < svga->hwcursor.xsize; x += 8) { - dat[0] = svga->vram[svga->hwcursor_latch.addr]; + dat[0] = svga->vram[svga->hwcursor_latch.addr & svga->vram_display_mask]; if (svga->hwcursor.xsize == 64) - dat[1] = svga->vram[svga->hwcursor_latch.addr + 0x08]; + dat[1] = svga->vram[(svga->hwcursor_latch.addr + 0x08) & svga->vram_display_mask]; else - dat[1] = svga->vram[svga->hwcursor_latch.addr + 0x80]; + dat[1] = svga->vram[(svga->hwcursor_latch.addr + 0x80) & svga->vram_display_mask]; for (xx = 0; xx < 8; xx++) { b0 = (dat[0] >> (7 - xx)) & 1; b1 = (dat[1] >> (7 - xx)) & 1; @@ -1447,6 +1980,7 @@ gd54xx_write_modes45(svga_t *svga, uint8_t val, uint32_t addr) case 4: if (svga->gdcreg[0xb] & 0x10) { addr <<= 2; + addr &= svga->decode_mask; for (i = 0; i < 8; i++) { if (val & svga->seqregs[2] & (0x80 >> i)) { @@ -1456,6 +1990,7 @@ gd54xx_write_modes45(svga_t *svga, uint8_t val, uint32_t addr) } } else { addr <<= 1; + addr &= svga->decode_mask; for (i = 0; i < 8; i++) { if (val & svga->seqregs[2] & (0x80 >> i)) @@ -1467,6 +2002,7 @@ gd54xx_write_modes45(svga_t *svga, uint8_t val, uint32_t addr) case 5: if (svga->gdcreg[0xb] & 0x10) { addr <<= 2; + addr &= svga->decode_mask; for (i = 0; i < 8; i++) { j = (0x80 >> i); @@ -1479,6 +2015,7 @@ gd54xx_write_modes45(svga_t *svga, uint8_t val, uint32_t addr) } } else { addr <<= 1; + addr &= svga->decode_mask; for (i = 0; i < 8; i++) { j = (0x80 >> i); @@ -1602,7 +2139,7 @@ gd54xx_readw_linear(uint32_t addr, void *p) temp |= (svga_readb_linear(addr, svga) << 8); if (svga->fast) - sub_cycles(video_timing_read_w); + cycles -= video_timing_read_w; return temp; case 3: @@ -1653,7 +2190,7 @@ gd54xx_readl_linear(uint32_t addr, void *p) temp |= (svga_readb_linear(addr + 2, svga) << 24); if (svga->fast) - sub_cycles(video_timing_read_l); + cycles -= video_timing_read_l; return temp; case 2: @@ -1663,7 +2200,7 @@ gd54xx_readl_linear(uint32_t addr, void *p) temp |= (svga_readb_linear(addr, svga) << 24); if (svga->fast) - sub_cycles(video_timing_read_l); + cycles -= video_timing_read_l; return temp; case 3: @@ -1852,7 +2389,7 @@ gd54xx_writew_linear(uint32_t addr, uint16_t val, void *p) svga_writeb_linear(addr, val >> 8, svga); if (svga->fast) - sub_cycles(video_timing_write_w); + cycles -= video_timing_write_w; case 3: return; } @@ -2449,6 +2986,98 @@ gd543x_mmio_readl(uint32_t addr, void *p) } +static void +gd5480_vgablt_write(uint32_t addr, uint8_t val, void *p) +{ + addr &= 0x00000fff; + + if ((addr >= 0x00000100) && (addr < 0x00000200)) + gd543x_mmio_writeb((addr & 0x000000ff) | 0x000b8000, val, p); + else if (addr < 0x00000100) + gd54xx_out(0x03c0 + addr, val, p); +} + + +static void +gd5480_vgablt_writew(uint32_t addr, uint16_t val, void *p) +{ + addr &= 0x00000fff; + + if ((addr >= 0x00000100) && (addr < 0x00000200)) + gd543x_mmio_writew((addr & 0x000000ff) | 0x000b8000, val, p); + else if (addr < 0x00000100) { + gd5480_vgablt_write(addr, val & 0xff, p); + gd5480_vgablt_write(addr + 1, val >> 8, p); + } +} + + +static void +gd5480_vgablt_writel(uint32_t addr, uint32_t val, void *p) +{ + addr &= 0x00000fff; + + if ((addr >= 0x00000100) && (addr < 0x00000200)) + gd543x_mmio_writel((addr & 0x000000ff) | 0x000b8000, val, p); + else if (addr < 0x00000100) { + gd5480_vgablt_writew(addr, val & 0xffff, p); + gd5480_vgablt_writew(addr + 2, val >> 16, p); + } +} + + +static uint8_t +gd5480_vgablt_read(uint32_t addr, void *p) +{ + uint8_t ret = 0xff; + + addr &= 0x00000fff; + + if ((addr >= 0x00000100) && (addr < 0x00000200)) + ret = gd543x_mmio_read((addr & 0x000000ff) | 0x000b8000, p); + else if (addr < 0x00000100) + ret = gd54xx_in(0x03c0 + addr, p); + + return ret; +} + + +static uint16_t +gd5480_vgablt_readw(uint32_t addr, void *p) +{ + uint16_t ret = 0xffff; + + addr &= 0x00000fff; + + if ((addr >= 0x00000100) && (addr < 0x00000200)) + ret = gd543x_mmio_readw((addr & 0x000000ff) | 0x000b8000, p); + else if (addr < 0x00000100) { + ret = gd5480_vgablt_read(addr, p); + ret |= (gd5480_vgablt_read(addr + 1, p) << 8); + } + + return ret; +} + + +static uint32_t +gd5480_vgablt_readl(uint32_t addr, void *p) +{ + uint32_t ret = 0xffffffff; + + addr &= 0x00000fff; + + if ((addr >= 0x00000100) && (addr < 0x00000200)) + ret = gd543x_mmio_readl((addr & 0x000000ff) | 0x000b8000, p); + else if (addr < 0x00000100) { + ret = gd5480_vgablt_readw(addr, p); + ret |= (gd5480_vgablt_readw(addr + 2, p) << 16); + } + + return ret; +} + + static uint8_t gd54xx_color_expand(gd54xx_t *gd54xx, int mask, int shift) { @@ -2945,6 +3574,21 @@ cl_pci_read(int func, int addr, void *p) break; case 0x13: ret = gd54xx->lfb_base >> 24; + if (svga->crtc[0x27] == CIRRUS_ID_CLGD5480) + ret = 0xfe; + break; + + case 0x14: + ret = 0x00; /*PCI VGA/BitBLT Register Base Address*/ + break; + case 0x15: + ret = (svga->crtc[0x27] == CIRRUS_ID_CLGD5480) ? ((gd54xx->vgablt_base >> 8) & 0xf0) : 0x00; + break; + case 0x16: + ret = (svga->crtc[0x27] == CIRRUS_ID_CLGD5480) ? ((gd54xx->vgablt_base >> 16) & 0xff) : 0x00; + break; + case 0x17: + ret = (svga->crtc[0x27] == CIRRUS_ID_CLGD5480) ? ((gd54xx->vgablt_base >> 24) & 0xff) : 0x00; break; case 0x30: @@ -2976,6 +3620,8 @@ static void cl_pci_write(int func, int addr, uint8_t val, void *p) { gd54xx_t *gd54xx = (gd54xx_t *)p; + svga_t *svga = &gd54xx->svga; + uint32_t byte; if ((addr >= 0x30) && (addr <= 0x33) && (!gd54xx->has_bios)) return; @@ -2983,17 +3629,37 @@ cl_pci_write(int func, int addr, uint8_t val, void *p) switch (addr) { case PCI_REG_COMMAND: gd54xx->pci_regs[PCI_REG_COMMAND] = val & 0x23; + mem_mapping_disable(&gd54xx->vgablt_mapping); io_removehandler(0x03c0, 0x0020, gd54xx_in, NULL, NULL, gd54xx_out, NULL, NULL, gd54xx); if (val & PCI_COMMAND_IO) io_sethandler(0x03c0, 0x0020, gd54xx_in, NULL, NULL, gd54xx_out, NULL, NULL, gd54xx); + if ((val & PCI_COMMAND_MEM) && (gd54xx->vgablt_base != 0x00000000) && (gd54xx->vgablt_base < 0xfff00000)) + mem_mapping_set_addr(&gd54xx->vgablt_mapping, gd54xx->vgablt_base, 0x1000); gd543x_recalc_mapping(gd54xx); break; - case 0x13: + case 0x13: + /* 5480, like 5446 rev. B, has a 32 MB aperture, with the second set used for + BitBLT transfers. */ + if (svga->crtc[0x27] == CIRRUS_ID_CLGD5480) + val &= 0xfe; gd54xx->lfb_base = val << 24; gd543x_recalc_mapping(gd54xx); break; + case 0x15: case 0x16: case 0x17: + if (svga->crtc[0x27] != CIRRUS_ID_CLGD5480) + return; + byte = (addr & 3) << 3; + gd54xx->vgablt_base &= ~(0xff << byte); + if (addr == 0x15) + val &= 0xf0; + gd54xx->vgablt_base |= (val << byte); + mem_mapping_disable(&gd54xx->vgablt_mapping); + if ((gd54xx->pci_regs[PCI_REG_COMMAND] & PCI_COMMAND_MEM) && (gd54xx->vgablt_base != 0x00000000) && (gd54xx->vgablt_base < 0xfff00000)) + mem_mapping_set_addr(&gd54xx->vgablt_mapping, gd54xx->vgablt_base, 0x1000); + break; + case 0x30: case 0x32: case 0x33: gd54xx->pci_regs[addr] = val; if (gd54xx->pci_regs[0x30] & 0x01) { @@ -3072,12 +3738,10 @@ static void romfn = BIOS_GD5420_PATH; break; -#if defined(DEV_BRANCH) && defined(USE_CL5422) case CIRRUS_ID_CLGD5422: case CIRRUS_ID_CLGD5424: romfn = BIOS_GD5422_PATH; break; -#endif case CIRRUS_ID_CLGD5426: if (info->local & 0x200) @@ -3173,7 +3837,6 @@ static void if (romfn) rom_init(&gd54xx->bios_rom, romfn, 0xc0000, 0x8000, 0x7fff, 0, MEM_MAPPING_EXTERNAL); - if (info->flags & DEVICE_ISA) video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_gd54xx_isa); else if (info->flags & DEVICE_PCI) @@ -3181,9 +3844,16 @@ static void else video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_gd54xx_vlb); - svga_init(info, &gd54xx->svga, gd54xx, gd54xx->vram_size, - gd54xx_recalctimings, gd54xx_in, gd54xx_out, - gd54xx_hwcursor_draw, NULL); + if (id >= CIRRUS_ID_CLGD5426) { + svga_init(info, &gd54xx->svga, gd54xx, gd54xx->vram_size, + gd54xx_recalctimings, gd54xx_in, gd54xx_out, + gd54xx_hwcursor_draw, gd54xx_overlay_draw); + } else { + svga_init(info, &gd54xx->svga, gd54xx, gd54xx->vram_size, + gd54xx_recalctimings, gd54xx_in, gd54xx_out, + gd54xx_hwcursor_draw, NULL); + } + svga->vblank_start = gd54xx_vblank_start; svga->ven_write = gd54xx_write_modes45; if (vram <= 1) svga->decode_mask = gd54xx->vram_mask; @@ -3202,6 +3872,10 @@ static void gd5436_aperture2_readb, gd5436_aperture2_readw, gd5436_aperture2_readl, gd5436_aperture2_writeb, gd5436_aperture2_writew, gd5436_aperture2_writel, NULL, MEM_MAPPING_EXTERNAL, gd54xx); + mem_mapping_add(&gd54xx->vgablt_mapping, 0, 0, + gd5480_vgablt_read, gd5480_vgablt_readw, gd5480_vgablt_readl, + gd5480_vgablt_write, gd5480_vgablt_writew, gd5480_vgablt_writel, + NULL, MEM_MAPPING_EXTERNAL, gd54xx); } else { mem_mapping_set_handler(&svga->mapping, gd54xx_read, gd54xx_readw, NULL, gd54xx_write, gd54xx_writew, NULL); mem_mapping_add(&gd54xx->mmio_mapping, 0, 0, @@ -3216,11 +3890,16 @@ static void gd5436_aperture2_readb, gd5436_aperture2_readw, NULL, gd5436_aperture2_writeb, gd5436_aperture2_writew, NULL, NULL, MEM_MAPPING_EXTERNAL, gd54xx); + mem_mapping_add(&gd54xx->vgablt_mapping, 0, 0, + gd5480_vgablt_read, gd5480_vgablt_readw, NULL, + gd5480_vgablt_write, gd5480_vgablt_writew, NULL, + NULL, MEM_MAPPING_EXTERNAL, gd54xx); } mem_mapping_set_p(&svga->mapping, gd54xx); mem_mapping_disable(&gd54xx->mmio_mapping); mem_mapping_disable(&gd54xx->linear_mapping); mem_mapping_disable(&gd54xx->aperture2_mapping); + mem_mapping_disable(&gd54xx->vgablt_mapping); io_sethandler(0x03c0, 0x0020, gd54xx_in, NULL, NULL, gd54xx_out, NULL, NULL, gd54xx); @@ -3274,6 +3953,13 @@ static void gd54xx->ddc = ddc_init(i2c_gpio_get_bus(gd54xx->i2c)); } + if (svga->crtc[0x27] >= CIRRUS_ID_CLGD5446) + gd54xx->crtcreg_mask = 0x7f; + else + gd54xx->crtcreg_mask = 0x3f; + + gd54xx->overlay.colorkeycompare = 0xff; + return gd54xx; } @@ -3295,13 +3981,11 @@ gd5420_available(void) return rom_present(BIOS_GD5420_PATH); } -#if defined(DEV_BRANCH) && defined(USE_CL5422) static int gd5422_available(void) { return rom_present(BIOS_GD5422_PATH); } -#endif static int gd5426_available(void) @@ -3589,7 +4273,6 @@ const device_t gd5420_isa_device = gd5422_config, }; -#if defined(DEV_BRANCH) && defined(USE_CL5422) const device_t gd5422_isa_device = { "Cirrus Logic GD-5422", DEVICE_AT | DEVICE_ISA, @@ -3613,7 +4296,6 @@ const device_t gd5424_vlb_device = { gd54xx_force_redraw, gd5422_config, }; -#endif const device_t gd5426_vlb_device = { diff --git a/src/video/vid_colorplus.c b/src/video/vid_colorplus.c index 371d23e5e..223c4d1c6 100644 --- a/src/video/vid_colorplus.c +++ b/src/video/vid_colorplus.c @@ -100,7 +100,7 @@ void colorplus_write(uint32_t addr, uint8_t val, void *p) colorplus->cga.charbuffer[offset | 1] = colorplus->cga.vram[addr & 0x7fff]; } egawrites++; - sub_cycles(4); + cycles -= 4; } uint8_t colorplus_read(uint32_t addr, void *p) @@ -117,7 +117,7 @@ uint8_t colorplus_read(uint32_t addr, void *p) { addr &= 0x3FFF; } - sub_cycles(4); + cycles -= 4; if (colorplus->cga.snow_enabled) { int offset = ((timer_get_remaining_u64(&colorplus->cga.timer) / CGACONST) * 2) & 0xfc; diff --git a/src/video/vid_ega.c b/src/video/vid_ega.c index e47aed584..bbd8e7da9 100644 --- a/src/video/vid_ega.c +++ b/src/video/vid_ega.c @@ -732,7 +732,7 @@ ega_write(uint32_t addr, uint8_t val, void *p) int writemask2 = ega->writemask; egawrites++; - sub_cycles(video_timing_write_b); + cycles -= video_timing_write_b; if (addr >= 0xB0000) addr &= 0x7fff; else addr &= 0xffff; @@ -859,7 +859,7 @@ ega_read(uint32_t addr, void *p) int readplane = ega->readplane; egareads++; - sub_cycles(video_timing_read_b); + cycles -= video_timing_read_b; if (addr >= 0xb0000) addr &= 0x7fff; else addr &= 0xffff; diff --git a/src/video/vid_et4000.c b/src/video/vid_et4000.c index aa1ccf52c..8da66fb7f 100644 --- a/src/video/vid_et4000.c +++ b/src/video/vid_et4000.c @@ -245,6 +245,15 @@ et4000_out(uint16_t addr, uint8_t val, void *priv) svga->read_bank = ((dev->banking >> 4) & 0x0f) * 0x10000; } else svga->write_bank = svga->read_bank = 0; + + old = svga->gdcreg[6]; + svga_out(addr, val, svga); + if ((old & 0xc) != 0 && (val & 0xc) == 0) + { + /*override mask - ET4000 supports linear 128k at A0000*/ + svga->banked_mask = 0x1ffff; + } + return; } break; diff --git a/src/video/vid_et4000w32.c b/src/video/vid_et4000w32.c index 9fb13de5b..2c04b454e 100644 --- a/src/video/vid_et4000w32.c +++ b/src/video/vid_et4000w32.c @@ -454,7 +454,7 @@ void et4000w32p_recalcmapping(et4000w32p_t *et4000) case 0x0: case 0x4: case 0x8: case 0xC: /*128k at A0000*/ mem_mapping_set_addr(&svga->mapping, 0xa0000, 0x20000); mem_mapping_disable(&et4000->mmu_mapping); - svga->banked_mask = 0xffff; + svga->banked_mask = 0x1ffff; break; case 0x1: /*64k at A0000*/ mem_mapping_set_addr(&svga->mapping, 0xa0000, 0x10000); diff --git a/src/video/vid_genius.c b/src/video/vid_genius.c index 6fbe02b94..d6afc1863 100644 --- a/src/video/vid_genius.c +++ b/src/video/vid_genius.c @@ -253,7 +253,7 @@ genius_waitstates(void) int ws; ws = ws_array[cycles & 0xf]; - sub_cycles(ws); + cycles -= ws; } diff --git a/src/video/vid_hercules.c b/src/video/vid_hercules.c index 760774fe3..94faf3fea 100644 --- a/src/video/vid_hercules.c +++ b/src/video/vid_hercules.c @@ -212,7 +212,7 @@ hercules_waitstates(void *p) int ws; ws = ws_array[cycles & 0xf]; - sub_cycles(ws); + cycles -= ws; } diff --git a/src/video/vid_ht216.c b/src/video/vid_ht216.c index 79cfb6bdf..083de1f26 100644 --- a/src/video/vid_ht216.c +++ b/src/video/vid_ht216.c @@ -729,7 +729,7 @@ ht216_write_common(ht216_t *ht216, uint32_t addr, uint8_t val) svga_t *svga = &ht216->svga; uint8_t bit_mask = 0, rop_select = 0; - sub_cycles(video_timing_write_b); + cycles -= video_timing_write_b; egawrites++; @@ -911,7 +911,7 @@ ht216_read_common(ht216_t *ht216, uint32_t addr) addr &= 0xfffff; - sub_cycles(video_timing_read_b); + cycles -= video_timing_read_b; egareads++; diff --git a/src/video/vid_mga.c b/src/video/vid_mga.c index 136531457..3433b7539 100644 --- a/src/video/vid_mga.c +++ b/src/video/vid_mga.c @@ -2132,7 +2132,7 @@ mystique_readb_linear(uint32_t addr, void *p) egareads++; - sub_cycles(video_timing_read_b); + cycles -= video_timing_read_b; addr &= svga->decode_mask; if (addr >= svga->vram_max) @@ -2149,7 +2149,7 @@ mystique_readw_linear(uint32_t addr, void *p) egareads += 2; - sub_cycles(video_timing_read_w); + cycles -= video_timing_read_w; addr &= svga->decode_mask; if (addr >= svga->vram_max) @@ -2166,7 +2166,7 @@ mystique_readl_linear(uint32_t addr, void *p) egareads += 4; - sub_cycles(video_timing_read_l); + cycles -= video_timing_read_l; addr &= svga->decode_mask; if (addr >= svga->vram_max) @@ -2183,7 +2183,7 @@ mystique_writeb_linear(uint32_t addr, uint8_t val, void *p) egawrites++; - sub_cycles(video_timing_write_b); + cycles -= video_timing_write_b; addr &= svga->decode_mask; if (addr >= svga->vram_max) @@ -2201,7 +2201,7 @@ mystique_writew_linear(uint32_t addr, uint16_t val, void *p) egawrites += 2; - sub_cycles(video_timing_write_w); + cycles -= video_timing_write_w; addr &= svga->decode_mask; if (addr >= svga->vram_max) @@ -2219,7 +2219,7 @@ mystique_writel_linear(uint32_t addr, uint32_t val, void *p) egawrites += 4; - sub_cycles(video_timing_write_l); + cycles -= video_timing_write_l; addr &= svga->decode_mask; if (addr >= svga->vram_max) diff --git a/src/video/vid_s3.c b/src/video/vid_s3.c index 89295c60a..4d61f9424 100644 --- a/src/video/vid_s3.c +++ b/src/video/vid_s3.c @@ -2697,7 +2697,7 @@ s3_accel_in(uint16_t port, void *p) if (FIFO_FULL && s3->chip >= S3_VISION964) temp |= 0xf8; /*FIFO full*/ } else { - if (s3->busy || s3->force_busy) { + if (s3->force_busy) { temp |= 0x02; /*Hardware busy*/ } s3->force_busy = 0; @@ -3343,10 +3343,6 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ dstbase >>= 2; } - if (((s3_cpu_src(s3) || s3_cpu_dest(s3))) && (s3->chip >= S3_86C928 && s3->chip < S3_TRIO64V)) { - s3->busy = 1; - } - if ((s3->accel.cmd & 0x100) && (s3_cpu_src(s3) || s3_cpu_dest(s3)) && !cpu_input) { s3->force_busy = 1; } @@ -3439,7 +3435,6 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ if (s3->bpp == 0) cpu_dat >>= 8; else cpu_dat >>= 16; if (!s3->accel.sy) { - s3->busy = 0; break; } @@ -3510,7 +3505,6 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ else cpu_dat >>= 16; if (!s3->accel.sy) { - s3->busy = 0; break; } @@ -3682,10 +3676,6 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ s3->accel.dest = dstbase + s3->accel.cy * s3->width; s3->accel.sy--; - if (s3->accel.sy < 0) { - s3->busy = 0; - } - if (cpu_input) { if (s3_cpu_dest(s3)) s3->data_available = 1; @@ -3766,7 +3756,6 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ s3->accel.sy--; if (s3->accel.sy < 0) { - s3->busy = 0; return; } } @@ -3854,9 +3843,6 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ s3->accel.sy--; - if (s3->accel.sy < 0) - s3->busy = 0; - if (cpu_input) return; @@ -3979,9 +3965,6 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ s3->accel.sy--; - if (s3->accel.sy < 0) - s3->busy = 0; - if (cpu_input/* && (s3->accel.multifunc[0xa] & 0xc0) == 0x80*/) return; if (s3->accel.sy < 0) return; @@ -4061,7 +4044,6 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ s3->accel.cur_x2 = s3->accel.poly_cx2 & 0xfff; s3->accel.cur_y2 = s3->accel.poly_cy & 0xfff; } - s3->busy = 0; break; case 11: /*Polygon Fill Pattern (Trio64 only)*/ @@ -4150,7 +4132,6 @@ s3_accel_start(int count, int cpu_input, uint32_t mix_dat, uint32_t cpu_dat, s3_ s3->accel.cur_x2 = s3->accel.poly_cx2 & 0xfff; s3->accel.cur_y2 = s3->accel.poly_cy & 0xfff; } - s3->busy = 0; break; } } diff --git a/src/video/vid_sigma.c b/src/video/vid_sigma.c index e8862a68b..78812d27d 100644 --- a/src/video/vid_sigma.c +++ b/src/video/vid_sigma.c @@ -344,7 +344,7 @@ sigma_write(uint32_t addr, uint8_t val, void *p) sigma->vram[sigma->plane * 0x8000 + (addr & 0x7fff)] = val; egawrites++; - sub_cycles(4); + cycles -= 4; } @@ -353,7 +353,7 @@ sigma_read(uint32_t addr, void *p) { sigma_t *sigma = (sigma_t *)p; - sub_cycles(4); + cycles -= 4; egareads++; return sigma->vram[sigma->plane * 0x8000 + (addr & 0x7fff)]; } diff --git a/src/video/vid_svga.c b/src/video/vid_svga.c index 7a8fc0981..0e1511af4 100644 --- a/src/video/vid_svga.c +++ b/src/video/vid_svga.c @@ -1015,7 +1015,7 @@ svga_write_common(uint32_t addr, uint8_t val, uint8_t linear, void *p) egawrites++; - sub_cycles(video_timing_write_b); + cycles -= video_timing_write_b; if (!linear) { addr = svga_decode_addr(svga, addr, 1); @@ -1057,14 +1057,21 @@ svga_write_common(uint32_t addr, uint8_t val, uint8_t linear, void *p) if (svga->adv_flags & FLAG_LATCH8) count = 8; + /* Undocumented Cirrus Logic behavior: The datasheet says that, with EXT_WRITE and FLAG_ADDR_BY8, the write mask only + changes meaning in write modes 4 and 5, as well as write mode 1. In reality, however, all other write modes are also + affected, as proven by the Windows 3.1 CL-GD 5422/4 drivers in 8bpp modes. */ switch (svga->writemode) { case 0: - if (svga->gdcreg[3] & 7) - val = svga_rotate[svga->gdcreg[3] & 7][val]; + val = ((val >> (svga->gdcreg[3] & 7)) | (val << (8 - (svga->gdcreg[3] & 7)))); if ((svga->gdcreg[8] == 0xff) && !(svga->gdcreg[3] & 0x18) && (!svga->gdcreg[1] || svga->set_reset_disabled)) { for (i = 0; i < count; i++) { - if (writemask2 & (1 << i)) - svga->vram[addr | i] = val; + if ((svga->adv_flags & FLAG_EXT_WRITE) && (svga->adv_flags & FLAG_ADDR_BY8)) { + if (writemask2 & (0x80 >> i)) + svga->vram[addr | i] = val; + } else { + if (writemask2 & (1 << i)) + svga->vram[addr | i] = val; + } } return; } else { @@ -1078,8 +1085,13 @@ svga_write_common(uint32_t addr, uint8_t val, uint8_t linear, void *p) break; case 1: for (i = 0; i < count; i++) { - if (writemask2 & (1 << i)) - svga->vram[addr | i] = svga->latch.b[i]; + if ((svga->adv_flags & FLAG_EXT_WRITE) && (svga->adv_flags & FLAG_ADDR_BY8)) { + if (writemask2 & (0x80 >> i)) + svga->vram[addr | i] = svga->latch.b[i]; + } else { + if (writemask2 & (1 << i)) + svga->vram[addr | i] = svga->latch.b[i]; + } } return; case 2: @@ -1088,15 +1100,19 @@ svga_write_common(uint32_t addr, uint8_t val, uint8_t linear, void *p) if (!(svga->gdcreg[3] & 0x18) && (!svga->gdcreg[1] || svga->set_reset_disabled)) { for (i = 0; i < count; i++) { - if (writemask2 & (1 << i)) - svga->vram[addr | i] = (vall.b[i] & svga->gdcreg[8]) | (svga->latch.b[i] & ~svga->gdcreg[8]); + if ((svga->adv_flags & FLAG_EXT_WRITE) && (svga->adv_flags & FLAG_ADDR_BY8)) { + if (writemask2 & (0x80 >> i)) + svga->vram[addr | i] = (vall.b[i] & svga->gdcreg[8]) | (svga->latch.b[i] & ~svga->gdcreg[8]); + } else { + if (writemask2 & (1 << i)) + svga->vram[addr | i] = (vall.b[i] & svga->gdcreg[8]) | (svga->latch.b[i] & ~svga->gdcreg[8]); + } } return; } break; case 3: - if (svga->gdcreg[3] & 7) - val = svga_rotate[svga->gdcreg[3] & 7][val]; + val = ((val >> (svga->gdcreg[3] & 7)) | (val << (8 - (svga->gdcreg[3] & 7)))); wm = svga->gdcreg[8]; svga->gdcreg[8] &= val; @@ -1114,26 +1130,46 @@ svga_write_common(uint32_t addr, uint8_t val, uint8_t linear, void *p) switch (svga->gdcreg[3] & 0x18) { case 0x00: /* Set */ for (i = 0; i < count; i++) { - if (writemask2 & (1 << i)) - svga->vram[addr | i] = (vall.b[i] & svga->gdcreg[8]) | (svga->latch.b[i] & ~svga->gdcreg[8]); + if ((svga->adv_flags & FLAG_EXT_WRITE) && (svga->adv_flags & FLAG_ADDR_BY8)) { + if (writemask2 & (0x80 >> i)) + svga->vram[addr | i] = (vall.b[i] & svga->gdcreg[8]) | (svga->latch.b[i] & ~svga->gdcreg[8]); + } else { + if (writemask2 & (1 << i)) + svga->vram[addr | i] = (vall.b[i] & svga->gdcreg[8]) | (svga->latch.b[i] & ~svga->gdcreg[8]); + } } break; case 0x08: /* AND */ for (i = 0; i < count; i++) { - if (writemask2 & (1 << i)) - svga->vram[addr | i] = (vall.b[i] | ~svga->gdcreg[8]) & svga->latch.b[i]; + if ((svga->adv_flags & FLAG_EXT_WRITE) && (svga->adv_flags & FLAG_ADDR_BY8)) { + if (writemask2 & (0x80 >> i)) + svga->vram[addr | i] = (vall.b[i] | ~svga->gdcreg[8]) & svga->latch.b[i]; + } else { + if (writemask2 & (1 << i)) + svga->vram[addr | i] = (vall.b[i] | ~svga->gdcreg[8]) & svga->latch.b[i]; + } } break; case 0x10: /* OR */ for (i = 0; i < count; i++) { - if (writemask2 & (1 << i)) - svga->vram[addr | i] = (vall.b[i] & svga->gdcreg[8]) | svga->latch.b[i]; + if ((svga->adv_flags & FLAG_EXT_WRITE) && (svga->adv_flags & FLAG_ADDR_BY8)) { + if (writemask2 & (0x80 >> i)) + svga->vram[addr | i] = (vall.b[i] & svga->gdcreg[8]) | svga->latch.b[i]; + } else { + if (writemask2 & (1 << i)) + svga->vram[addr | i] = (vall.b[i] & svga->gdcreg[8]) | svga->latch.b[i]; + } } break; case 0x18: /* XOR */ for (i = 0; i < count; i++) { - if (writemask2 & (1 << i)) - svga->vram[addr | i] = (vall.b[i] & svga->gdcreg[8]) ^ svga->latch.b[i]; + if ((svga->adv_flags & FLAG_EXT_WRITE) && (svga->adv_flags & FLAG_ADDR_BY8)) { + if (writemask2 & (0x80 >> i)) + svga->vram[addr | i] = (vall.b[i] & svga->gdcreg[8]) ^ svga->latch.b[i]; + } else { + if (writemask2 & (1 << i)) + svga->vram[addr | i] = (vall.b[i] & svga->gdcreg[8]) ^ svga->latch.b[i]; + } } break; } @@ -1156,7 +1192,7 @@ svga_read_common(uint32_t addr, uint8_t linear, void *p) if (svga->adv_flags & FLAG_ADDR_BY8) readplane = svga->gdcreg[4] & 7; - sub_cycles(video_timing_read_b); + cycles -= video_timing_read_b; egareads++; @@ -1182,6 +1218,9 @@ svga_read_common(uint32_t addr, uint8_t linear, void *p) addr = svga->translate_address(addr, p); if (addr >= svga->vram_max) return 0xff; + latch_addr = (addr & svga->vram_mask) & ~3; + for (i = 0; i < count; i++) + svga->latch.b[i] = svga->vram[latch_addr | i]; return svga->vram[addr & svga->vram_mask]; } else if (svga->chain2_read) { readplane = (readplane & 2) | (addr & 1); @@ -1381,7 +1420,7 @@ svga_writew_common(uint32_t addr, uint16_t val, uint8_t linear, void *p) egawrites += 2; - sub_cycles(video_timing_write_w); + cycles -= video_timing_write_w; if (!linear) { addr = svga_decode_addr(svga, addr, 1); @@ -1442,7 +1481,7 @@ svga_writel_common(uint32_t addr, uint32_t val, uint8_t linear, void *p) egawrites += 4; - sub_cycles(video_timing_write_l); + cycles -= video_timing_write_l; if (!linear) { addr = svga_decode_addr(svga, addr, 1); @@ -1526,7 +1565,7 @@ svga_readw_common(uint32_t addr, uint8_t linear, void *p) egareads += 2; - sub_cycles(video_timing_read_w); + cycles -= video_timing_read_w; if (!linear) { addr = svga_decode_addr(svga, addr, 0); @@ -1579,7 +1618,7 @@ svga_readl_common(uint32_t addr, uint8_t linear, void *p) egareads += 4; - sub_cycles(video_timing_read_l); + cycles -= video_timing_read_l; if (!linear) { addr = svga_decode_addr(svga, addr, 0); diff --git a/src/video/vid_table.c b/src/video/vid_table.c index 611be2524..e8605e16d 100644 --- a/src/video/vid_table.c +++ b/src/video/vid_table.c @@ -71,9 +71,7 @@ video_cards[] = { { "cl_gd5401_isa", &gd5401_isa_device }, { "cl_gd5402_isa", &gd5402_isa_device }, { "cl_gd5420_isa", &gd5420_isa_device }, -#if defined(DEV_BRANCH) && defined(USE_CL5422) { "cl_gd5422_isa", &gd5422_isa_device }, -#endif { "cl_gd5428_isa", &gd5428_isa_device }, { "cl_gd5429_isa", &gd5429_isa_device }, { "cl_gd5434_isa", &gd5434_isa_device }, @@ -154,9 +152,7 @@ video_cards[] = { { "voodoo3_3k_pci", &voodoo_3_3000_device }, { "mach64gx_vlb", &mach64gx_vlb_device }, { "et4000w32p_vlb", &et4000w32p_cardex_vlb_device }, -#if defined(DEV_BRANCH) && defined(USE_CL5422) { "cl_gd5424_vlb", &gd5424_vlb_device }, -#endif { "cl_gd5428_vlb", &gd5428_vlb_device }, { "cl_gd5429_vlb", &gd5429_vlb_device }, { "cl_gd5434_vlb", &gd5434_vlb_device }, diff --git a/src/video/vid_tgui9440.c b/src/video/vid_tgui9440.c index d5f28fefc..17415c10a 100644 --- a/src/video/vid_tgui9440.c +++ b/src/video/vid_tgui9440.c @@ -798,7 +798,7 @@ static uint8_t tgui_ext_linear_read(uint32_t addr, void *p) tgui_t *tgui = (tgui_t *)svga->p; int c; - sub_cycles(video_timing_read_b); + cycles -= video_timing_read_b; addr &= svga->decode_mask; if (addr >= svga->vram_max) @@ -829,7 +829,7 @@ static void tgui_ext_linear_write(uint32_t addr, uint8_t val, void *p) uint8_t bg[2] = {tgui->ext_gdc_regs[1], tgui->ext_gdc_regs[2]}; uint8_t mask = tgui->ext_gdc_regs[7]; - sub_cycles(video_timing_write_b); + cycles -= video_timing_write_b; addr &= svga->decode_mask; if (addr >= svga->vram_max) @@ -898,7 +898,7 @@ static void tgui_ext_linear_writew(uint32_t addr, uint16_t val, void *p) uint8_t bg[2] = {tgui->ext_gdc_regs[1], tgui->ext_gdc_regs[2]}; uint16_t mask = (tgui->ext_gdc_regs[7] << 8) | tgui->ext_gdc_regs[8]; - sub_cycles(video_timing_write_w); + cycles -= video_timing_write_w; addr &= svga->decode_mask; if (addr >= svga->vram_max) diff --git a/src/video/vid_tvga.c b/src/video/vid_tvga.c index 750144159..b8065457f 100644 --- a/src/video/vid_tvga.c +++ b/src/video/vid_tvga.c @@ -127,6 +127,15 @@ void tvga_out(uint16_t addr, uint8_t val, void *p) case 0x3CF: switch (svga->gdcaddr & 15) { + case 0x6: + old = svga->gdcreg[6]; + svga_out(addr, val, svga); + if ((old & 0xc) != 0 && (val & 0xc) == 0) + { + /*override mask - TVGA supports linear 128k at A0000*/ + svga->banked_mask = 0x1ffff; + } + return; case 0xE: svga->gdcreg[0xe] = val ^ 2; tvga->tvga_3d9 = svga->gdcreg[0xe] & 0xf; diff --git a/src/video/vid_voodoo.c b/src/video/vid_voodoo.c index d9929324a..c806bf243 100644 --- a/src/video/vid_voodoo.c +++ b/src/video/vid_voodoo.c @@ -151,7 +151,7 @@ static uint16_t voodoo_readw(uint32_t addr, void *p) addr &= 0xffffff; - sub_cycles(voodoo->read_time); + cycles -= voodoo->read_time; if ((addr & 0xc00000) == 0x400000) /*Framebuffer*/ { @@ -190,7 +190,7 @@ static uint32_t voodoo_readl(uint32_t addr, void *p) voodoo->rd_count++; addr &= 0xffffff; - sub_cycles(voodoo->read_time); + cycles -= voodoo->read_time; if (addr & 0x800000) /*Texture*/ { @@ -390,7 +390,7 @@ static uint32_t voodoo_readl(uint32_t addr, void *p) break; default: - fatal("voodoo_readl : bad addr %08X\n", addr); + voodoo_log("voodoo_readl : bad addr %08X\n", addr); temp = 0xffffffff; } @@ -403,7 +403,7 @@ static void voodoo_writew(uint32_t addr, uint16_t val, void *p) voodoo->wr_count++; addr &= 0xffffff; - sub_cycles(voodoo->write_time); + cycles -= voodoo->write_time; if ((addr & 0xc00000) == 0x400000) /*Framebuffer*/ voodoo_queue_command(voodoo, addr | FIFO_WRITEW_FB, val); @@ -418,9 +418,9 @@ static void voodoo_writel(uint32_t addr, uint32_t val, void *p) addr &= 0xffffff; if (addr == voodoo->last_write_addr+4) - sub_cycles(voodoo->burst_time); + cycles -= voodoo->burst_time; else - sub_cycles(voodoo->write_time); + cycles -= voodoo->write_time; voodoo->last_write_addr = addr; if (addr & 0x800000) /*Texture*/ diff --git a/src/video/vid_voodoo_banshee.c b/src/video/vid_voodoo_banshee.c index 29ffc1a3e..2d3a378d5 100644 --- a/src/video/vid_voodoo_banshee.c +++ b/src/video/vid_voodoo_banshee.c @@ -181,6 +181,8 @@ enum #define VIDPROCCFG_CURSOR_MODE (1 << 1) #define VIDPROCCFG_HALF_MODE (1 << 4) #define VIDPROCCFG_OVERLAY_ENABLE (1 << 8) +#define VIDPROCCFG_OVERLAY_CLUT_BYPASS (1 << 11) +#define VIDPROCCFG_OVERLAY_CLUT_SEL (1 << 13) #define VIDPROCCFG_H_SCALE_ENABLE (1 << 14) #define VIDPROCCFG_V_SCALE_ENABLE (1 << 15) #define VIDPROCCFG_FILTER_MODE_MASK (3 << 16) @@ -658,7 +660,7 @@ static void banshee_ext_outl(uint16_t addr, uint32_t val, void *p) case Video_hwCurPatAddr: banshee->hwCurPatAddr = val; - svga->hwcursor.addr = val & 0xfffff0; + svga->hwcursor.addr = (val & 0xfffff0) + (svga->hwcursor.yoff * 16); break; case Video_hwCurLoc: banshee->hwCurLoc = val; @@ -671,6 +673,7 @@ static void banshee_ext_outl(uint16_t addr, uint32_t val, void *p) } else svga->hwcursor.yoff = 0; + svga->hwcursor.addr = (banshee->hwCurPatAddr & 0xfffff0) + (svga->hwcursor.yoff * 16); svga->hwcursor.xsize = 64; svga->hwcursor.ysize = 64; // banshee_log("hwCurLoc %08x %i\n", val, svga->hwcursor.y); @@ -832,7 +835,7 @@ static uint32_t banshee_ext_inl(uint16_t addr, void *p) svga_t *svga = &banshee->svga; uint32_t ret = 0xffffffff; - sub_cycles(voodoo->read_time); + cycles -= voodoo->read_time; switch (addr & 0xff) { @@ -1023,7 +1026,7 @@ static uint32_t banshee_reg_readl(uint32_t addr, void *p) voodoo_t *voodoo = banshee->voodoo; uint32_t ret = 0xffffffff; - sub_cycles(voodoo->read_time); + cycles -= voodoo->read_time; switch (addr & 0x1f00000) { @@ -1154,7 +1157,7 @@ static uint32_t banshee_reg_readl(uint32_t addr, void *p) break; default: - fatal("banshee_reg_readl: 3D addr=%08x\n", addr); + banshee_log("banshee_reg_readl: 3D addr=%08x\n", addr); break; } break; @@ -1175,7 +1178,7 @@ static void banshee_reg_writew(uint32_t addr, uint16_t val, void *p) banshee_t *banshee = (banshee_t *)p; voodoo_t *voodoo = banshee->voodoo; - sub_cycles(voodoo->write_time); + cycles -= voodoo->write_time; // banshee_log("banshee_reg_writew: addr=%08x val=%04x\n", addr, val); switch (addr & 0x1f00000) @@ -1205,6 +1208,8 @@ static void banshee_cmd_write(banshee_t *banshee, uint32_t addr, uint32_t val) voodoo->cmdfifo_size = val; voodoo->cmdfifo_end = voodoo->cmdfifo_base + (((voodoo->cmdfifo_size & 0xff) + 1) << 12); voodoo->cmdfifo_enabled = val & 0x100; + if (!voodoo->cmdfifo_enabled) + voodoo->cmdfifo_in_sub = 0; /*Not sure exactly when this should be reset*/ // banshee_log("cmdfifo_base=%08x cmdfifo_end=%08x\n", voodoo->cmdfifo_base, voodoo->cmdfifo_end); break; @@ -1248,9 +1253,9 @@ static void banshee_reg_writel(uint32_t addr, uint32_t val, void *p) voodoo_t *voodoo = banshee->voodoo; if (addr == voodoo->last_write_addr+4) - sub_cycles(voodoo->burst_time); + cycles -= voodoo->burst_time; else - sub_cycles(voodoo->write_time); + cycles -= voodoo->write_time; voodoo->last_write_addr = addr; // banshee_log("banshee_reg_writel: addr=%08x val=%08x\n", addr, val); @@ -1346,7 +1351,7 @@ static uint8_t banshee_read_linear(uint32_t addr, void *p) voodoo_t *voodoo = banshee->voodoo; svga_t *svga = &banshee->svga; - sub_cycles(voodoo->read_time); + cycles -= voodoo->read_time; addr &= svga->decode_mask; if (addr >= voodoo->tile_base) @@ -1364,7 +1369,7 @@ static uint8_t banshee_read_linear(uint32_t addr, void *p) return 0xff; egareads++; - sub_cycles(video_timing_read_b); + cycles -= video_timing_read_b; // banshee_log("read_linear: addr=%08x val=%02x\n", addr, svga->vram[addr & svga->vram_mask]); @@ -1377,8 +1382,10 @@ static uint16_t banshee_read_linear_w(uint32_t addr, void *p) voodoo_t *voodoo = banshee->voodoo; svga_t *svga = &banshee->svga; - sub_cycles(voodoo->read_time); - + if (addr & 1) + return banshee_read_linear(addr, p) | (banshee_read_linear(addr+1, p) << 8); + + cycles -= voodoo->read_time; addr &= svga->decode_mask; if (addr >= voodoo->tile_base) { @@ -1395,7 +1402,7 @@ static uint16_t banshee_read_linear_w(uint32_t addr, void *p) return 0xff; egareads++; - sub_cycles(video_timing_read_w); + cycles -= video_timing_read_w; // banshee_log("read_linear: addr=%08x val=%02x\n", addr, svga->vram[addr & svga->vram_mask]); @@ -1408,7 +1415,10 @@ static uint32_t banshee_read_linear_l(uint32_t addr, void *p) voodoo_t *voodoo = banshee->voodoo; svga_t *svga = &banshee->svga; - sub_cycles(voodoo->read_time); + if (addr & 3) + return banshee_read_linear_w(addr, p) | (banshee_read_linear_w(addr+2, p) << 16); + + cycles -= voodoo->read_time; addr &= svga->decode_mask; if (addr >= voodoo->tile_base) @@ -1426,7 +1436,7 @@ static uint32_t banshee_read_linear_l(uint32_t addr, void *p) return 0xff; egareads++; - sub_cycles(video_timing_read_l); + cycles -= video_timing_read_l; // banshee_log("read_linear: addr=%08x val=%02x\n", addr, svga->vram[addr & svga->vram_mask]); @@ -1439,7 +1449,7 @@ static void banshee_write_linear(uint32_t addr, uint8_t val, void *p) voodoo_t *voodoo = banshee->voodoo; svga_t *svga = &banshee->svga; - sub_cycles(voodoo->write_time); + cycles -= voodoo->write_time; // banshee_log("write_linear: addr=%08x val=%02x\n", addr, val); addr &= svga->decode_mask; @@ -1459,7 +1469,7 @@ static void banshee_write_linear(uint32_t addr, uint8_t val, void *p) egawrites++; - sub_cycles(video_timing_write_b); + cycles -= video_timing_write_b; svga->changedvram[addr >> 12] = changeframecount; svga->vram[addr & svga->vram_mask] = val; @@ -1471,8 +1481,14 @@ static void banshee_write_linear_w(uint32_t addr, uint16_t val, void *p) voodoo_t *voodoo = banshee->voodoo; svga_t *svga = &banshee->svga; - sub_cycles(voodoo->write_time); - + if (addr & 1) + { + banshee_write_linear(addr, val, p); + banshee_write_linear(addr + 1, val >> 8, p); + return; + } + + cycles -= voodoo->write_time; // banshee_log("write_linear: addr=%08x val=%02x\n", addr, val); addr &= svga->decode_mask; if (addr >= voodoo->tile_base) @@ -1491,7 +1507,7 @@ static void banshee_write_linear_w(uint32_t addr, uint16_t val, void *p) egawrites++; - sub_cycles(video_timing_write_w); + cycles -= video_timing_write_w; svga->changedvram[addr >> 12] = changeframecount; *(uint16_t *)&svga->vram[addr & svga->vram_mask] = val; @@ -1504,11 +1520,18 @@ static void banshee_write_linear_l(uint32_t addr, uint32_t val, void *p) svga_t *svga = &banshee->svga; int timing; + if (addr & 3) + { + banshee_write_linear_w(addr, val, p); + banshee_write_linear_w(addr + 2, val >> 16, p); + return; + } + if (addr == voodoo->last_write_addr+4) timing = voodoo->burst_time; else timing = voodoo->write_time; - sub_cycles(timing); + cycles -= timing; voodoo->last_write_addr = addr; // /*if (val) */banshee_log("write_linear_l: addr=%08x val=%08x %08x\n", addr, val, voodoo->tile_base); @@ -1530,7 +1553,7 @@ static void banshee_write_linear_l(uint32_t addr, uint32_t val, void *p) egawrites += 4; - sub_cycles(video_timing_write_l); + cycles -= video_timing_write_l; svga->changedvram[addr >> 12] = changeframecount; *(uint32_t *)&svga->vram[addr & svga->vram_mask] = val; @@ -1670,7 +1693,12 @@ void banshee_hwcursor_draw(svga_t *svga, int displine) int g = (data >> 5) & 0x3f; \ int b = data >> 11; \ \ - buf[wp++] = (r << 3) | (g << 10) | (b << 19); \ + if (banshee->vidProcCfg & VIDPROCCFG_OVERLAY_CLUT_BYPASS) \ + buf[wp++] = (r << 3) | (g << 10) | (b << 19); \ + else \ + buf[wp++] = (clut[r << 3] & 0x0000ff) | \ + (clut[g << 2] & 0x00ff00) | \ + (clut[b << 3] & 0xff0000); \ src += 2; \ } \ } while (0) @@ -1680,7 +1708,7 @@ void banshee_hwcursor_draw(svga_t *svga, int displine) { \ int c; \ int wp = 0; \ - uint32_t base_addr = buf ? src_addr2 : src_addr; \ + uint32_t base_addr = (buf == banshee->overlay_buffer[1]) ? src_addr2 : src_addr; \ \ for (c = 0; c < voodoo->overlay.overlay_bytes; c += 2) \ { \ @@ -1689,7 +1717,12 @@ void banshee_hwcursor_draw(svga_t *svga, int displine) int g = (data >> 5) & 0x3f; \ int b = data >> 11; \ \ - buf[wp++] = (r << 3) | (g << 10) | (b << 19); \ + if (banshee->vidProcCfg & VIDPROCCFG_OVERLAY_CLUT_BYPASS) \ + buf[wp++] = (r << 3) | (g << 10) | (b << 19); \ + else \ + buf[wp++] = (clut[r << 3] & 0x0000ff) | \ + (clut[g << 2] & 0x00ff00) | \ + (clut[b << 3] & 0xff0000); \ } \ } while (0) @@ -1957,6 +1990,7 @@ static void banshee_overlay_draw(svga_t *svga, int displine) uint32_t src_x = 0; unsigned int y_coeff = (voodoo->overlay.src_y & 0xfffff) >> 4; int skip_filtering; + uint32_t *clut = &svga->pallook[(banshee->vidProcCfg & VIDPROCCFG_OVERLAY_CLUT_SEL) ? 256 : 0]; if (svga->render == svga_render_null && !svga->changedvram[src_addr >> 12] && !svga->changedvram[src_addr2 >> 12] && @@ -2323,11 +2357,11 @@ static uint8_t banshee_pci_read(int func, int addr, void *p) case 0x1a: ret = 0x00; break; case 0x1b: ret = 0x00; break; - /*Undocumented, but Voodoo 3 BIOS checks this*/ - case 0x2c: ret = 0x1a; break; - case 0x2d: ret = 0x12; break; - case 0x2e: ret = (banshee->type == TYPE_V3_3000) ? 0x3a : 0x30; break; - case 0x2f: ret = 0x00; break; + /*Subsystem vendor ID*/ + case 0x2c: ret = banshee->pci_regs[0x2c]; break; + case 0x2d: ret = banshee->pci_regs[0x2d]; break; + case 0x2e: ret = banshee->pci_regs[0x2e]; break; + case 0x2f: ret = banshee->pci_regs[0x2f]; break; case 0x30: ret = banshee->pci_regs[0x30] & 0x01; break; /*BIOS ROM address*/ case 0x31: ret = 0x00; break; @@ -2633,6 +2667,37 @@ static void *banshee_init_common(const device_t *info, wchar_t *fn, int has_sgra banshee->i2c_ddc = i2c_gpio_init("ddc_voodoo_banshee"); banshee->ddc = ddc_init(i2c_gpio_get_bus(banshee->i2c_ddc)); + switch (type) + { + case TYPE_BANSHEE: + if (has_sgram) { + banshee->pci_regs[0x2c] = 0x1a; + banshee->pci_regs[0x2d] = 0x12; + banshee->pci_regs[0x2e] = 0x04; + banshee->pci_regs[0x2f] = 0x00; + } else { + banshee->pci_regs[0x2c] = 0x02; + banshee->pci_regs[0x2d] = 0x11; + banshee->pci_regs[0x2e] = 0x17; + banshee->pci_regs[0x2f] = 0x10; + } + break; + + case TYPE_V3_2000: + banshee->pci_regs[0x2c] = 0x1a; + banshee->pci_regs[0x2d] = 0x12; + banshee->pci_regs[0x2e] = 0x30; + banshee->pci_regs[0x2f] = 0x00; + break; + + case TYPE_V3_3000: + banshee->pci_regs[0x2c] = 0x1a; + banshee->pci_regs[0x2d] = 0x12; + banshee->pci_regs[0x2e] = 0x3a; + banshee->pci_regs[0x2f] = 0x00; + break; + } + video_inform(VIDEO_FLAG_TYPE_SPECIAL, &timing_banshee); return banshee; diff --git a/src/video/vid_voodoo_banshee_blitter.c b/src/video/vid_voodoo_banshee_blitter.c index 2e5beef91..13ed8edbd 100644 --- a/src/video/vid_voodoo_banshee_blitter.c +++ b/src/video/vid_voodoo_banshee_blitter.c @@ -43,6 +43,7 @@ #define COMMAND_INITIATE (1 << 8) #define COMMAND_INC_X_START (1 << 10) #define COMMAND_INC_Y_START (1 << 11) +#define COMMAND_STIPPLE_LINE (1 << 12) #define COMMAND_PATTERN_MONO (1 << 13) #define COMMAND_DX (1 << 14) #define COMMAND_DY (1 << 15) @@ -246,6 +247,50 @@ static void PLOT(voodoo_t *voodoo, int x, int y, int pat_x, int pat_y, uint8_t p } } +static void PLOT_LINE(voodoo_t *voodoo, int x, int y, uint8_t rop, uint32_t pattern, int src_colorkey) +{ + switch (voodoo->banshee_blt.dstFormat & DST_FORMAT_COL_MASK) + { + case DST_FORMAT_COL_8_BPP: + { + uint32_t addr = get_addr(voodoo, x, y, 0, 0);//(voodoo->banshee_blt.dstBaseAddr + x + y*voodoo->banshee_blt.dst_stride) & voodoo->fb_mask; + uint32_t dest = voodoo->vram[addr]; + + voodoo->vram[addr] = MIX(voodoo, dest, voodoo->banshee_blt.colorFore, pattern, src_colorkey, COLORKEY_8); + voodoo->changedvram[addr >> 12] = changeframecount; + break; + } + case DST_FORMAT_COL_16_BPP: + { + uint32_t addr = get_addr(voodoo, x*2, y, 0, 0);//(voodoo->banshee_blt.dstBaseAddr + x*2 + y*voodoo->banshee_blt.dst_stride) & voodoo->fb_mask; + uint32_t dest = *(uint16_t *)&voodoo->vram[addr]; + + *(uint16_t *)&voodoo->vram[addr] = MIX(voodoo, dest, voodoo->banshee_blt.colorFore, pattern, src_colorkey, COLORKEY_16); + voodoo->changedvram[addr >> 12] = changeframecount; + break; + } + case DST_FORMAT_COL_24_BPP: + { + uint32_t addr = get_addr(voodoo, x*3, y, 0, 0);//(voodoo->banshee_blt.dstBaseAddr + x*3 + y*voodoo->banshee_blt.dst_stride) & voodoo->fb_mask; + uint32_t dest = *(uint32_t *)&voodoo->vram[addr]; + + *(uint32_t *)&voodoo->vram[addr] = (MIX(voodoo, dest, voodoo->banshee_blt.colorFore, pattern, src_colorkey, COLORKEY_32) & 0xffffff) | (dest & 0xff000000); + voodoo->changedvram[addr >> 12] = changeframecount; + break; + } + case DST_FORMAT_COL_32_BPP: + { + uint32_t addr = get_addr(voodoo, x*4, y, 0, 0);//(voodoo->banshee_blt.dstBaseAddr + x*4 + y*voodoo->banshee_blt.dst_stride) & voodoo->fb_mask; + uint32_t dest = *(uint32_t *)&voodoo->vram[addr]; + + *(uint32_t *)&voodoo->vram[addr] = MIX(voodoo, dest, voodoo->banshee_blt.colorFore, pattern, src_colorkey, COLORKEY_32); + voodoo->changedvram[addr >> 12] = changeframecount; + break; + } + } +} + + static void update_src_stride(voodoo_t *voodoo) { int bpp; @@ -858,14 +903,24 @@ static void banshee_do_host_to_screen_stretch_blt(voodoo_t *voodoo, int count, u } } -static void banshee_do_line(voodoo_t *voodoo) +static void step_line(voodoo_t *voodoo) +{ + if (voodoo->banshee_blt.line_pix_pos == voodoo->banshee_blt.line_rep_cnt) + { + voodoo->banshee_blt.line_pix_pos = 0; + if (voodoo->banshee_blt.line_bit_pos == voodoo->banshee_blt.line_bit_mask_size) + voodoo->banshee_blt.line_bit_pos = 0; + else + voodoo->banshee_blt.line_bit_pos++; + } + else + voodoo->banshee_blt.line_pix_pos++; +} + + +static void banshee_do_line(voodoo_t *voodoo, int draw_last_pixel) { clip_t *clip = &voodoo->banshee_blt.clip[(voodoo->banshee_blt.command & COMMAND_CLIP_SEL) ? 1 : 0]; - uint8_t *pattern_mono = (uint8_t *)voodoo->banshee_blt.colorPattern; - int pat_y = (voodoo->banshee_blt.commandExtra & CMDEXTRA_FORCE_PAT_ROW0) ? 0 : (voodoo->banshee_blt.patoff_y + voodoo->banshee_blt.srcY); - int pat_x = voodoo->banshee_blt.patoff_x + voodoo->banshee_blt.srcX; - int use_pattern_trans = (voodoo->banshee_blt.command & (COMMAND_PATTERN_MONO | COMMAND_TRANS_MONO)) == - (COMMAND_PATTERN_MONO | COMMAND_TRANS_MONO); uint8_t rop = voodoo->banshee_blt.command >> 24; int dx = ABS(voodoo->banshee_blt.dstX - voodoo->banshee_blt.srcX); int dy = ABS(voodoo->banshee_blt.dstY - voodoo->banshee_blt.srcY); @@ -874,27 +929,28 @@ static void banshee_do_line(voodoo_t *voodoo) int x = voodoo->banshee_blt.srcX; int y = voodoo->banshee_blt.srcY; int error; + uint32_t stipple = (voodoo->banshee_blt.command & COMMAND_STIPPLE_LINE) ? + voodoo->banshee_blt.lineStipple : ~0; if (dx > dy) /*X major*/ { error = dx/2; while (x != voodoo->banshee_blt.dstX) { - uint8_t pattern_mask = pattern_mono[pat_y & 7]; - int pattern_trans = use_pattern_trans ? (pattern_mask & (1 << (7-(pat_x & 7)))) : 1; + int mask = stipple & (1 << voodoo->banshee_blt.line_bit_pos); + int pattern_trans = (voodoo->banshee_blt.command & COMMAND_TRANS_MONO) ? mask : 1; if (y >= clip->y_min && y < clip->y_max && x >= clip->x_min && x < clip->x_max && pattern_trans) - PLOT(voodoo, x, y, pat_x, pat_y, pattern_mask, rop, voodoo->banshee_blt.colorFore, COLORKEY_32); + PLOT_LINE(voodoo, x, y, rop, mask ? voodoo->banshee_blt.colorFore : voodoo->banshee_blt.colorBack, COLORKEY_32); error -= dy; if (error < 0) { error += dx; y += y_inc; - pat_y += y_inc; } x += x_inc; - pat_x += x_inc; + step_line(voodoo); } } else /*Y major*/ @@ -902,24 +958,32 @@ static void banshee_do_line(voodoo_t *voodoo) error = dy/2; while (y != voodoo->banshee_blt.dstY) { - uint8_t pattern_mask = pattern_mono[pat_y & 7]; - int pattern_trans = use_pattern_trans ? (pattern_mask & (1 << (7-(pat_x & 7)))) : 1; + int mask = stipple & (1 << voodoo->banshee_blt.line_bit_pos); + int pattern_trans = (voodoo->banshee_blt.command & COMMAND_TRANS_MONO) ? mask : 1; if (y >= clip->y_min && y < clip->y_max && x >= clip->x_min && x < clip->x_max && pattern_trans) - PLOT(voodoo, x, y, pat_x, pat_y, pattern_mask, rop, voodoo->banshee_blt.colorFore, COLORKEY_32); + PLOT_LINE(voodoo, x, y, rop, mask ? voodoo->banshee_blt.colorFore : voodoo->banshee_blt.colorBack, COLORKEY_32); error -= dx; if (error < 0) { error += dy; x += x_inc; - pat_x += x_inc; } y += y_inc; - pat_y += y_inc; + step_line(voodoo); } } + if (draw_last_pixel) + { + int mask = stipple & (1 << voodoo->banshee_blt.line_bit_pos); + int pattern_trans = (voodoo->banshee_blt.command & COMMAND_TRANS_MONO) ? mask : 1; + + if (y >= clip->y_min && y < clip->y_max && x >= clip->x_min && x < clip->x_max && pattern_trans) + PLOT_LINE(voodoo, x, y, rop, mask ? voodoo->banshee_blt.colorFore : voodoo->banshee_blt.colorBack, COLORKEY_32); + } + voodoo->banshee_blt.srcXY = (x & 0xffff) | (y << 16); voodoo->banshee_blt.srcX = x; voodoo->banshee_blt.srcY = y; @@ -1057,8 +1121,12 @@ static void banshee_do_2d_blit(voodoo_t *voodoo, int count, uint32_t data) banshee_do_rectfill(voodoo); break; + case COMMAND_CMD_LINE: + banshee_do_line(voodoo, 1); + break; + case COMMAND_CMD_POLYLINE: - banshee_do_line(voodoo); + banshee_do_line(voodoo, 0); break; default: @@ -1141,6 +1209,16 @@ void voodoo_2d_reg_writel(voodoo_t *voodoo, uint32_t addr, uint32_t val) case 0x38: voodoo->banshee_blt.commandExtra = val; // bansheeblt_log("commandExtra=%08x\n", val); + break; + case 0x3c: + voodoo->banshee_blt.lineStipple = val; + break; + case 0x40: + voodoo->banshee_blt.lineStyle = val; + voodoo->banshee_blt.line_rep_cnt = val & 0xff; + voodoo->banshee_blt.line_bit_mask_size = (val >> 8) & 0x1f; + voodoo->banshee_blt.line_pix_pos = (val >> 16) & 0xff; + voodoo->banshee_blt.line_bit_pos = (val >> 24) & 0x1f; break; case 0x44: voodoo->banshee_blt.colorPattern[0] = val; @@ -1321,18 +1399,18 @@ void voodoo_2d_reg_writel(voodoo_t *voodoo, uint32_t addr, uint32_t val) banshee_do_rectfill(voodoo); break; -/* case COMMAND_CMD_LINE: + case COMMAND_CMD_LINE: voodoo->banshee_blt.dstXY = val; voodoo->banshee_blt.dstX = ((int32_t)(val << 19)) >> 19; voodoo->banshee_blt.dstY = ((int32_t)(val << 3)) >> 19; - banshee_do_line(voodoo); - break;*/ + banshee_do_line(voodoo, 1); + break; case COMMAND_CMD_POLYLINE: voodoo->banshee_blt.dstXY = val; voodoo->banshee_blt.dstX = ((int32_t)(val << 19)) >> 19; voodoo->banshee_blt.dstY = ((int32_t)(val << 3)) >> 19; - banshee_do_line(voodoo); + banshee_do_line(voodoo, 0); break; case COMMAND_CMD_POLYFILL: diff --git a/src/video/vid_voodoo_fifo.c b/src/video/vid_voodoo_fifo.c index 597c45051..9e1f9c61f 100644 --- a/src/video/vid_voodoo_fifo.c +++ b/src/video/vid_voodoo_fifo.c @@ -155,16 +155,19 @@ void voodoo_wait_for_swap_complete(voodoo_t *voodoo) static uint32_t cmdfifo_get(voodoo_t *voodoo) { uint32_t val; - - while (voodoo->cmdfifo_depth_rd == voodoo->cmdfifo_depth_wr) - { - thread_wait_event(voodoo->wake_fifo_thread, -1); - thread_reset_event(voodoo->wake_fifo_thread); - } + + if (!voodoo->cmdfifo_in_sub) { + while (voodoo->cmdfifo_depth_rd == voodoo->cmdfifo_depth_wr) + { + thread_wait_event(voodoo->wake_fifo_thread, -1); + thread_reset_event(voodoo->wake_fifo_thread); + } + } val = *(uint32_t *)&voodoo->fb_mem[voodoo->cmdfifo_rp & voodoo->fb_mask]; - voodoo->cmdfifo_depth_rd++; + if (!voodoo->cmdfifo_in_sub) + voodoo->cmdfifo_depth_rd++; voodoo->cmdfifo_rp += 4; // voodoo_fifo_log(" CMDFIFO get %08x\n", val); @@ -285,7 +288,7 @@ void voodoo_fifo_thread(void *param) voodoo->time += end_time - start_time; } - while (voodoo->cmdfifo_enabled && voodoo->cmdfifo_depth_rd != voodoo->cmdfifo_depth_wr) + while (voodoo->cmdfifo_enabled && (voodoo->cmdfifo_depth_rd != voodoo->cmdfifo_depth_wr || voodoo->cmdfifo_in_sub)) { uint64_t start_time = plat_timer_read(); uint64_t end_time; @@ -308,6 +311,18 @@ void voodoo_fifo_thread(void *param) case 0: /*NOP*/ break; + case 1: /*JSR*/ +// voodoo_fifo_log("JSR %08x\n", (header >> 4) & 0xfffffc); + voodoo->cmdfifo_ret_addr = voodoo->cmdfifo_rp; + voodoo->cmdfifo_rp = (header >> 4) & 0xfffffc; + voodoo->cmdfifo_in_sub = 1; + break; + + case 2: /*RET*/ + voodoo->cmdfifo_rp = voodoo->cmdfifo_ret_addr; + voodoo->cmdfifo_in_sub = 0; + break; + case 3: /*JMP local frame buffer*/ voodoo->cmdfifo_rp = (header >> 4) & 0xfffffc; // voodoo_fifo_log("JMP to %08x %04x\n", voodoo->cmdfifo_rp, header); diff --git a/src/video/vid_voodoo_setup.c b/src/video/vid_voodoo_setup.c index 283208960..74f312fd4 100644 --- a/src/video/vid_voodoo_setup.c +++ b/src/video/vid_voodoo_setup.c @@ -36,6 +36,24 @@ #include <86box/vid_voodoo_render.h> #include <86box/vid_voodoo_setup.h> +#ifdef ENABLE_VOODOO_SETUP_LOG +int voodoo_setup_do_log = ENABLE_VOODOO_SETUP_LOG; + +static void +voodoo_setup_log(const char *fmt, ...) +{ + va_list ap; + + if (voodoo_setup_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +} +#else +#define voodoo_setup_log(fmt, ...) +#endif + void voodoo_triangle_setup(voodoo_t *voodoo) { @@ -152,8 +170,10 @@ void voodoo_triangle_setup(voodoo_t *voodoo) voodoo->params.vertexCx = (int32_t)(int16_t)((int32_t)(verts[vc].sVx * 16.0f) & 0xffff); voodoo->params.vertexCy = (int32_t)(int16_t)((int32_t)(verts[vc].sVy * 16.0f) & 0xffff); - if (voodoo->params.vertexAy > voodoo->params.vertexBy || voodoo->params.vertexBy > voodoo->params.vertexCy) - fatal("triangle_setup wrong order %d %d %d\n", voodoo->params.vertexAy, voodoo->params.vertexBy, voodoo->params.vertexCy); + if (voodoo->params.vertexAy > voodoo->params.vertexBy || voodoo->params.vertexBy > voodoo->params.vertexCy) { + voodoo_setup_log("triangle_setup wrong order %d %d %d\n", voodoo->params.vertexAy, voodoo->params.vertexBy, voodoo->params.vertexCy); + return; + } if (voodoo->sSetupMode & SETUPMODE_RGB) { diff --git a/src/video/video.c b/src/video/video.c index ce77e9abf..2b1a9bcd5 100644 --- a/src/video/video.c +++ b/src/video/video.c @@ -336,7 +336,7 @@ static png_infop info_ptr; static void -video_take_screenshot(const wchar_t *fn, int startx, int starty, int w, int h) +video_take_screenshot(const wchar_t *fn, int startx, int starty, int y1, int y2, int w, int h) { int i, x, y; png_bytep *b_rgb = NULL; @@ -382,7 +382,7 @@ video_take_screenshot(const wchar_t *fn, int startx, int starty, int w, int h) for (y = 0; y < h; ++y) { b_rgb[y] = (png_byte *) malloc(png_get_rowbytes(png_ptr, info_ptr)); for (x = 0; x < w; ++x) { - temp = render_buffer->line[y + starty][x + startx]; + temp = render_buffer->dat[(y * w) + x]; b_rgb[y][(x) * 3 + 0] = (temp >> 16) & 0xff; b_rgb[y][(x) * 3 + 1] = (temp >> 8) & 0xff; @@ -407,7 +407,7 @@ video_take_screenshot(const wchar_t *fn, int startx, int starty, int w, int h) static void -video_screenshot(int x, int y, int w, int h) +video_screenshot(int x, int y, int y1, int y2, int w, int h) { wchar_t path[1024], fn[128]; @@ -426,7 +426,7 @@ video_screenshot(int x, int y, int w, int h) video_log("taking screenshot to: %S\n", path); - video_take_screenshot((const wchar_t *) path, x, y, w, h); + video_take_screenshot((const wchar_t *) path, x, y, y1, y2, w, h); png_destroy_write_struct(&png_ptr, &info_ptr); } @@ -449,20 +449,20 @@ video_blit_memtoscreen(int x, int y, int y1, int y2, int w, int h) { int yy; - if ((w > 0) && (h > 0)) { - for (yy = 0; yy < h; yy++) { + if (y2 > 0) { + for (yy = y1; yy < y2; yy++) { if (((y + yy) >= 0) && ((y + yy) < buffer32->h)) { if (video_grayscale || invert_display) - video_transform_copy(&(render_buffer->line[y + yy][x]), &(buffer32->line[y + yy][x]), w); + video_transform_copy(&(render_buffer->dat)[yy * w], &(buffer32->line[y + yy][x]), w); else - memcpy(&(render_buffer->line[y + yy][x]), &(buffer32->line[y + yy][x]), w << 2); + memcpy(&(render_buffer->dat)[yy * w], &(buffer32->line[y + yy][x]), w << 2); } } } if (screenshots) { if (render_buffer != NULL) - video_screenshot(x, y, w, h); + video_screenshot(x, y, y1, y2, w, h); screenshots--; video_log("screenshot taken, %i left\n", screenshots); } diff --git a/src/vnc.c b/src/vnc.c index 5813767fb..09c00831f 100644 --- a/src/vnc.c +++ b/src/vnc.c @@ -176,7 +176,7 @@ vnc_blit(int x, int y, int y1, int y2, int w, int h) p = (uint32_t *)&(((uint32_t *)rfb->frameBuffer)[yy*VNC_MAX_X]); if ((y+yy) >= 0 && (y+yy) < VNC_MAX_Y) - memcpy(p, &(render_buffer->line[y+yy][x]), w*4); + memcpy(p, &(render_buffer->dat[yy * w]), w*4); } video_blit_complete(); diff --git a/src/win/86Box.rc b/src/win/86Box.rc index d3529a82f..ae7f632e6 100644 --- a/src/win/86Box.rc +++ b/src/win/86Box.rc @@ -66,7 +66,8 @@ BEGIN POPUP "Re&nderer" BEGIN MENUITEM "&SDL (Software)", IDM_VID_SDL_SW - MENUITEM "&SDL (Hardware)", IDM_VID_SDL_HW + MENUITEM "SDL (&Hardware)", IDM_VID_SDL_HW + MENUITEM "SDL (&OpenGL)", IDM_VID_SDL_OPENGL #ifdef USE_VNC MENUITEM "&VNC", IDM_VID_VNC #endif @@ -324,7 +325,7 @@ FONT 9, "Segoe UI" BEGIN DEFPUSHBUTTON "OK",IDOK,246,235,50,14 PUSHBUTTON "Cancel",IDCANCEL,307,235,50,14 - CONTROL "List2",IDC_SETTINGSCATLIST,"SysListView32",LVS_LIST | + CONTROL "List2",IDC_SETTINGSCATLIST,"SysListView32",LVS_REPORT | LVS_NOCOLUMNHEADER | LVS_SHOWSELALWAYS | LVS_SINGLESEL | WS_BORDER | WS_TABSTOP,7,7,100,212 CONTROL "",-1,"Static",SS_BLACKFRAME | SS_SUNKEN,1,226,373,1 /* Leave this commented out until we get into localization. */ @@ -335,45 +336,45 @@ BEGIN #endif END -DLG_CFG_MACHINE DIALOG DISCARDABLE 107, 0, 305, 199 +DLG_CFG_MACHINE DIALOG DISCARDABLE 107, 0, 305, 200 STYLE DS_CONTROL | WS_CHILD FONT 9, "Segoe UI" BEGIN COMBOBOX IDC_COMBO_MACHINE_TYPE,71,7,189,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Machine type:",IDT_1708,7,8,60,10 + LTEXT "Machine type:",IDT_1708,7,9,60,10 COMBOBOX IDC_COMBO_MACHINE,71,26,138,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Machine:",IDT_1701,7,27,60,10 + LTEXT "Machine:",IDT_1701,7,28,60,10 PUSHBUTTON "Configure",IDC_CONFIGURE_MACHINE,214,26,46,12 - COMBOBOX IDC_COMBO_CPU_TYPE,71,44,110,120,CBS_DROPDOWNLIST | + COMBOBOX IDC_COMBO_CPU_TYPE,71,45,110,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "CPU type:",IDT_1702,7,45,59,10 - COMBOBOX IDC_COMBO_CPU,215,44,45,120,CBS_DROPDOWNLIST | + LTEXT "CPU type:",IDT_1702,7,47,59,10 + COMBOBOX IDC_COMBO_CPU,215,45,45,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Speed:",IDT_1704,189,45,24,10 - COMBOBOX IDC_COMBO_FPU,71,63,189,120,CBS_DROPDOWNLIST | WS_VSCROLL | + LTEXT "Speed:",IDT_1704,189,47,24,10 + COMBOBOX IDC_COMBO_FPU,71,64,189,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "FPU:",IDT_1707,7,63,59,10 - COMBOBOX IDC_COMBO_WS,71,82,189,120,CBS_DROPDOWNLIST | WS_VSCROLL | + LTEXT "FPU:",IDT_1707,7,66,59,10 + COMBOBOX IDC_COMBO_WS,71,83,189,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Wait states:",IDT_1703,7,83,60,10 - EDITTEXT IDC_MEMTEXT,70,101,45,12,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "Wait states:",IDT_1703,7,85,60,10 + EDITTEXT IDC_MEMTEXT,70,102,45,12,ES_AUTOHSCROLL | ES_NUMBER CONTROL "",IDC_MEMSPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS,113,101, 12,12 - LTEXT "MB",IDT_1705,123,102,10,10 - LTEXT "Memory:",IDT_1706,7,102,30,10 - GROUPBOX "Time synchronization",IDC_TIME_SYNC,7,134,100,56 + LTEXT "MB",IDT_1705,123,104,10,10 + LTEXT "Memory:",IDT_1706,7,104,30,10 + GROUPBOX "Time synchronization",IDC_TIME_SYNC,7,135,100,56 CONTROL "Disabled",IDC_RADIO_TS_DISABLED,"Button", - BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,146,84,10 + BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,147,84,10 CONTROL "Enabled (local time)", IDC_RADIO_TS_LOCAL,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,14,160,84,10 + BS_AUTORADIOBUTTON | WS_TABSTOP,14,161,84,10 CONTROL "Enabled (UTC)", IDC_RADIO_TS_UTC,"Button", - BS_AUTORADIOBUTTON | WS_TABSTOP,14,174,84,10 + BS_AUTORADIOBUTTON | WS_TABSTOP,14,175,84,10 #ifdef USE_DYNAREC CONTROL "Dynamic Recompiler",IDC_CHECK_DYNAREC,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,7,119,94,10 + BS_AUTOCHECKBOX | WS_TABSTOP,7,120,94,10 #endif END @@ -381,7 +382,7 @@ DLG_CFG_VIDEO DIALOG DISCARDABLE 107, 0, 267, 45 STYLE DS_CONTROL | WS_CHILD FONT 9, "Segoe UI" BEGIN - LTEXT "Video:",IDT_1707,7,8,48,10 + LTEXT "Video:",IDT_1707,7,9,48,10 COMBOBOX IDC_COMBO_VIDEO,64,7,155,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP PUSHBUTTON "Configure",IDC_CONFIGURE_VID,222,7,38,12 @@ -394,11 +395,11 @@ DLG_CFG_INPUT DIALOG DISCARDABLE 107, 0, 267, 65 STYLE DS_CONTROL | WS_CHILD FONT 9, "Segoe UI" BEGIN - LTEXT "Mouse :",IDT_1709,7,8,57,10 + LTEXT "Mouse :",IDT_1709,7,9,57,10 COMBOBOX IDC_COMBO_MOUSE,71,7,140,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP PUSHBUTTON "Configure",IDC_CONFIGURE_MOUSE,214,7,46,12 - LTEXT "Joystick :",IDT_1710,7,26,58,10 + LTEXT "Joystick :",IDT_1710,7,27,58,10 COMBOBOX IDC_COMBO_JOYSTICK,71,25,189,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP PUSHBUTTON "Joystick 1...",IDC_JOY1,7,44,50,14 @@ -407,73 +408,73 @@ BEGIN PUSHBUTTON "Joystick 4...",IDC_JOY4,209,44,50,14 END -DLG_CFG_SOUND DIALOG DISCARDABLE 107, 0, 267, 199 +DLG_CFG_SOUND DIALOG DISCARDABLE 107, 0, 267, 201 STYLE DS_CONTROL | WS_CHILD FONT 9, "Segoe UI" BEGIN COMBOBOX IDC_COMBO_SOUND,71,7,140,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Sound card:",IDT_1711,7,8,59,10 + LTEXT "Sound card:",IDT_1711,7,9,59,10 PUSHBUTTON "Configure",IDC_CONFIGURE_SND,214,7,46,12 - COMBOBOX IDC_COMBO_MIDI,71,25,140,120,CBS_DROPDOWNLIST | WS_VSCROLL | + COMBOBOX IDC_COMBO_MIDI,71,26,140,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "MIDI Out Device:",IDT_1712,7,26,59,10 - PUSHBUTTON "Configure",IDC_CONFIGURE_MIDI,214,25,46,12 + LTEXT "MIDI Out Device:",IDT_1712,7,28,59,10 + PUSHBUTTON "Configure",IDC_CONFIGURE_MIDI,214,26,46,12 - COMBOBOX IDC_COMBO_MIDI_IN,71,43,140,120,CBS_DROPDOWNLIST | WS_VSCROLL | + COMBOBOX IDC_COMBO_MIDI_IN,71,45,140,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "MIDI In Device:",IDT_1713,7,44,59,10 - PUSHBUTTON "Configure",IDC_CONFIGURE_MIDI_IN,214,43,46,12 + LTEXT "MIDI In Device:",IDT_1713,7,47,59,10 + PUSHBUTTON "Configure",IDC_CONFIGURE_MIDI_IN,214,45,46,12 CONTROL "Standalone MPU-401",IDC_CHECK_MPU401,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,7,65,199,10 + BS_AUTOCHECKBOX | WS_TABSTOP,7,66,199,10 PUSHBUTTON "Configure",IDC_CONFIGURE_MPU401,214,64,46,12 CONTROL "Innovation SSI-2001",IDC_CHECK_SSI,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,7,83,94,10 + BS_AUTOCHECKBOX | WS_TABSTOP,7,84,95,10 CONTROL "CMS / Game Blaster",IDC_CHECK_CMS,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,147,83,94,10 + BS_AUTOCHECKBOX | WS_TABSTOP,147,84,95,10 CONTROL "Gravis Ultrasound",IDC_CHECK_GUS,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,7,99,94,10 - PUSHBUTTON "Configure",IDC_CONFIGURE_GUS,214,99,46,12 + BS_AUTOCHECKBOX | WS_TABSTOP,7,101,94,10 + PUSHBUTTON "Configure",IDC_CONFIGURE_GUS,214,101,46,12 CONTROL "Use FLOAT32 sound",IDC_CHECK_FLOAT,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,7,115,94,10 + BS_AUTOCHECKBOX | WS_TABSTOP,7,117,94,10 END -DLG_CFG_NETWORK DIALOG DISCARDABLE 107, 0, 267, 63 +DLG_CFG_NETWORK DIALOG DISCARDABLE 107, 0, 267, 65 STYLE DS_CONTROL | WS_CHILD FONT 9, "Segoe UI" BEGIN - LTEXT "Network type:",IDT_1714,7,8,59,10 + LTEXT "Network type:",IDT_1714,7,9,59,10 COMBOBOX IDC_COMBO_NET_TYPE,71,7,189,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "PCap device:",IDT_1715,7,26,59,10 - COMBOBOX IDC_COMBO_PCAP,71,25,189,120,CBS_DROPDOWNLIST | WS_VSCROLL | + LTEXT "PCap device:",IDT_1715,7,28,59,10 + COMBOBOX IDC_COMBO_PCAP,71,26,189,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Network adapter:",IDT_1716,7,44,59,10 - COMBOBOX IDC_COMBO_NET,71,43,140,120,CBS_DROPDOWNLIST | WS_VSCROLL | + LTEXT "Network adapter:",IDT_1716,7,47,59,10 + COMBOBOX IDC_COMBO_NET,71,45,140,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Configure",IDC_CONFIGURE_NET,214,43,46,12 + PUSHBUTTON "Configure",IDC_CONFIGURE_NET,214,44,46,12 END DLG_CFG_PORTS DIALOG DISCARDABLE 107, 0, 267, 135 STYLE DS_CONTROL | WS_CHILD FONT 9, "Segoe UI" BEGIN - LTEXT "LPT1 Device:",IDT_1717,7,8,61,10 + LTEXT "LPT1 Device:",IDT_1717,7,9,61,10 COMBOBOX IDC_COMBO_LPT1,71,7,189,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "LPT2 Device:",IDT_1718,7,27,61,10 + LTEXT "LPT2 Device:",IDT_1718,7,28,61,10 COMBOBOX IDC_COMBO_LPT2,71,26,189,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "LPT3 Device:",IDT_1719,7,46,61,10 + LTEXT "LPT3 Device:",IDT_1719,7,47,61,10 COMBOBOX IDC_COMBO_LPT3,71,45,189,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP @@ -494,61 +495,32 @@ BEGIN BS_AUTOCHECKBOX | WS_TABSTOP,7,118,94,10 END -DLG_CFG_PERIPHERALS DIALOG DISCARDABLE 107, 0, 267, 220 +DLG_CFG_STORAGE DIALOG DISCARDABLE 107, 0, 267, 111 STYLE DS_CONTROL | WS_CHILD FONT 9, "Segoe UI" BEGIN - LTEXT "SCSI Controller:",IDT_1717,7,8,48,10 + LTEXT "SCSI Controller:",IDT_1717,7,9,48,10 COMBOBOX IDC_COMBO_SCSI,64,7,155,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP PUSHBUTTON "Configure",IDC_CONFIGURE_SCSI,222,7,38,12 - LTEXT "HD Controller:",IDT_1718,7,26,48,10 - COMBOBOX IDC_COMBO_HDC,64,25,155,120,CBS_DROPDOWNLIST | + LTEXT "HD Controller:",IDT_1718,7,28,48,10 + COMBOBOX IDC_COMBO_HDC,64,26,155,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Configure",IDC_CONFIGURE_HDC,222,25,38,12 + PUSHBUTTON "Configure",IDC_CONFIGURE_HDC,222,26,38,12 - LTEXT "FD Controller:",IDT_1768,7,44,48,10 - COMBOBOX IDC_COMBO_FDC,64,43,155,120,CBS_DROPDOWNLIST | + LTEXT "FD Controller:",IDT_1768,7,47,48,10 + COMBOBOX IDC_COMBO_FDC,64,45,155,120,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Configure",IDC_CONFIGURE_FDC,222,43,38,12 + PUSHBUTTON "Configure",IDC_CONFIGURE_FDC,222,45,38,12 CONTROL "Tertiary IDE Controller",IDC_CHECK_IDE_TER,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,7,62,199,10 - PUSHBUTTON "Configure",IDC_BUTTON_IDE_TER,222,61,38,12 + BS_AUTOCHECKBOX | WS_TABSTOP,7,66,199,10 + PUSHBUTTON "Configure",IDC_BUTTON_IDE_TER,222,64,38,12 CONTROL "Quaternary IDE Controller",IDC_CHECK_IDE_QUA,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,7,80,199,10 - PUSHBUTTON "Configure",IDC_BUTTON_IDE_QUA,222,79,38,12 - - CONTROL "ISABugger device",IDC_CHECK_BUGGER,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,7,98,94,10 - - CONTROL "POST card",IDC_CHECK_POSTCARD,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,147,98,94,10 - - LTEXT "ISA RTC",IDT_1767,7,117,48,10 - COMBOBOX IDC_COMBO_ISARTC,64,116,155,120, - CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Configure",IDC_CONFIGURE_ISARTC,222,116,38,12 - - GROUPBOX "ISA Memory Expansion",IDC_GROUP_ISAMEM,7,136,255,70 - LTEXT "#1:",IDT_1763,12,148,21,10 - COMBOBOX IDC_COMBO_ISAMEM_1,25,147,190,120, - CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Configure",IDC_CONFIGURE_ISAMEM_1,217,147,38,12 - LTEXT "#2:",IDT_1764,12,162,21,10 - COMBOBOX IDC_COMBO_ISAMEM_2,25,161,190,120, - CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Configure",IDC_CONFIGURE_ISAMEM_2,217,161,38,12 - LTEXT "#3:",IDT_1765,12,176,21,10 - COMBOBOX IDC_COMBO_ISAMEM_3,25,175,190,120, - CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Configure",IDC_CONFIGURE_ISAMEM_3,217,175,38,12 - LTEXT "#4:",IDT_1766,12,190,21,10 - COMBOBOX IDC_COMBO_ISAMEM_4,25,189,190,120, - CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - PUSHBUTTON "Configure",IDC_CONFIGURE_ISAMEM_4,217,189,38,12 + BS_AUTOCHECKBOX | WS_TABSTOP,7,85,199,10 + PUSHBUTTON "Configure",IDC_BUTTON_IDE_QUA,222,83,38,12 END DLG_CFG_HARD_DISKS DIALOG DISCARDABLE 107, 0, 267, 154 @@ -575,13 +547,13 @@ BEGIN WS_VSCROLL | WS_TABSTOP END -DLG_CFG_HARD_DISKS_ADD DIALOG DISCARDABLE 0, 0, 219, 111 +DLG_CFG_HARD_DISKS_ADD DIALOG DISCARDABLE 0, 0, 219, 149 STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Add Hard Disk" FONT 9, "Segoe UI" BEGIN - DEFPUSHBUTTON "OK",IDOK,55,89,50,14 - PUSHBUTTON "Cancel",IDCANCEL,112,89,50,14 + DEFPUSHBUTTON "OK",IDOK,55,127,50,14 + PUSHBUTTON "Cancel",IDCANCEL,112,127,50,14 EDITTEXT IDC_EDIT_HD_FILE_NAME,7,16,153,12 PUSHBUTTON "&Specify...",IDC_CFILE,167,16,44,12 EDITTEXT IDC_EDIT_HD_SPT,183,34,28,12 @@ -607,6 +579,12 @@ BEGIN LTEXT "ID:",IDT_1723,99,73,34,8 COMBOBOX IDC_COMBO_HD_CHANNEL_IDE,134,71,77,12,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Image Format:",IDT_1774,7,92,50,12 + COMBOBOX IDC_COMBO_HD_IMG_FORMAT,58,90,153,12,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP + LTEXT "Block Size:",IDT_1775,7,111,50,12 + COMBOBOX IDC_COMBO_HD_BLOCK_SIZE,58,109,153,12,CBS_DROPDOWNLIST | + WS_VSCROLL | WS_TABSTOP LTEXT "Progress:",IDT_1752,7,7,204,9 CONTROL "IMGCreateProgress",IDC_PBAR_IMG_CREATE,"msctls_progress32",PBS_SMOOTH | WS_BORDER,7,16,204,12 @@ -685,6 +663,40 @@ BEGIN BS_AUTOCHECKBOX | WS_TABSTOP,218,205,44,10 END +DLG_CFG_PERIPHERALS DIALOG DISCARDABLE 107, 0, 267, 154 +STYLE DS_CONTROL | WS_CHILD +FONT 9, "Segoe UI" +BEGIN + LTEXT "ISA RTC:",IDT_1767,7,9,48,10 + COMBOBOX IDC_COMBO_ISARTC,64,7,155,120, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Configure",IDC_CONFIGURE_ISARTC,222,7,38,12 + + GROUPBOX "ISA Memory Expansion",IDC_GROUP_ISAMEM,7,28,253,93 + LTEXT "Card 1:",IDT_1763,16,45,48,10 + COMBOBOX IDC_COMBO_ISAMEM_1,73,43,137,120, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Configure",IDC_CONFIGURE_ISAMEM_1,213,43,38,12 + LTEXT "Card 2:",IDT_1764,16,64,48,10 + COMBOBOX IDC_COMBO_ISAMEM_2,73,62,137,120, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Configure",IDC_CONFIGURE_ISAMEM_2,213,62,38,12 + LTEXT "Card 3:",IDT_1765,16,83,48,10 + COMBOBOX IDC_COMBO_ISAMEM_3,73,81,137,120, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Configure",IDC_CONFIGURE_ISAMEM_3,213,81,38,12 + LTEXT "Card 4:",IDT_1766,16,102,48,10 + COMBOBOX IDC_COMBO_ISAMEM_4,73,100,137,120, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "Configure",IDC_CONFIGURE_ISAMEM_4,213,100,38,12 + + CONTROL "ISABugger device",IDC_CHECK_BUGGER,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,7,128,94,10 + + CONTROL "POST card",IDC_CHECK_POSTCARD,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,147,128,94,10 +END + ///////////////////////////////////////////////////////////////////////////// // @@ -747,6 +759,7 @@ END 249 ICON DISCARDABLE "win/icons/cdrom_disabled.ico" 250 ICON DISCARDABLE "win/icons/zip_disabled.ico" 251 ICON DISCARDABLE "win/icons/mo_disabled.ico" +252 ICON DISCARDABLE "win/icons/storage_controllers.ico" #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// @@ -908,7 +921,7 @@ BEGIN 2048 "86Box" IDS_2049 "Error" IDS_2050 "Fatal error" - IDS_2051 "Are you sure you want to save the settings?" + IDS_2051 "" IDS_2052 "Press CTRL+ALT+PAGE DOWN to return to windowed mode." IDS_2053 "Speed" IDS_2054 "ZIP %03i %i (%s): %ls" @@ -932,11 +945,11 @@ BEGIN IDS_2068 "Sound" IDS_2069 "Network" IDS_2070 "Ports (COM & LPT)" - IDS_2071 "Other peripherals" + IDS_2071 "Storage controllers" IDS_2072 "Hard disks" IDS_2073 "Floppy & CD-ROM drives" IDS_2074 "Other removable devices" - IDS_2075 "CD-ROM images (*.ISO;*.CUE)\0*.ISO;*.CUE\0All files (*.*)\0*.*\0" + IDS_2075 "Other peripherals" IDS_2076 "Surface images (*.86F)\0*.86F\0" IDS_2077 "Click to capture mouse" IDS_2078 "Press F8+F12 to release mouse" @@ -986,9 +999,9 @@ BEGIN IDS_2118 "Internal controller" IDS_2119 "Exit" IDS_2120 "No ROMs found" - IDS_2121 "Save changes\nThis will hard reset the emulated machine." - IDS_2122 "Discard changes\nAll changes made to the settings will be lost." - IDS_2123 "Cancel\nGo back to the Settings window." + IDS_2121 "Do you want to save the settings?" + IDS_2122 "This will hard reset the virtual machine." + IDS_2123 "Save" IDS_2124 "About 86Box" IDS_2125 "86Box v" EMU_VERSION IDS_2126 "An emulator of old computers\n\nAuthors: Sarah Walker, Miran Grca, Fred N. van Kempen (waltje), SA1988, MoochMcGee, reenigne, leilei, JohnElliott, greatpsycho, and others.\n\nReleased under the GNU General Public License version 2. See LICENSE for more information." @@ -1021,10 +1034,12 @@ BEGIN IDS_2133 LIB_NAME_FLUIDSYNTH " is required for FluidSynth MIDI output." IDS_2134 "Entering fullscreen mode" IDS_2135 "Don't show this message again" - IDS_2136 "Don't Exit" + IDS_2136 "Don't exit" IDS_2137 "Reset" - IDS_2138 "Don't Reset" + IDS_2138 "Don't reset" IDS_2139 "MO images (*.IM?;*.MDI)\0*.IM?;*.MDI\0All files (*.*)\0*.*\0" + IDS_2140 "CD-ROM images (*.ISO;*.CUE)\0*.ISO;*.CUE\0All files (*.*)\0*.*\0" + IDS_2141 "%hs Device Configuration" END STRINGTABLE DISCARDABLE @@ -1054,7 +1069,20 @@ BEGIN IDS_4118 "The selected file will be overwritten. Are you sure you want to use it?" IDS_4119 "Unsupported disk image" IDS_4120 "Overwrite" - IDS_4121 "Don't Overwrite" + IDS_4121 "Don't overwrite" + IDS_4122 "Raw image (.img)" + IDS_4123 "HDI image (.hdi)" + IDS_4124 "HDX image (.hdx)" + IDS_4125 "Fixed-size VHD (.vhd)" + IDS_4126 "Dynamic-size VHD (.vhd)" + IDS_4127 "Differencing VHD (.vhd)" + IDS_4128 "Large blocks (2 MB)" + IDS_4129 "Small blocks (512 KB)" + IDS_4130 "VHD files (*.VHD)\0*.VHD\0All files (*.*)\0*.*\0" + IDS_4131 "Select the parent VHD" + IDS_4132 "This could mean that the parent image was modified after the differencing image was created.\n\nIt can also happen if the image files were moved or copied, or by a bug in the program that created this disk.\n\nDo you want to fix the timestamps?" + IDS_4133 "Parent and child disk timestamps do not match" + IDS_4134 "Could not fix VHD timestamp." IDS_4352 "MFM/RLL" IDS_4353 "XTA" diff --git a/src/win/Makefile.mingw b/src/win/Makefile.mingw index 819f1092b..1c541c254 100644 --- a/src/win/Makefile.mingw +++ b/src/win/Makefile.mingw @@ -303,7 +303,7 @@ endif # Nothing should need changing from here on.. # ######################################################################### VPATH := $(EXPATH) . $(CODEGEN) cpu \ - cdrom chipset device disk floppy \ + cdrom chipset device disk disk/minivhd floppy \ game machine mem printer \ sio sound \ sound/munt sound/munt/c_interface sound/munt/sha1 \ @@ -509,10 +509,6 @@ ifeq ($(AMD_K5), y) OPTS += -DUSE_AMD_K5 endif -ifeq ($(CL5422), y) -OPTS += -DUSE_CL5422 -endif - ifeq ($(CYRIX_6X86), y) OPTS += -DUSE_CYRIX_6X86 endif @@ -690,6 +686,10 @@ HDDOBJ := hdd.o \ hdc_xtide.o hdc_ide.o \ hdc_ide_opti611.o \ hdc_ide_cmd640.o hdc_ide_sff8038i.o + +MINIVHDOBJ := cwalk.o libxml2_encoding.o minivhd_convert.o \ + minivhd_create.o minivhd_io.o minivhd_manage.o \ + minivhd_struct_rw.o minivhd_util.o CDROMOBJ := cdrom.o \ cdrom_image_backend.o cdrom_image.o @@ -796,7 +796,7 @@ else endif OBJ := $(MAINOBJ) $(CPUOBJ) $(CHIPSETOBJ) $(MCHOBJ) $(DEVOBJ) $(MEMOBJ) \ - $(FDDOBJ) $(GAMEOBJ) $(CDROMOBJ) $(ZIPOBJ) $(MOOBJ) $(HDDOBJ) \ + $(FDDOBJ) $(GAMEOBJ) $(CDROMOBJ) $(ZIPOBJ) $(MOOBJ) $(HDDOBJ) $(MINIVHDOBJ) \ $(NETOBJ) $(PRINTOBJ) $(SCSIOBJ) $(SIOOBJ) $(SNDOBJ) $(VIDOBJ) \ $(PLATOBJ) $(UIOBJ) $(FSYNTHOBJ) $(MUNTOBJ) $(DEVBROBJ) \ $(DISCORDOBJ) @@ -813,7 +813,7 @@ endif ifneq ($(WX), n) LIBS += $(WX_LIBS) -lm endif -LIBS += -lpng -lz -lwsock32 -lshell32 -liphlpapi -lpsapi -lSDL2 -limm32 -lhid -lsetupapi -loleaut32 -lversion -lwinmm -static -lstdc++ +LIBS += -lpng -lz -lwsock32 -lshell32 -liphlpapi -lpsapi -lSDL2 -limm32 -lhid -lsetupapi -loleaut32 -luxtheme -lversion -lwinmm -static -lstdc++ ifneq ($(X64), y) LIBS += -Wl,--large-address-aware endif diff --git a/src/win/icons/storage_controllers.ico b/src/win/icons/storage_controllers.ico new file mode 100644 index 000000000..8fd38558a Binary files /dev/null and b/src/win/icons/storage_controllers.ico differ diff --git a/src/win/win.c b/src/win/win.c index 541a05d1a..5322d6c55 100644 --- a/src/win/win.c +++ b/src/win/win.c @@ -21,6 +21,7 @@ #define UNICODE #define NTDDI_VERSION 0x06010000 #include +#include #include #include #include @@ -86,21 +87,14 @@ static const struct { void (*resize)(int x, int y); int (*pause)(void); void (*enable)(int enable); -} vid_apis[2][RENDERERS_NUM] = { - { - { "SDL_Software", 1, (int(*)(void*))sdl_inits, sdl_close, NULL, sdl_pause, sdl_enable }, - { "SDL_Hardware", 1, (int(*)(void*))sdl_inith, sdl_close, NULL, sdl_pause, sdl_enable } + void (*set_fs)(int fs); +} vid_apis[RENDERERS_NUM] = { + { "SDL_Software", 1, (int(*)(void*))sdl_inits, sdl_close, NULL, sdl_pause, sdl_enable, sdl_set_fs }, + { "SDL_Hardware", 1, (int(*)(void*))sdl_inith, sdl_close, NULL, sdl_pause, sdl_enable, sdl_set_fs }, + { "SDL_OpenGL", 1, (int(*)(void*))sdl_initho, sdl_close, NULL, sdl_pause, sdl_enable, sdl_set_fs } #ifdef USE_VNC - ,{ "VNC", 0, vnc_init, vnc_close, vnc_resize, vnc_pause, NULL } + ,{ "VNC", 0, vnc_init, vnc_close, vnc_resize, vnc_pause, NULL, NULL } #endif - }, - { - { "SDL_Software", 1, (int(*)(void*))sdl_inits_fs, sdl_close, sdl_resize, sdl_pause, sdl_enable }, - { "SDL_Hardware", 1, (int(*)(void*))sdl_inith_fs, sdl_close, sdl_resize, sdl_pause, sdl_enable } -#ifdef USE_VNC - ,{ "VNC", 0, vnc_init, vnc_close, vnc_resize, vnc_pause, NULL } -#endif - }, }; @@ -679,8 +673,8 @@ plat_vidapi(char *name) if (!strcasecmp(name, "ddraw") || !strcasecmp(name, "sdl")) return(1); for (i = 0; i < RENDERERS_NUM; i++) { - if (vid_apis[0][i].name && - !strcasecmp(vid_apis[0][i].name, name)) return(i); + if (vid_apis[i].name && + !strcasecmp(vid_apis[i].name, name)) return(i); } /* Default value. */ @@ -700,9 +694,12 @@ plat_vidapi_name(int api) break; case 1: break; + case 2: + name = "sdl_opengl"; + break; #ifdef USE_VNC - case 2: + case 3: name = "vnc"; break; #endif @@ -725,16 +722,16 @@ plat_setvid(int api) video_wait_for_blit(); /* Close the (old) API. */ - vid_apis[0][vid_api].close(); + vid_apis[vid_api].close(); vid_api = api; - if (vid_apis[0][vid_api].local) + if (vid_apis[vid_api].local) ShowWindow(hwndRender, SW_SHOW); else ShowWindow(hwndRender, SW_HIDE); /* Initialize the (new) API. */ - i = vid_apis[0][vid_api].init((void *)hwndRender); + i = vid_apis[vid_api].init((void *)hwndRender); endblit(); if (! i) return(0); @@ -750,11 +747,11 @@ plat_setvid(int api) void plat_vidsize(int x, int y) { - if (!vid_api_inited || !vid_apis[video_fullscreen][vid_api].resize) return; + if (!vid_api_inited || !vid_apis[vid_api].resize) return; startblit(); video_wait_for_blit(); - vid_apis[video_fullscreen][vid_api].resize(x, y); + vid_apis[vid_api].resize(x, y); endblit(); } @@ -762,59 +759,93 @@ plat_vidsize(int x, int y) void plat_vidapi_enable(int enable) { - if (!vid_api_inited || !vid_apis[video_fullscreen][vid_api].enable) return; + int i = 1; + + if (!vid_api_inited || !vid_apis[vid_api].enable) + return; - startblit(); video_wait_for_blit(); - vid_apis[video_fullscreen][vid_api].enable(enable); - endblit(); + vid_apis[vid_api].enable(enable != 0); + + if (! i) + return; + + if (enable) + device_force_redraw(); } + int get_vidpause(void) { - return(vid_apis[video_fullscreen][vid_api].pause()); + return(vid_apis[vid_api].pause()); } void plat_setfullscreen(int on) { - HWND *hw; + RECT rect; + int temp_x, temp_y; + int dpi = win_get_dpi(hwndMain); - /* Want off and already off? */ - if (!on && !video_fullscreen) return; - - /* Want on and already on? */ - if (on && video_fullscreen) return; + /* Are we changing from the same state to the same state? */ + if ((!!on) == (!!video_fullscreen)) + return; if (on && video_fullscreen_first) { + video_fullscreen |= 2; if (ui_msgbox_header(MBX_INFO | MBX_DONTASK, (wchar_t *) IDS_2134, (wchar_t *) IDS_2052) == 10) { video_fullscreen_first = 0; config_save(); } + video_fullscreen &= 1; } /* OK, claim the video. */ - startblit(); video_wait_for_blit(); - - plat_vidapi_enable(0); - win_mouse_close(); /* Close the current mode, and open the new one. */ - vid_apis[video_fullscreen][vid_api].close(); - video_fullscreen = on; - hw = (video_fullscreen) ? &hwndMain : &hwndRender; - vid_apis[video_fullscreen][vid_api].init((void *) *hw); + video_fullscreen = on | 2; + if (vid_apis[vid_api].set_fs) + vid_apis[vid_api].set_fs(on); + if (!on) { + plat_resize(scrnsz_x, scrnsz_y); + if (vid_resize) { + /* scale the screen base on DPI */ + if (dpi_scale) { + temp_x = MulDiv(unscaled_size_x, dpi, 96); + temp_y = MulDiv(unscaled_size_y, dpi, 96); + } else { + temp_x = unscaled_size_x; + temp_y = unscaled_size_y; + } + /* Main Window. */ + ResizeWindowByClientArea(hwndMain, temp_x, temp_y + sbar_height); + + /* Render window. */ + MoveWindow(hwndRender, 0, 0, temp_x, temp_y, TRUE); + GetWindowRect(hwndRender, &rect); + + /* Status bar. */ + MoveWindow(hwndSBAR, 0, rect.bottom, temp_x, 17, TRUE); + + if (mouse_capture) + ClipCursor(&rect); + + scrnsz_x = unscaled_size_x; + scrnsz_y = unscaled_size_y; + } + } + video_fullscreen &= 1; + video_force_resize_set(1); + if (!on) + doresize = 1; win_mouse_init(); - plat_vidapi_enable(1); - /* Release video and make it redraw the screen. */ - endblit(); device_force_redraw(); /* Send a CTRL break code so CTRL does not get stuck. */ @@ -823,6 +854,10 @@ plat_setfullscreen(int on) /* Finally, handle the host's mouse cursor. */ /* win_log("%s full screen, %s cursor\n", on ? "enter" : "leave", on ? "hide" : "show"); */ show_cursor(video_fullscreen ? 0 : -1); + + /* This is needed for OpenGL. */ + plat_vidapi_enable(0); + plat_vidapi_enable(1); } diff --git a/src/win/win_devconf.c b/src/win/win_devconf.c index 280dce484..d0469660c 100644 --- a/src/win/win_devconf.c +++ b/src/win/win_devconf.c @@ -412,7 +412,7 @@ deviceconfig_dlgproc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) ws[c] = 0; } - if (!file_dlg(hdlg, ws, s, 0)) + if (!file_dlg(hdlg, ws, s, NULL, 0)) SendMessage(h, WM_SETTEXT, 0, (LPARAM)wopenfilestring); } break; @@ -454,7 +454,8 @@ deviceconfig_inst_open(HWND hwnd, const device_t *device, int inst) *data++ = 0; /*no menu*/ *data++ = 0; /*predefined dialog box class*/ - data += MultiByteToWideChar(CP_ACP, 0, "Device Configuration", -1, data, 120); + + data += wsprintf(data, plat_get_string(IDS_2141), device->name) + 1; *data++ = 9; /*Point*/ data += MultiByteToWideChar(CP_ACP, 0, "Segoe UI", -1, data, 120); @@ -499,7 +500,7 @@ deviceconfig_inst_open(HWND hwnd, const device_t *device, int inst) item->cx = 140; item->cy = 150; - item->style = WS_CHILD | WS_VISIBLE | CBS_DROPDOWN | WS_VSCROLL; + item->style = WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | WS_VSCROLL; data = (uint16_t *)(item + 1); *data++ = 0xFFFF; @@ -514,7 +515,7 @@ deviceconfig_inst_open(HWND hwnd, const device_t *device, int inst) /*Static text*/ item = (DLGITEMTEMPLATE *)data; item->x = 10; - item->y = y; + item->y = y + 2; item->id = id++; item->cx = 60; @@ -561,7 +562,7 @@ deviceconfig_inst_open(HWND hwnd, const device_t *device, int inst) /*Static text*/ item = (DLGITEMTEMPLATE *)data; item->x = 10; - item->y = y; + item->y = y + 2; item->id = id++; item->cx = 60; @@ -628,7 +629,7 @@ deviceconfig_inst_open(HWND hwnd, const device_t *device, int inst) /*Static text*/ item = (DLGITEMTEMPLATE *)data; item->x = 10; - item->y = y; + item->y = y + 2; item->id = id++; item->cx = 60; @@ -659,8 +660,8 @@ deviceconfig_inst_open(HWND hwnd, const device_t *device, int inst) dlg->cdit = (id - IDC_CONFIG_BASE) + 2; item = (DLGITEMTEMPLATE *)data; - item->x = 20; - item->y = y; + item->x = 100; + item->y = y + 5; item->cx = 50; item->cy = 14; item->id = IDOK; /* OK button identifier */ @@ -677,8 +678,8 @@ deviceconfig_inst_open(HWND hwnd, const device_t *device, int inst) data++; item = (DLGITEMTEMPLATE *)data; - item->x = 80; - item->y = y; + item->x = 160; + item->y = y + 5; item->cx = 50; item->cy = 14; item->id = IDCANCEL; /* OK button identifier */ @@ -691,7 +692,7 @@ deviceconfig_inst_open(HWND hwnd, const device_t *device, int inst) data += MultiByteToWideChar(CP_ACP, 0, "Cancel", -1, data, 50); *data++ = 0; /* no creation data */ - dlg->cy = y + 20; + dlg->cy = y + 25; device_set_context(&config_device, device, inst); diff --git a/src/win/win_dialog.c b/src/win/win_dialog.c index 563920cc3..83f19b4f1 100644 --- a/src/win/win_dialog.c +++ b/src/win/win_dialog.c @@ -100,13 +100,14 @@ ui_msgbox_ex(int flags, void *header, void *message, void *btn1, void *btn2, voi case MBX_QUESTION: /* question */ case MBX_QUESTION_YN: + case MBX_QUESTION_OK: if (!btn1) /* replace default "OK" button with "Yes" button */ - tdconfig.dwCommonButtons = TDCBF_YES_BUTTON; + tdconfig.dwCommonButtons = (flags & MBX_QUESTION_OK) ? TDCBF_OK_BUTTON : TDCBF_YES_BUTTON; if (btn2) /* "No" button */ tdbuttons[tdconfig.cButtons++] = tdb_no; else - tdconfig.dwCommonButtons |= TDCBF_NO_BUTTON; + tdconfig.dwCommonButtons |= (flags & MBX_QUESTION_OK) ? TDCBF_CANCEL_BUTTON : TDCBF_NO_BUTTON; if (flags & MBX_QUESTION) { if (btn3) /* "Cancel" button */ @@ -114,6 +115,9 @@ ui_msgbox_ex(int flags, void *header, void *message, void *btn1, void *btn2, voi else tdconfig.dwCommonButtons |= TDCBF_CANCEL_BUTTON; } + + if (flags & MBX_WARNING) + tdconfig.pszMainIcon = TD_WARNING_ICON; break; } @@ -152,7 +156,7 @@ ui_msgbox_ex(int flags, void *header, void *message, void *btn1, void *btn2, voi int -file_dlg_w(HWND hwnd, WCHAR *f, WCHAR *fn, int save) +file_dlg_w(HWND hwnd, WCHAR *f, WCHAR *fn, WCHAR *title, int save) { OPENFILENAME ofn; BOOL r; @@ -177,7 +181,9 @@ file_dlg_w(HWND hwnd, WCHAR *f, WCHAR *fn, int save) ofn.lpstrInitialDir = NULL; ofn.Flags = OFN_PATHMUSTEXIST; if (! save) - ofn.Flags |= OFN_FILEMUSTEXIST; + ofn.Flags |= OFN_FILEMUSTEXIST; + if (title) + ofn.lpstrTitle = title; /* Display the Open dialog box. */ if (save) @@ -199,37 +205,44 @@ file_dlg_w(HWND hwnd, WCHAR *f, WCHAR *fn, int save) int -file_dlg(HWND hwnd, WCHAR *f, char *fn, int save) +file_dlg(HWND hwnd, WCHAR *f, char *fn, char *title, int save) { - WCHAR ufn[512]; + WCHAR ufn[512], title_buf[512]; mbstowcs(ufn, fn, strlen(fn) + 1); + if (title) + mbstowcs(title_buf, title, sizeof title_buf); - return(file_dlg_w(hwnd, f, ufn, save)); + return(file_dlg_w(hwnd, f, ufn, title ? title_buf : NULL, save)); } int -file_dlg_mb(HWND hwnd, char *f, char *fn, int save) +file_dlg_mb(HWND hwnd, char *f, char *fn, char *title, int save) { - WCHAR uf[512], ufn[512]; + WCHAR uf[512], ufn[512], title_buf[512]; mbstowcs(uf, f, strlen(fn) + 1); mbstowcs(ufn, fn, strlen(fn) + 1); + if (title) + mbstowcs(title_buf, title, sizeof title_buf); - return(file_dlg_w(hwnd, uf, ufn, save)); + return(file_dlg_w(hwnd, uf, ufn, title ? title_buf : NULL, save)); } int -file_dlg_w_st(HWND hwnd, int id, WCHAR *fn, int save) +file_dlg_w_st(HWND hwnd, int id, WCHAR *fn, char *title, int save) { - return(file_dlg_w(hwnd, plat_get_string(id), fn, save)); + WCHAR title_buf[512]; + if (title) + mbstowcs(title_buf, title, sizeof title_buf); + return(file_dlg_w(hwnd, plat_get_string(id), fn, title ? title_buf : NULL, save)); } int -file_dlg_st(HWND hwnd, int id, char *fn, int save) -{ - return(file_dlg(hwnd, plat_get_string(id), fn, save)); +file_dlg_st(HWND hwnd, int id, char *fn, char *title, int save) +{ + return(file_dlg(hwnd, plat_get_string(id), fn, title, save)); } diff --git a/src/win/win_media_menu.c b/src/win/win_media_menu.c index 45963c15a..e04329c93 100644 --- a/src/win/win_media_menu.c +++ b/src/win/win_media_menu.c @@ -413,7 +413,7 @@ media_menu_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) wp = 1; /* FALLTHROUGH */ case IDM_FLOPPY_IMAGE_EXISTING: - ret = file_dlg_w_st(hwnd, IDS_2109, floppyfns[id], 0); + ret = file_dlg_w_st(hwnd, IDS_2109, floppyfns[id], NULL, 0); if (! ret) { floppy_mount(id, wopenfilestring, wp); } @@ -424,7 +424,7 @@ media_menu_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) break; case IDM_FLOPPY_EXPORT_TO_86F: - ret = file_dlg_w_st(hwnd, IDS_2076, floppyfns[id], 1); + ret = file_dlg_w_st(hwnd, IDS_2076, floppyfns[id], NULL, 1); if (! ret) { plat_pause(1); ret = d86f_export(id, wopenfilestring); @@ -450,7 +450,7 @@ media_menu_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) break; case IDM_CDROM_IMAGE: - if (!file_dlg_w_st(hwnd, IDS_2075, cdrom[id].image_path, 0)) { + if (!file_dlg_w_st(hwnd, IDS_2140, cdrom[id].image_path, NULL, 0)) { cdrom_mount(id, wopenfilestring); } break; @@ -463,7 +463,7 @@ media_menu_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) wp = 1; /* FALLTHROUGH */ case IDM_ZIP_IMAGE_EXISTING: - ret = file_dlg_w_st(hwnd, IDS_2058, zip_drives[id].image_path, 0); + ret = file_dlg_w_st(hwnd, IDS_2058, zip_drives[id].image_path, NULL, 0); if (! ret) zip_mount(id, wopenfilestring, wp); break; @@ -484,7 +484,7 @@ media_menu_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) wp = 1; /* FALLTHROUGH */ case IDM_MO_IMAGE_EXISTING: - ret = file_dlg_w_st(hwnd, IDS_2116, mo_drives[id].image_path, 0); + ret = file_dlg_w_st(hwnd, IDS_2116, mo_drives[id].image_path, NULL, 0); if (! ret) mo_mount(id, wopenfilestring, wp); break; diff --git a/src/win/win_new_floppy.c b/src/win/win_new_floppy.c index 9845c77cf..090bb9df4 100644 --- a/src/win/win_new_floppy.c +++ b/src/win/win_new_floppy.c @@ -777,7 +777,7 @@ NewFloppyDialogProcedure(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) return TRUE; case IDC_CFILE: - if (!file_dlg_w(hdlg, plat_get_string(is_mo ? IDS_2139 : (is_zip ? IDS_2055 : IDS_2062)), L"", 1)) { + if (!file_dlg_w(hdlg, plat_get_string(is_mo ? IDS_2139 : (is_zip ? IDS_2055 : IDS_2062)), L"", NULL, 1)) { if (!wcschr(wopenfilestring, L'.')) { if (wcslen(wopenfilestring) && (wcslen(wopenfilestring) <= 256)) { twcs = &wopenfilestring[wcslen(wopenfilestring)]; diff --git a/src/win/win_sdl.c b/src/win/win_sdl.c index 9d3a9b4cd..862927f59 100644 --- a/src/win/win_sdl.c +++ b/src/win/win_sdl.c @@ -75,20 +75,63 @@ #define RENDERER_FULL_SCREEN 1 #define RENDERER_HARDWARE 2 +#define RENDERER_OPENGL 4 static SDL_Window *sdl_win = NULL; static SDL_Renderer *sdl_render = NULL; static SDL_Texture *sdl_tex = NULL; static HWND sdl_parent_hwnd = NULL; -static HWND sdl_hwnd = NULL; static int sdl_w, sdl_h; -static int sdl_fs; +static int sdl_fs, sdl_flags = -1; static int cur_w, cur_h; +static int cur_wx = 0, cur_wy = 0, cur_ww =0, cur_wh = 0; static volatile int sdl_enabled = 0; static SDL_mutex* sdl_mutex = NULL; +typedef struct +{ + const void *magic; + Uint32 id; + char *title; + SDL_Surface *icon; + int x, y; + int w, h; + int min_w, min_h; + int max_w, max_h; + Uint32 flags; + Uint32 last_fullscreen_flags; + + /* Stored position and size for windowed mode */ + SDL_Rect windowed; + + SDL_DisplayMode fullscreen_mode; + + float brightness; + Uint16 *gamma; + Uint16 *saved_gamma; /* (just offset into gamma) */ + + SDL_Surface *surface; + SDL_bool surface_valid; + + SDL_bool is_hiding; + SDL_bool is_destroying; + + void *shaper; + + SDL_HitTest hit_test; + void *hit_test_data; + + void *data; + + void *driverdata; + + SDL_Window *prev; + SDL_Window *next; +} SDL_Window_Ex; + + #ifdef ENABLE_SDL_LOG int sdl_do_log = ENABLE_SDL_LOG; @@ -137,6 +180,7 @@ sdl_stretch(int *w, int *h, int *x, int *y) switch (video_fullscreen_scale) { case FULLSCR_SCALE_FULL: + default: *w = sdl_w; *h = sdl_h; *x = 0; @@ -181,8 +225,6 @@ sdl_stretch(int *w, int *h, int *x, int *y) *y = (int) dy; break; } - - sdl_reinit_texture(); } @@ -190,49 +232,23 @@ static void sdl_blit(int x, int y, int y1, int y2, int w, int h) { SDL_Rect r_src; - void *pixeldata; - int pitch; - int yy, ret; + int ret; - if (!sdl_enabled) { - video_blit_complete(); - return; - } - - if ((y1 == y2) || (h <= 0)) { - video_blit_complete(); - return; - } - - if (render_buffer == NULL) { + if (!sdl_enabled || (y1 == y2) || (h <= 0) || (render_buffer == NULL)) { video_blit_complete(); return; } SDL_LockMutex(sdl_mutex); - /* - * TODO: - * SDL_UpdateTexture() might be better here, as it is - * (reportedly) slightly faster. - */ - SDL_LockTexture(sdl_tex, 0, &pixeldata, &pitch); - - for (yy = y1; yy < y2; yy++) { - if ((y + yy) >= 0 && (y + yy) < render_buffer->h) - memcpy((uint32_t *) &(((uint8_t *)pixeldata)[yy * pitch]), &(render_buffer->line[y + yy][x]), w * 4); - } - + r_src.x = 0; + r_src.y = y1; + r_src.w = w; + r_src.h = y2 - y1; + SDL_UpdateTexture(sdl_tex, &r_src, &(render_buffer->dat)[y1 * w], w * 4); video_blit_complete(); - SDL_UnlockTexture(sdl_tex); - - if (sdl_fs) { - sdl_log("sdl_blit(%i, %i, %i, %i, %i, %i) (%i, %i)\n", x, y, y1, y2, w, h, unscaled_size_x, efscrnsz_y); - if (w == unscaled_size_x) - sdl_resize(w, h); - sdl_log("(%08X, %08X, %08X)\n", sdl_win, sdl_render, sdl_tex); - } + SDL_RenderClear(sdl_render); r_src.x = 0; r_src.y = 0; @@ -244,14 +260,36 @@ sdl_blit(int x, int y, int y1, int y2, int w, int h) sdl_log("SDL: unable to copy texture to renderer (%s)\n", sdl_GetError()); SDL_RenderPresent(sdl_render); - SDL_UnlockMutex(sdl_mutex); } +static void +sdl_destroy_window(void) +{ + if (sdl_win != NULL) { + SDL_DestroyWindow(sdl_win); + sdl_win = NULL; + } +} + + +static void +sdl_destroy_texture(void) +{ + /* SDL_DestroyRenderer also automatically destroys all associated textures. */ + if (sdl_render != NULL) { + SDL_DestroyRenderer(sdl_render); + sdl_render = NULL; + } +} + + void sdl_close(void) { + SDL_LockMutex(sdl_mutex); + /* Unregister our renderer! */ video_setblit(NULL); @@ -263,32 +301,10 @@ sdl_close(void) sdl_mutex = NULL; } - if (sdl_tex != NULL) { - SDL_DestroyTexture(sdl_tex); - sdl_tex = NULL; - } - - if (sdl_render != NULL) { - SDL_DestroyRenderer(sdl_render); - sdl_render = NULL; - } - - if (sdl_win != NULL) { - SDL_DestroyWindow(sdl_win); - sdl_win = NULL; - } - - if (sdl_hwnd != NULL) { - plat_set_input(hwndMain); - - ShowWindow(hwndRender, TRUE); - - SetFocus(hwndMain); - - if (sdl_fs) - DestroyWindow(sdl_hwnd); - sdl_hwnd = NULL; - } + sdl_destroy_texture(); + sdl_destroy_window(); + ImmAssociateContext(hwndMain, NULL); + SetFocus(hwndMain); if (sdl_parent_hwnd != NULL) { DestroyWindow(sdl_parent_hwnd); @@ -297,6 +313,7 @@ sdl_close(void) /* Quit. */ SDL_Quit(); + sdl_flags = -1; } @@ -320,13 +337,87 @@ sdl_select_best_hw_driver(void) } +static void +sdl_reinit_texture(void) +{ + if (sdl_flags == -1) + return; + + sdl_destroy_texture(); + + if (sdl_flags & RENDERER_HARDWARE) { + sdl_render = SDL_CreateRenderer(sdl_win, -1, SDL_RENDERER_ACCELERATED); + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2"); + } else + sdl_render = SDL_CreateRenderer(sdl_win, -1, SDL_RENDERER_SOFTWARE); + + sdl_tex = SDL_CreateTexture(sdl_render, SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STREAMING, 2048, 2048); +} + + +void +sdl_set_fs(int fs) +{ + int w = 0, h = 0, x = 0, y = 0; + RECT rect; + + SDL_LockMutex(sdl_mutex); + sdl_enabled = 0; + sdl_destroy_texture(); + + if (fs) { + ShowWindow(sdl_parent_hwnd, TRUE); + SetParent(hwndRender, sdl_parent_hwnd); + ShowWindow(hwndRender, TRUE); + MoveWindow(sdl_parent_hwnd, 0, 0, sdl_w, sdl_h, TRUE); + + /* Show the window, make it topmost, and give it focus. */ + w = unscaled_size_x; + h = efscrnsz_y; + sdl_stretch(&w, &h, &x, &y); + MoveWindow(hwndRender, x, y, w, h, TRUE); + ImmAssociateContext(sdl_parent_hwnd, NULL); + SetFocus(sdl_parent_hwnd); + + /* Redirect RawInput to this new window. */ + old_capture = mouse_capture; + GetWindowRect(hwndRender, &rect); + ClipCursor(&rect); + mouse_capture = 1; + } else { + SetParent(hwndRender, hwndMain); + ShowWindow(sdl_parent_hwnd, FALSE); + ShowWindow(hwndRender, TRUE); + ImmAssociateContext(hwndMain, NULL); + SetFocus(hwndMain); + mouse_capture = old_capture; + + if (mouse_capture) { + GetWindowRect(hwndRender, &rect); + ClipCursor(&rect); + } else + ClipCursor(&oldclip); + } + + sdl_fs = fs; + + if (fs) + sdl_flags |= RENDERER_FULL_SCREEN; + else + sdl_flags &= ~RENDERER_FULL_SCREEN; + + sdl_reinit_texture(); + sdl_enabled = 1; + SDL_UnlockMutex(sdl_mutex); +} + + static int sdl_init_common(int flags) { wchar_t temp[128]; SDL_version ver; - int w = 0, h = 0, x = 0, y = 0; - RECT rect; sdl_log("SDL: init (fs=%d)\n", fs); @@ -340,114 +431,31 @@ sdl_init_common(int flags) return(0); } - if (flags & RENDERER_HARDWARE) - sdl_select_best_hw_driver(); - - if (flags & RENDERER_FULL_SCREEN) { - /* Get the size of the (current) desktop. */ - sdl_w = GetSystemMetrics(SM_CXSCREEN); - sdl_h = GetSystemMetrics(SM_CYSCREEN); - - /* Create the desktop-covering window. */ - _swprintf(temp, L"%s v%s", EMU_NAME_W, EMU_VERSION_W); - sdl_parent_hwnd = CreateWindow(SUB_CLASS_NAME, - temp, - WS_POPUP, - 0, 0, sdl_w, sdl_h, - HWND_DESKTOP, - NULL, - hinstance, - NULL); - - SetWindowPos(sdl_parent_hwnd, HWND_TOPMOST, - 0, 0, sdl_w, sdl_h, SWP_SHOWWINDOW); - - /* Create the actual rendering window. */ - _swprintf(temp, L"%s v%s", EMU_NAME_W, EMU_VERSION_W); - sdl_hwnd = CreateWindow(SUB_CLASS_NAME, - temp, - WS_POPUP, - 0, 0, sdl_w, sdl_h, - sdl_parent_hwnd, - NULL, - hinstance, - NULL); - sdl_log("SDL: FS %dx%d window at %08lx\n", sdl_w, sdl_h, sdl_hwnd); - - /* Redirect RawInput to this new window. */ - plat_set_input(sdl_hwnd); - - SetFocus(sdl_hwnd); - - /* Show the window, make it topmost, and give it focus. */ - w = unscaled_size_x; - h = efscrnsz_y; - sdl_stretch(&w, &h, &x, &y); - SetWindowPos(sdl_hwnd, sdl_parent_hwnd, - x, y, w, h, SWP_SHOWWINDOW); - - /* Now create the SDL window from that. */ - sdl_win = SDL_CreateWindowFrom((void *)sdl_hwnd); - - old_capture = mouse_capture; - - GetWindowRect(sdl_hwnd, &rect); - - ClipCursor(&rect); - - mouse_capture = 1; - } else { - /* Create the SDL window from the render window. */ - sdl_win = SDL_CreateWindowFrom((void *)hwndRender); - - mouse_capture = old_capture; - - if (mouse_capture) { - GetWindowRect(hwndRender, &rect); - - ClipCursor(&rect); - } else { - ClipCursor(&oldclip); - } + if (flags & RENDERER_HARDWARE) { + if (flags & RENDERER_OPENGL) + SDL_SetHint(SDL_HINT_RENDER_DRIVER, "OpenGL"); + else + sdl_select_best_hw_driver(); } + + /* Get the size of the (current) desktop. */ + sdl_w = GetSystemMetrics(SM_CXSCREEN); + sdl_h = GetSystemMetrics(SM_CYSCREEN); + + /* Create the desktop-covering window. */ + _swprintf(temp, L"%s v%s", EMU_NAME_W, EMU_VERSION_W); + sdl_parent_hwnd = CreateWindow(SDL_CLASS_NAME, temp, WS_POPUP, 0, 0, sdl_w, sdl_h, + HWND_DESKTOP, NULL, hinstance, NULL); + ShowWindow(sdl_parent_hwnd, FALSE); + + sdl_flags = flags; + if (sdl_win == NULL) { sdl_log("SDL: unable to CreateWindowFrom (%s)\n", SDL_GetError()); - sdl_close(); - return(0); } - /* - * TODO: - * SDL_RENDERER_SOFTWARE, because SDL tries to do funky stuff - * otherwise (it turns off Win7 Aero and it looks like it's - * trying to switch to fullscreen even though the window is - * not a fullscreen window?) - */ - if (flags & RENDERER_HARDWARE) { - sdl_render = SDL_CreateRenderer(sdl_win, -1, SDL_RENDERER_ACCELERATED); - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "2"); - } else - sdl_render = SDL_CreateRenderer(sdl_win, -1, SDL_RENDERER_SOFTWARE); - - if (sdl_render == NULL) { - sdl_log("SDL: unable to create renderer (%s)\n", SDL_GetError()); - sdl_close(); - return(0); - } - - /* - * TODO: - * Actually the source is (apparently) XRGB8888, but the alpha - * channel seems to be set to 255 everywhere, so ARGB8888 works - * just as well. - */ - sdl_tex = SDL_CreateTexture(sdl_render, SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_STREAMING, 2048, 2048); - if (sdl_tex == NULL) { - sdl_log("SDL: unable to create texture (%s)\n", SDL_GetError()); - sdl_close(); - return(0); - } + sdl_win = SDL_CreateWindowFrom((void *)hwndRender); + sdl_set_fs(video_fullscreen & 1); /* Make sure we get a clean exit. */ atexit(sdl_close); @@ -455,10 +463,7 @@ sdl_init_common(int flags) /* Register our renderer! */ video_setblit(sdl_blit); - sdl_fs = !!(flags & RENDERER_FULL_SCREEN); - sdl_enabled = 1; - sdl_mutex = SDL_CreateMutex(); return(1); @@ -480,28 +485,9 @@ sdl_inith(HWND h) int -sdl_inits_fs(HWND h) +sdl_initho(HWND h) { - return sdl_init_common(RENDERER_FULL_SCREEN); -} - - -int -sdl_inith_fs(HWND h) -{ - return sdl_init_common(RENDERER_FULL_SCREEN | RENDERER_HARDWARE); -} - - -void -sdl_reinit_texture() -{ - if (sdl_render == NULL) - return; - - SDL_DestroyTexture(sdl_tex); - sdl_tex = SDL_CreateTexture(sdl_render, SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_STREAMING, 2048, 2048); + return sdl_init_common(RENDERER_HARDWARE | RENDERER_OPENGL); } @@ -517,26 +503,52 @@ sdl_resize(int x, int y) { int ww = 0, wh = 0, wx = 0, wy = 0; + if (video_fullscreen & 2) + return; + if ((x == cur_w) && (y == cur_h)) return; + SDL_LockMutex(sdl_mutex); + ww = x; wh = y; if (sdl_fs) { sdl_stretch(&ww, &wh, &wx, &wy); - MoveWindow(sdl_hwnd, wx, wy, ww, wh, TRUE); + MoveWindow(hwndRender, wx, wy, ww, wh, TRUE); } cur_w = x; cur_h = y; + cur_wx = wx; + cur_wy = wy; + cur_ww = ww; + cur_wh = wh; + + SDL_SetWindowSize(sdl_win, cur_ww, cur_wh); + SDL_SetWindowPosition(sdl_win, cur_wx, cur_wy); + sdl_reinit_texture(); + + SDL_UnlockMutex(sdl_mutex); } void sdl_enable(int enable) { - sdl_enabled = enable; + if (sdl_flags == -1) + return; + + SDL_LockMutex(sdl_mutex); + sdl_enabled = !!enable; + + if (enable == 1) { + SDL_SetWindowSize(sdl_win, cur_ww, cur_wh); + sdl_reinit_texture(); + } + + SDL_UnlockMutex(sdl_mutex); } diff --git a/src/win/win_settings.c b/src/win/win_settings.c index aad59192c..519da87d0 100644 --- a/src/win/win_settings.c +++ b/src/win/win_settings.c @@ -1,4 +1,4 @@ -/* +/* * 86Box A hypervisor and IBM PC system emulator that specializes in * running old operating systems and software designed for IBM * PC systems and compatibles from 1981 through fairly recent @@ -20,6 +20,7 @@ #define BITMAP WINDOWS_BITMAP #include #include +#include #undef BITMAP #ifdef ENABLE_SETTINGS_LOG #include @@ -64,6 +65,8 @@ #include <86box/plat_midi.h> #include <86box/ui.h> #include <86box/win.h> +#include "../disk/minivhd/minivhd.h" +#include "../disk/minivhd/minivhd_util.h" /* Icon, Bus, File, C, H, S, Size */ @@ -128,7 +131,7 @@ static uint32_t displayed_category = 0; extern int is486; static int listtomachinetype[256], listtomachine[256]; static int listtocpufamily[256], listtocpu[256]; -static int settings_list_to_device[2][20], settings_list_to_fdc[20]; +static int settings_list_to_device[2][256], settings_list_to_fdc[20]; static int settings_list_to_midi[20], settings_list_to_midi_in[20]; static int settings_list_to_hdc[20]; @@ -207,6 +210,17 @@ settings_show_window(HWND hdlg, int id, int condition) } +static void +settings_listview_enable_styles(HWND hdlg, int id) +{ + HWND h; + + h = GetDlgItem(hdlg, id); + SetWindowTheme(h, L"Explorer", NULL); + ListView_SetExtendedListViewStyle(h, LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER); +} + + static void settings_listview_select(HWND hdlg, int id, int selection) { @@ -217,6 +231,17 @@ settings_listview_select(HWND hdlg, int id, int selection) } +static void +settings_process_messages() +{ + MSG msg; + while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + + static BOOL image_list_init(HWND hdlg, int id, const uint8_t *icon_ids) { @@ -494,32 +519,6 @@ win_settings_changed(void) } -static int -settings_msgbox_reset(void) -{ - int changed, i = 0; - HWND h; - - changed = win_settings_changed(); - - if (changed) { - h = hwndMain; - hwndMain = hwndParentDialog; - - i = ui_msgbox_ex(MBX_QUESTION | MBX_LINKS, (wchar_t *) IDS_2051, NULL, (wchar_t *) IDS_2121, (wchar_t *) IDS_2122, (wchar_t *) IDS_2123); - - hwndMain = h; - - if (i == 1) return(1); /* no */ - - if (i == -1) return(0); /* cancel */ - - return(2); /* yes */ - } else - return(1); -} - - /* This saves the settings back to the global variables. */ static void win_settings_save(void) @@ -1067,6 +1066,8 @@ win_settings_video_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) } c++; + + settings_process_messages(); } settings_enable_window(hdlg, IDC_COMBO_VIDEO, !(machines[temp_machine].flags & MACHINE_VIDEO_ONLY)); @@ -1504,14 +1505,14 @@ static LRESULT CALLBACK #else static BOOL CALLBACK #endif -win_settings_peripherals_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) +win_settings_storage_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) { int c, d; - int e, is_at; + int is_at; LPTSTR lptsTemp; char *stransi; - const device_t *scsi_dev, *dev; - const device_t *fdc_dev, *hdc_dev; + const device_t *scsi_dev, *fdc_dev; + const device_t *hdc_dev; switch (message) { case WM_INITDIALOG: @@ -1522,6 +1523,12 @@ win_settings_peripherals_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lPa c = d = 0; settings_reset_content(hdlg, IDC_COMBO_HDC); while (1) { + /* Skip "internal" if machine doesn't have it. */ + if ((c == 1) && !(machines[temp_machine].flags & MACHINE_HDC)) { + c++; + continue; + } + generate_device_name(hdc_get_device(c), hdc_get_internal_name(c), 1); if (!device_name[0]) @@ -1616,48 +1623,6 @@ win_settings_peripherals_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lPa settings_enable_window(hdlg, IDC_BUTTON_IDE_QUA, is_at && temp_ide_qua); settings_set_check(hdlg, IDC_CHECK_IDE_TER, temp_ide_ter); settings_set_check(hdlg, IDC_CHECK_IDE_QUA, temp_ide_qua); - settings_set_check(hdlg, IDC_CHECK_BUGGER, temp_bugger); - settings_set_check(hdlg, IDC_CHECK_POSTCARD, temp_postcard); - - /* Populate the ISA RTC card dropdown. */ - e = 0; - settings_reset_content(hdlg, IDC_COMBO_ISARTC); - for (d = 0; ; d++) { - generate_device_name(isartc_get_device(d), isartc_get_internal_name(d), 0); - - if (!device_name[0]) - break; - - if (d == 0) { - settings_add_string(hdlg, IDC_COMBO_ISARTC, win_get_string(IDS_2103)); - settings_set_cur_sel(hdlg, IDC_COMBO_ISARTC, 0); - } else - settings_add_string(hdlg, IDC_COMBO_ISARTC, (LPARAM) device_name); - settings_list_to_device[1][e] = d; - if (d == temp_isartc) - settings_set_cur_sel(hdlg, IDC_COMBO_ISARTC, e); - e++; - } - settings_enable_window(hdlg, IDC_CONFIGURE_ISARTC, temp_isartc != 0); - - /* Populate the ISA memory card dropdowns. */ - settings_reset_content(hdlg, IDC_COMBO_ISAMEM_1 + c); - for (c = 0; c < ISAMEM_MAX; c++) { - for (d = 0; ; d++) { - generate_device_name(isamem_get_device(d), (char *) isamem_get_internal_name(d), 0); - - if (!device_name[0]) - break; - - if (d == 0) { - settings_add_string(hdlg, IDC_COMBO_ISAMEM_1 + c, win_get_string(IDS_2103)); - settings_set_cur_sel(hdlg, IDC_COMBO_ISAMEM_1 + c, 0); - } else - settings_add_string(hdlg, IDC_COMBO_ISAMEM_1 + c, (LPARAM) device_name); - } - settings_set_cur_sel(hdlg, IDC_COMBO_ISAMEM_1 + c, temp_isamem[c]); - settings_enable_window(hdlg, IDC_CONFIGURE_ISAMEM_1 + c, temp_isamem[c] != 0); - } free(stransi); free(lptsTemp); @@ -1696,30 +1661,6 @@ win_settings_peripherals_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lPa settings_enable_window(hdlg, IDC_CONFIGURE_SCSI, scsi_card_has_config(temp_scsi_card)); break; - case IDC_CONFIGURE_ISARTC: - temp_isartc = settings_list_to_device[1][settings_get_cur_sel(hdlg, IDC_COMBO_ISARTC)]; - temp_deviceconfig |= deviceconfig_open(hdlg, (void *)isartc_get_device(temp_isartc)); - break; - - case IDC_COMBO_ISARTC: - temp_isartc = settings_list_to_device[1][settings_get_cur_sel(hdlg, IDC_COMBO_ISARTC)]; - settings_enable_window(hdlg, IDC_CONFIGURE_ISARTC, temp_isartc != 0); - break; - - case IDC_COMBO_ISAMEM_1: case IDC_COMBO_ISAMEM_2: - case IDC_COMBO_ISAMEM_3: case IDC_COMBO_ISAMEM_4: - c = LOWORD(wParam) - IDC_COMBO_ISAMEM_1; - temp_isamem[c] = settings_get_cur_sel(hdlg, LOWORD(wParam)); - settings_enable_window(hdlg, IDC_CONFIGURE_ISAMEM_1 + c, temp_isamem[c] != 0); - break; - - case IDC_CONFIGURE_ISAMEM_1: case IDC_CONFIGURE_ISAMEM_2: - case IDC_CONFIGURE_ISAMEM_3: case IDC_CONFIGURE_ISAMEM_4: - c = LOWORD(wParam) - IDC_CONFIGURE_ISAMEM_1; - dev = isamem_get_device(temp_isamem[c]); - temp_deviceconfig |= deviceconfig_inst_open(hdlg, (void *)dev, c + 1); - break; - case IDC_CHECK_IDE_TER: temp_ide_ter = settings_get_check(hdlg, IDC_CHECK_IDE_TER); settings_enable_window(hdlg, IDC_BUTTON_IDE_TER, temp_ide_ter); @@ -1744,11 +1685,8 @@ win_settings_peripherals_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lPa temp_hdc = settings_list_to_hdc[settings_get_cur_sel(hdlg, IDC_COMBO_HDC)]; temp_fdc_card = settings_list_to_fdc[settings_get_cur_sel(hdlg, IDC_COMBO_FDC)]; temp_scsi_card = settings_list_to_device[0][settings_get_cur_sel(hdlg, IDC_COMBO_SCSI)]; - temp_isartc = settings_list_to_device[1][settings_get_cur_sel(hdlg, IDC_COMBO_ISARTC)]; temp_ide_ter = settings_get_check(hdlg, IDC_CHECK_IDE_TER); temp_ide_qua = settings_get_check(hdlg, IDC_CHECK_IDE_QUA); - temp_bugger = settings_get_check(hdlg, IDC_CHECK_BUGGER); - temp_postcard = settings_get_check(hdlg, IDC_CHECK_POSTCARD); default: return FALSE; @@ -2160,9 +2098,11 @@ win_settings_hard_disks_update_item(HWND hdlg, int i, int column) wsprintf(szText, plat_get_string(IDS_4610), temp_hdd[i].esdi_channel >> 1, temp_hdd[i].esdi_channel & 1); break; case HDD_BUS_IDE: - case HDD_BUS_ATAPI: wsprintf(szText, plat_get_string(IDS_4611), temp_hdd[i].ide_channel >> 1, temp_hdd[i].ide_channel & 1); break; + case HDD_BUS_ATAPI: + wsprintf(szText, plat_get_string(IDS_4612), temp_hdd[i].ide_channel >> 1, temp_hdd[i].ide_channel & 1); + break; case HDD_BUS_SCSI: wsprintf(szText, plat_get_string(IDS_4613), temp_hdd[i].scsi_id); break; @@ -2229,9 +2169,11 @@ win_settings_hard_disks_recalc_list(HWND hdlg) wsprintf(szText, plat_get_string(IDS_4610), temp_hdd[i].esdi_channel >> 1, temp_hdd[i].esdi_channel & 1); break; case HDD_BUS_IDE: - case HDD_BUS_ATAPI: wsprintf(szText, plat_get_string(IDS_4611), temp_hdd[i].ide_channel >> 1, temp_hdd[i].ide_channel & 1); break; + case HDD_BUS_ATAPI: + wsprintf(szText, plat_get_string(IDS_4612), temp_hdd[i].ide_channel >> 1, temp_hdd[i].ide_channel & 1); + break; case HDD_BUS_SCSI: wsprintf(szText, plat_get_string(IDS_4613), temp_hdd[i].scsi_id); break; @@ -2295,11 +2237,19 @@ static void win_settings_hard_disks_resize_columns(HWND hdlg) { /* Bus, File, Cylinders, Heads, Sectors, Size */ - int iCol, width[C_COLUMNS_HARD_DISKS] = {130, 130, 41, 25, 25, 41}; + int iCol, width[C_COLUMNS_HARD_DISKS] = {104, 177, 50, 26, 32, 50}; + int total = 0; HWND hwndList = GetDlgItem(hdlg, IDC_LIST_HARD_DISKS); + RECT r; - for (iCol = 0; iCol < C_COLUMNS_HARD_DISKS; iCol++) + GetWindowRect(hwndList, &r); + for (iCol = 0; iCol < (C_COLUMNS_HARD_DISKS - 1); iCol++) { + width[iCol] = MulDiv(width[iCol], dpi, 96); + total += width[iCol]; ListView_SetColumnWidth(hwndList, iCol, MulDiv(width[iCol], dpi, 96)); + } + width[C_COLUMNS_HARD_DISKS - 1] = (r.right - r.left) - 4 - total; + ListView_SetColumnWidth(hwndList, C_COLUMNS_HARD_DISKS - 1, width[C_COLUMNS_HARD_DISKS - 1]); } @@ -2318,24 +2268,27 @@ win_settings_hard_disks_init_columns(HWND hdlg) switch(iCol) { case 0: /* Bus */ - lvc.cx = 130; + lvc.cx = 104; + lvc.fmt = LVCFMT_LEFT; + break; + case 1: /* File */ + lvc.cx = 177; lvc.fmt = LVCFMT_LEFT; break; case 2: /* Cylinders */ - lvc.cx = 41; + lvc.cx = 50; lvc.fmt = LVCFMT_RIGHT; break; case 3: /* Heads */ - case 4: /* Sectors */ - lvc.cx = 25; + lvc.cx = 26; lvc.fmt = LVCFMT_RIGHT; break; - case 1: /* File */ - lvc.cx = 130; - lvc.fmt = LVCFMT_LEFT; + case 4: /* Sectors */ + lvc.cx = 32; + lvc.fmt = LVCFMT_RIGHT; break; case 5: /* Size (MB) 8 */ - lvc.cx = 41; + lvc.cx = 50; lvc.fmt = LVCFMT_RIGHT; break; } @@ -2374,6 +2327,17 @@ set_edit_box_contents(HWND hdlg, int id, uint32_t val) SendMessage(h, WM_SETTEXT, (WPARAM) wcslen(szText), (LPARAM) szText); } +static void set_edit_box_text_contents(HWND hdlg, int id, WCHAR* text) +{ + HWND h = GetDlgItem(hdlg, id); + SendMessage(h, WM_SETTEXT, (WPARAM) wcslen(text), (LPARAM) text); +} + +static void get_edit_box_text_contents(HWND hdlg, int id, WCHAR* text_buffer, int buffer_size) +{ + HWND h = GetDlgItem(hdlg, id); + SendMessage(h, WM_GETTEXT, (WPARAM) buffer_size, (LPARAM) text_buffer); +} static int hdconf_initialize_hdt_combo(HWND hdlg) { @@ -2417,6 +2381,151 @@ recalc_selection(HWND hdlg) settings_set_cur_sel(hdlg, IDC_COMBO_HD_TYPE, selection); } +HWND vhd_progress_hdlg; + +static void vhd_progress_callback(uint32_t current_sector, uint32_t total_sectors) +{ + MSG msg; + HWND h = GetDlgItem(vhd_progress_hdlg, IDC_PBAR_IMG_CREATE); + SendMessage(h, PBM_SETPOS, (WPARAM) current_sector, (LPARAM) 0); + while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +/* If the disk geometry requested in the 86Box GUI is not compatible with the internal VHD geometry, + * we adjust it to the next-largest size that is compatible. On average, this will be a difference + * of about 21 MB, and should only be necessary for VHDs larger than 31.5 GB, so should never be more + * than a tenth of a percent change in size. + */ +static void adjust_86box_geometry_for_vhd(MVHDGeom *_86box_geometry, MVHDGeom *vhd_geometry) +{ + if (_86box_geometry->cyl <= 65535) { + vhd_geometry->cyl = _86box_geometry->cyl; + vhd_geometry->heads = _86box_geometry->heads; + vhd_geometry->spt = _86box_geometry->spt; + return; + } + + int desired_sectors = _86box_geometry->cyl * _86box_geometry->heads * _86box_geometry->spt; + if (desired_sectors > 267321600) + desired_sectors = 267321600; + + int remainder = desired_sectors % 85680; /* 8560 is the LCM of 1008 (63*16) and 4080 (255*16) */ + if (remainder > 0) + desired_sectors += (85680 - remainder); + + _86box_geometry->cyl = desired_sectors / (16 * 63); + _86box_geometry->heads = 16; + _86box_geometry->spt = 63; + + vhd_geometry->cyl = desired_sectors / (16 * 255); + vhd_geometry->heads = 16; + vhd_geometry->spt = 255; +} + +static void adjust_vhd_geometry_for_86box(MVHDGeom *vhd_geometry) +{ + if (vhd_geometry->spt <= 63) + return; + + int desired_sectors = vhd_geometry->cyl * vhd_geometry->heads * vhd_geometry->spt; + if (desired_sectors > 267321600) + desired_sectors = 267321600; + + int remainder = desired_sectors % 85680; /* 8560 is the LCM of 1008 (63*16) and 4080 (255*16) */ + if (remainder > 0) + desired_sectors -= remainder; + + vhd_geometry->cyl = desired_sectors / (16 * 63); + vhd_geometry->heads = 16; + vhd_geometry->spt = 63; +} + +static MVHDGeom create_drive_vhd_fixed(char* filename, int cyl, int heads, int spt) +{ + MVHDGeom _86box_geometry = { .cyl = cyl, .heads = heads, .spt = spt }; + MVHDGeom vhd_geometry; + adjust_86box_geometry_for_vhd(&_86box_geometry, &vhd_geometry); + + HWND h = GetDlgItem(vhd_progress_hdlg, IDC_PBAR_IMG_CREATE); + settings_show_window(vhd_progress_hdlg, IDT_1731, FALSE); + settings_show_window(vhd_progress_hdlg, IDC_EDIT_HD_FILE_NAME, FALSE); + settings_show_window(vhd_progress_hdlg, IDC_CFILE, FALSE); + settings_show_window(vhd_progress_hdlg, IDC_PBAR_IMG_CREATE, TRUE); + settings_enable_window(vhd_progress_hdlg, IDT_1752, TRUE); + SendMessage(h, PBM_SETRANGE32, (WPARAM) 0, (LPARAM) vhd_geometry.cyl * vhd_geometry.heads * vhd_geometry.spt); + SendMessage(h, PBM_SETPOS, (WPARAM) 0, (LPARAM) 0); + + int vhd_error = 0; + MVHDMeta *vhd = mvhd_create_fixed(filename, vhd_geometry, &vhd_error, vhd_progress_callback); + if (vhd == NULL) { + _86box_geometry.cyl = 0; + _86box_geometry.heads = 0; + _86box_geometry.spt = 0; + } else { + mvhd_close(vhd); + } + + return _86box_geometry; +} + +static MVHDGeom create_drive_vhd_dynamic(char* filename, int cyl, int heads, int spt, int blocksize) +{ + MVHDGeom _86box_geometry = { .cyl = cyl, .heads = heads, .spt = spt }; + MVHDGeom vhd_geometry; + adjust_86box_geometry_for_vhd(&_86box_geometry, &vhd_geometry); + int vhd_error = 0; + MVHDCreationOptions options; + options.block_size_in_sectors = blocksize; + options.path = filename; + options.size_in_bytes = 0; + options.geometry = vhd_geometry; + options.type = MVHD_TYPE_DYNAMIC; + + MVHDMeta *vhd = mvhd_create_ex(options, &vhd_error); + if (vhd == NULL) { + _86box_geometry.cyl = 0; + _86box_geometry.heads = 0; + _86box_geometry.spt = 0; + } else { + mvhd_close(vhd); + } + + return _86box_geometry; +} + +static MVHDGeom create_drive_vhd_diff(char* filename, char* parent_filename, int blocksize) +{ + int vhd_error = 0; + MVHDCreationOptions options; + options.block_size_in_sectors = blocksize; + options.path = filename; + options.parent_path = parent_filename; + options.type = MVHD_TYPE_DIFF; + + MVHDMeta *vhd = mvhd_create_ex(options, &vhd_error); + MVHDGeom vhd_geometry; + if (vhd == NULL) { + vhd_geometry.cyl = 0; + vhd_geometry.heads = 0; + vhd_geometry.spt = 0; + } else { + vhd_geometry = mvhd_get_geometry(vhd); + + if (vhd_geometry.spt > 63) { + vhd_geometry.cyl = mvhd_calc_size_sectors(&vhd_geometry) / (16 * 63); + vhd_geometry.heads = 16; + vhd_geometry.spt = 63; + } + + mvhd_close(vhd); + } + + return vhd_geometry; +} + #if defined(__amd64__) || defined(__aarch64__) static LRESULT CALLBACK @@ -2425,25 +2534,30 @@ static BOOL CALLBACK #endif win_settings_hard_disks_add_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) { - HWND h; - FILE *f; - uint32_t temp, i = 0, sector_size = 512; - uint32_t zero = 0, base = 0x1000; - uint64_t signature = 0xD778A82044445459ll; - uint64_t temp_size, r = 0; - char buf[512], *big_buf; - int b = 0; - uint8_t channel = 0; - uint8_t id = 0; - wchar_t *twcs; - vhd_footer_t *vft = NULL; - MSG msg; + HWND h; + FILE *f; + uint32_t temp, i = 0, sector_size = 512; + uint32_t zero = 0, base = 0x1000; + uint64_t signature = 0xD778A82044445459ll; + uint64_t r = 0; + char *big_buf; + char hd_file_name_multibyte[1200]; + int b = 0; + int vhd_error = 0; + uint8_t channel = 0; + uint8_t id = 0; + wchar_t *twcs; + int img_format, block_size; + WCHAR text_buf[256]; + RECT rect; + POINT point; + int dlg_height_adjust; - switch (message) { + switch (message) { case WM_INITDIALOG: memset(hd_file_name, 0, sizeof(hd_file_name)); - hdd_ptr = &(temp_hdd[next_free_id]); + hdd_ptr = &(temp_hdd[next_free_id]); SetWindowText(hdlg, plat_get_string((existing & 1) ? IDS_4103 : IDS_4102)); @@ -2457,15 +2571,53 @@ win_settings_hard_disks_add_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM size = (tracks * hpc * spt) << 9; set_edit_box_contents(hdlg, IDC_EDIT_HD_SIZE, (uint32_t) (size >> 20LL)); hdconf_initialize_hdt_combo(hdlg); + + settings_add_string(hdlg, IDC_COMBO_HD_IMG_FORMAT, win_get_string(IDS_4122)); + settings_add_string(hdlg, IDC_COMBO_HD_IMG_FORMAT, win_get_string(IDS_4123)); + settings_add_string(hdlg, IDC_COMBO_HD_IMG_FORMAT, win_get_string(IDS_4124)); + settings_add_string(hdlg, IDC_COMBO_HD_IMG_FORMAT, win_get_string(IDS_4125)); + settings_add_string(hdlg, IDC_COMBO_HD_IMG_FORMAT, win_get_string(IDS_4126)); + settings_add_string(hdlg, IDC_COMBO_HD_IMG_FORMAT, win_get_string(IDS_4127)); + settings_set_cur_sel(hdlg, IDC_COMBO_HD_IMG_FORMAT, 0); + + settings_add_string(hdlg, IDC_COMBO_HD_BLOCK_SIZE, win_get_string(IDS_4128)); + settings_add_string(hdlg, IDC_COMBO_HD_BLOCK_SIZE, win_get_string(IDS_4129)); + settings_set_cur_sel(hdlg, IDC_COMBO_HD_BLOCK_SIZE, 0); + + settings_show_window(hdlg, IDC_COMBO_HD_BLOCK_SIZE, FALSE); + settings_show_window(hdlg, IDT_1775, FALSE); + if (existing & 1) { settings_enable_window(hdlg, IDC_EDIT_HD_SPT, FALSE); settings_enable_window(hdlg, IDC_EDIT_HD_HPC, FALSE); settings_enable_window(hdlg, IDC_EDIT_HD_CYL, FALSE); settings_enable_window(hdlg, IDC_EDIT_HD_SIZE, FALSE); settings_enable_window(hdlg, IDC_COMBO_HD_TYPE, FALSE); + settings_show_window(hdlg, IDC_COMBO_HD_IMG_FORMAT, FALSE); + settings_show_window(hdlg, IDT_1774, FALSE); + + /* adjust window size */ + GetWindowRect(hdlg, &rect); + OffsetRect(&rect, -rect.left, -rect.top); + dlg_height_adjust = rect.bottom / 5; + SetWindowPos(hdlg, NULL, 0, 0, rect.right, rect.bottom - dlg_height_adjust, SWP_NOMOVE | SWP_NOREPOSITION | SWP_NOZORDER); + h = GetDlgItem(hdlg, IDOK); + GetWindowRect(h, &rect); + point.x = rect.left; + point.y = rect.top; + ScreenToClient(hdlg, &point); + SetWindowPos(h, NULL, point.x, point.y - dlg_height_adjust, 0, 0, SWP_NOSIZE | SWP_NOREPOSITION | SWP_NOZORDER); + h = GetDlgItem(hdlg, IDCANCEL); + GetWindowRect(h, &rect); + point.x = rect.left; + point.y = rect.top; + ScreenToClient(hdlg, &point); + SetWindowPos(h, NULL, point.x, point.y - dlg_height_adjust, 0, 0, SWP_NOSIZE | SWP_NOREPOSITION | SWP_NOZORDER); + chs_enabled = 0; } else chs_enabled = 1; + add_locations(hdlg); hdd_ptr->bus = HDD_BUS_IDE; max_spt = 63; @@ -2533,19 +2685,22 @@ win_settings_hard_disks_add_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM memset(hdd_ptr->fn, 0, sizeof(hdd_ptr->fn)); wcscpy(hdd_ptr->fn, hd_file_name); + wcstombs(hd_file_name_multibyte, hd_file_name, sizeof hd_file_name_multibyte); sector_size = 512; if (!(existing & 1) && (wcslen(hd_file_name) > 0)) { - f = _wfopen(hd_file_name, L"wb"); - if (size > 0x1FFFFFFE00ll) { - fclose(f); settings_msgbox_header(MBX_ERROR, (wchar_t *) IDS_4116, (wchar_t *) IDS_4105); - return TRUE; + return TRUE; } - if (image_is_hdi(hd_file_name)) { + img_format = settings_get_cur_sel(hdlg, IDC_COMBO_HD_IMG_FORMAT); + if (img_format < 3) { + f = _wfopen(hd_file_name, L"wb"); + } + + if (img_format == 1) { /* HDI file */ if (size >= 0x100000000ll) { fclose(f); settings_msgbox_header(MBX_ERROR, (wchar_t *) IDS_4116, (wchar_t *) IDS_4104); @@ -2563,7 +2718,7 @@ win_settings_hard_disks_add_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM for (i = 0; i < 0x3f8; i++) fwrite(&zero, 1, 4, f); - } else if (image_is_hdx(hd_file_name, 0)) { + } else if (img_format == 2) { /* HDX file */ fwrite(&signature, 1, 8, f); /* 00000000: Signature */ fwrite(&size, 1, 8, f); /* 00000008: Full size of the data (64-bit) */ fwrite(§or_size, 1, 4, f); /* 00000010: Sector size in bytes */ @@ -2572,13 +2727,40 @@ win_settings_hard_disks_add_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM fwrite(&tracks, 1, 4, f); /* 0000001C: Cylinders */ fwrite(&zero, 1, 4, f); /* 00000020: [Translation] Sectors per cylinder */ fwrite(&zero, 1, 4, f); /* 00000004: [Translation] Heads per cylinder */ - } + } else if (img_format >= 3) { /* VHD file */ + MVHDGeom _86box_geometry; + block_size = settings_get_cur_sel(hdlg, IDC_COMBO_HD_BLOCK_SIZE) == 0 ? MVHD_BLOCK_LARGE : MVHD_BLOCK_SMALL; + switch (img_format) { + case 3: + vhd_progress_hdlg = hdlg; + _86box_geometry = create_drive_vhd_fixed(hd_file_name_multibyte, tracks, hpc, spt); + break; + case 4: + _86box_geometry = create_drive_vhd_dynamic(hd_file_name_multibyte, tracks, hpc, spt, block_size); + break; + case 5: + if (file_dlg_w(hdlg, plat_get_string(IDS_4130), L"", plat_get_string(IDS_4131), 0)) { + return TRUE; + } + _86box_geometry = create_drive_vhd_diff(hd_file_name_multibyte, openfilestring, block_size); + break; + } + + if (img_format != 5) + settings_msgbox_header(MBX_INFO, (wchar_t *) IDS_4113, (wchar_t *) IDS_4117); + + hdd_ptr->tracks = _86box_geometry.cyl; + hdd_ptr->hpc = _86box_geometry.heads; + hdd_ptr->spt = _86box_geometry.spt; + + hard_disk_added = 1; + EndDialog(hdlg, 0); + return TRUE; + } big_buf = (char *) malloc(1048576); memset(big_buf, 0, 1048576); - temp_size = size; - r = size >> 20; size &= 0xfffff; @@ -2606,32 +2788,14 @@ win_settings_hard_disks_add_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM fwrite(big_buf, 1, 1048576, f); SendMessage(h, PBM_SETPOS, (WPARAM) (i + 1), (LPARAM) 0); - while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE | PM_NOYIELD)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } + settings_process_messages(); } } - if (image_is_vhd(hd_file_name, 0)) { - /* VHD image. */ - /* Generate new footer. */ - new_vhd_footer(&vft); - vft->orig_size = vft->curr_size = temp_size; - vft->geom.cyl = tracks; - vft->geom.heads = hpc; - vft->geom.spt = spt; - generate_vhd_checksum(vft); - vhd_footer_to_bytes((uint8_t *) big_buf, vft); - fwrite(big_buf, 1, 512, f); - free(vft); - vft = NULL; - } - free(big_buf); fclose(f); - settings_msgbox_header(MBX_INFO, (wchar_t *) IDS_4113, (wchar_t *) IDS_4117); + settings_msgbox_header(MBX_INFO, (wchar_t *) IDS_4113, (wchar_t *) IDS_4117); } hard_disk_added = 1; @@ -2645,7 +2809,7 @@ win_settings_hard_disks_add_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM return TRUE; case IDC_CFILE: - if (!file_dlg_w(hdlg, plat_get_string(IDS_4106), L"", !(existing & 1))) { + if (!file_dlg_w(hdlg, plat_get_string(IDS_4106), L"", NULL, !(existing & 1))) { if (!wcschr(wopenfilestring, L'.')) { if (wcslen(wopenfilestring) && (wcslen(wopenfilestring) <= 256)) { twcs = &wopenfilestring[wcslen(wopenfilestring)]; @@ -2686,16 +2850,33 @@ hdd_add_file_open_error: fread(&hpc, 1, 4, f); fread(&tracks, 1, 4, f); } else if (image_is_vhd(wopenfilestring, 1)) { - fseeko64(f, -512, SEEK_END); - fread(buf, 1, 512, f); - new_vhd_footer(&vft); - vhd_footer_from_bytes(vft, (uint8_t *) buf); - size = vft->orig_size; - tracks = vft->geom.cyl; - hpc = vft->geom.heads; - spt = vft->geom.spt; - free(vft); - vft = NULL; + fclose(f); + wcstombs(hd_file_name_multibyte, wopenfilestring, sizeof hd_file_name_multibyte); + MVHDMeta* vhd = mvhd_open(hd_file_name_multibyte, 0, &vhd_error); + if (vhd == NULL) { + settings_msgbox_header(MBX_ERROR, (existing & 1) ? (wchar_t *) IDS_4114 : (wchar_t *) IDS_4115, (existing & 1) ? (wchar_t *) IDS_4107 : (wchar_t *) IDS_4108); + return TRUE; + } else if (vhd_error == MVHD_ERR_TIMESTAMP) { + if (settings_msgbox_ex(MBX_QUESTION_YN | MBX_WARNING, plat_get_string(IDS_4133), plat_get_string(IDS_4132), NULL, NULL, NULL) != 0) { + int ts_res = mvhd_diff_update_par_timestamp(vhd, &vhd_error); + if (ts_res != 0) { + settings_msgbox_header(MBX_ERROR, plat_get_string(IDS_2049), plat_get_string(IDS_4134)); + mvhd_close(vhd); + return TRUE; + } + } else { + mvhd_close(vhd); + return TRUE; + } + } + + MVHDGeom vhd_geom = mvhd_get_geometry(vhd); + adjust_vhd_geometry_for_86box(&vhd_geom); + tracks = vhd_geom.cyl; + hpc = vhd_geom.heads; + spt = vhd_geom.spt; + size = (uint64_t)tracks * hpc * spt * 512; + mvhd_close(vhd); } else { fseeko64(f, 0, SEEK_END); size = ftello64(f); @@ -2723,7 +2904,7 @@ hdd_add_file_open_error: } if ((spt > max_spt) || (hpc > max_hpc) || (tracks > max_tracks)) - goto hdd_add_file_open_error; + goto hdd_add_file_open_error; no_update = 1; set_edit_box_contents(hdlg, IDC_EDIT_HD_SPT, spt); @@ -3001,12 +3182,60 @@ hdd_add_file_open_error: no_update = 0; break; + case IDC_COMBO_HD_IMG_FORMAT: + img_format = settings_get_cur_sel(hdlg, IDC_COMBO_HD_IMG_FORMAT); + + no_update = 1; + if (img_format == 5) { /* They switched to a diff VHD; disable the geometry fields. */ + settings_enable_window(hdlg, IDC_EDIT_HD_SPT, FALSE); + set_edit_box_text_contents(hdlg, IDC_EDIT_HD_SPT, L"(N/A)"); + settings_enable_window(hdlg, IDC_EDIT_HD_HPC, FALSE); + set_edit_box_text_contents(hdlg, IDC_EDIT_HD_HPC, L"(N/A)"); + settings_enable_window(hdlg, IDC_EDIT_HD_CYL, FALSE); + set_edit_box_text_contents(hdlg, IDC_EDIT_HD_CYL, L"(N/A)"); + settings_enable_window(hdlg, IDC_EDIT_HD_SIZE, FALSE); + set_edit_box_text_contents(hdlg, IDC_EDIT_HD_SIZE, L"(N/A)"); + settings_enable_window(hdlg, IDC_COMBO_HD_TYPE, FALSE); + settings_reset_content(hdlg, IDC_COMBO_HD_TYPE); + settings_add_string(hdlg, IDC_COMBO_HD_TYPE, (LPARAM) L"(use parent)"); + settings_set_cur_sel(hdlg, IDC_COMBO_HD_TYPE, 0); + } else { + get_edit_box_text_contents(hdlg, IDC_EDIT_HD_SPT, text_buf, 256); + if (!wcscmp(text_buf, L"(N/A)")) { + settings_enable_window(hdlg, IDC_EDIT_HD_SPT, TRUE); + set_edit_box_contents(hdlg, IDC_EDIT_HD_SPT, 17); + spt = 17; + settings_enable_window(hdlg, IDC_EDIT_HD_HPC, TRUE); + set_edit_box_contents(hdlg, IDC_EDIT_HD_HPC, 15); + hpc = 15; + settings_enable_window(hdlg, IDC_EDIT_HD_CYL, TRUE); + set_edit_box_contents(hdlg, IDC_EDIT_HD_CYL, 1023); + tracks = 1023; + settings_enable_window(hdlg, IDC_EDIT_HD_SIZE, TRUE); + set_edit_box_contents(hdlg, IDC_EDIT_HD_SIZE, (uint32_t) ((uint64_t)17 * 15 * 1023 * 512 >> 20)); + size = (uint64_t)17 * 15 * 1023 * 512; + + settings_reset_content(hdlg, IDC_COMBO_HD_TYPE); + hdconf_initialize_hdt_combo(hdlg); + settings_enable_window(hdlg, IDC_COMBO_HD_TYPE, TRUE); + } + } + no_update = 0; + + if (img_format == 4 || img_format == 5) { /* For dynamic and diff VHDs, show the block size dropdown. */ + settings_show_window(hdlg, IDC_COMBO_HD_BLOCK_SIZE, TRUE); + settings_show_window(hdlg, IDT_1775, TRUE); + } else { /* Hide it otherwise. */ + settings_show_window(hdlg, IDC_COMBO_HD_BLOCK_SIZE, FALSE); + settings_show_window(hdlg, IDT_1775, FALSE); + } + break; } return FALSE; - } + } - return FALSE; + return FALSE; } @@ -3114,6 +3343,8 @@ win_settings_hard_disks_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lPar lv1_current_sel = -1; recalc_location_controls(hdlg, 0, 0); + settings_listview_enable_styles(hdlg, IDC_LIST_HARD_DISKS); + ignore_change = 0; return TRUE; @@ -3461,11 +3692,19 @@ win_settings_zip_drives_recalc_list(HWND hdlg) static void win_settings_floppy_drives_resize_columns(HWND hdlg) { + int iCol, width[3] = {292, 58, 89}; + int total = 0; HWND hwndList = GetDlgItem(hdlg, IDC_LIST_FLOPPY_DRIVES); + RECT r; - ListView_SetColumnWidth(hwndList, 0, MulDiv(250, dpi, 96)); - ListView_SetColumnWidth(hwndList, 1, MulDiv(50, dpi, 96)); - ListView_SetColumnWidth(hwndList, 2, MulDiv(75, dpi, 96)); + GetWindowRect(hwndList, &r); + for (iCol = 0; iCol < 2; iCol++) { + width[iCol] = MulDiv(width[iCol], dpi, 96); + total += width[iCol]; + ListView_SetColumnWidth(hwndList, iCol, MulDiv(width[iCol], dpi, 96)); + } + width[2] = (r.right - r.left) - 4 - total; + ListView_SetColumnWidth(hwndList, 2, width[2]); } @@ -3480,7 +3719,7 @@ win_settings_floppy_drives_init_columns(HWND hdlg) lvc.iSubItem = 0; lvc.pszText = plat_get_string(IDS_2092); - lvc.cx = 250; + lvc.cx = 292; lvc.fmt = LVCFMT_LEFT; if (ListView_InsertColumn(hwndList, 0, &lvc) == -1) @@ -3489,7 +3728,7 @@ win_settings_floppy_drives_init_columns(HWND hdlg) lvc.iSubItem = 1; lvc.pszText = plat_get_string(IDS_2059); - lvc.cx = 50; + lvc.cx = 58; lvc.fmt = LVCFMT_LEFT; if (ListView_InsertColumn(hwndList, 1, &lvc) == -1) @@ -3498,7 +3737,7 @@ win_settings_floppy_drives_init_columns(HWND hdlg) lvc.iSubItem = 2; lvc.pszText = plat_get_string(IDS_2087); - lvc.cx = 75; + lvc.cx = 89; lvc.fmt = LVCFMT_LEFT; if (ListView_InsertColumn(hwndList, 2, &lvc) == -1) @@ -3512,10 +3751,15 @@ win_settings_floppy_drives_init_columns(HWND hdlg) static void win_settings_cdrom_drives_resize_columns(HWND hdlg) { + int width[2] = {292, 147}; HWND hwndList = GetDlgItem(hdlg, IDC_LIST_CDROM_DRIVES); + RECT r; - ListView_SetColumnWidth(hwndList, 0, MulDiv(342, dpi, 96)); - ListView_SetColumnWidth(hwndList, 1, MulDiv(50, dpi, 96)); + GetWindowRect(hwndList, &r); + width[0] = MulDiv(width[0], dpi, 96); + ListView_SetColumnWidth(hwndList, 0, MulDiv(width[0], dpi, 96)); + width[1] = (r.right - r.left) - 4 - width[0]; + ListView_SetColumnWidth(hwndList, 1, width[1]); } @@ -3530,7 +3774,7 @@ win_settings_cdrom_drives_init_columns(HWND hdlg) lvc.iSubItem = 0; lvc.pszText = plat_get_string(IDS_2081); - lvc.cx = 342; + lvc.cx = 292; lvc.fmt = LVCFMT_LEFT; if (ListView_InsertColumn(hwndList, 0, &lvc) == -1) @@ -3539,7 +3783,7 @@ win_settings_cdrom_drives_init_columns(HWND hdlg) lvc.iSubItem = 1; lvc.pszText = plat_get_string(IDS_2053); - lvc.cx = 50; + lvc.cx = 147; lvc.fmt = LVCFMT_LEFT; if (ListView_InsertColumn(hwndList, 1, &lvc) == -1) @@ -3553,10 +3797,15 @@ win_settings_cdrom_drives_init_columns(HWND hdlg) static void win_settings_mo_drives_resize_columns(HWND hdlg) { + int width[2] = {292, 147}; HWND hwndList = GetDlgItem(hdlg, IDC_LIST_MO_DRIVES); + RECT r; - ListView_SetColumnWidth(hwndList, 0, MulDiv(120, dpi, 96)); - ListView_SetColumnWidth(hwndList, 1, MulDiv(260, dpi, 96)); + GetWindowRect(hwndList, &r); + width[0] = MulDiv(width[0], dpi, 96); + ListView_SetColumnWidth(hwndList, 0, MulDiv(width[0], dpi, 96)); + width[1] = (r.right - r.left) - 4 - width[0]; + ListView_SetColumnWidth(hwndList, 1, width[1]); } @@ -3571,7 +3820,7 @@ win_settings_mo_drives_init_columns(HWND hdlg) lvc.iSubItem = 0; lvc.pszText = plat_get_string(IDS_2081); - lvc.cx = 120; + lvc.cx = 292; lvc.fmt = LVCFMT_LEFT; if (ListView_InsertColumn(hwndList, 0, &lvc) == -1) @@ -3580,7 +3829,7 @@ win_settings_mo_drives_init_columns(HWND hdlg) lvc.iSubItem = 1; lvc.pszText = plat_get_string(IDS_2092); - lvc.cx = 260; + lvc.cx = 147; lvc.fmt = LVCFMT_LEFT; if (ListView_InsertColumn(hwndList, 1, &lvc) == -1) @@ -3594,10 +3843,15 @@ win_settings_mo_drives_init_columns(HWND hdlg) static void win_settings_zip_drives_resize_columns(HWND hdlg) { + int width[2] = {292, 147}; HWND hwndList = GetDlgItem(hdlg, IDC_LIST_ZIP_DRIVES); + RECT r; - ListView_SetColumnWidth(hwndList, 0, MulDiv(342, dpi, 96)); - ListView_SetColumnWidth(hwndList, 1, MulDiv(50, dpi, 96)); + GetWindowRect(hwndList, &r); + width[0] = MulDiv(width[0], dpi, 96); + ListView_SetColumnWidth(hwndList, 0, MulDiv(width[0], dpi, 96)); + width[1] = (r.right - r.left) - 4 - width[0]; + ListView_SetColumnWidth(hwndList, 1, width[1]); } @@ -3612,7 +3866,7 @@ win_settings_zip_drives_init_columns(HWND hdlg) lvc.iSubItem = 0; lvc.pszText = plat_get_string(IDS_2081); - lvc.cx = 342; + lvc.cx = 292; lvc.fmt = LVCFMT_LEFT; if (ListView_InsertColumn(hwndList, 0, &lvc) == -1) @@ -3621,7 +3875,7 @@ win_settings_zip_drives_init_columns(HWND hdlg) lvc.iSubItem = 1; lvc.pszText = plat_get_string(IDS_2092); - lvc.cx = 50; + lvc.cx = 147; lvc.fmt = LVCFMT_LEFT; if (ListView_InsertColumn(hwndList, 1, &lvc) == -1) @@ -4168,6 +4422,8 @@ win_settings_floppy_and_cdrom_drives_proc(HWND hdlg, UINT message, WPARAM wParam settings_set_check(hdlg, IDC_CHECKTURBO, temp_fdd_turbo[lv1_current_sel]); settings_set_check(hdlg, IDC_CHECKBPB, temp_fdd_check_bpb[lv1_current_sel]); + settings_listview_enable_styles(hdlg, IDC_LIST_FLOPPY_DRIVES); + lv2_current_sel = 0; win_settings_cdrom_drives_init_columns(hdlg); image_list_init(hdlg, IDC_LIST_CDROM_DRIVES, (const uint8_t *) cd_icons); @@ -4190,6 +4446,8 @@ win_settings_floppy_and_cdrom_drives_proc(HWND hdlg, UINT message, WPARAM wParam settings_set_cur_sel(hdlg, IDC_COMBO_CD_BUS, b); cdrom_recalc_location_controls(hdlg, 0); + settings_listview_enable_styles(hdlg, IDC_LIST_CDROM_DRIVES); + ignore_change = 0; return TRUE; @@ -4352,6 +4610,8 @@ win_settings_other_removable_devices_proc(HWND hdlg, UINT message, WPARAM wParam settings_set_cur_sel(hdlg, IDC_COMBO_MO_BUS, b); mo_recalc_location_controls(hdlg, 0); + settings_listview_enable_styles(hdlg, IDC_LIST_MO_DRIVES); + lv2_current_sel = 0; win_settings_zip_drives_init_columns(hdlg); image_list_init(hdlg, IDC_LIST_ZIP_DRIVES, (const uint8_t *) zip_icons); @@ -4374,6 +4634,8 @@ win_settings_other_removable_devices_proc(HWND hdlg, UINT message, WPARAM wParam settings_set_cur_sel(hdlg, IDC_COMBO_ZIP_BUS, b); zip_recalc_location_controls(hdlg, 0); + settings_listview_enable_styles(hdlg, IDC_LIST_ZIP_DRIVES); + ignore_change = 0; return TRUE; @@ -4538,6 +4800,112 @@ win_settings_other_removable_devices_proc(HWND hdlg, UINT message, WPARAM wParam } +#if defined(__amd64__) || defined(__aarch64__) +static LRESULT CALLBACK +#else +static BOOL CALLBACK +#endif +win_settings_peripherals_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + int c, d; + int e; + LPTSTR lptsTemp; + char *stransi; + const device_t *dev; + + switch (message) { + case WM_INITDIALOG: + lptsTemp = (LPTSTR) malloc(512 * sizeof(WCHAR)); + stransi = (char *) malloc(512); + + /* Populate the ISA RTC card dropdown. */ + e = 0; + settings_reset_content(hdlg, IDC_COMBO_ISARTC); + for (d = 0; ; d++) { + generate_device_name(isartc_get_device(d), isartc_get_internal_name(d), 0); + + if (!device_name[0]) + break; + + if (d == 0) { + settings_add_string(hdlg, IDC_COMBO_ISARTC, win_get_string(IDS_2103)); + settings_set_cur_sel(hdlg, IDC_COMBO_ISARTC, 0); + } else + settings_add_string(hdlg, IDC_COMBO_ISARTC, (LPARAM) device_name); + settings_list_to_device[1][e] = d; + if (d == temp_isartc) + settings_set_cur_sel(hdlg, IDC_COMBO_ISARTC, e); + e++; + } + settings_enable_window(hdlg, IDC_CONFIGURE_ISARTC, temp_isartc != 0); + + /* Populate the ISA memory card dropdowns. */ + for (c = 0; c < ISAMEM_MAX; c++) { + settings_reset_content(hdlg, IDC_COMBO_ISAMEM_1 + c); + for (d = 0; ; d++) { + generate_device_name(isamem_get_device(d), (char *) isamem_get_internal_name(d), 0); + + if (!device_name[0]) + break; + + if (d == 0) { + settings_add_string(hdlg, IDC_COMBO_ISAMEM_1 + c, win_get_string(IDS_2103)); + settings_set_cur_sel(hdlg, IDC_COMBO_ISAMEM_1 + c, 0); + } else + settings_add_string(hdlg, IDC_COMBO_ISAMEM_1 + c, (LPARAM) device_name); + } + settings_set_cur_sel(hdlg, IDC_COMBO_ISAMEM_1 + c, temp_isamem[c]); + settings_enable_window(hdlg, IDC_CONFIGURE_ISAMEM_1 + c, temp_isamem[c] != 0); + } + + settings_set_check(hdlg, IDC_CHECK_BUGGER, temp_bugger); + settings_set_check(hdlg, IDC_CHECK_POSTCARD, temp_postcard); + + free(stransi); + free(lptsTemp); + + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDC_CONFIGURE_ISARTC: + temp_isartc = settings_list_to_device[1][settings_get_cur_sel(hdlg, IDC_COMBO_ISARTC)]; + temp_deviceconfig |= deviceconfig_open(hdlg, (void *)isartc_get_device(temp_isartc)); + break; + + case IDC_COMBO_ISARTC: + temp_isartc = settings_list_to_device[1][settings_get_cur_sel(hdlg, IDC_COMBO_ISARTC)]; + settings_enable_window(hdlg, IDC_CONFIGURE_ISARTC, temp_isartc != 0); + break; + + case IDC_COMBO_ISAMEM_1: case IDC_COMBO_ISAMEM_2: + case IDC_COMBO_ISAMEM_3: case IDC_COMBO_ISAMEM_4: + c = LOWORD(wParam) - IDC_COMBO_ISAMEM_1; + temp_isamem[c] = settings_get_cur_sel(hdlg, LOWORD(wParam)); + settings_enable_window(hdlg, IDC_CONFIGURE_ISAMEM_1 + c, temp_isamem[c] != 0); + break; + + case IDC_CONFIGURE_ISAMEM_1: case IDC_CONFIGURE_ISAMEM_2: + case IDC_CONFIGURE_ISAMEM_3: case IDC_CONFIGURE_ISAMEM_4: + c = LOWORD(wParam) - IDC_CONFIGURE_ISAMEM_1; + dev = isamem_get_device(temp_isamem[c]); + temp_deviceconfig |= deviceconfig_inst_open(hdlg, (void *)dev, c + 1); + break; + } + return FALSE; + + case WM_SAVESETTINGS: + temp_isartc = settings_list_to_device[1][settings_get_cur_sel(hdlg, IDC_COMBO_ISARTC)]; + temp_bugger = settings_get_check(hdlg, IDC_CHECK_BUGGER); + temp_postcard = settings_get_check(hdlg, IDC_CHECK_POSTCARD); + + default: + return FALSE; + } + return FALSE; +} + + void win_settings_show_child(HWND hwndParent, DWORD child_id) { if (child_id == displayed_category) @@ -4568,8 +4936,8 @@ void win_settings_show_child(HWND hwndParent, DWORD child_id) case SETTINGS_PAGE_PORTS: hwndChildDialog = CreateDialog(hinstance, (LPCWSTR)DLG_CFG_PORTS, hwndParent, win_settings_ports_proc); break; - case SETTINGS_PAGE_PERIPHERALS: - hwndChildDialog = CreateDialog(hinstance, (LPCWSTR)DLG_CFG_PERIPHERALS, hwndParent, win_settings_peripherals_proc); + case SETTINGS_PAGE_STORAGE: + hwndChildDialog = CreateDialog(hinstance, (LPCWSTR)DLG_CFG_STORAGE, hwndParent, win_settings_storage_proc); break; case SETTINGS_PAGE_HARD_DISKS: hwndChildDialog = CreateDialog(hinstance, (LPCWSTR)DLG_CFG_HARD_DISKS, hwndParent, win_settings_hard_disks_proc); @@ -4580,6 +4948,9 @@ void win_settings_show_child(HWND hwndParent, DWORD child_id) case SETTINGS_PAGE_OTHER_REMOVABLE_DEVICES: hwndChildDialog = CreateDialog(hinstance, (LPCWSTR)DLG_CFG_OTHER_REMOVABLE_DEVICES, hwndParent, win_settings_other_removable_devices_proc); break; + case SETTINGS_PAGE_PERIPHERALS: + hwndChildDialog = CreateDialog(hinstance, (LPCWSTR)DLG_CFG_PERIPHERALS, hwndParent, win_settings_peripherals_proc); + break; default: fatal("Invalid child dialog ID\n"); return; @@ -4598,7 +4969,7 @@ win_settings_main_insert_categories(HWND hwndList) lvI.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE; lvI.stateMask = lvI.iSubItem = lvI.state = 0; - for (i = 0; i < 10; i++) { + for (i = 0; i < 11; i++) { lvI.pszText = plat_get_string(IDS_2065+i); lvI.iItem = i; lvI.iImage = i; @@ -4617,23 +4988,67 @@ static LRESULT CALLBACK #else static BOOL CALLBACK #endif -win_settings_confirm(HWND hdlg, int button) +win_settings_confirm(HWND hdlg) { int i; SendMessage(hwndChildDialog, WM_SAVESETTINGS, 0, 0); - i = settings_msgbox_reset(); - if (i > 0) { - if (i == 2) + + if (win_settings_changed()) { + if (confirm_save) + i = settings_msgbox_ex(MBX_QUESTION_OK | MBX_WARNING | MBX_DONTASK, (wchar_t *) IDS_2121, (wchar_t *) IDS_2122, (wchar_t *) IDS_2123, NULL, NULL); + else + i = 0; + + if (i == 10) { + confirm_save = 0; + i = 0; + } + + if (i == 0) win_settings_save(); + else + return FALSE; + } - DestroyWindow(hwndChildDialog); - EndDialog(hdlg, 0); - win_notify_dlg_closed(); + DestroyWindow(hwndChildDialog); + EndDialog(hdlg, 0); + win_notify_dlg_closed(); + return TRUE; +} - return button ? TRUE : FALSE; - } else - return button ? FALSE : TRUE; + +static void +win_settings_categories_resize_columns(HWND hdlg) +{ + HWND hwndList = GetDlgItem(hdlg, IDC_SETTINGSCATLIST); + RECT r; + + GetWindowRect(hwndList, &r); + ListView_SetColumnWidth(hwndList, 0, (r.right - r.left) + 1 - 5); +} + + +static BOOL +win_settings_categories_init_columns(HWND hdlg) +{ + LVCOLUMN lvc; + int iCol; + HWND hwndList = GetDlgItem(hdlg, IDC_SETTINGSCATLIST); + + lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; + + lvc.iSubItem = 0; + lvc.pszText = plat_get_string(2048); + + lvc.cx = 171; + lvc.fmt = LVCFMT_LEFT; + + if (ListView_InsertColumn(hwndList, iCol, &lvc) == -1) + return FALSE; + + win_settings_categories_resize_columns(hdlg); + return TRUE; } @@ -4646,7 +5061,7 @@ win_settings_main_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) { HWND h = NULL; int category, i = 0, j = 0; - const uint8_t cat_icons[12] = { 240, 241, 242, 243, 96, 244, 245, 80, 246, 247, 0 }; + const uint8_t cat_icons[12] = { 240, 241, 242, 243, 96, 244, 252, 80, 246, 247, 245, 0 }; hwndParentDialog = hdlg; @@ -4656,9 +5071,11 @@ win_settings_main_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) win_settings_init(); displayed_category = -1; h = GetDlgItem(hdlg, IDC_SETTINGSCATLIST); + win_settings_categories_init_columns(hdlg); image_list_init(hdlg, IDC_SETTINGSCATLIST, (const uint8_t *) cat_icons); win_settings_main_insert_categories(h); settings_listview_select(hdlg, IDC_SETTINGSCATLIST, first_cat); + settings_listview_enable_styles(hdlg, IDC_SETTINGSCATLIST); return TRUE; case WM_NOTIFY: if ((((LPNMHDR)lParam)->code == LVN_ITEMCHANGED) && (((LPNMHDR)lParam)->idFrom == IDC_SETTINGSCATLIST)) { @@ -4674,11 +5091,14 @@ win_settings_main_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) } break; case WM_CLOSE: - return win_settings_confirm(hdlg, 0); + DestroyWindow(hwndChildDialog); + EndDialog(hdlg, 0); + win_notify_dlg_closed(); + return TRUE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: - return win_settings_confirm(hdlg, 1); + return win_settings_confirm(hdlg); case IDCANCEL: DestroyWindow(hwndChildDialog); EndDialog(hdlg, 0); @@ -4686,8 +5106,10 @@ win_settings_main_proc(HWND hdlg, UINT message, WPARAM wParam, LPARAM lParam) return TRUE; } break; + case WM_DPICHANGED: dpi = HIWORD(wParam); + win_settings_categories_resize_columns(hdlg); image_list_init(hdlg, IDC_SETTINGSCATLIST, (const uint8_t *) cat_icons); break; default: diff --git a/src/win/win_ui.c b/src/win/win_ui.c index 51dbb9e46..7d9c88164 100644 --- a/src/win/win_ui.c +++ b/src/win/win_ui.c @@ -28,6 +28,7 @@ #include #include #include +#include <86box/plat.h> #include <86box/86box.h> #include <86box/config.h> #include "../cpu/cpu.h" @@ -38,7 +39,6 @@ #include <86box/nvr.h> #include <86box/video.h> #include <86box/vid_ega.h> // for update_overscan -#include <86box/plat.h> #include <86box/plat_midi.h> #include <86box/plat_dynld.h> #include <86box/ui.h> @@ -216,6 +216,7 @@ ResetAllMenus(void) CheckMenuItem(menuMain, IDM_VID_RESIZE, MF_UNCHECKED); CheckMenuItem(menuMain, IDM_VID_SDL_SW, MF_UNCHECKED); CheckMenuItem(menuMain, IDM_VID_SDL_HW, MF_UNCHECKED); + CheckMenuItem(menuMain, IDM_VID_SDL_OPENGL, MF_UNCHECKED); #ifdef USE_VNC CheckMenuItem(menuMain, IDM_VID_VNC, MF_UNCHECKED); #endif @@ -372,6 +373,85 @@ plat_power_off(void) } +/* Catch WM_INPUT messages for 'current focus' window. */ +#if defined(__amd64__) || defined(__aarch64__) +static LRESULT CALLBACK +#else +static BOOL CALLBACK +#endif +input_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) { + case WM_INPUT: + if (infocus) { + UINT size = 0; + PRAWINPUT raw = NULL; + + /* Here we read the raw input data */ + GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)); + raw = (PRAWINPUT)malloc(size); + if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, raw, &size, sizeof(RAWINPUTHEADER)) == size) { + switch(raw->header.dwType) + { + case RIM_TYPEKEYBOARD: + keyboard_handle(raw); + break; + case RIM_TYPEMOUSE: + win_mouse_handle(raw); + break; + case RIM_TYPEHID: + win_joystick_handle(raw); + break; + } + } + free(raw); + } + break; + case WM_SETFOCUS: + infocus = 1; +#ifndef NO_KEYBOARD_HOOK + if (! hook_enabled) { + hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, + LowLevelKeyboardProc, + GetModuleHandle(NULL), + 0); + hook_enabled = 1; + } +#endif + break; + + case WM_KILLFOCUS: + infocus = 0; + plat_mouse_capture(0); +#ifndef NO_KEYBOARD_HOOK + if (hook_enabled) { + UnhookWindowsHookEx(hKeyboardHook); + hook_enabled = 0; + } +#endif + break; + + case WM_LBUTTONUP: + pclog("video_fullscreen = %i\n", video_fullscreen); + if (! video_fullscreen) + plat_mouse_capture(1); + break; + + case WM_MBUTTONUP: + if (mouse_get_buttons() < 3) + plat_mouse_capture(0); + break; + + default: + return(1); + /* return(CallWindowProc((WNDPROC)input_orig_proc, + hwnd, message, wParam, lParam)); */ + } + + return(0); +} + + static LRESULT CALLBACK MainWindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { @@ -382,6 +462,9 @@ MainWindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) int temp_x, temp_y; + if (input_proc(hwnd, message, wParam, lParam) == 0) + return(0); + switch (message) { case WM_CREATE: SetTimer(hwnd, TIMER_1SEC, 1000, NULL); @@ -418,7 +501,6 @@ MainWindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) break; case IDM_ACTION_RESET_CAD: - pclog("-\n"); pc_send_cad(); break; @@ -538,6 +620,7 @@ MainWindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) case IDM_VID_SDL_SW: case IDM_VID_SDL_HW: + case IDM_VID_SDL_OPENGL: #ifdef USE_VNC case IDM_VID_VNC: #endif @@ -570,6 +653,7 @@ MainWindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) CheckMenuItem(hmenu, IDM_VID_SCALE_1X+scale, MF_UNCHECKED); scale = LOWORD(wParam) - IDM_VID_SCALE_1X; CheckMenuItem(hmenu, IDM_VID_SCALE_1X+scale, MF_CHECKED); + reset_screen_size(); device_force_redraw(); video_force_resize_set(1); config_save(); @@ -715,11 +799,10 @@ MainWindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) GetWindowRect(hwndSBAR, &rect); sbar_height = rect.bottom - rect.top; rect_p = (RECT*)lParam; - if (vid_resize) { + if (vid_resize) MoveWindow(hwnd, rect_p->left, rect_p->top, rect_p->right - rect_p->left, rect_p->bottom - rect_p->top, TRUE); - } else if (!user_resize) { + else if (!user_resize) doresize = 1; - } break; case WM_SIZE: @@ -730,6 +813,7 @@ MainWindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) temp_y = (lParam >> 16); if ((temp_x <= 0) || (temp_y <= 0)) { + plat_vidapi_enable(0); minimized = 1; break; } else if (minimized == 1) { @@ -769,7 +853,7 @@ MainWindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) window_h = rect.bottom - rect.top; save_window_pos = 1; } - plat_vidapi_enable(1); + plat_vidapi_enable(2); config_save(); break; @@ -936,6 +1020,14 @@ MainWindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) #endif break; + case WM_ACTIVATE: + if ((wParam != WA_INACTIVE) && !(video_fullscreen & 2)) { + video_force_resize_set(1); + plat_vidapi_enable(0); + plat_vidapi_enable(1); + } + break; + case WM_ENTERSIZEMOVE: user_resize = 1; break; @@ -956,6 +1048,38 @@ MainWindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) static LRESULT CALLBACK SubWindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) { + case WM_LBUTTONUP: + if (! video_fullscreen) + plat_mouse_capture(1); + break; + + case WM_MBUTTONUP: + if (mouse_get_buttons() < 3) + plat_mouse_capture(0); + break; + + default: + return(DefWindowProc(hwnd, message, wParam, lParam)); + } + + return(0); +} + + +static LRESULT CALLBACK +SDLMainWindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + if (input_proc(hwnd, message, wParam, lParam) == 0) + return(0); + + return(DefWindowProc(hwnd, message, wParam, lParam)); +} + + +static LRESULT CALLBACK +SDLSubWindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { return(DefWindowProc(hwnd, message, wParam, lParam)); } @@ -1046,6 +1170,14 @@ ui_init(int nCmdShow) return(2); wincl.lpszClassName = SUB_CLASS_NAME; wincl.lpfnWndProc = SubWindowProcedure; + if (! RegisterClassEx(&wincl)) + return(2); + wincl.lpszClassName = SDL_CLASS_NAME; + wincl.lpfnWndProc = SDLMainWindowProcedure; + if (! RegisterClassEx(&wincl)) + return(2); + wincl.lpszClassName = SDL_SUB_CLASS_NAME; + wincl.lpfnWndProc = SDLSubWindowProcedure; if (! RegisterClassEx(&wincl)) return(2); @@ -1071,18 +1203,18 @@ ui_init(int nCmdShow) ui_window_title(title); - /* Get the current DPI */ - dpi = win_get_dpi(hwndMain); + /* Get the current DPI */ + dpi = win_get_dpi(hwndMain); - /* Check if we have a padded window frame */ - padded_frame = (GetSystemMetrics(SM_CXPADDEDBORDER) != 0); + /* Check if we have a padded window frame */ + padded_frame = (GetSystemMetrics(SM_CXPADDEDBORDER) != 0); /* Create the status bar window. */ StatusBarCreate(hwndMain, IDC_STATUS, hinstance); - /* Get the actual height of the status bar */ - GetWindowRect(hwndSBAR, &sbar_rect); - sbar_height = sbar_rect.bottom - sbar_rect.top; + /* Get the actual height of the status bar */ + GetWindowRect(hwndSBAR, &sbar_rect); + sbar_height = sbar_rect.bottom - sbar_rect.top; /* Set up main window for resizing if configured. */ if (vid_resize) @@ -1118,9 +1250,6 @@ ui_init(int nCmdShow) } keyboard_getkeymap(); - /* Set up the main window for RawInput. */ - plat_set_input(hwndMain); - /* Load the accelerator table */ haccel = LoadAccelerators(hinstance, ACCEL_NAME); if (haccel == NULL) { @@ -1139,7 +1268,7 @@ ui_init(int nCmdShow) ghMutex = CreateMutex(NULL, FALSE, NULL); /* Create the Machine Rendering window. */ - hwndRender = CreateWindow(L"STATIC", NULL, WS_CHILD|SS_BITMAP, + hwndRender = CreateWindow(/*L"STATIC"*/ SUB_CLASS_NAME, NULL, WS_CHILD|SS_BITMAP, 0, 0, 1, 1, hwnd, NULL, hinstance, NULL); MoveWindow(hwndRender, 0, 0, scrnsz_x, scrnsz_y, TRUE); @@ -1231,6 +1360,8 @@ ui_init(int nCmdShow) /* Close down the emulator. */ do_stop(); + UnregisterClass(SDL_SUB_CLASS_NAME, hinstance); + UnregisterClass(SDL_CLASS_NAME, hinstance); UnregisterClass(SUB_CLASS_NAME, hinstance); UnregisterClass(CLASS_NAME, hinstance); @@ -1374,100 +1505,3 @@ plat_mouse_capture(int on) mouse_capture = 0; } } - - -/* Catch WM_INPUT messages for 'current focus' window. */ -static LONG_PTR input_orig_proc; -static HWND input_orig_hwnd = NULL; -#if defined(__amd64__) || defined(__aarch64__) -static LRESULT CALLBACK -#else -static BOOL CALLBACK -#endif -input_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - switch (message) { - case WM_INPUT: - if (infocus) { - UINT size = 0; - PRAWINPUT raw = NULL; - - /* Here we read the raw input data */ - GetRawInputData((HRAWINPUT)lParam, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)); - raw = (PRAWINPUT)malloc(size); - if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, raw, &size, sizeof(RAWINPUTHEADER)) == size) { - switch(raw->header.dwType) - { - case RIM_TYPEKEYBOARD: - keyboard_handle(raw); - break; - case RIM_TYPEMOUSE: - win_mouse_handle(raw); - break; - case RIM_TYPEHID: - win_joystick_handle(raw); - break; - } - } - free(raw); - } - break; - case WM_SETFOCUS: - infocus = 1; -#ifndef NO_KEYBOARD_HOOK - if (! hook_enabled) { - hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, - LowLevelKeyboardProc, - GetModuleHandle(NULL), - 0); - hook_enabled = 1; - } -#endif - break; - - case WM_KILLFOCUS: - infocus = 0; - plat_mouse_capture(0); -#ifndef NO_KEYBOARD_HOOK - if (hook_enabled) { - UnhookWindowsHookEx(hKeyboardHook); - hook_enabled = 0; - } -#endif - break; - - case WM_LBUTTONUP: - if (! video_fullscreen) - plat_mouse_capture(1); - break; - - case WM_MBUTTONUP: - if (mouse_get_buttons() < 3) - plat_mouse_capture(0); - break; - - default: - return(CallWindowProc((WNDPROC)input_orig_proc, - hwnd, message, wParam, lParam)); - } - - return(0); -} - - -/* Set up a handler for the 'currently active' window. */ -void -plat_set_input(HWND h) -{ - /* If needed, rest the old one first. */ - if (input_orig_hwnd != NULL) { - SetWindowLongPtr(input_orig_hwnd, GWLP_WNDPROC, - (LONG_PTR)input_orig_proc); - } - - /* Redirect the window procedure so we can catch WM_INPUT. */ - input_orig_proc = GetWindowLongPtr(h, GWLP_WNDPROC); - input_orig_hwnd = h; - SetWindowLongPtr(h, GWLP_WNDPROC, (LONG_PTR)&input_proc); - ImmAssociateContext(h, NULL); -}