From 088cf0ad1995d29546d434f607015f141debf588 Mon Sep 17 00:00:00 2001 From: Patrick Mackinlay Date: Wed, 7 Oct 2020 19:30:15 +0700 Subject: ns32000: many bug fixes * fixed many instruction bugs * implemented cmpm instruction * reworked pc increment to better support prefetch * tightened up decoding --- src/devices/cpu/ns32000/ns32000.cpp | 385 +++++++++++++++++------------------- src/devices/cpu/ns32000/ns32000.h | 1 + 2 files changed, 186 insertions(+), 200 deletions(-) diff --git a/src/devices/cpu/ns32000/ns32000.cpp b/src/devices/cpu/ns32000/ns32000.cpp index 6a95afc0146..1680955a7ac 100644 --- a/src/devices/cpu/ns32000/ns32000.cpp +++ b/src/devices/cpu/ns32000/ns32000.cpp @@ -358,10 +358,7 @@ template u64 ns32000_device::gen_read(addr_mode mode) break; case REG: - if (mode.size == SIZE_Q) - data = (u64(m_r[mode.gen ^ 1]) << 32) | m_r[mode.gen ^ 0]; - else - data = m_r[mode.gen] & size_mask[mode.size]; + data = m_r[mode.gen] & size_mask[mode.size]; break; case TOS: @@ -412,13 +409,7 @@ template void ns32000_device::gen_write(addr_mode mode, u64 d switch (mode.type) { case REG: - if (mode.size == SIZE_Q) - { - m_r[mode.gen ^ 0] = u32(data); - m_r[mode.gen ^ 1] = data >> 32; - } - else - m_r[mode.gen] = (m_r[mode.gen] & ~size_mask[mode.size]) | (data & size_mask[mode.size]); + m_r[mode.gen] = (m_r[mode.gen] & ~size_mask[mode.size]) | (data & size_mask[mode.size]); break; case TOS: @@ -538,6 +529,7 @@ template void ns32000_device::interrupt(unsigned const vector m_pc = space(0).read_dword_unaligned(m_mod + 8) + u16(descriptor >> 16); // TODO: flush queue + m_sequential = false; } template void ns32000_device::execute_run() @@ -594,6 +586,7 @@ template void ns32000_device::execute_run() u8 const opbyte = space(0).read_byte(m_pc); unsigned bytes = 1; + m_sequential = true; if ((opbyte & 15) == 10) { @@ -603,9 +596,10 @@ template void ns32000_device::execute_run() s32 const dst = displacement(bytes); if (condition(opbyte >> 4)) + { m_pc += dst; - else - m_pc += bytes; + m_sequential = false; + } } else if ((opbyte & 15) == 2) { @@ -622,6 +616,7 @@ template void ns32000_device::execute_run() space(0).write_dword_unaligned(SP, m_pc + bytes); m_pc += dst; + m_sequential = false; } break; case 0x1: @@ -634,6 +629,7 @@ template void ns32000_device::execute_run() SP += 4 + constant; m_pc = addr; + m_sequential = false; } break; case 0x2: @@ -653,6 +649,7 @@ template void ns32000_device::execute_run() m_mod = u16(descriptor); m_sb = space(0).read_dword_unaligned(m_mod + 0); m_pc = space(0).read_dword_unaligned(m_mod + 8) + u16(descriptor >> 16); + m_sequential = false; } break; case 0x3: @@ -668,6 +665,7 @@ template void ns32000_device::execute_run() m_sb = space(0).read_dword_unaligned(m_mod + 0); SP += constant; + m_sequential = false; } break; case 0x4: @@ -688,6 +686,7 @@ template void ns32000_device::execute_run() m_sb = space(0).read_dword_unaligned(m_mod); SP += constant; + m_sequential = false; } else interrupt(ILL, m_pc); @@ -708,6 +707,7 @@ template void ns32000_device::execute_run() sp += 2; m_sb = space(0).read_dword_unaligned(m_mod); + m_sequential = false; } else interrupt(ILL, m_pc); @@ -727,7 +727,6 @@ template void ns32000_device::execute_run() } } } - m_pc += bytes; break; case 0x7: // RESTORE reglist @@ -744,7 +743,6 @@ template void ns32000_device::execute_run() } } } - m_pc += bytes; break; case 0x8: // ENTER reglist,constant @@ -767,7 +765,6 @@ template void ns32000_device::execute_run() } } } - m_pc += bytes; break; case 0x9: // EXIT reglist @@ -787,16 +784,13 @@ template void ns32000_device::execute_run() m_fp = space(0).read_dword_unaligned(SP); SP += 4; } - m_pc += bytes; break; case 0xa: // NOP - m_pc += bytes; break; case 0xb: // WAIT m_wait = true; - m_pc += bytes; break; case 0xc: // DIA @@ -806,8 +800,6 @@ template void ns32000_device::execute_run() // FLAG if (m_psr & PSR_F) interrupt(FLG, m_pc); - else - m_pc += bytes; break; case 0xe: // SVC @@ -849,7 +841,6 @@ template void ns32000_device::execute_run() gen_write(mode[0], dst); } - m_pc += bytes; break; case 1: // CMPQi src1,src2 @@ -859,7 +850,7 @@ template void ns32000_device::execute_run() mode[0].read_i(size); decode(mode, bytes); - u32 const src1 = s32(quick << 28) >> 28; + u32 const src1 = (s32(quick << 28) >> 28) & size_mask[size]; u32 const src2 = gen_read(mode[0]); m_psr &= ~(PSR_N | PSR_Z | PSR_L); @@ -877,7 +868,6 @@ template void ns32000_device::execute_run() || ((size == SIZE_B && u8(src1) > u8(src2))))) m_psr |= PSR_L; } - m_pc += bytes; break; case 2: // SPRi procreg,dst @@ -891,41 +881,30 @@ template void ns32000_device::execute_run() case 0x0: // US // TODO: user stack pointer? gen_write(mode[0], m_sp1); - m_pc += bytes; break; case 0x8: // FP gen_write(mode[0], m_fp); - m_pc += bytes; break; case 0x9: // SP gen_write(mode[0], SP); - m_pc += bytes; break; case 0xa: // SB gen_write(mode[0], m_sb); - m_pc += bytes; break; case 0xd: // PSR if (!(m_psr & PSR_U)) - { gen_write(mode[0], m_psr); - m_pc += bytes; - } else interrupt(ILL, m_pc); break; case 0xe: // INTBASE if (!(m_psr & PSR_U)) - { gen_write(mode[0], m_intbase); - m_pc += bytes; - } else interrupt(ILL, m_pc); break; case 0xf: // MOD gen_write(mode[0], m_mod); - m_pc += bytes; break; } break; @@ -937,7 +916,6 @@ template void ns32000_device::execute_run() decode(mode, bytes); gen_write(mode[0], condition(quick)); - m_pc += bytes; break; case 4: // ACBi inc,index,dst @@ -955,9 +933,10 @@ template void ns32000_device::execute_run() gen_write(mode[0], index); if (index & size_mask[size]) + { m_pc += dst; - else - m_pc += bytes; + m_sequential = false; + } } break; case 5: @@ -968,7 +947,6 @@ template void ns32000_device::execute_run() decode(mode, bytes); gen_write(mode[0], s32(quick << 28) >> 28); - m_pc += bytes; break; case 6: // LPRi procreg,src @@ -979,22 +957,17 @@ template void ns32000_device::execute_run() switch (quick) { - case 0x0: // US - // TODO: user stack pointer? - m_sp1 = gen_read(mode[0]); - m_pc += bytes; + case 0x0: // UPSR + m_psr = (m_psr & 0xff00) | u8(gen_read(mode[0])); break; case 0x8: // FP m_fp = gen_read(mode[0]); - m_pc += bytes; break; case 0x9: // SP SP = gen_read(mode[0]); - m_pc += bytes; break; case 0xa: // SB m_sb = gen_read(mode[0]); - m_pc += bytes; break; case 0xd: // PSR if (!(m_psr & PSR_U)) @@ -1005,24 +978,21 @@ template void ns32000_device::execute_run() m_psr = (m_psr & 0xff00) | u8(src); else m_psr = src; - - m_pc += bytes; } else interrupt(ILL, m_pc); break; case 0xe: // INTBASE if (!(m_psr & PSR_U)) - { m_intbase = gen_read(mode[0]); - m_pc += bytes; - } else interrupt(ILL, m_pc); break; case 0xf: // MOD m_mod = gen_read(mode[0]); - m_pc += bytes; + break; + default: + interrupt(UND, m_pc); break; } break; @@ -1034,37 +1004,40 @@ template void ns32000_device::execute_run() // CXPD desc // gen // addr + if (size == SIZE_D) { mode[0].addr(); decode(mode, bytes); - u32 const desc = gen_read(mode[0]); + u32 const descriptor = gen_read(mode[0]); SP -= 4; space(0).write_dword_unaligned(SP, m_mod); SP -= 4; space(0).write_dword_unaligned(SP, m_pc + bytes); - m_mod = u16(desc); + m_mod = u16(descriptor); m_sb = space(0).read_dword_unaligned(m_mod + 0); - m_pc = u16(desc >> 16) + space(0).read_dword_unaligned(m_mod + 8); + m_pc = space(0).read_dword_unaligned(m_mod + 8) + u16(descriptor >> 16); + m_sequential = false; } + else + interrupt(UND, m_pc); break; case 0x2: // BICPSRi src // gen // read.[BW] - mode[0].read_i(size); - decode(mode, bytes); - - if (size < SIZE_D) + if (size == SIZE_B || size == SIZE_W) { + mode[0].read_i(size); + decode(mode, bytes); + if (size == SIZE_B || !(m_psr & PSR_U)) { u16 const src = gen_read(mode[0]); m_psr &= ~src; - m_pc += bytes; } else interrupt(ILL, m_pc); @@ -1076,27 +1049,31 @@ template void ns32000_device::execute_run() // JUMP dst // gen // addr - mode[0].addr(); - decode(mode, bytes); + if (size == SIZE_D) + { + mode[0].addr(); + decode(mode, bytes); - // FIXME: size != 3? - m_pc = ea(mode[0]); + m_pc = ea(mode[0]); + m_sequential = false; + } + else + interrupt(UND, m_pc); break; case 0x6: // BISPSRi src // gen // read.[BW] - mode[0].read_i(size); - decode(mode, bytes); - - if (size < SIZE_D) + if (size == SIZE_B || size == SIZE_W) { + mode[0].read_i(size); + decode(mode, bytes); + if (size == SIZE_B || !(m_psr & PSR_U)) { u16 const src = gen_read(mode[0]); m_psr |= src; - m_pc += bytes; } else interrupt(ILL, m_pc); @@ -1116,12 +1093,12 @@ template void ns32000_device::execute_run() SP -= src; } - m_pc += bytes; break; case 0xc: // JSR dst // gen // addr + if (size == SIZE_D) { mode[0].addr(); decode(mode, bytes); @@ -1130,7 +1107,10 @@ template void ns32000_device::execute_run() space(0).write_dword_unaligned(SP, m_pc + bytes); m_pc = ea(mode[0]); + m_sequential = false; } + else + interrupt(UND, m_pc); break; case 0xe: // CASEi src @@ -1143,6 +1123,7 @@ template void ns32000_device::execute_run() s32 const src = gen_read_sx(mode[0]); m_pc += src; + m_sequential = false; } break; @@ -1181,7 +1162,6 @@ template void ns32000_device::execute_run() gen_write(mode[1], dst); } - m_pc += bytes; break; case 0x1: // CMPi src1,src2 @@ -1210,7 +1190,6 @@ template void ns32000_device::execute_run() || ((size == SIZE_B && u8(src1) > u8(src2))))) m_psr |= PSR_L; } - m_pc += bytes; break; case 0x2: // BICi src,dst @@ -1226,7 +1205,6 @@ template void ns32000_device::execute_run() gen_write(mode[1], dst & ~src); } - m_pc += bytes; break; case 0x4: // ADDCi src,dst @@ -1245,7 +1223,6 @@ template void ns32000_device::execute_run() gen_write(mode[1], dst); } - m_pc += bytes; break; case 0x5: // MOVi src,dst @@ -1260,7 +1237,6 @@ template void ns32000_device::execute_run() gen_write(mode[1], src); } - m_pc += bytes; break; case 0x6: // ORi src,dst @@ -1276,7 +1252,6 @@ template void ns32000_device::execute_run() gen_write(mode[1], src | dst); } - m_pc += bytes; break; case 0x8: // SUBi src,dst @@ -1295,18 +1270,21 @@ template void ns32000_device::execute_run() gen_write(mode[1], dst); } - m_pc += bytes; break; case 0x9: // ADDR src,dst // gen,gen // addr,write.D - mode[0].addr(); - mode[1].write_i(SIZE_D); - decode(mode, bytes); + if (size == SIZE_D) + { + mode[0].addr(); + mode[1].write_i(size); + decode(mode, bytes); - gen_write(mode[1], ea(mode[0])); - m_pc += bytes; + gen_write(mode[1], ea(mode[0])); + } + else + interrupt(UND, m_pc); break; case 0xa: // ANDi src,dst @@ -1322,7 +1300,6 @@ template void ns32000_device::execute_run() gen_write(mode[1], src & dst); } - m_pc += bytes; break; case 0xc: // SUBCi src,dst @@ -1341,7 +1318,6 @@ template void ns32000_device::execute_run() gen_write(mode[1], dst); } - m_pc += bytes; break; case 0xd: // TBITi offset,base @@ -1371,7 +1347,6 @@ template void ns32000_device::execute_run() m_psr &= ~PSR_F; } } - m_pc += bytes; break; case 0xe: // XORi src,dst @@ -1387,7 +1362,6 @@ template void ns32000_device::execute_run() gen_write(mode[1], src ^ dst); } - m_pc += bytes; break; } } @@ -1448,7 +1422,6 @@ template void ns32000_device::execute_run() m_r[0]--; } - m_pc += bytes; break; case 1: // CMPSi options @@ -1505,16 +1478,12 @@ template void ns32000_device::execute_run() m_r[0]--; } - m_pc += bytes; break; case 2: // SETCFG cfglist // short if (!(m_psr & PSR_U)) - { m_cfg = (opword >> 7) & 15; - m_pc += bytes; - } else interrupt(ILL, m_pc); break; @@ -1545,7 +1514,6 @@ template void ns32000_device::execute_run() m_r[0]--; } - m_pc += bytes; break; default: interrupt(UND, m_pc); @@ -1577,11 +1545,10 @@ template void ns32000_device::execute_run() u32 const src = gen_read(mode[1]); unsigned const limit = (size + 1) * 8 - 1; - u32 const dst = ((src << (count & limit)) & size_mask[size]) | ((src & size_mask[size]) >> (limit - (count & limit))); + u32 const dst = ((src << (count & limit)) & size_mask[size]) | ((src & size_mask[size]) >> (limit - (count & limit) + 1)); gen_write(mode[1], dst); } - m_pc += bytes; break; case 0x1: // ASHi count,dst @@ -1599,7 +1566,6 @@ template void ns32000_device::execute_run() gen_write(mode[1], dst); } - m_pc += bytes; break; case 0x2: // CBITi offset,base @@ -1638,7 +1604,6 @@ template void ns32000_device::execute_run() space(0).write_byte(byte_ea, byte & ~(1U << (offset & 7))); } } - m_pc += bytes; break; case 0x4: interrupt(UND, m_pc); @@ -1659,7 +1624,6 @@ template void ns32000_device::execute_run() gen_write(mode[1], dst); } - m_pc += bytes; break; case 0x6: // SBITi offset,base @@ -1695,10 +1659,9 @@ template void ns32000_device::execute_run() else m_psr &= ~PSR_F; - space(0).write_byte(byte_ea, byte | ~(1U << (offset & 7))); + space(0).write_byte(byte_ea, byte | (1U << (offset & 7))); } } - m_pc += bytes; break; case 0x8: // NEGi src,dst @@ -1727,7 +1690,6 @@ template void ns32000_device::execute_run() gen_write(mode[1], src); } } - m_pc += bytes; break; case 0x9: // NOTi src,dst @@ -1742,7 +1704,6 @@ template void ns32000_device::execute_run() gen_write(mode[1], src ^ 1U); } - m_pc += bytes; break; case 0xa: interrupt(UND, m_pc); @@ -1764,18 +1725,15 @@ template void ns32000_device::execute_run() s32 const src = gen_read_sx(mode[0]); - s32 const dst = std::abs(src); - m_psr &= ~PSR_F; - if (BIT(dst, ((size + 1) * 8 - 1))) + if (src == s32(0x80000000) >> (32 - (size + 1) * 8)) { m_psr |= PSR_F; gen_write(mode[1], src); } else - gen_write(mode[1], dst); + gen_write(mode[1], std::abs(src)); } - m_pc += bytes; break; case 0xd: // COMi src,dst @@ -1790,7 +1748,6 @@ template void ns32000_device::execute_run() gen_write(mode[1], ~src); } - m_pc += bytes; break; case 0xe: // IBITi offset,base @@ -1825,7 +1782,6 @@ template void ns32000_device::execute_run() space(0).write_byte(byte_ea, byte ^ (1U << (offset & 7))); } } - m_pc += bytes; break; case 0xf: // ADDPi src,dst @@ -1858,10 +1814,10 @@ template void ns32000_device::execute_run() u32 block1 = ea(mode[0]); u32 block2 = ea(mode[1]); - s32 length = displacement(bytes); + unsigned const num = displacement(bytes) / (size + 1) + 1; // TODO: aligned/unaligned transfers? - for (unsigned num = 0; num * (size + 1) < length; num++) + for (unsigned i = 0; i < num; i++) { switch (size) { @@ -1877,13 +1833,63 @@ template void ns32000_device::execute_run() block2 += (size + 1); } } - m_pc += bytes; break; case 0x1: // CMPMi block1,block2,length // gen,gen,disp // addr,addr - fatalerror("unimplemented: cmpm (%s)\n", machine().describe_context()); + { + mode[0].addr(); + mode[1].addr(); + decode(mode, bytes); + + u32 block1 = ea(mode[0]); + u32 block2 = ea(mode[1]); + unsigned const num = displacement(bytes) / (size + 1) + 1; + + m_psr |= PSR_Z; + m_psr &= ~(PSR_N | PSR_L); + + // TODO: aligned/unaligned transfers? + for (unsigned i = 0; i < num; i++) + { + s32 int1 = 0; + s32 int2 = 0; + + switch (size) + { + case SIZE_B: + int1 = s8(space(0).read_byte(block1)); + int2 = s8(space(0).read_byte(block2)); + break; + case SIZE_W: + int1 = s16(space(0).read_word_unaligned(block1)); + int2 = s16(space(0).read_word_unaligned(block2)); + break; + case SIZE_D: + int1 = s32(space(0).read_dword_unaligned(block1)); + int2 = s32(space(0).read_dword_unaligned(block2)); + break; + default: + // can't happen + break; + } + + if (int1 != int2) + { + m_psr &= ~PSR_Z; + if (int1 > int2) + m_psr |= PSR_N; + if (u32(int1) > u32(int2)) + m_psr |= PSR_L; + + break; + } + + block1 += (size + 1); + block2 += (size + 1); + } + } break; case 0x2: // INSSi src,base,offset,length @@ -1903,7 +1909,6 @@ template void ns32000_device::execute_run() gen_write(mode[1], (base & ~mask) | ((src << offset) & mask)); } - m_pc += bytes; break; case 0x3: // EXTSi base,dst,offset,length @@ -1923,12 +1928,12 @@ template void ns32000_device::execute_run() gen_write(mode[1], dst); } - m_pc += bytes; break; case 0x4: // MOVXBW src,dst // gen,gen // read.B,write.W + if (size == SIZE_B) { mode[0].read_i(size); mode[1].write_i(SIZE_W); @@ -1939,12 +1944,14 @@ template void ns32000_device::execute_run() gen_write(mode[1], dst); } - m_pc += bytes; + else + interrupt(UND, m_pc); break; case 0x5: // MOVZBW src,dst // gen,gen // read.B,write.W + if (size == SIZE_B) { mode[0].read_i(size); mode[1].write_i(SIZE_W); @@ -1955,12 +1962,14 @@ template void ns32000_device::execute_run() gen_write(mode[1], dst); } - m_pc += bytes; + else + interrupt(UND, m_pc); break; case 0x6: // MOVZiD src,dst // gen,gen // read.[BW],write.D + if (size == SIZE_B || size == SIZE_W) { mode[0].read_i(size); mode[1].write_i(SIZE_D); @@ -1970,12 +1979,14 @@ template void ns32000_device::execute_run() gen_write(mode[1], src); } - m_pc += bytes; + else + interrupt(UND, m_pc); break; case 0x7: // MOVXiD src,dst // gen,gen // read.[BW],write.D + if (size == SIZE_B || size == SIZE_W) { mode[0].read_i(size); mode[1].write_i(SIZE_D); @@ -1986,7 +1997,8 @@ template void ns32000_device::execute_run() gen_write(mode[1], dst); } - m_pc += bytes; + else + interrupt(UND, m_pc); break; case 0x8: // MULi src,dst @@ -2004,7 +2016,6 @@ template void ns32000_device::execute_run() gen_write(mode[1], dst); } - m_pc += bytes; break; case 0x9: // MEIi src,dst @@ -2012,7 +2023,7 @@ template void ns32000_device::execute_run() // read.i,rmw.2i { mode[0].read_i(size); - mode[1].rmw_i(size_code(size * 2 + 1)); + mode[1].rmw_i(size); decode(mode, bytes); u32 const src1 = gen_read(mode[0]); @@ -2020,9 +2031,15 @@ template void ns32000_device::execute_run() u64 const dst = mulu_32x32(src1, src2); - gen_write(mode[1], dst); + mode[1].rmw_i(size_code(size * 2 + 1)); + if (mode[1].type == REG) + { + m_r[mode[1].gen ^ 0] = (m_r[mode[1].gen ^ 0] & ~size_mask[size]) | ((dst >> 0) & size_mask[size]); + m_r[mode[1].gen ^ 1] = (m_r[mode[1].gen ^ 1] & ~size_mask[size]) | ((dst >> ((size + 1) * 8)) & size_mask[size]); + } + else + gen_write(mode[1], dst); } - m_pc += bytes; break; case 0xa: interrupt(UND, m_pc); @@ -2039,7 +2056,9 @@ template void ns32000_device::execute_run() u32 const src1 = gen_read(mode[0]); if (src1) { - u64 const src2 = gen_read(mode[1]); + u64 const src2 = (mode[1].type == REG) ? + (u64(m_r[mode[1].gen ^ 1] & size_mask[size]) << ((size + 1) * 8)) | (m_r[mode[1].gen ^ 0] & size_mask[size]) : + gen_read(mode[1]); u32 const quotient = src2 / src1; u32 const remainder = src2 % src1; @@ -2051,8 +2070,6 @@ template void ns32000_device::execute_run() } else gen_write(mode[1], (u64(quotient) << ((size + 1) * 8)) | remainder); - - m_pc += bytes; } else { @@ -2081,7 +2098,6 @@ template void ns32000_device::execute_run() s32 const dst = src2 / src1; gen_write(mode[1], dst); - m_pc += bytes; } else { @@ -2110,7 +2126,6 @@ template void ns32000_device::execute_run() s32 const dst = src2 % src1; gen_write(mode[1], dst); - m_pc += bytes; } else { @@ -2131,15 +2146,14 @@ template void ns32000_device::execute_run() mode[1].rmw_i(size); decode(mode, bytes); - u32 const src1 = gen_read_sx(mode[0]); + s32 const src1 = gen_read_sx(mode[0]); if (src1) { - u32 const src2 = gen_read_sx(mode[1]); + s32 const src2 = gen_read_sx(mode[1]); s32 const dst = (src1 + (src2 % src1)) % src1; gen_write(mode[1], dst); - m_pc += bytes; } else { @@ -2172,8 +2186,6 @@ template void ns32000_device::execute_run() gen_write(mode[1], quotient - 1); else gen_write(mode[1], quotient); - - m_pc += bytes; } else { @@ -2234,8 +2246,8 @@ template void ns32000_device::execute_run() // CVTP offset,base,dst // reg,gen,gen // addr,write.D + if (size == SIZE_D) { - // TODO: size != SIZE_D? mode[0].addr(); mode[1].write_i(size); decode(mode, bytes); @@ -2245,6 +2257,8 @@ template void ns32000_device::execute_run() gen_write(mode[1], base * 8 + offset); } + else + interrupt(UND, m_pc); break; case 2: // INSi offset,src,base,length @@ -2269,7 +2283,7 @@ template void ns32000_device::execute_run() } else { - u32 const base_ea = ea(mode[0]) + (offset >> 3); + u32 const base_ea = ea(mode[1]) + (offset >> 3); u32 const base = space(0).read_dword_unaligned(base_ea); u32 const mask = ((1U << length) - 1) << (offset & 7); @@ -2295,9 +2309,18 @@ template void ns32000_device::execute_run() s32 upper = 0; switch (size) { - case SIZE_B: lower = s8(space(0).read_byte(bounds + 0)); upper = s8(space(0).read_byte(bounds + 1)); break; - case SIZE_W: lower = s16(space(0).read_word_unaligned(bounds + 0)); upper = s16(space(0).read_word_unaligned(bounds + 2)); break; - case SIZE_D: lower = s32(space(0).read_dword_unaligned(bounds + 0)); upper = s32(space(0).read_dword_unaligned(bounds + 4)); break; + case SIZE_B: + upper = s8(space(0).read_byte(bounds + 0)); + lower = s8(space(0).read_byte(bounds + 1)); + break; + case SIZE_W: + upper = s16(space(0).read_word_unaligned(bounds + 0)); + lower = s16(space(0).read_word_unaligned(bounds + 2)); + break; + case SIZE_D: + upper = s32(space(0).read_dword_unaligned(bounds + 0)); + lower = s32(space(0).read_dword_unaligned(bounds + 4)); + break; default: // can't happen break; @@ -2350,7 +2373,7 @@ template void ns32000_device::execute_run() else offset++; - gen_write(mode[1], offset & limit); + gen_write(mode[1], offset & (limit - 1)); } break; case 6: @@ -2367,7 +2390,6 @@ template void ns32000_device::execute_run() break; } } - m_pc += bytes; break; case 0x3e: // format 9: xxxx xyyy yyoo ofii 0011 1110 @@ -2396,9 +2418,7 @@ template void ns32000_device::execute_run() mode[1].write_f(size_f); decode(mode, bytes); - if (!slave(mode[0], mode[1])) - m_pc += bytes; - else + if (slave(mode[0], mode[1])) interrupt(FPU, m_pc); break; case 1: @@ -2408,9 +2428,7 @@ template void ns32000_device::execute_run() mode[0].read_i(size); decode(mode, bytes); - if (!slave(mode[0], mode[1])) - m_pc += bytes; - else + if (slave(mode[0], mode[1])) interrupt(FPU, m_pc); break; case 2: @@ -2421,9 +2439,7 @@ template void ns32000_device::execute_run() mode[1].write_f(size_f); decode(mode, bytes); - if (!slave(mode[0], mode[1])) - m_pc += bytes; - else + if (slave(mode[0], mode[1])) interrupt(FPU, m_pc); break; case 3: @@ -2434,9 +2450,7 @@ template void ns32000_device::execute_run() mode[1].write_f(size_f); decode(mode, bytes); - if (!slave(mode[0], mode[1])) - m_pc += bytes; - else + if (slave(mode[0], mode[1])) interrupt(FPU, m_pc); break; case 4: @@ -2447,9 +2461,7 @@ template void ns32000_device::execute_run() mode[1].write_i(size); decode(mode, bytes); - if (!slave(mode[0], mode[1])) - m_pc += bytes; - else + if (slave(mode[0], mode[1])) interrupt(FPU, m_pc); break; case 5: @@ -2460,9 +2472,7 @@ template void ns32000_device::execute_run() mode[1].write_i(size); decode(mode, bytes); - if (!slave(mode[0], mode[1])) - m_pc += bytes; - else + if (slave(mode[0], mode[1])) interrupt(FPU, m_pc); break; case 6: @@ -2472,9 +2482,7 @@ template void ns32000_device::execute_run() mode[0].write_i(size); decode(mode, bytes); - if (!slave(mode[0], mode[1])) - m_pc += bytes; - else + if (slave(mode[1], mode[0])) interrupt(FPU, m_pc); break; case 7: @@ -2485,9 +2493,7 @@ template void ns32000_device::execute_run() mode[1].write_i(size); decode(mode, bytes); - if (!slave(mode[0], mode[1])) - m_pc += bytes; - else + if (slave(mode[0], mode[1])) interrupt(FPU, m_pc); break; } @@ -2524,9 +2530,7 @@ template void ns32000_device::execute_run() mode[1].rmw_f(size_f); decode(mode, bytes); - if (!slave(mode[0], mode[1])) - m_pc += bytes; - else + if (slave(mode[0], mode[1])) interrupt(FPU, m_pc); break; case 0x1: @@ -2537,9 +2541,7 @@ template void ns32000_device::execute_run() mode[1].write_f(size_f); decode(mode, bytes); - if (!slave(mode[0], mode[1])) - m_pc += bytes; - else + if (slave(mode[0], mode[1])) interrupt(FPU, m_pc); break; case 0x2: @@ -2556,8 +2558,6 @@ template void ns32000_device::execute_run() { m_psr &= ~(PSR_N | PSR_Z | PSR_L); m_psr |= status & (ns32000_slave_interface::SLAVE_N | ns32000_slave_interface::SLAVE_Z | ns32000_slave_interface::SLAVE_L); - - m_pc += bytes; } else interrupt(FPU, m_pc); @@ -2570,9 +2570,7 @@ template void ns32000_device::execute_run() mode[1].read_f(size_f); decode(mode, bytes); - if (!slave(mode[0], mode[1])) - m_pc += bytes; - else + if (slave(mode[0], mode[1])) interrupt(FPU, m_pc); break; case 0x4: @@ -2583,9 +2581,7 @@ template void ns32000_device::execute_run() mode[1].rmw_f(size_f); decode(mode, bytes); - if (!slave(mode[0], mode[1])) - m_pc += bytes; - else + if (slave(mode[0], mode[1])) interrupt(FPU, m_pc); break; case 0x5: @@ -2596,9 +2592,7 @@ template void ns32000_device::execute_run() mode[1].write_f(size_f); decode(mode, bytes); - if (!slave(mode[0], mode[1])) - m_pc += bytes; - else + if (slave(mode[0], mode[1])) interrupt(FPU, m_pc); break; case 0x8: @@ -2609,9 +2603,7 @@ template void ns32000_device::execute_run() mode[1].rmw_f(size_f); decode(mode, bytes); - if (!slave(mode[0], mode[1])) - m_pc += bytes; - else + if (slave(mode[0], mode[1])) interrupt(FPU, m_pc); break; case 0x9: @@ -2621,9 +2613,7 @@ template void ns32000_device::execute_run() mode[1].write_f(size_f); decode(mode, bytes); - if (!slave(mode[0], mode[1])) - m_pc += bytes; - else + if (slave(mode[0], mode[1])) interrupt(FPU, m_pc); break; case 0xc: @@ -2634,9 +2624,7 @@ template void ns32000_device::execute_run() mode[1].rmw_f(size_f); decode(mode, bytes); - if (!slave(mode[0], mode[1])) - m_pc += bytes; - else + if (slave(mode[0], mode[1])) interrupt(FPU, m_pc); break; case 0xd: @@ -2647,9 +2635,7 @@ template void ns32000_device::execute_run() mode[1].write_f(size_f); decode(mode, bytes); - if (!slave(mode[0], mode[1])) - m_pc += bytes; - else + if (slave(mode[0], mode[1])) interrupt(FPU, m_pc); break; } @@ -2684,8 +2670,7 @@ template void ns32000_device::execute_run() // addr mode[0].addr(); decode(mode, bytes); - - m_pc += bytes; + logerror("rdval (%s)\n", machine().describe_context()); break; case 1: // WRVAL loc @@ -2693,8 +2678,7 @@ template void ns32000_device::execute_run() // addr mode[0].addr(); decode(mode, bytes); - - m_pc += bytes; + logerror("wrval (%s)\n", machine().describe_context()); break; case 2: // LMR mmureg,src @@ -2702,8 +2686,7 @@ template void ns32000_device::execute_run() // read.D mode[0].read_i(size); decode(mode, bytes); - - m_pc += bytes; + logerror("lmr (%s)\n", machine().describe_context()); break; case 3: // SMR mmureg,dst @@ -2711,8 +2694,7 @@ template void ns32000_device::execute_run() // write.D mode[0].write_i(size); decode(mode, bytes); - - m_pc += bytes; + logerror("smr (%s)\n", machine().describe_context()); break; default: interrupt(UND, m_pc); @@ -2746,6 +2728,9 @@ template void ns32000_device::execute_run() break; } + if (m_sequential) + m_pc += bytes; + // trace trap if (m_psr & PSR_P) interrupt(TRC, m_pc); diff --git a/src/devices/cpu/ns32000/ns32000.h b/src/devices/cpu/ns32000/ns32000.h index f76325b65fb..f5254fa068d 100644 --- a/src/devices/cpu/ns32000/ns32000.h +++ b/src/devices/cpu/ns32000/ns32000.h @@ -142,6 +142,7 @@ private: bool m_nmi_line; bool m_int_line; bool m_wait; + bool m_sequential; }; class ns32008_device : public ns32000_device<0> -- cgit v1.2.3 From a9844643c461ab4341feecfe76f6a5329c05cc76 Mon Sep 17 00:00:00 2001 From: Patrick Mackinlay Date: Wed, 7 Oct 2020 19:32:25 +0700 Subject: ns32081: movif bug fixes * fixed byte-sized operand case * fixed sign extension --- src/devices/machine/ns32081.cpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/devices/machine/ns32081.cpp b/src/devices/machine/ns32081.cpp index c7a231a6d59..c12972fc21e 100644 --- a/src/devices/machine/ns32081.cpp +++ b/src/devices/machine/ns32081.cpp @@ -206,7 +206,7 @@ void ns32081_device::write_op(u16 data) switch ((m_opword >> 3) & 7) { case 0: // movif - m_op[0].expected = size + 1; + m_op[0].expected = (size == SIZE_B) ? 2 : (size + 1); m_op[2].expected = f_length; break; case 1: // lfsr @@ -327,12 +327,19 @@ void ns32081_device::execute() // MOVif src,dest // gen,gen // read.i,write.f - if (single) - m_op[2].value = i32_to_f32(m_op[0].value).v; - else - m_op[2].value = i32_to_f64(m_op[0].value).v; - m_op[2].expected = f_length; - m_tcy = 53; + { + s32 const src = + (size == SIZE_D) ? s32(m_op[0].value) : + (size == SIZE_W) ? s16(m_op[0].value) : + s8(m_op[0].value); + + if (single) + m_op[2].value = i32_to_f32(src).v; + else + m_op[2].value = i32_to_f64(src).v; + m_op[2].expected = f_length; + m_tcy = 53; + } break; case 1: // LFSR src -- cgit v1.2.3 From d6d18f9574a6c9d15182260acf5e3125e636a7b9 Mon Sep 17 00:00:00 2001 From: Robbbert Date: Wed, 7 Oct 2020 23:48:04 +1100 Subject: *kaypro: added back save state support; - fixed attributes off by one position; - removed anonymous timer --- src/mame/drivers/kaypro.cpp | 35 +++++++++++++++++++--------------- src/mame/includes/kaypro.h | 10 ++++------ src/mame/machine/kaypro.cpp | 46 ++++++++++++++++++++------------------------- src/mame/video/kaypro.cpp | 2 +- 4 files changed, 45 insertions(+), 48 deletions(-) diff --git a/src/mame/drivers/kaypro.cpp b/src/mame/drivers/kaypro.cpp index a72ec49a831..d57bc7ccfa2 100644 --- a/src/mame/drivers/kaypro.cpp +++ b/src/mame/drivers/kaypro.cpp @@ -212,6 +212,8 @@ void kaypro_state::kayproii(machine_config &config) m_screen->set_screen_update(FUNC(kaypro_state::screen_update_kayproii)); m_screen->set_palette(m_palette); + TIMER(config, m_floppy_timer).configure_generic(FUNC(kaypro_state::floppy_timer)); + GFXDECODE(config, "gfxdecode", m_palette, gfx_kayproii); PALETTE(config, m_palette, palette_device::MONOCHROME); @@ -300,6 +302,8 @@ void kaypro_state::kaypro484(machine_config &config) m_screen->set_visarea(0,80*8-1,0,25*16-1); m_screen->set_screen_update(FUNC(kaypro_state::screen_update_kaypro484)); + TIMER(config, m_floppy_timer).configure_generic(FUNC(kaypro_state::floppy_timer)); + GFXDECODE(config, "gfxdecode", m_palette, gfx_kaypro484); PALETTE(config, m_palette, FUNC(kaypro_state::kaypro_palette), 3); @@ -385,6 +389,7 @@ void kaypro_state::kaypro284(machine_config &config) config.device_remove("fdc:1"); FLOPPY_CONNECTOR(config, "fdc:0", kaypro_floppies, "525ssdd", floppy_image_device::default_floppy_formats).enable_sound(true); FLOPPY_CONNECTOR(config, "fdc:1", kaypro_floppies, "525ssdd", floppy_image_device::default_floppy_formats).enable_sound(true); + SOFTWARE_LIST(config.replace(), "flop_list").set_original("kayproii"); } void kaypro_state::init_kaypro() @@ -647,18 +652,18 @@ ROM_END /* YEAR NAME PARENT COMPAT MACHINE INPUT CLASS INIT COMPANY FULLNAME */ -COMP( 1982, kayproii, 0, 0, kayproii, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro II - 2/83" , 0 ) -COMP( 1983, kayproiv, kayproii, 0, kayproiv, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro IV - 4/83" , 0 ) // model 81-004 -COMP( 1983, kaypro10, 0, 0, kaypro10, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro 10 - 1983", 0 ) -COMP( 1983, kayproiip88, kayproii, 0, kayproii, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro 4 plus88 - 4/83" , MACHINE_NOT_WORKING ) // model 81-004 with an added 8088 daughterboard and rom -COMP( 1984, kaypro484, 0, 0, kaypro484, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro 4/84" , MACHINE_NOT_WORKING ) // model 81-015 -COMP( 1984, kaypro284, kaypro484, 0, kaypro284, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro 2/84" , MACHINE_NOT_WORKING ) // model 81-015 -COMP( 1984, kaypro484p88, kaypro484, 0, kaypro484, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro 4/84 plus88", MACHINE_NOT_WORKING ) // model 81-015 with an added 8088 daughterboard and rom -COMP( 1984, kaypro1084, kaypro10, 0, kaypro10, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro 10" , MACHINE_NOT_WORKING ) // model 81-005 -COMP( 1984, robie, 0, 0, kaypro484, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro Robie" , MACHINE_NOT_WORKING ) -COMP( 1985, kaypro2x, kaypro484, 0, kaypro484, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro 2x" , MACHINE_NOT_WORKING ) // model 81-025 -COMP( 1985, kaypronew2, 0, 0, kaypronew2, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro New 2", MACHINE_NOT_WORKING ) -COMP( 1985, kaypro4x, robie, 0, kaypro484, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro 4x" , MACHINE_NOT_WORKING ) -COMP( 1986, kaypro1, kaypro484, 0, kaypro484, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro 1", MACHINE_NOT_WORKING ) -COMP( 198?, omni2, kayproii, 0, omni2, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Omni II Logic Analyzer" , 0 ) -COMP( 198?, omni4, kaypro484, 0, kaypro484, kaypro, kaypro_state, init_kaypro, "Omni Logic Inc.", "Omni 4 Logic Analyzer" , MACHINE_NOT_WORKING ) +COMP( 1982, kayproii, 0, 0, kayproii, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro II - 2/83", MACHINE_SUPPORTS_SAVE ) +COMP( 1983, kayproiv, kayproii, 0, kayproiv, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro IV - 4/83", MACHINE_SUPPORTS_SAVE ) // model 81-004 +COMP( 1983, kaypro10, 0, 0, kaypro10, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro 10 - 1983", MACHINE_SUPPORTS_SAVE ) +COMP( 1983, kayproiip88, kayproii, 0, kayproii, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro 4 plus88 - 4/83" , MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) // model 81-004 with an added 8088 daughterboard and rom +COMP( 1984, kaypro484, 0, 0, kaypro484, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro 4/84", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) // model 81-015 +COMP( 1984, kaypro284, kaypro484, 0, kaypro284, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro 2/84", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) // model 81-015 +COMP( 1984, kaypro484p88, kaypro484, 0, kaypro484, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro 4/84 plus88", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) // model 81-015 with an added 8088 daughterboard and rom +COMP( 1984, kaypro1084, kaypro10, 0, kaypro10, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro 10", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) // model 81-005 +COMP( 1984, robie, 0, 0, kaypro484, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro Robie", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) +COMP( 1985, kaypro2x, kaypro484, 0, kaypro484, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro 2x", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) // model 81-025 +COMP( 1985, kaypronew2, 0, 0, kaypronew2, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro New 2", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) +COMP( 1985, kaypro4x, robie, 0, kaypro484, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro 4x", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) +COMP( 1986, kaypro1, kaypro484, 0, kaypro484, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Kaypro 1", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) +COMP( 198?, omni2, kayproii, 0, omni2, kaypro, kaypro_state, init_kaypro, "Non Linear Systems", "Omni II Logic Analyzer", MACHINE_SUPPORTS_SAVE ) +COMP( 198?, omni4, kaypro484, 0, kaypro484, kaypro, kaypro_state, init_kaypro, "Omni Logic Inc.", "Omni 4 Logic Analyzer", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) diff --git a/src/mame/includes/kaypro.h b/src/mame/includes/kaypro.h index e119ce1b5c7..370ea1d75d1 100644 --- a/src/mame/includes/kaypro.h +++ b/src/mame/includes/kaypro.h @@ -14,17 +14,13 @@ #include "sound/beep.h" #include "video/mc6845.h" #include "machine/wd_fdc.h" +#include "machine/timer.h" #include "emupal.h" #include "screen.h" class kaypro_state : public driver_device { public: - enum - { - TIMER_FLOPPY - }; - kaypro_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag) , m_palette(*this, "palette") @@ -42,6 +38,7 @@ public: , m_bankr(*this, "bankr") , m_bankw(*this, "bankw") , m_bank3(*this, "bank3") + , m_floppy_timer(*this, "floppy_timer") {} void omni2(machine_config &config); @@ -60,6 +57,7 @@ private: void kayproii_io(address_map &map); DECLARE_WRITE_LINE_MEMBER(write_centronics_busy); + TIMER_DEVICE_CALLBACK_MEMBER(floppy_timer); u8 kaypro484_87_r(); u8 kaypro484_system_port_r(); u8 kaypro484_status_r(); @@ -85,7 +83,6 @@ private: DECLARE_QUICKLOAD_LOAD_MEMBER(quickload_cb); void mc6845_screen_configure(); - virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; u8 m_mc6845_reg[32]; u8 m_mc6845_ind; @@ -115,6 +112,7 @@ private: required_memory_bank m_bankr; required_memory_bank m_bankw; required_memory_bank m_bank3; + required_device m_floppy_timer; }; #endif // MAME_INCLUDES_KAYPRO_H diff --git a/src/mame/machine/kaypro.cpp b/src/mame/machine/kaypro.cpp index f3700fa0c9c..decc44dd4c7 100644 --- a/src/mame/machine/kaypro.cpp +++ b/src/mame/machine/kaypro.cpp @@ -176,37 +176,31 @@ void kaypro_state::kaypro484_system_port_w(u8 data) *************************************************************************************/ -void kaypro_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +TIMER_DEVICE_CALLBACK_MEMBER(kaypro_state::floppy_timer) { bool halt; - switch (id) + halt = (bool)m_maincpu->state_int(Z80_HALT); + if (m_is_motor_off) { - case TIMER_FLOPPY: - halt = (bool)m_maincpu->state_int(Z80_HALT); - if (m_is_motor_off) - { - timer_set(attotime::from_hz(10), TIMER_FLOPPY); - break; - } - if ((halt) && (m_fdc_rq & 3) && (m_fdc_rq < 0x80)) - { - m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE); - m_fdc_rq |= 0x80; - } - else - if ((m_fdc_rq == 0x80) || ((!halt) && BIT(m_fdc_rq, 7))) - { - m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE); - m_fdc_rq &= 0x7f; - } - timer_set(attotime::from_hz(1e5), TIMER_FLOPPY); - - break; - default: - throw emu_fatalerror("Unknown id in kaypro_state::device_timer"); + m_floppy_timer->adjust(attotime::from_hz(10)); + return; } + + if ((halt) && (m_fdc_rq & 3) && (m_fdc_rq < 0x80)) + { + m_maincpu->set_input_line(INPUT_LINE_NMI, ASSERT_LINE); + m_fdc_rq |= 0x80; + } + else + if ((m_fdc_rq == 0x80) || ((!halt) && BIT(m_fdc_rq, 7))) + { + m_maincpu->set_input_line(INPUT_LINE_NMI, CLEAR_LINE); + m_fdc_rq &= 0x7f; + } + m_floppy_timer->adjust(attotime::from_hz(1e5)); } + WRITE_LINE_MEMBER( kaypro_state::fdc_intrq_w ) { m_fdc_rq = (m_fdc_rq & 0x82) | state; @@ -249,7 +243,7 @@ void kaypro_state::machine_reset() m_system_port = 0x80; m_fdc_rq = 0; m_maincpu->reset(); - timer_set(attotime::from_hz(1), TIMER_FLOPPY); /* kick-start the nmi timer */ + m_floppy_timer->adjust(attotime::from_hz(1)); /* kick-start the nmi timer */ } diff --git a/src/mame/video/kaypro.cpp b/src/mame/video/kaypro.cpp index b579a0f4484..03ae32f4547 100644 --- a/src/mame/video/kaypro.cpp +++ b/src/mame/video/kaypro.cpp @@ -144,7 +144,7 @@ MC6845_UPDATE_ROW( kaypro_state::kaypro484_update_row ) if (x == cursor_x) inv=0xff; u16 const mem = (ma + x) & 0x7ff; u8 const chr = m_vram[mem]; - u8 const attr = m_vram[mem | 0x800]; + u8 const attr = m_vram[(mem+1) | 0x800]; u8 fg, bg; if ((attr & 3) == 3) -- cgit v1.2.3 From 3ff42028e6bba2ffd0121ae2950f8ee0132c68d7 Mon Sep 17 00:00:00 2001 From: Nigel Barnes Date: Wed, 7 Oct 2020 15:05:09 +0100 Subject: bbc_tube_32016: Added FPU and fixed dipswitches. --- src/devices/bus/bbc/tube/tube_32016.cpp | 85 +++++++++++++++------------------ src/devices/bus/bbc/tube/tube_32016.h | 7 ++- 2 files changed, 42 insertions(+), 50 deletions(-) diff --git a/src/devices/bus/bbc/tube/tube_32016.cpp b/src/devices/bus/bbc/tube/tube_32016.cpp index 6a54665937d..f621cef4f07 100644 --- a/src/devices/bus/bbc/tube/tube_32016.cpp +++ b/src/devices/bus/bbc/tube/tube_32016.cpp @@ -38,10 +38,6 @@ DEFINE_DEVICE_TYPE(BBC_TUBE_32016L, bbc_tube_32016l_device, "bbc_tube_32016l", " void bbc_tube_32016_device::tube_32016_mem(address_map &map) { - map(0x000000, 0xffffff).rw(FUNC(bbc_tube_32016_device::read), FUNC(bbc_tube_32016_device::write)); - map(0xf00000, 0xf07fff).rom().region("rom", 0).mirror(0x0f8000); - map(0xf90000, 0xf90001).portr("CONFIG"); - map(0xfffff0, 0xffffff).rw("ula", FUNC(tube_device::parasite_r), FUNC(tube_device::parasite_w)).umask16(0x00ff); } //------------------------------------------------- @@ -76,36 +72,36 @@ ROM_END static INPUT_PORTS_START(tube_32016) PORT_START("CONFIG") - PORT_DIPNAME(0x80, 0x80, "H") PORT_DIPLOCATION("LKS:1") - PORT_DIPSETTING(0x80, "FPU") + PORT_DIPNAME(0x01, 0x01, "H") PORT_DIPLOCATION("LKS:1") + PORT_DIPSETTING(0x01, "FPU") PORT_DIPSETTING(0x00, "No FPU") - PORT_DIPNAME(0x40, 0x00, "G") PORT_DIPLOCATION("LKS:2") - PORT_DIPSETTING(0x40, "MMU") + PORT_DIPNAME(0x02, 0x00, "G") PORT_DIPLOCATION("LKS:2") + PORT_DIPSETTING(0x02, "MMU") PORT_DIPSETTING(0x00, "No MMU") - PORT_DIPNAME(0x20, 0x00, "F") PORT_DIPLOCATION("LKS:3") - PORT_DIPSETTING(0x20, "Reserved") + PORT_DIPNAME(0x04, 0x00, "F") PORT_DIPLOCATION("LKS:3") + PORT_DIPSETTING(0x04, "Reserved") PORT_DIPSETTING(0x00, "Reserved") - PORT_DIPNAME(0x10, 0x00, "E") PORT_DIPLOCATION("LKS:4") - PORT_DIPSETTING(0x10, "Reserved") + PORT_DIPNAME(0x08, 0x00, "E") PORT_DIPLOCATION("LKS:4") + PORT_DIPSETTING(0x08, "Reserved") PORT_DIPSETTING(0x00, "Reserved") - PORT_DIPNAME(0x08, 0x00, "D") PORT_DIPLOCATION("LKS:5") - PORT_DIPSETTING(0x08, "Reserved") + PORT_DIPNAME(0x10, 0x00, "D") PORT_DIPLOCATION("LKS:5") + PORT_DIPSETTING(0x10, "Reserved") PORT_DIPSETTING(0x00, "Reserved") - PORT_DIPNAME(0x04, 0x00, "C") PORT_DIPLOCATION("LKS:6") - PORT_DIPSETTING(0x04, "Reserved") + PORT_DIPNAME(0x20, 0x00, "C") PORT_DIPLOCATION("LKS:6") + PORT_DIPSETTING(0x20, "Reserved") PORT_DIPSETTING(0x00, "Reserved") - PORT_DIPNAME(0x02, 0x00, "B") PORT_DIPLOCATION("LKS:7") - PORT_DIPSETTING(0x02, "Reserved") + PORT_DIPNAME(0x40, 0x00, "B") PORT_DIPLOCATION("LKS:7") + PORT_DIPSETTING(0x40, "Reserved") PORT_DIPSETTING(0x00, "Reserved") - PORT_DIPNAME(0x01, 0x00, "A") PORT_DIPLOCATION("LKS:8") - PORT_DIPSETTING(0x01, "Reserved") + PORT_DIPNAME(0x80, 0x00, "A") PORT_DIPLOCATION("LKS:8") + PORT_DIPSETTING(0x80, "Reserved") PORT_DIPSETTING(0x00, "Reserved") INPUT_PORTS_END @@ -117,13 +113,16 @@ void bbc_tube_32016_device::device_add_mconfig(machine_config &config) { NS32016(config, m_maincpu, 12_MHz_XTAL / 2); m_maincpu->set_addrmap(AS_PROGRAM, &bbc_tube_32016_device::tube_32016_mem); + m_maincpu->set_fpu(m_ns32081); + + NS32081(config, m_ns32081, 12_MHz_XTAL / 2); TUBE(config, m_ula); m_ula->pnmi_handler().set_inputline(m_maincpu, INPUT_LINE_NMI); m_ula->pirq_handler().set_inputline(m_maincpu, INPUT_LINE_IRQ0); /* internal ram */ - RAM(config, m_ram).set_default_size("1M").set_extra_options("256K"); + RAM(config, m_ram).set_default_size("1M").set_extra_options("256K").set_default_value(0); /* software lists */ SOFTWARE_LIST(config, "flop_ls_32016").set_original("bbc_flop_32016"); @@ -137,7 +136,7 @@ void bbc_tube_32016l_device::device_add_mconfig(machine_config &config) m_maincpu->set_clock(16_MHz_XTAL / 2); /* internal ram */ - m_ram->set_default_size("4M"); + m_ram->set_default_size("4M").set_extra_options("1M").set_default_value(0); } //------------------------------------------------- @@ -175,10 +174,10 @@ bbc_tube_32016_device::bbc_tube_32016_device(const machine_config &mconfig, devi : device_t(mconfig, type, tag, owner, clock) , device_bbc_tube_interface(mconfig, *this) , m_maincpu(*this, "maincpu") + , m_ns32081(*this, "ns32081") , m_ula(*this, "ula") , m_ram(*this, "ram") , m_rom(*this, "rom") - , m_rom_enabled(true) { } @@ -198,7 +197,6 @@ bbc_tube_32016l_device::bbc_tube_32016l_device(const machine_config &mconfig, co void bbc_tube_32016_device::device_start() { - save_item(NAME(m_rom_enabled)); } //------------------------------------------------- @@ -207,7 +205,23 @@ void bbc_tube_32016_device::device_start() void bbc_tube_32016_device::device_reset() { - m_rom_enabled = true; + address_space &program = m_maincpu->space(AS_PROGRAM); + + // address map during booting + program.install_rom(0x000000, 0x007fff, 0xff8000, m_rom->base()); + + m_rom_shadow_tap = program.install_write_tap(0x000000, 0xffffff, "rom_shadow_w",[this](offs_t offset, u16 &data, u16 mem_mask) + { + // delete this tap + m_rom_shadow_tap->remove(); + + // address map after booting + m_maincpu->space(AS_PROGRAM).nop_readwrite(0x000000, 0xffffff); + m_maincpu->space(AS_PROGRAM).install_ram(0x000000, m_ram->mask(), 0x7fffff ^ m_ram->mask(), m_ram->pointer()); + m_maincpu->space(AS_PROGRAM).install_rom(0xf00000, 0xf07fff, 0x038000, m_rom->base()); + m_maincpu->space(AS_PROGRAM).install_read_port(0xf90000, 0xf90001, 0x00fffe, "CONFIG"); + m_maincpu->space(AS_PROGRAM).install_readwrite_handler(0xfffff0, 0xffffff, read8sm_delegate(*m_ula, FUNC(tube_device::parasite_r)), write8sm_delegate(*m_ula, FUNC(tube_device::parasite_w)), 0xff); + }); } @@ -224,24 +238,3 @@ void bbc_tube_32016_device::host_w(offs_t offset, uint8_t data) { m_ula->host_w(offset, data); } - - -uint8_t bbc_tube_32016_device::read(offs_t offset) -{ - uint16_t data; - - if (m_rom_enabled) - data = m_rom->base()[offset & 0x7fff]; - else - data = m_ram->pointer()[offset & m_ram->mask()]; - - return data; -} - -void bbc_tube_32016_device::write(offs_t offset, uint8_t data) -{ - /* clear ROM select on first write */ - m_rom_enabled = false; - - m_ram->pointer()[offset & m_ram->mask()] = data; -} diff --git a/src/devices/bus/bbc/tube/tube_32016.h b/src/devices/bus/bbc/tube/tube_32016.h index 7e7753ae5f6..aa772e1369c 100644 --- a/src/devices/bus/bbc/tube/tube_32016.h +++ b/src/devices/bus/bbc/tube/tube_32016.h @@ -18,6 +18,7 @@ #include "tube.h" #include "cpu/ns32000/ns32000.h" +#include "machine/ns32081.h" #include "machine/ram.h" #include "machine/tube.h" @@ -51,16 +52,14 @@ protected: virtual void host_w(offs_t offset, uint8_t data) override; required_device m_maincpu; + required_device m_ns32081; required_device m_ula; required_device m_ram; required_memory_region m_rom; - uint8_t read(offs_t offset); - void write(offs_t offset, uint8_t data); + memory_passthrough_handler *m_rom_shadow_tap; void tube_32016_mem(address_map &map); - - bool m_rom_enabled; }; -- cgit v1.2.3 From 43fe5ff51b986121de338bb7b4c19714205c1fcc Mon Sep 17 00:00:00 2001 From: Ivan Vangelista Date: Wed, 7 Oct 2020 16:55:11 +0200 Subject: astrof.cpp: dumped PROMs for asterion [Lorenzo Fongaro, Xadhoom, Paolo Bolognesi, Paolo Rinoldi] --- src/mame/drivers/astrof.cpp | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/mame/drivers/astrof.cpp b/src/mame/drivers/astrof.cpp index 700eca70dd3..9dcbc841cb6 100644 --- a/src/mame/drivers/astrof.cpp +++ b/src/mame/drivers/astrof.cpp @@ -1146,11 +1146,11 @@ ROM_START( asterion ) ROM_LOAD( "asterion eprom 3.bin", 0xf800, 0x0400, CRC(b206deda) SHA1(9ab52920c06ed6beb38bc7f97ffd00e8ad46c17d) ) ROM_LOAD( "asterion eprom 1.bin", 0xfc00, 0x0400, CRC(99008e90) SHA1(bfa1c3a66992f432fad37203f052f820b098e05f) ) - ROM_REGION( 0x0100, "proms", 0 ) // not dumped yet, using the one from abattle for now - ROM_LOAD( "8f-clr.bin", 0x0000, 0x0100, BAD_DUMP CRC(3bf3ccb0) SHA1(d61d19d38045f42a9adecf295e479fee239bed48) ) + ROM_REGION( 0x0100, "proms", 0 ) + ROM_LOAD( "sn74s470.bin", 0x0000, 0x0100, CRC(3bf3ccb0) SHA1(d61d19d38045f42a9adecf295e479fee239bed48) ) - ROM_REGION( 0x0100, "user1", 0 ) // not dumped yet, using the one from abattle for now, decryption is correct so this dump should too - ROM_LOAD( "2h-prot.bin", 0x0000, 0x0100, BAD_DUMP CRC(a6bdd18c) SHA1(438bfc543730afdb531204585f17a68ddc03ded0) ) + ROM_REGION( 0x0200, "user1", 0 ) // data relevant for the decryption is the same as the one for abattle, but it's on a larger PROM + ROM_LOAD( "sn74s472.bin", 0x0000, 0x0200, CRC(d437a9d8) SHA1(32e3513ab2ce1cde5196d80c53f5aa98b339929f) ) ROM_END @@ -1416,6 +1416,23 @@ void astrof_state::init_acombat3() } +void astrof_state::init_asterion() +{ + // use the protection PROM to decrypt the ROMs + uint8_t *rom = memregion("maincpu")->base(); + uint8_t *prom = memregion("user1")->base(); + + for (int i = 0xd000; i < 0x10000; i++) + { + rom[i] = prom[(rom[i] & 0x1f) + ((rom[i] & 0xe0) << 1)]; + } + + // set up protection handlers + m_maincpu->space(AS_PROGRAM).install_read_handler(0xa003, 0xa003, read8smo_delegate(*this, FUNC(astrof_state::shoot_r))); + m_maincpu->space(AS_PROGRAM).install_read_handler(0xa004, 0xa004, read8smo_delegate(*this, FUNC(astrof_state::abattle_coin_prot_r))); +} + + /************************************* * * Game drivers @@ -1429,7 +1446,7 @@ GAME( 1979, astroff, astrof, astrof, astrof, astrof_state, empty_init, GAME( 1979, abattle, astrof, abattle, abattle, astrof_state, init_abattle, ROT90, "bootleg? (Sidam)", "Astro Battle (set 1)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) GAME( 1979, abattle2, astrof, abattle, abattle, astrof_state, init_abattle, ROT90, "bootleg? (Sidam)", "Astro Battle (set 2)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) GAME( 1979, afire, astrof, abattle, abattle, astrof_state, init_afire, ROT90, "bootleg (Rene Pierre)", "Astro Fire", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) -GAME( 1980, asterion, astrof, abattle, abattle, astrof_state, init_abattle, ROT90, "bootleg? (Olympia)", "Asterion", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) +GAME( 1980, asterion, astrof, abattle, abattle, astrof_state, init_asterion, ROT90, "bootleg? (Olympia)", "Asterion", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) GAME( 1979, acombat, astrof, abattle, abattle, astrof_state, init_afire, ROT90, "bootleg", "Astro Combat (newer, CB)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) GAME( 1979, acombato, astrof, abattle, abattle, astrof_state, init_afire, ROT90, "bootleg", "Astro Combat (older, PZ)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) GAME( 1979, acombat3, astrof, abattle, abattle, astrof_state, init_acombat3, ROT90, "bootleg (Proel)", "Astro Combat (unencrypted)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) -- cgit v1.2.3 From 7fd77a36f5b659db6796c9246aec3d6fe40ebac6 Mon Sep 17 00:00:00 2001 From: Ivan Vangelista Date: Wed, 7 Oct 2020 16:56:46 +0200 Subject: astrof.h: missing file update for 43fe5ff51b986121de338bb7b4c19714205c1fcc --- src/mame/includes/astrof.h | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/mame/includes/astrof.h b/src/mame/includes/astrof.h index e4bf8971d1f..a7310bc85e9 100644 --- a/src/mame/includes/astrof.h +++ b/src/mame/includes/astrof.h @@ -25,6 +25,30 @@ public: m_sn(*this, "snsnd"), m_screen(*this, "screen") { } + void init_afire(); + void init_abattle(); + void init_sstarbtl(); + void init_acombat3(); + void init_asterion(); + + void astrof(machine_config &config); + void abattle(machine_config &config); + void spfghmk2(machine_config &config); + void tomahawk(machine_config &config); + void astrof_audio(machine_config &config); + void spfghmk2_audio(machine_config &config); + void tomahawk_audio(machine_config &config); + + DECLARE_CUSTOM_INPUT_MEMBER(astrof_p1_controls_r); + DECLARE_CUSTOM_INPUT_MEMBER(astrof_p2_controls_r); + DECLARE_CUSTOM_INPUT_MEMBER(tomahawk_controls_r); + DECLARE_INPUT_CHANGED_MEMBER(coin_inserted); + DECLARE_INPUT_CHANGED_MEMBER(service_coin_inserted); + +protected: + virtual void video_start() override; + +private: /* video-related */ required_shared_ptr m_videoram; @@ -62,20 +86,10 @@ public: uint8_t abattle_coin_prot_r(); uint8_t afire_coin_prot_r(); uint8_t tomahawk_protection_r(); - DECLARE_CUSTOM_INPUT_MEMBER(astrof_p1_controls_r); - DECLARE_CUSTOM_INPUT_MEMBER(astrof_p2_controls_r); - DECLARE_CUSTOM_INPUT_MEMBER(tomahawk_controls_r); void astrof_audio_1_w(uint8_t data); void astrof_audio_2_w(uint8_t data); void spfghmk2_audio_w(uint8_t data); void tomahawk_audio_w(uint8_t data); - DECLARE_INPUT_CHANGED_MEMBER(coin_inserted); - DECLARE_INPUT_CHANGED_MEMBER(service_coin_inserted); - void init_afire(); - void init_abattle(); - void init_sstarbtl(); - void init_acombat3(); - virtual void video_start() override; DECLARE_MACHINE_START(astrof); DECLARE_MACHINE_START(abattle); DECLARE_MACHINE_RESET(abattle); @@ -92,13 +106,6 @@ public: void tomahawk_set_video_control_2( uint8_t data ); void video_update_common( bitmap_rgb32 &bitmap, const rectangle &cliprect, pen_t *pens, int num_pens ); void base(machine_config &config); - void astrof(machine_config &config); - void abattle(machine_config &config); - void spfghmk2(machine_config &config); - void tomahawk(machine_config &config); - void astrof_audio(machine_config &config); - void spfghmk2_audio(machine_config &config); - void tomahawk_audio(machine_config &config); void astrof_map(address_map &map); void spfghmk2_map(address_map &map); void tomahawk_map(address_map &map); -- cgit v1.2.3 From 4b0903bfdbe88d4e220fc2c2f140e7b503060909 Mon Sep 17 00:00:00 2001 From: Ivan Vangelista Date: Wed, 7 Oct 2020 16:59:13 +0200 Subject: Mega Double Strip (Ver. 1.10b) [Jorge Silva, Fernando Oliveira] --- src/mame/drivers/blitz68k.cpp | 39 +++++++++++++++++++++++++++++++++++++++ src/mame/mame.lst | 1 + 2 files changed, 40 insertions(+) diff --git a/src/mame/drivers/blitz68k.cpp b/src/mame/drivers/blitz68k.cpp index ba96cc81447..2fdfb11ef41 100644 --- a/src/mame/drivers/blitz68k.cpp +++ b/src/mame/drivers/blitz68k.cpp @@ -12,6 +12,7 @@ Year Game Manufacturer ---------------------------------------------------------------------- 1990 Mega Double Poker Blitz Systems Inc. 1990 Mega Double Poker Jackpot Blitz Systems Inc. +1992 Mega Double Strip Blitz Systems Inc. 1993 Bank Robbery Entertainment Technology Corp. 1993? Poker 52 Blitz Systems Inc. 1993 Strip Teaser @@ -94,6 +95,7 @@ public: void init_megadblj(); void init_hermit(); void init_dualgame(); + void init_megastrp(); void hermit(machine_config &config); void bankrob(machine_config &config); @@ -2211,6 +2213,32 @@ ROM_START( bankrobb ) // DK-B main PCB + 8L74 sub PCB ROM_LOAD( "palce16v8h.u71", 0x000, 0x117, NO_DUMP ) ROM_END +// Mega Strip on ROM labels +// Maxi Strip Poker and both copyright 91 and 1992 in ROMs +// Mega Double Strip on title screen, 1992 copyright on following screen +// English and French strings in ROMs +// Main PCB: DK-B Copyright 1991 Blitz System inc. +// Sub PCB: BLZ AB1 Copyright 1992, Blitz System Inc. + +ROM_START( megastrp ) + ROM_REGION( 0x40000, "maincpu", 0 ) // 68000 code, on main board + ROM_LOAD16_BYTE( "1992_mega_strip_1.10b_b.u32", 0x00000, 0x20000, CRC(aaac8916) SHA1(74320bdf8b0f4de8a571fc6494252f01eff32cf9) ) // 1xxxxxxxxxxxxxxxx = 0xFF + ROM_LOAD16_BYTE( "1992_mega_strip_1.10b_a.u31", 0x00001, 0x20000, CRC(7661bd45) SHA1(b25b20b893ca428e0364f6b6d0ad435354958c31) ) // 1xxxxxxxxxxxxxxxx = 0xFF + + ROM_REGION( 0x2000, "mcu1", 0 ) // 68HC705C8P code, on main board + ROM_LOAD( "1992_mega_strip_control_1.0.u2", 0x0000, 0x2000, NO_DUMP ) + + ROM_REGION( 0x2000, "mcu2", 0 ) // 68HC705C8P code, on sub board + ROM_LOAD( "blz_10b-4.01.u2", 0x0000, 0x2000, NO_DUMP ) + + ROM_REGION16_BE( 0x100000, "blitter", 0 ) // on main board + ROM_LOAD( "1992_mega_strip_1.10b_c.u46", 0x00000, 0x80000, CRC(d3813101) SHA1(80c6311ddea1b161a03fcd69c1b8d4e8a2d99636) ) + ROM_LOAD( "1992_mega_strip_1.10b_d.u51", 0x80000, 0x80000, CRC(ee45ca2f) SHA1(9d1faeac17f60f1ea72d3fd3355544ea2d89a2bb) ) + + ROM_REGION( 0x80000, "samples", 0 ) // on main board + ROM_LOAD( "1992_mega_strip_1.10_sound.u18", 0x00000, 0x80000, CRC(8c345dc2) SHA1(2ad9dea4543d2c16f42a38653d740ff0c0fa1798) ) +ROM_END + /************************************************************************************************************* Triple Play @@ -3018,11 +3046,22 @@ void blitz68k_state::init_megadble() ROM[0x1d40/2] = 0x4e71; } +void blitz68k_state::init_megastrp() +{ + uint16_t *ROM = (uint16_t *)memregion("maincpu")->base(); + + // skip loops until the MCUs are dumped and the hardware better understood + ROM[0x1678/2] = 0x4e71; + + ROM[0x10c80/2] = 0x4e71; +} + GAME( 1992, maxidbl, 0, maxidbl, maxidbl, blitz68k_state, init_maxidbl, ROT0, "Blitz Systems Inc.", "Maxi Double Poker (Ver. 1.10)", MACHINE_NOT_WORKING | MACHINE_UNEMULATED_PROTECTION | MACHINE_NO_SOUND | MACHINE_WRONG_COLORS ) GAME( 1990, megadblj, 0, maxidbl, maxidbl, blitz68k_state, init_megadblj, ROT0, "Blitz Systems Inc.", "Mega Double Poker Jackpot (Ver. 1.26)", MACHINE_NOT_WORKING | MACHINE_UNEMULATED_PROTECTION | MACHINE_NO_SOUND ) // JUNE 28TH, 1993 GAME( 1990, megadble, 0, maxidbl, maxidbl, blitz68k_state, init_megadble, ROT0, "Blitz Systems Inc.", "Mega Double Poker (Ver. 1.63 Espagnol)", MACHINE_NOT_WORKING | MACHINE_UNEMULATED_PROTECTION | MACHINE_NO_SOUND | MACHINE_WRONG_COLORS ) // NOVEMBER 1994 +GAME( 1992, megastrp, 0, bankroba, bankrob, blitz68k_state, init_megastrp, ROT0, "Blitz Systems Inc.", "Mega Double Strip (Ver. 1.10b)", MACHINE_NOT_WORKING | MACHINE_UNEMULATED_PROTECTION | MACHINE_NO_SOUND ) // @ 1993 BLITZ SYSTEM INC GAME( 1993, steaser, 0, steaser, steaser, blitz68k_state, empty_init, ROT0, "", "Strip Teaser (Italy, Ver. 1.22)", MACHINE_NOT_WORKING | MACHINE_UNEMULATED_PROTECTION | MACHINE_NO_SOUND ) // In-game strings are in Italian but service mode is half English / half French? GAME( 1993, bankrob, 0, bankrob, bankrob, blitz68k_state, init_bankrob, ROT0, "Entertainment Technology Corp.", "Bank Robbery (Ver. 3.32)", MACHINE_NOT_WORKING | MACHINE_UNEMULATED_PROTECTION | MACHINE_NO_SOUND ) // BLITZ SYSTEM INC APRIL 1995 GAME( 1993, bankroba, bankrob, bankroba, bankrob, blitz68k_state, init_bankroba, ROT0, "Entertainment Technology Corp.", "Bank Robbery (Ver. 2.00)", MACHINE_NOT_WORKING | MACHINE_UNEMULATED_PROTECTION | MACHINE_NO_SOUND ) // BLITZ SYSTEM INC MAY 10TH, 1993 diff --git a/src/mame/mame.lst b/src/mame/mame.lst index ac19a0460e0..b19a036926d 100644 --- a/src/mame/mame.lst +++ b/src/mame/mame.lst @@ -9306,6 +9306,7 @@ ilpag // (c) 199? unknown maxidbl // (c) 1992 Blitz Systems Inc. megadble // (c) 1990 Blitz Systems Inc. megadblj // (c) 1990 Blitz Systems Inc. +megastrp // (c) 1992 Blitz Systems Inc. poker52 // (c) 1993 Blitz Systems Inc. ? steaser // (c) 1993 unknown texasrls // (c) 1998 Cadillac Jack -- cgit v1.2.3 From b90fd355150a0f56e368bb33619459b19ec92bee Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Thu, 8 Oct 2020 02:04:31 +1100 Subject: Various improvements to image file handling: Moved MS DIB parser out of ICO file reader and made it available for artwork and layout images. Added more efficient I/O and better error checking for JPEG file loading (MAME will no longer exit immediately on a bad JPEG file). Made caller responsible for opening files for loading images, to avoid decompressing images used in ZIP/7z artwork multiple times. Added support for JPEG and Windows DIB to picture_image_device. Added support for SVG image files in external artwork. Added support for using I/O port value for animation state and masking animation state values. Made bounds elements more flexible in layouts. Reworked headers to reduce dependencies. Updated layout file format documentation. --- docs/source/techspecs/layout_files.rst | 350 ++++++++++---- scripts/build/complay.py | 95 ++-- scripts/src/emu.lua | 1 + scripts/src/lib.lua | 3 + src/devices/imagedev/picture.cpp | 26 +- src/devices/video/fixfreq.cpp | 5 +- src/devices/video/vector.cpp | 5 +- src/emu/crsshair.cpp | 36 +- src/emu/recording.cpp | 16 +- src/emu/render.cpp | 26 +- src/emu/render.h | 80 +-- src/emu/rendertypes.h | 153 ++++++ src/emu/rendlay.cpp | 448 +++++++++++++---- src/emu/rendutil.cpp | 319 ++++++++---- src/emu/rendutil.h | 81 +--- src/emu/screen.cpp | 37 +- src/emu/video.cpp | 8 +- src/frontend/mame/ui/icorender.cpp | 508 +------------------ src/frontend/mame/ui/menu.cpp | 13 +- src/frontend/mame/ui/selmenu.cpp | 135 +++--- src/lib/util/msdib.cpp | 642 +++++++++++++++++++++++++ src/lib/util/msdib.h | 46 ++ src/lib/util/nanosvg.h | 33 ++ src/lib/util/png.cpp | 253 +++++----- src/lib/util/png.h | 42 +- src/mame/drivers/alg.cpp | 1 - src/mame/drivers/by17.cpp | 5 - src/mame/drivers/by35.cpp | 5 - src/mame/drivers/cliffhgr.cpp | 1 - src/mame/drivers/decwritr.cpp | 2 +- src/mame/drivers/dlair.cpp | 1 - src/mame/drivers/istellar.cpp | 1 - src/mame/drivers/lgp.cpp | 1 - src/mame/layout/unkeinv.lay | 52 +- src/mame/video/midtunit.cpp | 2 +- src/mame/video/ssv.cpp | 1 - src/mame/video/st0020.cpp | 4 +- src/mame/video/tx1.cpp | 4 +- src/osd/modules/render/bgfx/texturemanager.cpp | 6 +- src/osd/modules/render/d3d/d3dhlsl.cpp | 26 +- src/tools/pngcmp.cpp | 20 +- src/tools/regrep.cpp | 12 +- 42 files changed, 2192 insertions(+), 1313 deletions(-) create mode 100644 src/emu/rendertypes.h create mode 100644 src/lib/util/msdib.cpp create mode 100644 src/lib/util/msdib.h create mode 100644 src/lib/util/nanosvg.h diff --git a/docs/source/techspecs/layout_files.rst b/docs/source/techspecs/layout_files.rst index 31864f1c70f..9ebb5863de5 100644 --- a/docs/source/techspecs/layout_files.rst +++ b/docs/source/techspecs/layout_files.rst @@ -64,7 +64,7 @@ in the rightward and downward directions. The origin (0,0) has no particular significance, and you may freely use negative coordinates in layouts. Coordinates are supplied as floating-point numbers. -MAME assumes that view coordinates have the same aspect ratio as pixel on the +MAME assumes that view coordinates have the same aspect ratio as pixels on the output device (host screen or window). Assuming square pixels and no rotation, this means equal distances in X and Y axes correspond to equal horizontal and vertical distances in the rendered output. @@ -73,20 +73,30 @@ Views, groups and elements all have their own internal coordinate systems. When an element or group is referenced from a view or another group, its coordinates are scaled as necessary to fit the specified bounds. -Objects are positioned and sized using ``bounds`` elements. A bounds element -may specify the position of the top left corner and the size using ``x``, ``y``, -``width`` and ``height`` attributes, or it may specify the coordinates of the -edges with the ``left``, ``top``, ``right`` and ``bottom`` attributes. These -two ``bounds`` elements are equivalent:: +Objects are positioned and sized using ``bounds`` elements. The horizontal +position and size may be specified in three ways: left edge and width using +``x`` and ``width`` attributes, horizontal centre and width using ``xc`` and +``width`` attributes, or left and right edges using ``left`` and ``right`` +attributes. Similarly, the vertical position and size may be specified in terms +of the top edge and height using ``y`` and ``height`` attributes, vertical +centre and height using ``yc`` and ``height`` attributes, or top and bottom +edges using ``top`` and ``bottom`` attributes. - - +These three ``bounds`` elements are equivalent:: -Either the ``x`` or ``left`` attribute must be present to distinguish between -the two schemes. The ``width`` and ``height`` or ``right`` and ``bottom`` -default to 1.0 if not supplied. It is an error if ``width`` or ``height`` are -negative, if ``right`` is less than ``left``, or if ``bottom`` is less than -``top``. + + + + +It’s possible to use different schemes in the horizontal and vertical +directions. For example, these equivalent ``bounds`` elements are also valid:: + + + + +The ``width``/``height`` or ``right``/``bottom`` default to 1.0 if not supplied. +It is an error if ``width`` or ``height`` are negative, if ``right`` is less +than ``left``, or if ``bottom`` is less than ``top``. .. _layout-concepts-colours: @@ -376,12 +386,12 @@ and rendering. An element may be used in multiple views, and may be used multiple times within a view. An element’s appearance depends on its *state*. The state is an integer which -usually comes from an I/O port field or an emulated output (see the discussion -of :ref:`layout-parts-views` for information on connecting an element to an I/O -port or output). Any component of an element may be restricted to only drawing -when the element’s state is a particular value. Some components (e.g. -multi-segment displays and reels) use the state directly to determine their -appearance. +usually comes from an I/O port field or an emulated output (see +:ref:`layout-interact-elemstate` for information on connecting an element to an +emulated I/O port or output). Any component of an element may be restricted to +only drawing when the element’s state is a particular value. Some components +(e.g. multi-segment displays and reels) use the state directly to determine +their appearance. Each element has its own internal coordinate system. The bounds of the element’s coordinate system are computed as the union of the bounds of the @@ -395,8 +405,9 @@ attribute, to be used if not connected to an emulated output or I/O port. If present, the ``defstate`` attribute must be a non-negative integer. Child elements of the ``element`` element instantiate components, which are -drawn in reading order from first to last (components draw on top of components -that come before them). All components support a few common features: +drawn into the element texture in reading order from first to last using alpha +blending (components draw over and may obscure components that come before +them). All components support a few common features: * Components may be conditionally drawn depending on the element’s state by supplying ``state`` and/or ``statemask`` attributes. If present, these @@ -459,18 +470,25 @@ rect disk Draws a uniform colour ellipse fitted to its bounds. image - Draws an image loaded from a PNG or JPEG file. The name of the file to load - (including the file name extension) is supplied with the required ``file`` - attribute. Additionally, an optional ``alphafile`` attribute may be used to - specify the name of a PNG file (including the file name extension) to load - into the alpha channel of the image. The image file(s) should be placed in - the same directory/archive as the layout file. If the ``alphafile`` - attribute refers refers to a file, it must have the same dimensions as the - file referred to by the ``file`` attribute, and must have a bit depth no - greater than eight bits per channel per pixel. The intensity from this - image (brightness) is copied to the alpha channel, with full intensity (white - in a greyscale image) corresponding to fully opaque, and black corresponding - to fully transparent. + Draws an image loaded from a PNG, JPEG, Windows DIB (BMP) or SVG file. The + name of the file to load (including the file name extension) is supplied + using the required ``file`` attribute. Additionally, an optional + ``alphafile`` attribute may be used to specify the name of a PNG file + (including the file name extension) to load into the alpha channel of the + image. + + If the ``alphafile`` attribute refers refers to a file, it must have the + same dimensions (in pixels) as the file referred to by the ``file`` + attribute, and must have a bit depth no greater than eight bits per channel + per pixel. The intensity from this image (brightness) is copied to the + alpha channel, with full intensity (white in a greyscale image) + corresponding to fully opaque, and black corresponding to fully transparent. + The ``alphafile`` attribute will be ignored if the ``file`` attribute refers + to an SVG image; it is only used in conjunction with bitmap images. + + The image file(s) should be placed in the same directory/archive as the + layout file. Image file formats are detected by examining the content of + the files, file name extensions are ignored. text Draws text in using the UI font in the specified colour. The text to draw must be supplied using a ``string`` attribute. An ``align`` attribute may @@ -680,11 +698,15 @@ param element Adds an element to the view (see :ref:`layout-parts-elements`). The name of the element to add is specified using the required ``ref`` attribute. It is - an error if no element with this name is defined in the layout file. May - optionally be connected to an emulated I/O port using ``inputtag`` and + an error if no element with this name is defined in the layout file. Within + a layer, elements are drawn in the order they appear in the layout file, + from front to back. See below for more details. + + May optionally be connected to an emulated I/O port using ``inputtag`` and ``inputmask`` attributes, and/or an emulated output using a ``name`` - attribute. Within a layer, elements are drawn in the order they appear in - the layout file, from front to back. See below for more details. + attribute. See :ref:`layout-interact-clickable` for details. See + :ref:`layout-interact-elemstate` for details on supplying a state value to + the instantiated element. screen Adds an emulated screen image to the view. The screen must be identified using either an ``index`` attribute or a ``tag`` attribute (it is an error @@ -694,6 +716,10 @@ screen zero (0). If present, the ``tag`` attribute must be the tag path to the screen relative to the device that causes the layout to be loaded. Screens are drawn in the order they appear in the layout file, from front to back. + + May optionally be connected to an emulated I/O port using ``inputtag`` and + ``inputmask`` attributes, and/or an emulated output using a ``name`` + attribute. See :ref:`layout-interact-clickable` for details. collection Adds screens and/or items in a collection that can be shown or hidden by the user (see :ref:`layout-parts-collections`). The name of the collection is @@ -765,51 +791,10 @@ Screens (``screen`` elements), layout elements (``element`` elements) and groups :ref:`layout-concepts-colours`) specifying a modifier colour. The component colours of the screen or layout element(s) are multiplied by this colour. -If an ``element`` element has ``inputtag`` and ``inputmask`` attributes, -clicking it is equivalent to pressing a key/button mapped to the corresponding -input(s). The ``inputtag`` specifies the tag path of an I/O port relative to -the device that caused the layout file to be loaded. The ``inputmask`` -attribute must be an integer specifying the bits of the I/O port that the -element should activate. This sample shows instantiation of clickable buttons:: - - - - - - - - - - - -If an ``element`` element has a ``name`` attribute, it will take its state from -the value of the correspondingly named emulated output. Note that output names -are global, which can become an issue when a machine uses multiple instances of -the same type of device. See :ref:`layout-parts-elements` for details on how an -element’s state affects its appearance. This example shows how digital displays -may be connected to emulated outputs:: - - - - - - - - -If an element instantiating a layout element has ``inputtag`` and ``inputmask`` -attributes but lacks a ``name`` attribute, it will take its state from the value -of the corresponding I/O port, masked with the ``inputmask`` value and XORed -with the I/O port default field value. The latter is useful for inputs that are -active-low. If the result is non-zero, the state is 1, otherwise it’s 0. This -is often used to allow clickable buttons and toggle switches to provide visible -feedback. By using ``inputraw="1"``, it’s possible to obtain the raw data from -the I/O port, masked with the ``inputmask`` value and shifted to the right to -remove trailing zeroes (for example a mask of 0x05 will result in no shift, while -a mask of 0xb0 will result in the value being shifted four bits to the right). - -When handling mouse input, MAME treats all layout elements as being rectangular, -and only activates the frontmost element whose area includes the location of the -mouse pointer. +Screens (``screen`` elements) and layout elements (``element`` elements) may +have their colour and position/size animated by supplying multiple ``color`` +and/or ``bounds`` child elements with ``state`` attributes. See +:ref:`layout-interact-itemanim` for details. .. _layout-parts-collections: @@ -1087,6 +1072,207 @@ tiles on each iteration. Rows are connected to I/O ports ``board:IN.7`` at the top to ``board.IN.0`` at the bottom. +.. _layout-interact: + +Interactivity +------------- + +Interactive views are supported by allowing items to be bound to emulated +outputs and I/O ports. Five kinds of interactivity are supported: + +Clickable items + If an item in a view is bound to an I/O port switch field, clicking the + item will activate the emulated switch. +State-dependent components + Some components will be drawn differently depending on the containing + element’s state. These include the dot matrix, multi-segment LED display, + simple counter and reel elements. See :ref:`layout-parts-elements` for + details. +Conditionally-drawn components + Components may be conditionally drawn or hidden depending on the containing + element’s state by supplying ``state`` and/or ``statemask`` attributes. See + :ref:`layout-parts-elements` for details. +Component parameter animation + Components’ colour and position/size within their containing element may be + animated according the element’s state by providing multiple ``color`` + and/or ``bounds`` elements with ``state`` attributes. See + :ref:`layout-parts-elements` for details. +Item parameter animation + Items’ colour and position/size within their containing view may be animated + according to their animation state. + + +.. _layout-interact-clickable: + +Clickable items +~~~~~~~~~~~~~~~ + +If a view item (``element`` or ``screen`` element) has ``inputtag`` and +``inputmask`` attribute values that correspond to a digital switch field in the +emulated system, clicking the element will activate the switch. The switch +will remain active as long as the mouse button is held down and the pointer is +within the item’s current bounds. (Note that the bounds may change depending on +the item’s animation state, see :ref:`layout-interact-itemanim`). + +The ``inputtag`` attribute specifies the tag path of an I/O port relative to the +device that caused the layout file to be loaded. The ``inputmask`` attribute +must be an integer specifying the bits of the I/O port field that the item +should activate. This sample shows instantiation of clickable buttons:: + + + + + + + + + + + +When handling mouse input, MAME treats all layout elements as being rectangular, +and only activates the first clickable item whose area includes the location of +the mouse pointer. + + +.. _layout-interact-elemstate: + +Element state +~~~~~~~~~~~~~ + +A view item that instantiates an element (``element`` element) may supply a +state value to the element from an emulated I/O port or output. See +:ref:`layout-parts-elements` for details on how an element’s state affects its +appearance. + +If the ``element`` element has a ``name`` attribute, the element state value +will be taken from the value of the correspondingly named emulated output. Note +that output names are global, which can become an issue when a machine uses +multiple instances of the same type of device. This example shows how digital +displays may be connected to emulated outputs:: + + + + + + + + +If the ``element`` element has ``inputtag`` and ``inputmask`` attributes but +lacks a ``name`` attribute, the element state value will be taken from the value +of the corresponding I/O port, masked with the ``inputmask`` value. The +``inputtag`` attribute specifies the tag path of an I/O port relative to the +device that caused the layout file to be loaded. The ``inputmask`` attribute +must be an integer specifying the bits of the I/O port field to use. + +If the ``element`` element has no ``inputraw`` attribute, or if the value of the +``inputraw`` attribute is ``no``, the I/O port’s value is masked with the +``inputmask`` value and XORed with the I/O port default field value. If the +result is non-zero, the element state is 1, otherwise it’s 0. This is often +used or provide visual feedback for clickable buttons, as values for active-high +and active-low switches are normalised. + +If the ``element`` element has an ``inputraw`` attribute with the value ``yes``, +the element state will be taken from the I/O port’s value masked with the +``inputmask`` value and shifted to the right to remove trailing zeroes (for +example a mask of 0x05 will result in no shift, while a mask of 0xb0 will result +in the value being shifted four bits to the right). This is useful for +obtaining the value of analog or positional inputs. + + +.. _layout-interact-itemanim: + +View item animation +~~~~~~~~~~~~~~~~~~~ + +Items’ colour and position/size within their containing view may be animated. +This is achieved by supplying multiple ``color`` and/or ``bounds`` child +elements with ``state`` attributes. The ``state`` attribute of each ``color`` +or ``bounds`` child element must be a non-negative integer. Withing a view +item, no two ``color`` elements may have equal state ``state`` attributes, and +no two ``bounds`` elements may have equal ``state`` attributes. + +If the item’s animation state is lower than the ``state`` value of any +``bounds`` child element, the position/size specified by the ``bounds`` child +element with the lowest ``state`` value will be used. If the item’s +animation state is higher than the ``state`` value of any ``bounds`` child +element, the position/size specified by the ``bounds`` child element with the +highest ``state`` value will be used. If the item’s animation state is between +the ``state`` values of two ``bounds`` child elements, the position/size will be +interpolated linearly. + +If the item’s animation state is lower than the ``state`` value of any ``color`` +child element, the colour specified by the ``color`` child element with the +lowest ``state`` value will be used. If the item’s animation state is higher +than the ``state`` value of any ``color`` child element, the colour specified by +the ``color`` child element with the highest ``state`` value will be used. If +the item’s animation state is between the ``state`` values of two ``color`` +child elements, the RGBA colour components will be interpolated linearly. + +An item’s animation state may be bound to an emulated output or input port by +supplying an ``animate`` child element. If present, the ``animate`` element +must have either an ``inputtag`` attribute or a ``name`` attribute (but not +both). If the ``animate`` child element is not present, the item’s animation +state is the same as its element state (see :ref:`layout-interact-elemstate`). + +If the ``animate`` child element is present and has an ``inputtag`` +attribute, the item’s animation state will be taken from the value of the +corresponding I/O port. The ``inputtag`` attribute specifies the tag path of an +I/O port relative to the device that caused the layout file to be loaded. The +raw value from the input port is used, active-low switch values are not +normalised. + +If the ``animate`` child element is present and has a ``name`` attribute, the +item’s animation state will be taken from the value of the correspondingly named +emulated output. Note that output names are global, which can become an issue +when a machine uses multiple instances of the same type of device. + +If the ``animate`` child element has a ``mask`` attribute, the item’s animation +state will be masked with the ``mask`` value and shifted to the right to remove +trailing zeroes (for example a mask of 0x05 will result in no shift, while a +mask of 0xb0 will result in the value being shifted four bits to the right). +Note that the ``mask`` attribute applies to output value (specified with the +``name`` attribute) as well as input port values (specified with the +``inputtag`` attribute). If the ``mask`` attribute is present, it must be an +integer value. If the ``mask`` attribute is not present, it is equivalent to +all 32 bits being set. + +This example shows elements with independent element state and animation state, +using the animation state taken from emulated outputs to control their +position:: + + + + + + + + + + + + + + + + + + + +This example shows elements with independent element state and animation state, +using the animation state taken from an emulated positional input to control +their positions:: + + + + + + + + + + + + .. _layout-errors: Error handling diff --git a/scripts/build/complay.py b/scripts/build/complay.py index a88d0d9f4a5..3e9d6ed693c 100755 --- a/scripts/build/complay.py +++ b/scripts/build/complay.py @@ -225,6 +225,8 @@ class LayoutChecker(Minifyer): bottom = self.checkFloatAttribute('bounds', attrs, 'bottom', 1.0) x = self.checkFloatAttribute('bounds', attrs, 'x', 0.0) y = self.checkFloatAttribute('bounds', attrs, 'y', 0.0) + xc = self.checkFloatAttribute('bounds', attrs, 'xc', 0.0) + yc = self.checkFloatAttribute('bounds', attrs, 'yc', 0.0) width = self.checkFloatAttribute('bounds', attrs, 'width', 1.0) height = self.checkFloatAttribute('bounds', attrs, 'height', 1.0) if (left is not None) and (right is not None) and (left > right): @@ -239,12 +241,14 @@ class LayoutChecker(Minifyer): self.handleError('Element bounds attribute width "%s" is negative' % (attrs['width'], )) if (height is not None) and (0.0 > height): self.handleError('Element bounds attribute height "%s" is negative' % (attrs['height'], )) - if ('left' not in attrs) and ('x' not in attrs): - self.handleError('Element bounds has neither attribute left nor attribute x') - has_ltrb = ('left' in attrs) or ('top' in attrs) or ('right' in attrs) or ('bottom' in attrs) - has_origin_size = ('x' in attrs) or ('y' in attrs) or ('width' in attrs) or ('height' in attrs) - if has_ltrb and has_origin_size: - self.handleError('Element bounds has both left/top/right/bottom and origin/size attributes') + if (('left' in attrs) and (('x' in attrs) or ('xc' in attrs))) or (('x' in attrs) and ('xc' in attrs)): + self.handleError('Element bounds has multiple horizontal origin attributes (left/x/xc)') + if (('left' in attrs) and ('width' in attrs)) or ((('x' in attrs) or ('xc' in attrs)) and ('right' in attrs)): + self.handleError('Element bounds has both left/right and x/xc/width attributes') + if (('top' in attrs) and (('y' in attrs) or ('yc' in attrs))) or (('y' in attrs) and ('yc' in attrs)): + self.handleError('Element bounds has multiple vertical origin attributes (top/y/yc)') + if (('top' in attrs) and ('height' in attrs)) or ((('y' in attrs) or ('yc' in attrs)) and ('bottom' in attrs)): + self.handleError('Element bounds has both top/bottom and y/yc/height attributes') def checkOrientation(self, attrs): if self.have_orientation[-1]: @@ -291,8 +295,32 @@ class LayoutChecker(Minifyer): self.have_bounds.append({ }) self.have_color.append({ }) - def startObject(self, name): - self.handlers.append((self.objectStartHandler, self.objectEndHandler)) + def checkViewItem(self, name, attrs): + if ('blend' in attrs) and (attrs['blend'] not in self.BLENDMODES) and not self.VARPATTERN.match(attrs['blend']): + self.handleError('Element %s attribute blend "%s" is unsupported' % (name, attrs['blend'])) + if 'inputtag' in attrs: + if 'inputmask' not in attrs: + self.handleError('Element %s has inputtag attribute without inputmask attribute' % (name, )) + self.checkTag(attrs['inputtag'], name, 'inputtag') + elif 'inputmask' in attrs: + self.handleError('Element %s has inputmask attribute without inputtag attribute' % (name, )) + inputraw = None + if 'inputraw' in attrs: + if (attrs['inputraw'] not in self.YESNO) and (not self.VARPATTERN.match(attrs['inputraw'])): + self.handleError('Element %s attribute inputraw "%s" is not "yes" or "no"' % (name, attrs['inputraw'])) + else: + inputraw = 'yes' == attrs['inputraw'] + if 'inputmask' not in attrs: + self.handleError('Element %s has inputraw attribute without inputmask attribute' % (name, )) + if 'inputtag' not in attrs: + self.handleError('Element %s has inputraw attribute without inputtag attribute' % (name, )) + inputmask = self.checkIntAttribute(name, attrs, 'inputmask', None) + if (inputmask is not None) and (not inputmask): + if (inputraw is None) or (not inputraw): + self.handleError('Element %s attribute inputmask "%s" is zero' % (name, attrs['inputmask'])) + + def startViewItem(self, name): + self.handlers.append((self.viewItemStartHandler, self.viewItemEndHandler)) self.have_bounds.append(None if 'group' == name else { }) self.have_orientation.append(False) self.have_color.append(None if 'group' == name else { }) @@ -477,29 +505,8 @@ class LayoutChecker(Minifyer): self.handleError('Element %s missing attribute ref' % (name, )) elif attrs['ref'] not in self.referenced_elements: self.referenced_elements[attrs['ref']] = self.formatLocation() - if ('blend' in attrs) and (attrs['blend'] not in self.BLENDMODES) and not self.VARPATTERN.match(attrs['blend']): - self.handleError('Element %s attribute blend "%s" is unsupported' % (name, attrs['blend'])) - if 'inputtag' in attrs: - if 'inputmask' not in attrs: - self.handleError('Element %s has inputtag attribute without inputmask attribute' % (name, )) - self.checkTag(attrs['inputtag'], name, 'inputtag') - elif 'inputmask' in attrs: - self.handleError('Element %s has inputmask attribute without inputtag attribute' % (name, )) - inputraw = None - if 'inputraw' in attrs: - if (attrs['inputraw'] not in self.YESNO) and (not self.VARPATTERN.match(attrs['inputraw'])): - self.handleError('Element %s attribute inputraw "%s" is not "yes" or "no"' % (name, attrs['inputraw'])) - else: - inputraw = 'yes' == attrs['inputraw'] - if 'inputmask' not in attrs: - self.handleError('Element %s has inputraw attribute without inputmask attribute' % (name, )) - if 'inputtag' not in attrs: - self.handleError('Element %s has inputraw attribute without inputtag attribute' % (name, )) - inputmask = self.checkIntAttribute(name, attrs, 'inputmask', None) - if (inputmask is not None) and (0 == inputmask): - if (inputraw is None) or (0 == inputraw): - self.handleError('Element %s attribute inputmask "%s" is zero' % (name, attrs['inputmask'])) - self.startObject(name) + self.checkViewItem(name, attrs) + self.startViewItem(name) elif 'screen' == name: if 'index' in attrs: index = self.checkIntAttribute(name, attrs, 'index', None) @@ -512,7 +519,8 @@ class LayoutChecker(Minifyer): self.checkTag(tag, name, 'tag') if self.BADTAGPATTERN.search(tag): self.handleError('Element screen attribute tag "%s" contains invalid characters' % (tag, )) - self.startObject(name) + self.checkViewItem(name, attrs) + self.startViewItem(name) elif 'group' == name: if 'ref' not in attrs: self.handleError('Element group missing attribute ref') @@ -525,7 +533,7 @@ class LayoutChecker(Minifyer): self.current_collections[n] = l else: self.handleError('Element group instantiates collection with duplicate name "%s" from %s (previous %s)' % (n, l, self.current_collections[n])) - self.startObject(name) + self.startViewItem(name) elif 'repeat' == name: if 'count' not in attrs: self.handleError('Element repeat missing attribute count') @@ -577,8 +585,19 @@ class LayoutChecker(Minifyer): self.have_bounds.pop() self.handlers.pop() - def objectStartHandler(self, name, attrs): - if 'bounds' == name: + def viewItemStartHandler(self, name, attrs): + if 'animate' == name: + if isinstance(self.have_bounds[-1], dict): + if 'inputtag' in attrs: + if 'name' in attrs: + self.handleError('Element animate has both attribute inputtag and attribute name') + self.checkTag(attrs['inputtag'], name, 'inputtag') + elif 'name' not in attrs: + self.handleError('Element animate has neither attribute inputtag nor attribute name') + self.checkIntAttribute(name, attrs, 'mask', None) + else: + self.handleError('Encountered unexpected element %s' % (name, )) + elif 'bounds' == name: if self.have_bounds[-1] is None: self.have_bounds[-1] = self.formatLocation() elif isinstance(self.have_bounds[-1], dict): @@ -595,7 +614,7 @@ class LayoutChecker(Minifyer): self.checkBounds(attrs) elif 'orientation' == name: self.checkOrientation(attrs) - if 'color' == name: + elif 'color' == name: if self.have_color[-1] is None: self.have_color[-1] = self.formatLocation() elif isinstance(self.have_color[-1], dict): @@ -610,9 +629,11 @@ class LayoutChecker(Minifyer): else: self.handleError('Duplicate element color (previous %s)' % (self.have_color[-1], )) self.checkColor(attrs) + else: + self.handleError('Encountered unexpected element %s' % (name, )) self.ignored_depth = 1 - def objectEndHandler(self, name): + def viewItemEndHandler(self, name): self.have_bounds.pop() self.have_orientation.pop() self.have_color.pop() diff --git a/scripts/src/emu.lua b/scripts/src/emu.lua index 89c7d3ccd20..d65ea40c21d 100644 --- a/scripts/src/emu.lua +++ b/scripts/src/emu.lua @@ -181,6 +181,7 @@ files { MAME_DIR .. "src/emu/recording.h", MAME_DIR .. "src/emu/render.cpp", MAME_DIR .. "src/emu/render.h", + MAME_DIR .. "src/emu/rendertypes.h", MAME_DIR .. "src/emu/rendfont.cpp", MAME_DIR .. "src/emu/rendfont.h", MAME_DIR .. "src/emu/rendlay.cpp", diff --git a/scripts/src/lib.lua b/scripts/src/lib.lua index a5ce6180857..edc68ee3c13 100644 --- a/scripts/src/lib.lua +++ b/scripts/src/lib.lua @@ -74,7 +74,10 @@ project "utils" MAME_DIR .. "src/lib/util/jedparse.h", MAME_DIR .. "src/lib/util/md5.cpp", MAME_DIR .. "src/lib/util/md5.h", + MAME_DIR .. "src/lib/util/msdib.cpp", + MAME_DIR .. "src/lib/util/msdib.h", MAME_DIR .. "src/lib/util/nanosvg.cpp", + MAME_DIR .. "src/lib/util/nanosvg.h", MAME_DIR .. "src/lib/util/opresolv.cpp", MAME_DIR .. "src/lib/util/opresolv.h", MAME_DIR .. "src/lib/util/options.cpp", diff --git a/src/devices/imagedev/picture.cpp b/src/devices/imagedev/picture.cpp index e50f2c0d4c6..e6736fb10c9 100644 --- a/src/devices/imagedev/picture.cpp +++ b/src/devices/imagedev/picture.cpp @@ -10,7 +10,7 @@ #include "emu.h" #include "picture.h" -#include "png.h" +#include "rendutil.h" /*************************************************************************** TYPE DEFINITIONS @@ -44,15 +44,25 @@ void picture_image_device::device_start() image_init_result picture_image_device::call_load() { - if (png_read_bitmap(image_core_file(), m_picture) != PNGERR_NONE) - { - m_picture.reset(); - - // todo: try JPEG here. - return image_init_result::FAIL; + switch (render_detect_image(image_core_file())) + { + case RENDUTIL_IMGFORMAT_PNG: + render_load_png(m_picture, image_core_file()); + break; + + case RENDUTIL_IMGFORMAT_JPEG: + render_load_jpeg(m_picture, image_core_file()); + break; + + case RENDUTIL_IMGFORMAT_MSDIB: + render_load_msdib(m_picture, image_core_file()); + break; + + default: + m_picture.reset(); } - return image_init_result::PASS; + return m_picture.valid() ? image_init_result::PASS : image_init_result::FAIL; } void picture_image_device::call_unload() diff --git a/src/devices/video/fixfreq.cpp b/src/devices/video/fixfreq.cpp index 3810dc76a76..5de27dc0b08 100644 --- a/src/devices/video/fixfreq.cpp +++ b/src/devices/video/fixfreq.cpp @@ -14,10 +14,11 @@ ***************************************************************************/ #include "emu.h" -#include "ui/uimain.h" -#include "rendutil.h" #include "fixfreq.h" +#include "render.h" +#include "ui/uimain.h" + // for quick and dirty debugging #define VERBOSE 0 #define LOG_OUTPUT_STREAM std::cerr diff --git a/src/devices/video/vector.cpp b/src/devices/video/vector.cpp index 4e44603c136..46a21b535be 100644 --- a/src/devices/video/vector.cpp +++ b/src/devices/video/vector.cpp @@ -42,10 +42,11 @@ **************************************************************************** */ #include "emu.h" -#include "emuopts.h" -#include "rendutil.h" #include "vector.h" +#include "emuopts.h" +#include "render.h" + #define VECTOR_WIDTH_DENOM 512 diff --git a/src/emu/crsshair.cpp b/src/emu/crsshair.cpp index dd75ba119f1..027d8828e32 100644 --- a/src/emu/crsshair.cpp +++ b/src/emu/crsshair.cpp @@ -9,11 +9,15 @@ ***************************************************************************/ #include "emu.h" +#include "crsshair.h" + +#include "config.h" #include "emuopts.h" +#include "render.h" #include "rendutil.h" -#include "config.h" +#include "screen.h" + #include "xmlfile.h" -#include "crsshair.h" @@ -171,28 +175,42 @@ void render_crosshair::create_bitmap() rgb_t color = m_player < ARRAY_LENGTH(crosshair_colors) ? crosshair_colors[m_player] : rgb_t::white(); // if we have a bitmap and texture for this player, kill it - if (m_bitmap == nullptr) + if (!m_bitmap) { m_bitmap = std::make_unique(); m_texture = m_machine.render().texture_alloc(render_texture::hq_scale); } + else + { + m_bitmap->reset(); + } emu_file crossfile(m_machine.options().crosshair_path(), OPEN_FLAG_READ); if (!m_name.empty()) { // look for user specified file - std::string filename = m_name + ".png"; - render_load_png(*m_bitmap, crossfile, nullptr, filename.c_str()); + if (crossfile.open(m_name + ".png") == osd_file::error::NONE) + { + render_load_png(*m_bitmap, crossfile); + crossfile.close(); + } } else { // look for default cross?.png in crsshair/game dir - std::string filename = string_format("cross%d.png", m_player + 1); - render_load_png(*m_bitmap, crossfile, m_machine.system().name, filename.c_str()); + std::string const filename = string_format("cross%d.png", m_player + 1); + if (crossfile.open(m_machine.system().name + (PATH_SEPARATOR + filename)) == osd_file::error::NONE) + { + render_load_png(*m_bitmap, crossfile); + crossfile.close(); + } // look for default cross?.png in crsshair dir - if (!m_bitmap->valid()) - render_load_png(*m_bitmap, crossfile, nullptr, filename.c_str()); + if (!m_bitmap->valid() && (crossfile.open(filename) == osd_file::error::NONE)) + { + render_load_png(*m_bitmap, crossfile); + crossfile.close(); + } } /* if that didn't work, use the built-in one */ diff --git a/src/emu/recording.cpp b/src/emu/recording.cpp index 2e134f03f4a..dd75c27d62f 100644 --- a/src/emu/recording.cpp +++ b/src/emu/recording.cpp @@ -248,7 +248,7 @@ mng_movie_recording::mng_movie_recording(screen_device *screen, std::map &&file, bitmap_t set_frame_period(attotime::from_hz(rate)); m_mng_file = std::move(file); - png_error pngerr = mng_capture_start(*m_mng_file, snap_bitmap, rate); - if (pngerr != PNGERR_NONE) - osd_printf_error("Error capturing MNG, png_error=%d\n", pngerr); - return pngerr == PNGERR_NONE; + util::png_error pngerr = util::mng_capture_start(*m_mng_file, snap_bitmap, rate); + if (pngerr != util::png_error::NONE) + osd_printf_error("Error capturing MNG, png_error=%d\n", std::underlying_type_t(pngerr)); + return pngerr == util::png_error::NONE; } @@ -278,15 +278,15 @@ bool mng_movie_recording::initialize(std::unique_ptr &&file, bitmap_t bool mng_movie_recording::append_single_video_frame(bitmap_rgb32 &bitmap, const rgb_t *palette, int palette_entries) { // set up the text fields in the movie info - png_info pnginfo; + util::png_info pnginfo; if (current_frame() == 0) { for (auto &ent : m_info_fields) pnginfo.add_text(ent.first.c_str(), ent.second.c_str()); } - png_error error = mng_capture_frame(*m_mng_file, pnginfo, bitmap, palette_entries, palette); - return error == png_error::PNGERR_NONE; + util::png_error error = util::mng_capture_frame(*m_mng_file, pnginfo, bitmap, palette_entries, palette); + return error == util::png_error::NONE; } diff --git a/src/emu/render.cpp b/src/emu/render.cpp index 531e327c978..b1518d87dd4 100644 --- a/src/emu/render.cpp +++ b/src/emu/render.cpp @@ -1371,9 +1371,9 @@ render_primitive_list &render_target::get_primitives() { // if we are not in the running stage, draw an outer box render_primitive *prim = list.alloc(render_primitive::QUAD); - set_render_bounds_xy(prim->bounds, 0.0f, 0.0f, (float)m_width, (float)m_height); + prim->bounds.set_xy(0.0f, 0.0f, (float)m_width, (float)m_height); prim->full_bounds = prim->bounds; - set_render_color(&prim->color, 1.0f, 0.1f, 0.1f, 0.1f); + prim->color.set(1.0f, 0.1f, 0.1f, 0.1f); prim->texture.base = nullptr; prim->flags = PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA); list.append(*prim); @@ -1381,9 +1381,9 @@ render_primitive_list &render_target::get_primitives() if (m_width > 1 && m_height > 1) { prim = list.alloc(render_primitive::QUAD); - set_render_bounds_xy(prim->bounds, 1.0f, 1.0f, (float)(m_width - 1), (float)(m_height - 1)); + prim->bounds.set_xy(1.0f, 1.0f, float(m_width - 1), float(m_height - 1)); prim->full_bounds = prim->bounds; - set_render_color(&prim->color, 1.0f, 0.0f, 0.0f, 0.0f); + prim->color.set(1.0f, 0.0f, 0.0f, 0.0f); prim->texture.base = nullptr; prim->flags = PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA); list.append(*prim); @@ -1535,7 +1535,7 @@ bool render_target::map_point_input(s32 target_x, s32 target_y, ioport_port *&in if (item.has_input()) { // point successfully mapped - input_port = item.input_tag_and_mask(input_mask); + std::tie(input_port, input_mask) = item.input_tag_and_mask(); input_x = (target_f.first - bounds.x0) / bounds.width(); input_y = (target_f.second - bounds.y0) / bounds.height(); return true; @@ -2237,7 +2237,7 @@ void render_target::add_container_primitives(render_primitive_list &list, const cliprect.y0 = xform.yoffs; cliprect.x1 = xform.xoffs + xform.xscale; cliprect.y1 = xform.yoffs + xform.yscale; - sect_render_bounds(cliprect, m_bounds); + cliprect &= m_bounds; float root_xoffs = root_xform.xoffs + fabsf(root_xform.xscale - xform.xscale) * 0.5f; float root_yoffs = root_xform.yoffs + fabsf(root_xform.yscale - xform.yscale) * 0.5f; @@ -2247,7 +2247,7 @@ void render_target::add_container_primitives(render_primitive_list &list, const root_cliprect.y0 = root_yoffs; root_cliprect.x1 = root_xoffs + root_xform.xscale; root_cliprect.y1 = root_yoffs + root_xform.yscale; - sect_render_bounds(root_cliprect, m_bounds); + root_cliprect &= m_bounds; // compute the container transform object_transform container_xform; @@ -2477,7 +2477,7 @@ void render_target::add_container_primitives(render_primitive_list &list, const // allocate a primitive render_primitive *prim = list.alloc(render_primitive::QUAD); - set_render_bounds_wh(prim->bounds, xform.xoffs, xform.yoffs, xform.xscale, xform.yscale); + prim->bounds.set_wh(xform.xoffs, xform.yoffs, xform.xscale, xform.yscale); prim->full_bounds = prim->bounds; prim->color = container_xform.color; width = render_round_nearest(prim->bounds.x1) - render_round_nearest(prim->bounds.x0); @@ -2525,7 +2525,7 @@ void render_target::add_element_primitives(render_primitive_list &list, const ob // compute the bounds s32 width = render_round_nearest(xform.xscale); s32 height = render_round_nearest(xform.yscale); - set_render_bounds_wh(prim->bounds, render_round_nearest(xform.xoffs), render_round_nearest(xform.yoffs), (float) width, (float) height); + prim->bounds.set_wh(render_round_nearest(xform.xoffs), render_round_nearest(xform.yoffs), float(width), float(height)); prim->full_bounds = prim->bounds; if (xform.orientation & ORIENTATION_SWAP_XY) std::swap(width, height); @@ -2542,7 +2542,7 @@ void render_target::add_element_primitives(render_primitive_list &list, const ob cliprect.y0 = render_round_nearest(xform.yoffs); cliprect.x1 = render_round_nearest(xform.xoffs + xform.xscale); cliprect.y1 = render_round_nearest(xform.yoffs + xform.yscale); - sect_render_bounds(cliprect, m_bounds); + cliprect &= m_bounds; // determine UV coordinates and apply clipping prim->texcoords = oriented_texcoords[xform.orientation]; @@ -2956,9 +2956,9 @@ void render_target::add_clear_extents(render_primitive_list &list) if (x1 - x0 > 0) { render_primitive *prim = list.alloc(render_primitive::QUAD); - set_render_bounds_xy(prim->bounds, (float)x0, (float)y0, (float)x1, (float)y1); + prim->bounds.set_xy(float(x0), float(y0), float(x1), float(y1)); prim->full_bounds = prim->bounds; - set_render_color(&prim->color, 1.0f, 0.0f, 0.0f, 0.0f); + prim->color.set(1.0f, 0.0f, 0.0f, 0.0f); prim->texture.base = nullptr; prim->flags = PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA); clearlist.append(*prim); @@ -3012,7 +3012,7 @@ void render_target::add_clear_and_optimize_primitive_list(render_primitive_list if (PRIMFLAG_GET_BLENDMODE(prim.flags) == BLENDMODE_RGB_MULTIPLY) { // RGB multiply will multiply against 0, leaving nothing - set_render_color(&prim.color, 1.0f, 0.0f, 0.0f, 0.0f); + prim.color.set(1.0f, 0.0f, 0.0f, 0.0f); prim.texture.base = nullptr; prim.flags = (prim.flags & ~PRIMFLAG_BLENDMODE_MASK) | PRIMFLAG_BLENDMODE(BLENDMODE_NONE); } diff --git a/src/emu/render.h b/src/emu/render.h index 6a76e23eda4..c78fa8fb7d2 100644 --- a/src/emu/render.h +++ b/src/emu/render.h @@ -46,6 +46,7 @@ #ifndef MAME_EMU_RENDER_H #define MAME_EMU_RENDER_H +#include "rendertypes.h" #include "screen.h" #include @@ -65,17 +66,6 @@ // CONSTANTS //************************************************************************** -// blending modes -enum -{ - BLENDMODE_NONE = 0, // no blending - BLENDMODE_ALPHA, // standard alpha blend - BLENDMODE_RGB_MULTIPLY, // apply source alpha to source pix, then multiply RGB values - BLENDMODE_ADD, // apply source alpha to source pix, then add to destination - - BLENDMODE_COUNT -}; - // render creation flags constexpr u8 RENDER_CREATE_NO_ART = 0x01; // ignore any views that have art in them @@ -166,62 +156,6 @@ constexpr u32 PRIMFLAG_GET_VECTORBUF(u32 x) { return (x & PRIMFLAG_VECTORBUF_MAS // texture scaling callback typedef void (*texture_scaler_func)(bitmap_argb32 &dest, bitmap_argb32 &source, const rectangle &sbounds, void *param); -// render_bounds - floating point bounding rectangle -struct render_bounds -{ - float x0; // leftmost X coordinate - float y0; // topmost Y coordinate - float x1; // rightmost X coordinate - float y1; // bottommost Y coordinate - - constexpr float width() const { return x1 - x0; } - constexpr float height() const { return y1 - y0; } - constexpr float aspect() const { return width() / height(); } - constexpr bool includes(float x, float y) const { return (x >= x0) && (x <= x1) && (y >= y0) && (y <= y1); } -}; - - -// render_color - floating point set of ARGB values -struct render_color -{ - float a; // alpha component (0.0 = transparent, 1.0 = opaque) - float r; // red component (0.0 = none, 1.0 = max) - float g; // green component (0.0 = none, 1.0 = max) - float b; // blue component (0.0 = none, 1.0 = max) - - constexpr render_color operator*(render_color const &c) const - { - return render_color{ a * c.a, r * c.r, g * c.g, b * c.b }; - } - - render_color &operator*=(render_color const &c) - { - a *= c.a; - r *= c.r; - g *= c.g; - b *= c.b; - return *this; - } -}; - - -// render_texuv - floating point set of UV texture coordinates -struct render_texuv -{ - float u; // U coordinate (0.0-1.0) - float v; // V coordinate (0.0-1.0) -}; - - -// render_quad_texuv - floating point set of UV texture coordinates -struct render_quad_texuv -{ - render_texuv tl; // top-left UV coordinate - render_texuv tr; // top-right UV coordinate - render_texuv bl; // bottom-left UV coordinate - render_texuv br; // bottom-right UV coordinate -}; - // render_texinfo - texture information struct render_texinfo @@ -818,7 +752,7 @@ public: // interactivity bool has_input() const { return bool(m_input_port); } - ioport_port *input_tag_and_mask(ioport_value &mask) const { mask = m_input_mask; return m_input_port; }; + std::pair input_tag_and_mask() const { return std::make_pair(m_input_port, m_input_mask); }; bool clickthrough() const { return m_clickthrough; } // fetch state based on configured source @@ -831,13 +765,17 @@ public: using bounds_vector = emu::render::detail::bounds_vector; using color_vector = emu::render::detail::color_vector; + int animation_state() const; + static layout_element *find_element(view_environment &env, util::xml::data_node const &itemnode, element_map &elemmap); static bounds_vector make_bounds(view_environment &env, util::xml::data_node const &itemnode, layout_group::transform const &trans); static color_vector make_color(view_environment &env, util::xml::data_node const &itemnode, render_color const &mult); static std::string make_animoutput_tag(view_environment &env, util::xml::data_node const &itemnode); + static std::string make_animinput_tag(view_environment &env, util::xml::data_node const &itemnode); + static ioport_value make_animmask(view_environment &env, util::xml::data_node const &itemnode); static std::string make_input_tag(view_environment &env, util::xml::data_node const &itemnode); static int get_blend_mode(view_environment &env, util::xml::data_node const &itemnode); - static unsigned get_input_shift(ioport_value mask); + static unsigned get_state_shift(ioport_value mask); // internal state layout_element *const m_element; // pointer to the associated element (non-screens only) @@ -845,6 +783,9 @@ public: output_finder<> m_animoutput; // associated output for animation if different bool const m_have_output; // whether we actually have an output bool const m_have_animoutput; // whether we actually have an output for animation + ioport_port * m_animinput_port; // input port used for animation + ioport_value const m_animmask; // mask for animation state + u8 const m_animshift; // shift for animation state ioport_port * m_input_port; // input port of this item ioport_field const * m_input_field; // input port field of this item ioport_value const m_input_mask; // input mask of this item @@ -860,6 +801,7 @@ public: // cold items std::string const m_input_tag; // input tag of this item + std::string const m_animinput_tag; // tag of input port for animation state bounds_vector const m_rawbounds; // raw (original) bounds of the item bool const m_has_clickthrough; // whether clickthrough was explicitly configured }; diff --git a/src/emu/rendertypes.h b/src/emu/rendertypes.h new file mode 100644 index 00000000000..0fa698cc931 --- /dev/null +++ b/src/emu/rendertypes.h @@ -0,0 +1,153 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles, Vas Crabb +/*************************************************************************** + + render.h + + Core renderer constants and structures for MAME. + +***************************************************************************/ +#ifndef MAME_EMU_RENDERTYPES_H +#define MAME_EMU_RENDERTYPES_H + +#pragma once + +#include + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + + +// blending modes +enum +{ + BLENDMODE_NONE = 0, // no blending + BLENDMODE_ALPHA, // standard alpha blend + BLENDMODE_RGB_MULTIPLY, // apply source alpha to source pix, then multiply RGB values + BLENDMODE_ADD, // apply source alpha to source pix, then add to destination + + BLENDMODE_COUNT +}; + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// render_bounds - floating point bounding rectangle +struct render_bounds +{ + float x0; // leftmost X coordinate + float y0; // topmost Y coordinate + float x1; // rightmost X coordinate + float y1; // bottommost Y coordinate + + constexpr float width() const { return x1 - x0; } + constexpr float height() const { return y1 - y0; } + constexpr float aspect() const { return width() / height(); } + constexpr bool includes(float x, float y) const { return (x >= x0) && (x <= x1) && (y >= y0) && (y <= y1); } + + // intersection + constexpr render_bounds operator&(render_bounds const &b) const + { + return render_bounds{ (std::max)(x0, b.x0), (std::max)(y0, b.y0), (std::min)(x1, b.x1), (std::min)(y1, b.y1) }; + } + + render_bounds &operator&=(render_bounds const &b) + { + x0 = (std::max)(x0, b.x0); + y0 = (std::max)(y0, b.y0); + x1 = (std::min)(x1, b.x1); + y1 = (std::min)(y1, b.y1); + return *this; + } + + // union + constexpr render_bounds operator|(render_bounds const &b) const + { + return render_bounds{ (std::min)(x0, b.x0), (std::min)(y0, b.y0), (std::max)(x1, b.x1), (std::max)(y1, b.y1) }; + } + + render_bounds &operator|=(render_bounds const &b) + { + x0 = (std::min)(x0, b.x0); + y0 = (std::min)(y0, b.y0); + x1 = (std::max)(x1, b.x1); + y1 = (std::max)(y1, b.y1); + return *this; + } + + render_bounds &set_xy(float left, float top, float right, float bottom) + { + x0 = left; + y0 = top; + x1 = right; + y1 = bottom; + return *this; + } + + render_bounds &set_wh(float left, float top, float width, float height) + { + x0 = left; + y0 = top; + x1 = left + width; + y1 = top + height; + return *this; + } +}; + + +// render_color - floating point set of ARGB values +struct render_color +{ + float a; // alpha component (0.0 = transparent, 1.0 = opaque) + float r; // red component (0.0 = none, 1.0 = max) + float g; // green component (0.0 = none, 1.0 = max) + float b; // blue component (0.0 = none, 1.0 = max) + + constexpr render_color operator*(render_color const &c) const + { + return render_color{ a * c.a, r * c.r, g * c.g, b * c.b }; + } + + render_color &operator*=(render_color const &c) + { + a *= c.a; + r *= c.r; + g *= c.g; + b *= c.b; + return *this; + } + + render_color &set(float alpha, float red, float green, float blue) + { + a = alpha; + r = red; + g = green; + b = blue; + return *this; + } +}; + + +// render_texuv - floating point set of UV texture coordinates +struct render_texuv +{ + float u; // U coordinate (0.0-1.0) + float v; // V coordinate (0.0-1.0) +}; + + +// render_quad_texuv - floating point set of UV texture coordinates +struct render_quad_texuv +{ + render_texuv tl; // top-left UV coordinate + render_texuv tr; // top-right UV coordinate + render_texuv bl; // bottom-left UV coordinate + render_texuv br; // bottom-right UV coordinate +}; + +#endif // MAME_EMU_RENDERTYPES_H diff --git a/src/emu/rendlay.cpp b/src/emu/rendlay.cpp index 940923ae0e1..2f57c7ade1d 100644 --- a/src/emu/rendlay.cpp +++ b/src/emu/rendlay.cpp @@ -17,6 +17,7 @@ #include "rendutil.h" #include "video/rgbutil.h" +#include "nanosvg.h" #include "vecstream.h" #include "xmlfile.h" @@ -27,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -578,25 +580,29 @@ private: entry_vector m_entries; util::ovectorstream m_buffer; + std::shared_ptr const m_svg_rasterizer; device_t &m_device; layout_environment *const m_next = nullptr; bool m_cached = false; public: explicit layout_environment(device_t &device) - : m_device(device) + : m_svg_rasterizer(nsvgCreateRasterizer(), util::nsvg_deleter()) + , m_device(device) { } explicit layout_environment(layout_environment &next) - : m_device(next.m_device) + : m_svg_rasterizer(next.m_svg_rasterizer) + , m_device(next.m_device) , m_next(&next) { } layout_environment(layout_environment const &) = delete; - device_t &device() { return m_device; } - running_machine &machine() { return device().machine(); } - bool is_root_device() { return &device() == &machine().root_device(); } + device_t &device() const { return m_device; } + running_machine &machine() const { return device().machine(); } + bool is_root_device() const { return &device() == &machine().root_device(); } + std::shared_ptr const &svg_rasterizer() const { return m_svg_rasterizer; } void set_parameter(std::string &&name, std::string &&value) { @@ -785,34 +791,44 @@ public: void parse_bounds(util::xml::data_node const *node, render_bounds &result) { - // default to unit rectangle if (!node) { + // default to unit rectangle result.x0 = result.y0 = 0.0F; result.x1 = result.y1 = 1.0F; } else { - // parse attributes + // horizontal position/size if (node->has_attribute("left")) { - // left/right/top/bottom format result.x0 = get_attribute_float(*node, "left", 0.0F); result.x1 = get_attribute_float(*node, "right", 1.0F); - result.y0 = get_attribute_float(*node, "top", 0.0F); - result.y1 = get_attribute_float(*node, "bottom", 1.0F); } - else if (node->has_attribute("x")) + else { - // x/y/width/height format - result.x0 = get_attribute_float(*node, "x", 0.0F); - result.x1 = result.x0 + get_attribute_float(*node, "width", 1.0F); - result.y0 = get_attribute_float(*node, "y", 0.0F); - result.y1 = result.y0 + get_attribute_float(*node, "height", 1.0F); + float const width = get_attribute_float(*node, "width", 1.0F); + if (node->has_attribute("xc")) + result.x0 = get_attribute_float(*node, "xc", 0.0F) - (width / 2.0F); + else + result.x0 = get_attribute_float(*node, "x", 0.0F); + result.x1 = result.x0 + width; + } + + // vertical position/size + if (node->has_attribute("top")) + { + result.y0 = get_attribute_float(*node, "top", 0.0F); + result.y1 = get_attribute_float(*node, "bottom", 1.0F); } else { - throw layout_syntax_error("bounds element requires either left or x attribute"); + float const height = get_attribute_float(*node, "height", 1.0F); + if (node->has_attribute("yc")) + result.y0 = get_attribute_float(*node, "yc", 0.0F) - (height / 2.0F); + else + result.y0 = get_attribute_float(*node, "y", 0.0F); + result.y1 = result.y0 + height; } // check for errors @@ -978,7 +994,7 @@ render_bounds accumulate_bounds(emu::render::detail::bounds_vector const &steps) auto i(steps.begin()); render_bounds result(i->bounds); while (steps.end() != ++i) - union_render_bounds(result, i->bounds); + result |= i->bounds; return result; } @@ -1123,7 +1139,7 @@ layout_element::layout_element(environment &env, util::xml::data_node const &ele if (first) bounds = newcomp.overall_bounds(); else - union_render_bounds(bounds, newcomp.overall_bounds()); + bounds |= newcomp.overall_bounds(); first = false; // determine the maximum state @@ -1286,7 +1302,7 @@ void layout_group::resolve_bounds(environment &env, group_map &groupmap, std::ve seen.push_back(this); if (!m_bounds_resolved) { - set_render_bounds_xy(m_bounds, 0.0F, 0.0F, 1.0F, 1.0F); + m_bounds.set_xy(0.0F, 0.0F, 1.0F, 1.0F); environment local(env); bool empty(true); resolve_bounds(local, m_groupnode, groupmap, seen, empty, false, false, true); @@ -1349,13 +1365,13 @@ void layout_group::resolve_bounds( { render_bounds b; env.parse_bounds(boundsnode, b); - union_render_bounds(itembounds, b); + itembounds |= b; } } if (empty) m_bounds = itembounds; else - union_render_bounds(m_bounds, itembounds); + m_bounds |= itembounds; empty = false; LOGMASKED(LOG_GROUP_BOUNDS_RESOLUTION, "Accumulate item bounds (%s %s %s %s) -> (%s %s %s %s)\n", itembounds.x0, itembounds.y0, itembounds.x1, itembounds.y1, @@ -1371,7 +1387,7 @@ void layout_group::resolve_bounds( if (empty) m_bounds = itembounds; else - union_render_bounds(m_bounds, itembounds); + m_bounds |= itembounds; empty = false; LOGMASKED(LOG_GROUP_BOUNDS_RESOLUTION, "Accumulate group '%s' reference explicit bounds (%s %s %s %s) -> (%s %s %s %s)\n", itemnode->get_attribute_string("ref", ""), @@ -1399,7 +1415,7 @@ void layout_group::resolve_bounds( if (empty) m_bounds = itembounds; else - union_render_bounds(m_bounds, itembounds); + m_bounds |= itembounds; empty = false; unresolved = false; LOGMASKED(LOG_GROUP_BOUNDS_RESOLUTION, "Accumulate group '%s' reference computed bounds (%s %s %s %s) -> (%s %s %s %s)\n", @@ -1523,11 +1539,13 @@ class layout_element::image_component : public component { public: // construction/destruction - image_component(environment &env, util::xml::data_node const &compnode, const char *dirname) + image_component(environment &env, util::xml::data_node const &compnode, char const *dirname) : component(env, compnode, dirname) + , m_rasterizer(env.svg_rasterizer()) , m_dirname(dirname ? dirname : "") , m_imagefile(env.get_attribute_string(compnode, "file", "")) , m_alphafile(env.get_attribute_string(compnode, "alphafile", "")) + , m_data(get_data(compnode)) { } @@ -1535,97 +1553,204 @@ public: virtual void preload(running_machine &machine) override { if (!m_bitmap.valid()) - load_bitmap(machine); + load_image(machine); } - virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override + virtual void draw(running_machine &machine, bitmap_argb32 &dest, rectangle const &bounds, int state) override { - if (!m_bitmap.valid()) - load_bitmap(machine); + if (!m_bitmap.valid() && !m_svg) + load_image(machine); + + if (m_bitmap.valid()) + draw_bitmap(dest, bounds, state); + else if (m_svg) + draw_svg(dest, bounds, state); + } +private: + // internal helpers + void draw_bitmap(bitmap_argb32 &dest, rectangle const &bounds, int state) + { render_color const c(color(state)); if (m_hasalpha || (1.0F > c.a)) { bitmap_argb32 tempbitmap(dest.width(), dest.height()); render_resample_argb_bitmap_hq(tempbitmap, m_bitmap, c); - for (s32 y0 = 0, y1 = bounds.top(); bounds.bottom() >= y1; ++y0, ++y1) + alpha_blend(tempbitmap, dest, bounds); + } + else + { + bitmap_argb32 destsub(dest, bounds); + render_resample_argb_bitmap_hq(destsub, m_bitmap, c); + } + } + + void draw_svg(bitmap_argb32 &dest, rectangle const &bounds, int state) + { + // rasterise into a temporary bitmap + float const xscale(bounds.width() / m_svg->width); + float const yscale(bounds.height() / m_svg->height); + float const drawscale((std::max)(xscale, yscale)); + bitmap_argb32 tempbitmap(int(m_svg->width * drawscale), int(m_svg->height * drawscale)); + nsvgRasterize( + m_rasterizer.get(), + m_svg.get(), + 0, 0, drawscale, + reinterpret_cast(&tempbitmap.pix(0)), + tempbitmap.width(), tempbitmap.height(), + tempbitmap.rowbytes()); + + // correct colour format and multiply by state colour + bool havealpha(false); + render_color const c(color(state)); + for (s32 y = 0; tempbitmap.height() > y; ++y) + { + u32 *dst(&tempbitmap.pix(y)); + for (s32 x = 0; tempbitmap.width() > x; ++x, ++dst) + { + u8 const *const src(reinterpret_cast(dst)); + rgb_t const d( + u8((float(src[3]) * c.a) + 0.5), + u8((float(src[0]) * c.r) + 0.5), + u8((float(src[1]) * c.g) + 0.5), + u8((float(src[2]) * c.b) + 0.5)); + *dst = d; + havealpha = havealpha || (d.a() < 255U); + } + } + + // find most efficient way to insert it in the target bitmap + if (!havealpha) + { + if ((tempbitmap.width() == bounds.width()) && (tempbitmap.height() == bounds.height())) + { + for (s32 y = 0; tempbitmap.height() > y; ++y) + std::copy_n(&tempbitmap.pix(y), bounds.width(), &dest.pix(y + bounds.top(), bounds.left())); + } + else { - u32 const *src(&tempbitmap.pix(y0, 0)); - u32 *dst(&dest.pix(y1, bounds.left())); - for (s32 x1 = bounds.left(); bounds.right() >= x1; ++x1, ++src, ++dst) + bitmap_argb32 destsub(dest, bounds); + render_resample_argb_bitmap_hq(destsub, tempbitmap, render_color{ 1.0F, 1.0F, 1.0F, 1.0F }); + } + } + else if ((tempbitmap.width() == bounds.width()) && (tempbitmap.height() == bounds.height())) + { + alpha_blend(tempbitmap, dest, bounds); + } + else + { + bitmap_argb32 scaled(bounds.width(), bounds.height()); + render_resample_argb_bitmap_hq(scaled, tempbitmap, render_color{ 1.0F, 1.0F, 1.0F, 1.0F }); + tempbitmap.reset(); + alpha_blend(scaled, dest, bounds); + } + } + + void alpha_blend(bitmap_argb32 const &srcbitmap, bitmap_argb32 &dstbitmap, rectangle const &bounds) + { + for (s32 y0 = 0, y1 = bounds.top(); bounds.bottom() >= y1; ++y0, ++y1) + { + u32 const *src(&srcbitmap.pix(y0, 0)); + u32 *dst(&dstbitmap.pix(y1, bounds.left())); + for (s32 x1 = bounds.left(); bounds.right() >= x1; ++x1, ++src, ++dst) + { + rgb_t const a(*src); + u32 const aa(a.a()); + if (255 == aa) + { + *dst = *src; + } + else if (aa) { - rgb_t const a(*src); - u32 const aa(a.a()); - if (255 == aa) + rgb_t const b(*dst); + u32 const ba(b.a()); + if (ba) { - *dst = *src; + u32 const ca((aa * 255) + (ba * (255 - aa))); + *dst = rgb_t( + u8(ca / 255), + u8(((a.r() * aa * 255) + (b.r() * ba * (255 - aa))) / ca), + u8(((a.g() * aa * 255) + (b.g() * ba * (255 - aa))) / ca), + u8(((a.b() * aa * 255) + (b.b() * ba * (255 - aa))) / ca)); } - else if (aa) + else { - rgb_t const b(*dst); - u32 const ba(b.a()); - if (ba) - { - u32 const ca((aa * 255) + (ba * (255 - aa))); - *dst = rgb_t( - u8(ca / 255), - u8(((a.r() * aa * 255) + (b.r() * ba * (255 - aa))) / ca), - u8(((a.g() * aa * 255) + (b.g() * ba * (255 - aa))) / ca), - u8(((a.b() * aa * 255) + (b.b() * ba * (255 - aa))) / ca)); - } - else - { - *dst = *src; - } + *dst = *src; } } } } - else - { - bitmap_argb32 destsub(dest, bounds); - render_resample_argb_bitmap_hq(destsub, m_bitmap, c); - } } -private: - // internal helpers - void load_bitmap(running_machine &machine) + void load_image(running_machine &machine) { - LOGMASKED(LOG_IMAGE_LOAD, "Image component attempt to load image file '%s/%s'\n", m_dirname, m_imagefile); + // attempt to open the file + LOGMASKED(LOG_IMAGE_LOAD, "Image component attempt to load image file '%s%s%s'\n", m_dirname, PATH_SEPARATOR, m_imagefile); emu_file file(machine.options().art_path(), OPEN_FLAG_READ); - - ru_imgformat const format = render_detect_image(file, m_dirname.c_str(), m_imagefile.c_str()); - switch (format) + osd_file::error const imgerr = file.open(m_dirname.empty() ? m_imagefile : (m_dirname + PATH_SEPARATOR + m_imagefile)); + if (osd_file::error::NONE == imgerr) { - case RENDUTIL_IMGFORMAT_ERROR: - LOGMASKED(LOG_IMAGE_LOAD, "Image component error detecting image file format\n"); - break; - - case RENDUTIL_IMGFORMAT_PNG: - // load the basic bitmap - LOGMASKED(LOG_IMAGE_LOAD, "Image component detected PNG file format\n"); - m_hasalpha = render_load_png(m_bitmap, file, m_dirname.c_str(), m_imagefile.c_str()); - break; - - default: - // try JPG - LOGMASKED(LOG_IMAGE_LOAD, "Image component failed to detect image file format, trying JPEG\n"); - render_load_jpeg(m_bitmap, file, m_dirname.c_str(), m_imagefile.c_str()); - break; + ru_imgformat const format = render_detect_image(file); + switch (format) + { + case RENDUTIL_IMGFORMAT_ERROR: + LOGMASKED(LOG_IMAGE_LOAD, "Image component error detecting image file format\n"); + break; + + case RENDUTIL_IMGFORMAT_PNG: + LOGMASKED(LOG_IMAGE_LOAD, "Image component detected PNG file format\n"); + m_hasalpha = render_load_png(m_bitmap, file); + break; + + case RENDUTIL_IMGFORMAT_JPEG: + LOGMASKED(LOG_IMAGE_LOAD, "Image component detected JPEG file format\n"); + render_load_jpeg(m_bitmap, file); + break; + + case RENDUTIL_IMGFORMAT_MSDIB: + LOGMASKED(LOG_IMAGE_LOAD, "Image component detected Microsoft DIB file format\n"); + render_load_msdib(m_bitmap, file); + break; + + default: + LOGMASKED(LOG_IMAGE_LOAD, "Image component failed to detect image file format, trying SVG\n"); + load_svg(file); + break; + } + file.close(); + } + else + { + LOGMASKED(LOG_IMAGE_LOAD, "Image component unable to open image file '%s%s%s'\n", m_dirname, PATH_SEPARATOR, m_imagefile); } // load the alpha bitmap if specified - if (m_bitmap.valid() && !m_alphafile.empty()) + if (!m_alphafile.empty()) { - // TODO: no way to detect corner case where we had alpha from the image but the alpha PNG makes it entirely opaque - LOGMASKED(LOG_IMAGE_LOAD, "Image component attempt to load alpha channel from file '%s/%s'\n", m_dirname, m_alphafile); - if (render_load_png(m_bitmap, file, m_dirname.c_str(), m_alphafile.c_str(), true)) - m_hasalpha = true; + if (m_bitmap.valid()) + { + LOGMASKED(LOG_IMAGE_LOAD, "Image component attempt to load alpha channel from file '%s%s%s'\n", m_dirname, PATH_SEPARATOR, m_alphafile); + osd_file::error const alferr = file.open(m_dirname.empty() ? m_alphafile : (m_dirname + PATH_SEPARATOR + m_alphafile)); + if (osd_file::error::NONE == alferr) + { + // TODO: no way to detect corner case where we had alpha from the image but the alpha PNG makes it entirely opaque + if (render_load_png(m_bitmap, file, true)) + m_hasalpha = true; + file.close(); + } + else + { + LOGMASKED(LOG_IMAGE_LOAD, "Image component unable to open alpha channel file '%s%s%s'\n", m_dirname, PATH_SEPARATOR, m_alphafile); + } + } + else if (m_svg) + { + osd_printf_warning("Component alpha channel file '%s' ignored for SVG image '%s'\n", m_alphafile, m_imagefile); + } } - // if we can't load the bitmap, allocate a dummy one and report an error - if (!m_bitmap.valid()) + // if we can't load an image, allocate a dummy one and report an error + if (!m_bitmap.valid() && !m_svg) { // draw some stripes in the bitmap m_bitmap.allocate(100, 100); @@ -1636,18 +1761,87 @@ private: // log an error if (m_alphafile.empty()) - osd_printf_warning("Unable to load component bitmap '%s'\n", m_imagefile); + osd_printf_warning("Unable to load component image '%s'\n", m_imagefile); else - osd_printf_warning("Unable to load component bitmap '%s'/'%s'\n", m_imagefile, m_alphafile); + osd_printf_warning("Unable to load component image '%s'/'%s'\n", m_imagefile, m_alphafile); + } + + // clear out this stuff in case it's large + if (!m_svg) + m_rasterizer.reset(); + m_dirname.clear(); + m_imagefile.clear(); + m_alphafile.clear(); + m_data.clear(); + } + + void load_svg(util::core_file &file) + { + if (!m_rasterizer) + { + osd_printf_warning("No SVG rasteriser available, won't attempt to parse component image '%s' as SVG\n", m_imagefile); + return; + } + u64 len(file.size()); + if ((std::numeric_limits::max() - 1) < len) + { + osd_printf_warning("Component image '%s' is too large to read into memory\n", m_imagefile); + return; + } + std::unique_ptr svgbuf(new (std::nothrow) char [size_t(len) + 1]); + if (!svgbuf) + { + osd_printf_warning("Error allocating memory to read component image '%s'\n", m_imagefile); + return; + } + svgbuf[len] = '\0'; + for (char *ptr = svgbuf.get(); len; ) + { + u32 const block(u32(std::min(std::numeric_limits::max(), len))); + u32 const read(file.read(ptr, block)); + if (!read) + { + osd_printf_warning("Error reading component image '%s'\n", m_imagefile); + return; + } + ptr += read; + len -= read; + } + m_svg.reset(nsvgParse(svgbuf.get(), "px", 72)); + svgbuf.reset(); + if (!m_svg) + { + osd_printf_warning("Failed to parse component image '%s' as SVG\n", m_imagefile); + return; + } + if ((0.0F >= m_svg->width) || (0.0F >= m_svg->height)) + { + osd_printf_warning("Parsing component image '%s' as SVG produced empty image\n", m_imagefile); + m_svg.reset(); + return; } } + static std::string get_data(util::xml::data_node const &compnode) + { + util::xml::data_node const *datanode(compnode.get_child("data")); + if (datanode && datanode->get_value()) + return datanode->get_value(); + else + return ""; + } + // internal state - bitmap_argb32 m_bitmap; // source bitmap for images - std::string const m_dirname; // directory name of image file (for lazy loading) - std::string const m_imagefile; // name of the image file (for lazy loading) - std::string const m_alphafile; // name of the alpha file (for lazy loading) - bool m_hasalpha = false; // is there any alpha component present? + util::nsvg_image_ptr m_svg; // parsed SVG image + std::shared_ptr m_rasterizer; // SVG rasteriser + bitmap_argb32 m_bitmap; // source bitmap for images + bool m_hasalpha = false; // is there any alpha component present? + + // cold state + std::string m_dirname; // directory name of image file (for lazy loading) + std::string m_imagefile; // name of the image file (for lazy loading) + std::string m_alphafile; // name of the alpha file (for lazy loading) + std::string m_data; // embedded image data }; @@ -2881,7 +3075,11 @@ private: { // load the basic bitmap assert(m_file != nullptr); - /*m_hasalpha[number] = */ render_load_png(m_bitmap[number], *m_file[number], m_dirname.c_str(), m_imagefile[number].c_str()); + if (m_file[number]->open(m_dirname + PATH_SEPARATOR + m_imagefile[number]) == osd_file::error::NONE) + { + /*m_hasalpha[number] = */ render_load_png(m_bitmap[number], *m_file[number]); + m_file[number]->close(); + } // load the alpha bitmap if specified //if (m_bitmap[number].valid() && m_alphafile[number]) @@ -3593,7 +3791,7 @@ void layout_view::recompute(u32 visibility_mask, bool zoom_to_screen) if (first) m_bounds = rawbounds; else - union_render_bounds(m_bounds, rawbounds); + m_bounds |= rawbounds; first = false; // accumulate visible screens and their bounds bounds @@ -3602,7 +3800,7 @@ void layout_view::recompute(u32 visibility_mask, bool zoom_to_screen) if (scrfirst) scrbounds = rawbounds; else - union_render_bounds(scrbounds, rawbounds); + scrbounds |= rawbounds; scrfirst = false; // accumulate active screens @@ -3912,10 +4110,13 @@ layout_view::item::item( , m_animoutput(env.device(), make_animoutput_tag(env, itemnode)) , m_have_output(env.get_attribute_string(itemnode, "name", "")[0]) , m_have_animoutput(!make_animoutput_tag(env, itemnode).empty()) + , m_animinput_port(nullptr) + , m_animmask(make_animmask(env, itemnode)) + , m_animshift(get_state_shift(m_animmask)) , m_input_port(nullptr) , m_input_field(nullptr) , m_input_mask(env.get_attribute_int(itemnode, "inputmask", 0)) - , m_input_shift(get_input_shift(m_input_mask)) + , m_input_shift(get_state_shift(m_input_mask)) , m_input_raw(env.get_attribute_bool(itemnode, "inputraw", 0)) , m_clickthrough(env.get_attribute_bool(itemnode, "clickthrough", "yes")) , m_screen(nullptr) @@ -3924,6 +4125,7 @@ layout_view::item::item( , m_blend_mode(get_blend_mode(env, itemnode)) , m_visibility_mask(env.visibility_mask()) , m_input_tag(make_input_tag(env, itemnode)) + , m_animinput_tag(make_animinput_tag(env, itemnode)) , m_rawbounds(make_bounds(env, itemnode, trans)) , m_has_clickthrough(env.get_attribute_string(itemnode, "clickthrough", "")[0]) { @@ -3972,7 +4174,7 @@ render_bounds layout_view::item::bounds() const if (m_bounds.size() == 1U) return m_bounds.front().bounds; else - return interpolate_bounds(m_bounds, m_have_animoutput ? s32(m_animoutput) : state()); + return interpolate_bounds(m_bounds, animation_state()); } @@ -3985,7 +4187,7 @@ render_color layout_view::item::color() const if (m_color.size() == 1U) return m_color.front().color; else - return interpolate_color(m_color, m_have_animoutput ? s32(m_animoutput) : state()); + return interpolate_color(m_color, animation_state()); } @@ -4038,6 +4240,9 @@ void layout_view::item::resolve_tags() if (m_have_animoutput) m_animoutput.resolve(); + if (!m_animinput_tag.empty()) + m_animinput_port = m_element->machine().root_device().ioport(m_animinput_tag); + if (!m_input_tag.empty()) { m_input_port = m_element->machine().root_device().ioport(m_input_tag); @@ -4062,6 +4267,21 @@ void layout_view::item::resolve_tags() } +//--------------------------------------------- +// find_element - find element definition +//--------------------------------------------- + +inline int layout_view::item::animation_state() const +{ + if (m_have_animoutput) + return (s32(m_animoutput) & m_animmask) >> m_animshift; + else if (m_animinput_port) + return (m_animinput_port->read() & m_animmask) >> m_animshift; + else + return state(); +} + + //--------------------------------------------- // find_element - find element definition //--------------------------------------------- @@ -4156,6 +4376,30 @@ std::string layout_view::item::make_animoutput_tag(view_environment &env, util:: } +//--------------------------------------------- +// make_animmask - get animation state mask +//--------------------------------------------- + +ioport_value layout_view::item::make_animmask(view_environment &env, util::xml::data_node const &itemnode) +{ + util::xml::data_node const *const animate(itemnode.get_child("animate")); + return animate ? env.get_attribute_int(*animate, "mask", ~ioport_value(0)) : ~ioport_value(0); +} + + +//--------------------------------------------- +// make_animinput_tag - get absolute tag for +// animation input +//--------------------------------------------- + +std::string layout_view::item::make_animinput_tag(view_environment &env, util::xml::data_node const &itemnode) +{ + util::xml::data_node const *const animate(itemnode.get_child("animate")); + char const *tag(animate ? env.get_attribute_string(*animate, "inputtag", nullptr) : nullptr); + return tag ? env.device().subtag(tag) : std::string(); +} + + //--------------------------------------------- // make_input_tag - get absolute input tag //--------------------------------------------- @@ -4200,10 +4444,10 @@ int layout_view::item::get_blend_mode(view_environment &env, util::xml::data_nod //--------------------------------------------- -// get_input_shift - shift to right-align LSB +// get_state_shift - shift to right-align LSB //--------------------------------------------- -unsigned layout_view::item::get_input_shift(ioport_value mask) +unsigned layout_view::item::get_state_shift(ioport_value mask) { unsigned result(0U); while (mask && !BIT(mask, 0)) diff --git a/src/emu/rendutil.cpp b/src/emu/rendutil.cpp index 18755ffb340..c66492a8895 100644 --- a/src/emu/rendutil.cpp +++ b/src/emu/rendutil.cpp @@ -11,9 +11,109 @@ #include "emu.h" #include "rendutil.h" +#include "msdib.h" #include "png.h" #include "jpeglib.h" +#include "jerror.h" + + +namespace { + +struct jpeg_corefile_source : public jpeg_source_mgr +{ + static void source(j_decompress_ptr cinfo, util::core_file &file); + +private: + static constexpr unsigned INPUT_BUF_SIZE = 4096; + + static void do_init(j_decompress_ptr cinfo) + { + jpeg_corefile_source &src = *static_cast(cinfo->src); + src.start_of_file = true; + } + + static boolean do_fill(j_decompress_ptr cinfo) + { + jpeg_corefile_source &src = *static_cast(cinfo->src); + + size_t nbytes = src.infile->read(src.buffer, INPUT_BUF_SIZE); + + if (0 >= nbytes) + { + if (src.start_of_file) + ERREXIT(cinfo, JERR_INPUT_EMPTY); + WARNMS(cinfo, JWRN_JPEG_EOF); + src.buffer[0] = JOCTET(0xff); + src.buffer[1] = JOCTET(JPEG_EOI); + nbytes = 2; + } + + src.next_input_byte = src.buffer; + src.bytes_in_buffer = nbytes; + src.start_of_file = false; + + return TRUE; + } + + static void do_skip(j_decompress_ptr cinfo, long num_bytes) + { + jpeg_corefile_source &src = *static_cast(cinfo->src); + + if (0 < num_bytes) + { + while (long(src.bytes_in_buffer) < num_bytes) + { + num_bytes -= long(src.bytes_in_buffer); + (void)(*src.fill_input_buffer)(cinfo); + } + src.next_input_byte += size_t(num_bytes); + src.bytes_in_buffer -= size_t(num_bytes); + } + } + + static void do_term(j_decompress_ptr cinfo) + { + } + + util::core_file *infile; + JOCTET *buffer; + bool start_of_file; +}; + +void jpeg_corefile_source::source(j_decompress_ptr cinfo, util::core_file &file) +{ + jpeg_corefile_source *src; + if (!cinfo->src) + { + src = reinterpret_cast( + (*cinfo->mem->alloc_small)( + reinterpret_cast(cinfo), + JPOOL_PERMANENT, + sizeof(jpeg_corefile_source))); + cinfo->src = src; + src->buffer = reinterpret_cast( + (*cinfo->mem->alloc_small)( + reinterpret_cast(cinfo), + JPOOL_PERMANENT, + INPUT_BUF_SIZE * sizeof(JOCTET))); + } + else + { + src = static_cast(cinfo->src); + } + + src->init_source = &jpeg_corefile_source::do_init; + src->fill_input_buffer = &jpeg_corefile_source::do_fill; + src->skip_input_data = &jpeg_corefile_source::do_skip; + src->resync_to_restart = jpeg_resync_to_restart; + src->term_source = &jpeg_corefile_source::do_term; + src->infile = &file; + src->bytes_in_buffer = 0; + src->next_input_byte = nullptr; +} + +} // anonymous namespace /*************************************************************************** @@ -23,7 +123,7 @@ /* utilities */ static void resample_argb_bitmap_average(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy); static void resample_argb_bitmap_bilinear(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy); -static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png); +static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const util::png_info &png); @@ -543,81 +643,103 @@ void render_line_to_quad(const render_bounds *bounds, float width, float length_ /*------------------------------------------------- - render_load_jpeg - load a JPG file into a - bitmap + render_load_msdib - load a Microsoft DIB file + into a bitmap -------------------------------------------------*/ -void render_load_jpeg(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, const char *filename) +void render_load_msdib(bitmap_argb32 &bitmap, util::core_file &file) { // deallocate previous bitmap bitmap.reset(); - // define file's full name - std::string fname; + // read the DIB data + util::msdib_error const result = util::msdib_read_bitmap(file, bitmap); + if (result != util::msdib_error::NONE) + { + osd_printf_error("Error reading Microsoft DIB file\n"); + bitmap.reset(); + } +} - if (dirname == nullptr) - fname = filename; - else - fname.assign(dirname).append(PATH_SEPARATOR).append(filename); - if (file.open(fname) != osd_file::error::NONE) - return; +/*------------------------------------------------- + render_load_jpeg - load a JPEG file into a + bitmap +-------------------------------------------------*/ + +void render_load_jpeg(bitmap_argb32 &bitmap, util::core_file &file) +{ + // deallocate previous bitmap + bitmap.reset(); - // define standard JPEG structures + // create a JPEG source for the file jpeg_decompress_struct cinfo; jpeg_error_mgr jerr; cinfo.err = jpeg_std_error(&jerr); - jpeg_create_decompress(&cinfo); - - // allocates a buffer for the image - u32 jpg_size = file.size(); - std::unique_ptr jpg_buffer = std::make_unique(jpg_size); - - // read data from the file and set them in the buffer - file.read(jpg_buffer.get(), jpg_size); - jpeg_mem_src(&cinfo, jpg_buffer.get(), jpg_size); - - // read JPEG header and start decompression - jpeg_read_header(&cinfo, TRUE); - jpeg_start_decompress(&cinfo); - - // allocates the destination bitmap - int w = cinfo.output_width; - int h = cinfo.output_height; - int s = cinfo.output_components; - bitmap.allocate(w, h); - - // allocates a buffer to receive the information and copy them into the bitmap - int row_stride = cinfo.output_width * cinfo.output_components; - JSAMPARRAY buffer = (JSAMPARRAY)malloc(sizeof(JSAMPROW)); - buffer[0] = (JSAMPROW)malloc(sizeof(JSAMPLE) * row_stride); - - while ( cinfo.output_scanline < cinfo.output_height ) + jerr.error_exit = [] (j_common_ptr cinfo) { throw cinfo->err; }; + JSAMPARRAY buffer = nullptr; + try { - int j = cinfo.output_scanline; - jpeg_read_scanlines(&cinfo, buffer, 1); + jpeg_create_decompress(&cinfo); + cinfo.mem->max_memory_to_use = 128 * 1024 * 1024; + jpeg_corefile_source::source(&cinfo, file); + + // read JPEG header and start decompression + jpeg_read_header(&cinfo, TRUE); + jpeg_start_decompress(&cinfo); + + // allocates the destination bitmap + int w = cinfo.output_width; + int h = cinfo.output_height; + int s = cinfo.output_components; + bitmap.allocate(w, h); + + // allocates a buffer to receive the information and copy them into the bitmap + int row_stride = cinfo.output_width * cinfo.output_components; + JSAMPARRAY buffer = reinterpret_cast(malloc(sizeof(JSAMPROW))); + buffer[0] = reinterpret_cast(malloc(sizeof(JSAMPLE) * row_stride)); + + while (cinfo.output_scanline < cinfo.output_height) + { + int j = cinfo.output_scanline; + jpeg_read_scanlines(&cinfo, buffer, 1); - if (s == 1) - for (int i = 0; i < w; ++i) - bitmap.pix(j, i) = rgb_t(0xFF, buffer[0][i], buffer[0][i], buffer[0][i]); + if (s == 1) + { + for (int i = 0; i < w; ++i) + bitmap.pix(j, i) = rgb_t(0xFF, buffer[0][i], buffer[0][i], buffer[0][i]); - else if (s == 3) - for (int i = 0; i < w; ++i) - bitmap.pix(j, i) = rgb_t(0xFF, buffer[0][i * s], buffer[0][i * s + 1], buffer[0][i * s + 2]); - else - { - osd_printf_error("Cannot read JPEG data from %s file.\n", fname); - bitmap.reset(); - break; + } + else if (s == 3) + { + for (int i = 0; i < w; ++i) + bitmap.pix(j, i) = rgb_t(0xFF, buffer[0][i * s], buffer[0][i * s + 1], buffer[0][i * s + 2]); + } + else + { + osd_printf_error("Cannot read JPEG data from file.\n"); + bitmap.reset(); + break; + } } - } - // finish decompression and frees the memory - jpeg_finish_decompress(&cinfo); + // finish decompression and frees the memory + jpeg_finish_decompress(&cinfo); + } + catch (jpeg_error_mgr *err) + { + char msg[1024]; + (cinfo.err->format_message)(reinterpret_cast(&cinfo), msg); + osd_printf_error("JPEG error reading data from file: %s\n", msg); + bitmap.reset(); + } jpeg_destroy_decompress(&cinfo); - file.close(); - free(buffer[0]); - free(buffer); + if (buffer) + { + if (buffer[0]) + free(buffer[0]); + free(buffer); + } } @@ -626,36 +748,25 @@ void render_load_jpeg(bitmap_argb32 &bitmap, emu_file &file, const char *dirname bitmap -------------------------------------------------*/ -bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, const char *filename, bool load_as_alpha_to_existing) +bool render_load_png(bitmap_argb32 &bitmap, util::core_file &file, bool load_as_alpha_to_existing) { // deallocate if we're not overlaying alpha if (!load_as_alpha_to_existing) bitmap.reset(); - // open the file - std::string fname; - if (dirname) - fname.assign(dirname).append(PATH_SEPARATOR).append(filename); - else - fname.assign(filename); - osd_file::error const filerr = file.open(fname); - if (filerr != osd_file::error::NONE) - return false; - // read the PNG data - png_info png; - png_error const result = png.read_file(file); - file.close(); - if (result != PNGERR_NONE) + util::png_info png; + util::png_error const result = png.read_file(file); + if (result != util::png_error::NONE) { - osd_printf_error("%s: Error reading PNG file\n", filename); + osd_printf_error("Error reading PNG file\n"); return false; } // if less than 8 bits, upsample - if (PNGERR_NONE != png.expand_buffer_8bit()) + if (util::png_error::NONE != png.expand_buffer_8bit()) { - osd_printf_error("%s: Error upsampling PNG bitmap\n", filename); + osd_printf_error("Error upsampling PNG bitmap\n"); return false; } @@ -663,9 +774,9 @@ bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, if (!load_as_alpha_to_existing) { // non-alpha case - if (PNGERR_NONE != png.copy_to_bitmap(bitmap, hasalpha)) + if (util::png_error::NONE != png.copy_to_bitmap(bitmap, hasalpha)) { - osd_printf_error("%s: Error copying PNG bitmap to MAME bitmap\n", filename); + osd_printf_error("Error copying PNG bitmap to MAME bitmap\n"); return false; } } @@ -674,7 +785,7 @@ bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, // verify we can handle this PNG if (png.bit_depth > 8) { - osd_printf_error("%s: Unsupported bit depth %d (8 bit max)\n", filename, png.bit_depth); + osd_printf_error("Unsupported bit depth %d (8 bit max)\n", png.bit_depth); return false; } @@ -692,7 +803,7 @@ bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, to the alpha channel of a bitmap -------------------------------------------------*/ -static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png) +static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const util::png_info &png) { // FIXME: this function is basically copy/pasted from the PNG code in util, and should be unified with it u8 accumalpha = 0xff; @@ -803,29 +914,41 @@ static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png) render_detect_image - detect image format -------------------------------------------------*/ -ru_imgformat render_detect_image(emu_file &file, const char *dirname, const char *filename) +ru_imgformat render_detect_image(util::core_file &file) { - // open the file - std::string fname; - if (dirname) - fname.assign(dirname).append(PATH_SEPARATOR).append(filename); - else - fname.assign(filename); - osd_file::error const filerr = file.open(fname); - if (filerr != osd_file::error::NONE) - return RENDUTIL_IMGFORMAT_ERROR; - // PNG: check for valid header - png_error const result = png_info::verify_header(file); - if (result == PNGERR_NONE) - { - file.close(); + util::png_error const png = util::png_info::verify_header(file); + file.seek(0, SEEK_SET); + if (util::png_error::NONE == png) return RENDUTIL_IMGFORMAT_PNG; - } + // JPEG: attempt to read header with libjpeg + jpeg_decompress_struct cinfo; + jpeg_error_mgr jerr; + cinfo.err = jpeg_std_error(&jerr); + jerr.error_exit = [] (j_common_ptr cinfo) { throw cinfo->err; }; + try + { + jpeg_create_decompress(&cinfo); + cinfo.mem->max_memory_to_use = 128 * 1024 * 1024; + jpeg_corefile_source::source(&cinfo, file); + jpeg_read_header(&cinfo, TRUE); + jpeg_destroy_decompress(&cinfo); + file.seek(0, SEEK_SET); + return RENDUTIL_IMGFORMAT_JPEG; + } + catch (jpeg_error_mgr *err) + { + jpeg_destroy_decompress(&cinfo); + file.seek(0, SEEK_SET); + } + + // Microsoft DIB: check for valid header + util::msdib_error const msdib = util::msdib_verify_header(file); file.seek(0, SEEK_SET); - // TODO: add more when needed + if (util::msdib_error::NONE == msdib) + return RENDUTIL_IMGFORMAT_MSDIB; - file.close(); + // TODO: add more as necessary return RENDUTIL_IMGFORMAT_UNKNOWN; } diff --git a/src/emu/rendutil.h b/src/emu/rendutil.h index b5c06d6d0fc..c5cdd7d2c4c 100644 --- a/src/emu/rendutil.h +++ b/src/emu/rendutil.h @@ -13,7 +13,7 @@ #pragma once -#include "render.h" +#include "rendertypes.h" #include @@ -23,6 +23,8 @@ enum ru_imgformat { RENDUTIL_IMGFORMAT_PNG, + RENDUTIL_IMGFORMAT_JPEG, + RENDUTIL_IMGFORMAT_MSDIB, RENDUTIL_IMGFORMAT_UNKNOWN, RENDUTIL_IMGFORMAT_ERROR @@ -40,9 +42,10 @@ void render_resample_argb_bitmap_hq(bitmap_argb32 &dest, bitmap_argb32 &source, bool render_clip_line(render_bounds *bounds, const render_bounds *clip); bool render_clip_quad(render_bounds *bounds, const render_bounds *clip, render_quad_texuv *texcoords); void render_line_to_quad(const render_bounds *bounds, float width, float length_extension, render_bounds *bounds0, render_bounds *bounds1); -void render_load_jpeg(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, const char *filename); -bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, const char *filename, bool load_as_alpha_to_existing = false); -ru_imgformat render_detect_image(emu_file &file, const char *dirname, const char *filename); +void render_load_msdib(bitmap_argb32 &bitmap, util::core_file &file); +void render_load_jpeg(bitmap_argb32 &bitmap, util::core_file &file); +bool render_load_png(bitmap_argb32 &bitmap, util::core_file &file, bool load_as_alpha_to_existing = false); +ru_imgformat render_detect_image(util::core_file &file); @@ -61,75 +64,6 @@ static inline float render_round_nearest(float f) } -/*------------------------------------------------- - set_render_bounds_xy - cleaner way to set the - bounds --------------------------------------------------*/ - -static inline void set_render_bounds_xy(render_bounds &bounds, float x0, float y0, float x1, float y1) -{ - bounds.x0 = x0; - bounds.y0 = y0; - bounds.x1 = x1; - bounds.y1 = y1; -} - - -/*------------------------------------------------- - set_render_bounds_wh - cleaner way to set the - bounds --------------------------------------------------*/ - -static inline void set_render_bounds_wh(render_bounds &bounds, float x0, float y0, float width, float height) -{ - bounds.x0 = x0; - bounds.y0 = y0; - bounds.x1 = x0 + width; - bounds.y1 = y0 + height; -} - - -/*------------------------------------------------- - sect_render_bounds - compute the intersection - of two render_bounds --------------------------------------------------*/ - -static inline void sect_render_bounds(render_bounds &dest, const render_bounds &src) -{ - dest.x0 = (std::max)(dest.x0, src.x0); - dest.x1 = (std::min)(dest.x1, src.x1); - dest.y0 = (std::max)(dest.y0, src.y0); - dest.y1 = (std::min)(dest.y1, src.y1); -} - - -/*------------------------------------------------- - union_render_bounds - compute the union of two - render_bounds --------------------------------------------------*/ - -static inline void union_render_bounds(render_bounds &dest, const render_bounds &src) -{ - dest.x0 = (std::min)(dest.x0, src.x0); - dest.x1 = (std::max)(dest.x1, src.x1); - dest.y0 = (std::min)(dest.y0, src.y0); - dest.y1 = (std::max)(dest.y1, src.y1); -} - - -/*------------------------------------------------- - set_render_color - cleaner way to set a color --------------------------------------------------*/ - -static inline void set_render_color(render_color *color, float a, float r, float g, float b) -{ - color->a = a; - color->r = r; - color->g = g; - color->b = b; -} - - /*------------------------------------------------- orientation_swap_flips - swap the X and Y flip flags @@ -213,5 +147,4 @@ static inline u8 apply_brightness_contrast_gamma(u8 src, float brightness, float return u8(result * 255.0f); } - #endif // MAME_EMU_RENDUTIL_H diff --git a/src/emu/screen.cpp b/src/emu/screen.cpp index c1e575819db..c265b14e31b 100644 --- a/src/emu/screen.cpp +++ b/src/emu/screen.cpp @@ -9,14 +9,15 @@ ***************************************************************************/ #include "emu.h" +#include "screen.h" + #include "emuopts.h" -#include "png.h" +#include "render.h" #include "rendutil.h" -#include -#include +#include "nanosvg.h" +#include "png.h" -#include #include @@ -43,7 +44,6 @@ u32 screen_device::m_id_counter = 0; class screen_device::svg_renderer { public: svg_renderer(memory_region *region); - ~svg_renderer(); int width() const; int height() const; @@ -69,8 +69,8 @@ private: int x0, y0, x1, y1; }; - NSVGimage *m_image; - NSVGrasterizer *m_rasterizer; + util::nsvg_image_ptr m_image; + util::nsvg_rasterizer_ptr m_rasterizer; std::vector m_key_state; std::vector> m_keyed_shapes; std::unordered_map m_key_ids; @@ -97,8 +97,8 @@ screen_device::svg_renderer::svg_renderer(memory_region *region) const std::unique_ptr s(new char[region->bytes() + 1]); memcpy(s.get(), region->base(), region->bytes()); s[region->bytes()] = 0; - m_image = nsvgParse(s.get(), "px", 72); - m_rasterizer = nsvgCreateRasterizer(); + m_image.reset(nsvgParse(s.get(), "px", 72)); + m_rasterizer.reset(nsvgCreateRasterizer()); m_key_count = 0; @@ -123,12 +123,6 @@ screen_device::svg_renderer::svg_renderer(memory_region *region) osd_printf_verbose("Parsed SVG '%s', aspect ratio %f\n", region->name(), (m_image->height == 0.0f) ? 0 : m_image->width / m_image->height); } -screen_device::svg_renderer::~svg_renderer() -{ - nsvgDeleteRasterizer(m_rasterizer); - nsvgDelete(m_image); -} - int screen_device::svg_renderer::width() const { return int(m_image->width + 0.5); @@ -150,7 +144,7 @@ void screen_device::svg_renderer::render_state(std::vector &dest, const std s->flags &= ~NSVG_FLAGS_VISIBLE; } - nsvgRasterize(m_rasterizer, m_image, 0, 0, m_scale, (unsigned char *)&dest[0], m_sx, m_sy, m_sx*4); + nsvgRasterize(m_rasterizer.get(), m_image.get(), 0, 0, m_scale, (unsigned char *)&dest[0], m_sx, m_sy, m_sx*4); // Nanosvg generates non-premultiplied alpha, so remultiply by // alpha to "blend" against a black background. Plus align the @@ -1921,14 +1915,14 @@ void screen_device::finalize_burnin() osd_file::error filerr = file.open(util::string_format("%s" PATH_SEPARATOR "burnin-%s.png", machine().basename(), tag() + 1)); if (filerr == osd_file::error::NONE) { - png_info pnginfo; + util::png_info pnginfo; // add two text entries describing the image pnginfo.add_text("Software", util::string_format("%s %s", emulator_info::get_appname(), emulator_info::get_build_version()).c_str()); pnginfo.add_text("System", util::string_format("%s %s", machine().system().manufacturer, machine().system().type.fullname()).c_str()); // now do the actual work - png_write_bitmap(file, &pnginfo, finalmap, 0, nullptr); + util::png_write_bitmap(file, &pnginfo, finalmap, 0, nullptr); } } @@ -1947,8 +1941,13 @@ void screen_device::load_effect_overlay(const char *filename) fullname.append(".png"); // load the file + m_screen_overlay_bitmap.reset(); emu_file file(machine().options().art_path(), OPEN_FLAG_READ); - render_load_png(m_screen_overlay_bitmap, file, nullptr, fullname.c_str()); + if (file.open(fullname) == osd_file::error::NONE) + { + render_load_png(m_screen_overlay_bitmap, file); + file.close(); + } if (m_screen_overlay_bitmap.valid()) m_container->set_overlay(&m_screen_overlay_bitmap); else diff --git a/src/emu/video.cpp b/src/emu/video.cpp index 6f88d9ae03d..56d60ba1661 100644 --- a/src/emu/video.cpp +++ b/src/emu/video.cpp @@ -329,16 +329,16 @@ void video_manager::save_snapshot(screen_device *screen, emu_file &file) // add two text entries describing the image std::string text1 = std::string(emulator_info::get_appname()).append(" ").append(emulator_info::get_build_version()); std::string text2 = std::string(machine().system().manufacturer).append(" ").append(machine().system().type.fullname()); - png_info pnginfo; + util::png_info pnginfo; pnginfo.add_text("Software", text1.c_str()); pnginfo.add_text("System", text2.c_str()); // now do the actual work const rgb_t *palette = (screen != nullptr && screen->has_palette()) ? screen->palette().palette()->entry_list_adjusted() : nullptr; int entries = (screen != nullptr && screen->has_palette()) ? screen->palette().entries() : 0; - png_error error = png_write_bitmap(file, &pnginfo, m_snap_bitmap, entries, palette); - if (error != PNGERR_NONE) - osd_printf_error("Error generating PNG for snapshot: png_error = %d\n", error); + util::png_error error = util::png_write_bitmap(file, &pnginfo, m_snap_bitmap, entries, palette); + if (error != util::png_error::NONE) + osd_printf_error("Error generating PNG for snapshot: png_error = %d\n", std::underlying_type_t(error)); } diff --git a/src/frontend/mame/ui/icorender.cpp b/src/frontend/mame/ui/icorender.cpp index bea85f9dd60..5bda0836838 100644 --- a/src/frontend/mame/ui/icorender.cpp +++ b/src/frontend/mame/ui/icorender.cpp @@ -19,6 +19,7 @@ #include "emu.h" #include "icorender.h" +#include "util/msdib.h" #include "util/png.h" #include @@ -26,11 +27,10 @@ #include #include -// need to set LOG_OUTPUT_STREAM because there's no logerror outside devices -#define LOG_OUTPUT_STREAM std::cerr +// need to set LOG_OUTPUT_FUNC or LOG_OUTPUT_STREAM because there's no logerror outside devices +#define LOG_OUTPUT_FUNC osd_printf_verbose #define LOG_GENERAL (1U << 0) -#define LOG_DIB (1U << 1) //#define VERBOSE (LOG_GENERAL | LOG_DIB) @@ -41,16 +41,6 @@ namespace ui { namespace { -// DIB compression schemes -enum : uint32_t -{ - DIB_COMP_NONE = 0, - DIB_COMP_RLE8 = 1, - DIB_COMP_RLE4 = 2, - DIB_COMP_BITFIELDS = 3 -}; - - // ICO file header struct icon_dir_t { @@ -83,67 +73,6 @@ struct icon_dir_entry_t uint32_t offset; // offset to image data from start of file }; -// old-style DIB header -struct bitmap_core_header_t -{ - uint32_t size; // size of the header (12, 16 or 64) - int16_t width; // width of bitmap in pixels - int16_t height; // height of the image in pixels - uint16_t planes; // number of colour planes (must be 1) - uint16_t bpp; // bits per pixel -}; - -// new-style DIB header -struct bitmap_info_header_t -{ - uint32_t size; // size of the header - int32_t width; // width of bitmap in pixels - int32_t height; // height of bitmap in pixels - uint16_t planes; // number of colour planes (must be 1) - uint16_t bpp; // bits per pixel - uint32_t comp; // compression method - uint32_t rawsize; // size of bitmap data after decompression or 0 if uncompressed - int32_t hres; // horizontal resolution in pixels/metre - int32_t vres; // horizontal resolution in pixels/metre - uint32_t colors; // number of colours or 0 for 1 << bpp - uint32_t important; // number of important colours or 0 if all important - uint32_t red; // red field mask - must be contiguous - uint32_t green; // green field mask - must be contiguous - uint32_t blue; // blue field mask - must be contiguous - uint32_t alpha; // alpha field mask - must be contiguous -}; - - -bool dib_parse_mask(uint32_t mask, unsigned &shift, unsigned &bits) -{ - shift = count_leading_zeros(mask); - mask <<= shift; - bits = count_leading_ones(mask); - mask <<= shift; - shift = 32 - shift - bits; - return !mask; -} - - -void dib_truncate_channel(unsigned &shift, unsigned &bits) -{ - if (8U < bits) - { - unsigned const excess(bits - 8); - shift += excess; - bits -= excess; - } -} - - -uint8_t dib_splat_sample(uint8_t val, unsigned bits) -{ - assert(8U >= bits); - for (val <<= (8U - bits); bits && (8U > bits); bits <<= 1) - val |= val >> bits; - return val; -} - bool load_ico_png(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb32 &bitmap) { @@ -151,10 +80,10 @@ bool load_ico_png(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb3 if (9U >= dir.size) return false; fp.seek(dir.offset, SEEK_SET); - png_error const err(png_read_bitmap(fp, bitmap)); + util::png_error const err(util::png_read_bitmap(fp, bitmap)); switch (err) { - case PNGERR_NONE: + case util::png_error::NONE: // found valid PNG image assert(bitmap.valid()); if ((dir.get_width() == bitmap.width()) && ((dir.get_height() == bitmap.height()))) @@ -172,7 +101,7 @@ bool load_ico_png(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb3 } return true; - case PNGERR_BAD_SIGNATURE: + case util::png_error::BAD_SIGNATURE: // doesn't look like PNG data - just fall back to DIB without the file header return false; @@ -190,426 +119,37 @@ bool load_ico_png(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb3 bool load_ico_dib(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb32 &bitmap) { - // check that these things haven't been padded somehow - static_assert(sizeof(bitmap_core_header_t) == 12U, "compiler has applied padding to bitmap_core_header_t"); - static_assert(sizeof(bitmap_info_header_t) == 56U, "compiler has applied padding to bitmap_info_header_t"); - - // ensure the header fits in the space for the image data - union { bitmap_core_header_t core; bitmap_info_header_t info; } header; - assert(&header.core.size == &header.info.size); - if (sizeof(header.core) > dir.size) - return false; - std::memset(&header, 0, sizeof(header)); fp.seek(dir.offset, SEEK_SET); - if (fp.read(&header.core.size, sizeof(header.core.size)) != sizeof(header.core.size)) - { - LOG( - "Error reading DIB header size from ICO file at offset %u (directory size %u)\n", - dir.offset, - dir.size); - return false; - } - header.core.size = little_endianize_int32(header.core.size); - if (dir.size < header.core.size) - { - LOG( - "ICO file image data at %u (%u bytes) is too small for DIB header (%u bytes)\n", - dir.offset, - dir.size, - header.core.size); - return false; - } - - // identify and read the header - convert OS/2 headers to Windows 3 format - unsigned palette_bytes(4U); - switch (header.core.size) + util::msdib_error const err(util::msdib_read_bitmap_data(fp, bitmap, dir.size, dir.get_height())); + switch (err) { - case 16U: - case 64U: - // extended OS/2 bitmap header with support for compression - LOG( - "ICO image data at %u (%u bytes) uses unsupported OS/2 DIB header (size %u)\n", - dir.offset, - dir.size, - header.core.size); - return false; - - case 12U: - // introduced in OS/2 and Windows 2.0 + case util::msdib_error::NONE: + // found valid DIB image + assert(bitmap.valid()); + if ((dir.get_width() == bitmap.width()) && ((dir.get_height() == bitmap.height()))) { - palette_bytes = 3U; - uint32_t const header_read(std::min(header.core.size, sizeof(header.core)) - sizeof(header.core.size)); - if (fp.read(&header.core.width, header_read) != header_read) - { - LOG("Error reading DIB core header from ICO file image data at %u (%u bytes)\n", dir.offset, dir.size); - return false; - } - fp.seek(header.core.size - sizeof(header.core.size) - header_read, SEEK_CUR); - header.core.width = little_endianize_int16(header.core.width); - header.core.height = little_endianize_int16(header.core.height); - header.core.planes = little_endianize_int16(header.core.planes); - header.core.bpp = little_endianize_int16(header.core.bpp); - LOGMASKED( - LOG_DIB, - "Read DIB core header from ICO file image data at %u: %d*%d, %u planes, %u bpp\n", - dir.offset, - header.core.width, - header.core.height, - header.core.planes, - header.core.bpp); - - // this works because the core header only aliases the width/height of the info header - header.info.bpp = header.core.bpp; - header.info.planes = header.core.planes; - header.info.height = header.core.height; - header.info.width = header.core.width; - header.info.size = 40U; + LOG("Loaded %d*%d pixel DIB image from ICO file\n", bitmap.width(), bitmap.height()); } - break; - - default: - // the next version will be longer - if (124U >= header.core.size) + else { LOG( - "ICO image data at %u (%u bytes) uses unsupported DIB header format (size %u)\n", - dir.offset, - dir.size, - header.core.size); - return false; - } - // fall through - case 40U: - case 52U: - case 56U: - case 108U: - case 124U: - // the Windows 3 bitmap header with optional extensions - { - palette_bytes = 4U; - uint32_t const header_read(std::min(header.info.size, sizeof(header.info)) - sizeof(header.info.size)); - if (fp.read(&header.info.width, header_read) != header_read) - { - LOG("Error reading DIB info header from ICO file image data at %u (%u bytes)\n", dir.offset, dir.size); - return false; - } - fp.seek(header.info.size - sizeof(header.info.size) - header_read, SEEK_CUR); - header.info.width = little_endianize_int32(header.info.width); - header.info.height = little_endianize_int32(header.info.height); - header.info.planes = little_endianize_int16(header.info.planes); - header.info.bpp = little_endianize_int16(header.info.bpp); - header.info.comp = little_endianize_int32(header.info.comp); - header.info.rawsize = little_endianize_int32(header.info.rawsize); - header.info.hres = little_endianize_int32(header.info.hres); - header.info.vres = little_endianize_int32(header.info.vres); - header.info.colors = little_endianize_int32(header.info.colors); - header.info.important = little_endianize_int32(header.info.important); - header.info.red = little_endianize_int32(header.info.red); - header.info.green = little_endianize_int32(header.info.green); - header.info.blue = little_endianize_int32(header.info.blue); - header.info.alpha = little_endianize_int32(header.info.alpha); - LOGMASKED( - LOG_DIB, - "Read DIB info header from ICO file image data at %u: %d*%d (%d*%d ppm), %u planes, %u bpp %u/%s%u colors\n", - dir.offset, - header.info.width, - header.info.height, - header.info.hres, - header.info.vres, - header.info.planes, - header.info.bpp, - header.info.important, - header.info.colors ? "" : "2^", - header.info.colors ? header.info.colors : header.info.bpp); - } - break; - } - - // check for unsupported planes/bit depth - if ((1U != header.info.planes) || !header.info.bpp || (32U < header.info.bpp) || ((8U < header.info.bpp) ? (header.info.bpp % 8) : (8 % header.info.bpp))) - { - LOG( - "ICO file DIB image data at %u uses unsupported planes/bits per pixel %u*%u\n", - dir.offset, - header.info.planes, - header.info.bpp); - return false; - } - - // check dimensions - if ((0 >= header.info.width) || (0 == header.info.height)) - { - LOG( - "ICO file DIB image data at %u has invalid dimensions %u*%u\n", - dir.offset, - header.info.width, - header.info.height); - return false; - } - bool const top_down(0 > header.info.height); - if (top_down) - header.info.height = -header.info.height; - bool have_and_mask((2 * dir.get_height()) == header.info.height); - if (!have_and_mask && (dir.get_height() != header.info.height)) - { - osd_printf_verbose( - "ICO file DIB image data at %lu height %ld doesn't match directory height %u with or without AND mask\n", - (unsigned long)dir.offset, - (long)header.info.height, - dir.get_height()); - return false; - } - if (have_and_mask) - header.info.height >>= 1; - - // ensure compression scheme is supported - bool indexed(true), no_palette(false); - switch (header.info.comp) - { - case DIB_COMP_NONE: - // uncompressed - direct colour with implied bitfields if more than eight bits/pixel - indexed = 8U >= header.info.bpp; - if (indexed) - { - if ((1U << header.info.bpp) < header.info.colors) - { - osd_printf_verbose( - "ICO file DIB image data at %lu has oversized palette with %lu entries for %u bits per pixel\n", - (unsigned long)dir.offset, - (unsigned long)header.info.colors, - (unsigned)header.info.bpp); - } - } - if (!indexed) - { - no_palette = true; - switch(header.info.bpp) - { - case 16U: - header.info.red = 0x00007c00; - header.info.green = 0x000003e0; - header.info.blue = 0x0000001f; - header.info.alpha = 0x00000000; - break; - case 24U: - case 32U: - header.info.red = 0x00ff0000; - header.info.green = 0x0000ff00; - header.info.blue = 0x000000ff; - header.info.alpha = 0x00000000; - break; - } - } - break; - - case DIB_COMP_BITFIELDS: - // uncompressed direct colour with explicitly-specified bitfields - indexed = false; - if (offsetof(bitmap_info_header_t, alpha) > header.info.size) - { - osd_printf_verbose( - "ICO file DIB image data at %lu specifies bit masks but is too small (size %lu)\n", - (unsigned long)dir.offset, - (unsigned long)header.info.size); - return false; + "Loaded %d*%d pixel DIB image from ICO file (directory indicated %u*%u)\n", + bitmap.width(), + bitmap.height(), + dir.get_width(), + dir.get_height()); } - break; + return true; default: - LOG("ICO file DIB image data at %u uses unsupported compression scheme %u\n", header.info.comp); - return false; - } - - // we can now calculate the size of the palette and row data - size_t const palette_entries( - indexed - ? ((1U == header.info.bpp) ? 2U : header.info.colors ? header.info.colors : (1U << header.info.bpp)) - : (no_palette ? 0U : header.info.colors)); - size_t const palette_size(palette_bytes * palette_entries); - size_t const row_bytes(((31 + (header.info.width * header.info.bpp)) >> 5) << 2); - size_t const mask_row_bytes(((31 + header.info.width) >> 5) << 2); - size_t const required_size( - header.info.size + - palette_size + - ((row_bytes + (have_and_mask ? mask_row_bytes : 0U)) * header.info.height)); - if (required_size > dir.size) - { + // invalid DIB data or I/O error LOG( - "ICO file image data at %u (%u bytes) smaller than calculated DIB data size (%u bytes)\n", + "Error %u reading DIB image data from ICO file at offset %u (directory size %u)\n", + unsigned(err), dir.offset, - dir.size, - required_size); + dir.size); return false; } - - // load the palette for indexed colour formats or the shifts for direct colour formats - unsigned red_shift(0), green_shift(0), blue_shift(0), alpha_shift(0); - unsigned red_bits(0), green_bits(0), blue_bits(0), alpha_bits(0); - std::unique_ptr palette; - if (indexed) - { - // read palette and convert - std::unique_ptr palette_data(new uint8_t [palette_size]); - if (fp.read(palette_data.get(), palette_size) != palette_size) - { - LOG("Error reading palette from ICO file DIB image data at %u (%u bytes)\n", dir.offset, dir.size); - return false; - } - size_t const palette_usable(std::min(palette_entries, size_t(1) << header.info.bpp)); - palette.reset(new rgb_t [palette_usable]); - uint8_t const *ptr(palette_data.get()); - for (size_t i = 0; palette_usable > i; ++i, ptr += palette_bytes) - palette[i] = rgb_t(ptr[2], ptr[1], ptr[0]); - } - else - { - // skip over the palette if necessary - if (palette_entries) - fp.seek(palette_bytes * palette_entries, SEEK_CUR); - - // convert masks to shifts - bool const masks_contiguous( - dib_parse_mask(header.info.red, red_shift, red_bits) && - dib_parse_mask(header.info.green, green_shift, green_bits) && - dib_parse_mask(header.info.blue, blue_shift, blue_bits) && - dib_parse_mask(header.info.alpha, alpha_shift, alpha_bits)); - if (!masks_contiguous) - { - osd_printf_verbose( - "ICO file DIB image data at %lu specifies non-contiguous channel masks 0x%lx | 0x%lx | 0x%lx | 0x%lx\n", - (unsigned long)dir.offset, - (unsigned long)header.info.red, - (unsigned long)header.info.green, - (unsigned long)header.info.blue, - (unsigned long)header.info.alpha); - } - if ((32U != header.info.bpp) && ((header.info.red | header.info.green | header.info.blue | header.info.alpha) >> header.info.bpp)) - { - LOG( - "ICO file DIB image data at %lu specifies channel masks 0x%x | 0x%x | 0x%x | 0x%x that exceed %u bits per pixel\n", - dir.offset, - header.info.red, - header.info.green, - header.info.blue, - header.info.alpha, - header.info.bpp); - return false; - } - LOGMASKED( - LOG_DIB, - "DIB from ICO file image data at %1$u using channels: R((x >> %3$u) & 0x%4$0*2$x) G((x >> %5$u) & 0x%6$0*2$x) B((x >> %7$u) & 0x%8$0*2$x) A((x >> %9$u) & 0x%10$0*2$x)\n", - dir.offset, - (header.info.bpp + 3) >> 2, - red_shift, - (uint32_t(1) << red_bits) - 1, - green_shift, - (uint32_t(1) << green_bits) - 1, - blue_shift, - (uint32_t(1) << blue_bits) - 1, - alpha_shift, - (uint32_t(1) << alpha_bits) - 1); - - // the MAME bitmap only supports 8 bits/sample maximum - dib_truncate_channel(red_shift, red_bits); - dib_truncate_channel(green_shift, green_bits); - dib_truncate_channel(blue_shift, blue_bits); - dib_truncate_channel(alpha_shift, alpha_bits); - } - - // allocate the bitmap and process row data - std::unique_ptr row_data(new uint8_t [row_bytes]); - bitmap.allocate(header.info.width, header.info.height); - int const y_inc(top_down ? 1 : -1); - for (int32_t i = 0, y = top_down ? 0 : (header.info.height - 1); header.info.height > i; ++i, y += y_inc) - { - if (fp.read(row_data.get(), row_bytes) != row_bytes) - { - LOG("Error reading DIB row %d data from ICO image data at %u\n", i, dir.offset); - return false; - } - uint8_t *src(row_data.get()); - uint32_t *dest(&bitmap.pix(y)); - unsigned shift(0U); - for (int32_t x = 0; header.info.width > x; ++x, ++dest) - { - // extract or compose a pixel - uint32_t pix(0U); - if (8U >= header.info.bpp) - { - assert(8U > shift); - pix = *src >> (8U - header.info.bpp); - *src <<= header.info.bpp; - shift += header.info.bpp; - if (8U <= shift) - { - shift = 0U; - ++src; - } - } - else for (shift = 0; header.info.bpp > shift; shift += 8U, ++src) - { - pix |= uint32_t(*src) << shift; - } - - // convert to RGB - if (indexed) - { - if (palette_entries > pix) - { - *dest = palette[pix]; - } - else - { - *dest = rgb_t::transparent(); - osd_printf_verbose( - "ICO file DIB image data at %lu has out-of-range color %lu at (%ld, %ld) with %lu palette entries\n", - (unsigned long)dir.offset, - (unsigned long)pix, - (long)x, - (long)y, - (unsigned long)palette_entries); - } - } - else - { - uint8_t r(dib_splat_sample((pix >> red_shift) & ((uint32_t(1) << red_bits) - 1), red_bits)); - uint8_t g(dib_splat_sample((pix >> green_shift) & ((uint32_t(1) << green_bits) - 1), green_bits)); - uint8_t b(dib_splat_sample((pix >> blue_shift) & ((uint32_t(1) << blue_bits) - 1), blue_bits)); - uint8_t a(dib_splat_sample((pix >> alpha_shift) & ((uint32_t(1) << alpha_bits) - 1), alpha_bits)); - *dest = rgb_t(alpha_bits ? a : 255, r, g, b); - } - } - } - - // process the AND mask if present - if (have_and_mask) - { - for (int32_t i = 0, y = top_down ? 0 : (header.info.height - 1); header.info.height > i; ++i, y += y_inc) - { - if (fp.read(row_data.get(), mask_row_bytes) != mask_row_bytes) - { - LOG("Error reading DIB mask row %d data from ICO image data at %u\n", i, dir.offset); - return false; - } - uint8_t *src(row_data.get()); - uint32_t *dest(&bitmap.pix(y)); - unsigned shift(0U); - for (int32_t x = 0; header.info.width > x; ++x, ++dest) - { - assert(8U > shift); - rgb_t pix(*dest); - *dest = pix.set_a(BIT(*src, 7U - shift) ? 0U : pix.a()); - if (8U <= ++shift) - { - shift = 0U; - ++src; - } - } - } - } - - // we're done! - return true; } diff --git a/src/frontend/mame/ui/menu.cpp b/src/frontend/mame/ui/menu.cpp index b1bcbb908b7..aa7d90f0ea3 100644 --- a/src/frontend/mame/ui/menu.cpp +++ b/src/frontend/mame/ui/menu.cpp @@ -92,10 +92,17 @@ menu::global_state::global_state(running_machine &machine, ui_options const &opt { m_bgrnd_bitmap = std::make_unique(0, 0); emu_file backgroundfile(".", OPEN_FLAG_READ); - render_load_jpeg(*m_bgrnd_bitmap, backgroundfile, nullptr, "background.jpg"); + if (backgroundfile.open("background.jpg") == osd_file::error::NONE) + { + render_load_jpeg(*m_bgrnd_bitmap, backgroundfile); + backgroundfile.close(); + } - if (!m_bgrnd_bitmap->valid()) - render_load_png(*m_bgrnd_bitmap, backgroundfile, nullptr, "background.png"); + if (!m_bgrnd_bitmap->valid() && (backgroundfile.open("background.png") == osd_file::error::NONE)) + { + render_load_png(*m_bgrnd_bitmap, backgroundfile); + backgroundfile.close(); + } if (m_bgrnd_bitmap->valid()) m_bgrnd_texture->set_bitmap(*m_bgrnd_bitmap, m_bgrnd_bitmap->cliprect(), TEXFORMAT_ARGB32); diff --git a/src/frontend/mame/ui/selmenu.cpp b/src/frontend/mame/ui/selmenu.cpp index b05991b02e9..7a652c1b6bf 100644 --- a/src/frontend/mame/ui/selmenu.cpp +++ b/src/frontend/mame/ui/selmenu.cpp @@ -95,6 +95,62 @@ char const *const hover_msg[] = { __("Show DATs view"), }; + +void load_image(bitmap_argb32 &bitmap, emu_file &file, std::string const &base) +{ + if (file.open(base + ".png") == osd_file::error::NONE) + { + render_load_png(bitmap, file); + file.close(); + } + + if (!bitmap.valid() && (file.open(base + ".jpg") == osd_file::error::NONE)) + { + render_load_jpeg(bitmap, file); + file.close(); + } + + if (!bitmap.valid() && (file.open(base + ".bmp") == osd_file::error::NONE)) + { + render_load_msdib(bitmap, file); + file.close(); + } +} + + +void load_driver_image(bitmap_argb32 &bitmap, emu_file &file, game_driver const &driver) +{ + // try to load snapshot first from saved "0000.png" file + std::string fullname = driver.name; + load_image(bitmap, file, fullname + PATH_SEPARATOR + "0000"); + + // if fail, attempt to load from standard file + if (!bitmap.valid()) + load_image(bitmap, file, fullname); + + // if fail again, attempt to load from parent file + if (!bitmap.valid()) + { + // ignore BIOS sets + bool isclone = strcmp(driver.parent, "0") != 0; + if (isclone) + { + int const cx = driver_list::find(driver.parent); + if ((0 <= cx) && (driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT)) + isclone = false; + } + + if (isclone) + { + fullname = driver.parent; + load_image(bitmap, file, fullname + PATH_SEPARATOR + "0000"); + + if (!bitmap.valid()) + load_image(bitmap, file, fullname); + } + } +} + } // anonymous namespace constexpr std::size_t menu_select_launch::MAX_VISIBLE_SEARCH; // stupid non-inline semantics @@ -2226,41 +2282,16 @@ void menu_select_launch::arts_render(float origx1, float origy1, float origx2, f if (software->startempty == 1) { // Load driver snapshot - std::string fullname = std::string(software->driver->name) + ".png"; - render_load_png(tmp_bitmap, snapfile, nullptr, fullname.c_str()); - - if (!tmp_bitmap.valid()) - { - fullname.assign(software->driver->name).append(".jpg"); - render_load_jpeg(tmp_bitmap, snapfile, nullptr, fullname.c_str()); - } + load_driver_image(tmp_bitmap, snapfile, *software->driver); } else { // First attempt from name list - std::string pathname = software->listname; - std::string fullname = software->shortname + ".png"; - render_load_png(tmp_bitmap, snapfile, pathname.c_str(), fullname.c_str()); + load_image(tmp_bitmap, snapfile, software->listname + PATH_SEPARATOR + software->shortname); + // Second attempt from driver name + part name if (!tmp_bitmap.valid()) - { - fullname.assign(software->shortname).append(".jpg"); - render_load_jpeg(tmp_bitmap, snapfile, pathname.c_str(), fullname.c_str()); - } - - if (!tmp_bitmap.valid()) - { - // Second attempt from driver name + part name - pathname.assign(software->driver->name).append(software->part); - fullname.assign(software->shortname).append(".png"); - render_load_png(tmp_bitmap, snapfile, pathname.c_str(), fullname.c_str()); - - if (!tmp_bitmap.valid()) - { - fullname.assign(software->shortname).append(".jpg"); - render_load_jpeg(tmp_bitmap, snapfile, pathname.c_str(), fullname.c_str()); - } - } + load_image(tmp_bitmap, snapfile, software->driver->name + software->part + PATH_SEPARATOR + software->shortname); } m_cache->set_snapx_software(software); @@ -2285,51 +2316,7 @@ void menu_select_launch::arts_render(float origx1, float origy1, float origx2, f { emu_file snapfile(searchstr, OPEN_FLAG_READ); bitmap_argb32 tmp_bitmap; - - // try to load snapshot first from saved "0000.png" file - std::string fullname(driver->name); - render_load_png(tmp_bitmap, snapfile, fullname.c_str(), "0000.png"); - - if (!tmp_bitmap.valid()) - render_load_jpeg(tmp_bitmap, snapfile, fullname.c_str(), "0000.jpg"); - - // if fail, attemp to load from standard file - if (!tmp_bitmap.valid()) - { - fullname.assign(driver->name).append(".png"); - render_load_png(tmp_bitmap, snapfile, nullptr, fullname.c_str()); - - if (!tmp_bitmap.valid()) - { - fullname.assign(driver->name).append(".jpg"); - render_load_jpeg(tmp_bitmap, snapfile, nullptr, fullname.c_str()); - } - } - - // if fail again, attemp to load from parent file - if (!tmp_bitmap.valid()) - { - // set clone status - bool cloneof = strcmp(driver->parent, "0"); - if (cloneof) - { - int cx = driver_list::find(driver->parent); - if ((cx >= 0) && (driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT)) - cloneof = false; - } - - if (cloneof) - { - fullname.assign(driver->parent).append(".png"); - render_load_png(tmp_bitmap, snapfile, nullptr, fullname.c_str()); - - if (!tmp_bitmap.valid()) - { - fullname.assign(driver->parent).append(".jpg"); - render_load_jpeg(tmp_bitmap, snapfile, nullptr, fullname.c_str()); - } - } - } + load_driver_image(tmp_bitmap, snapfile, *driver); m_cache->set_snapx_driver(driver); m_switch_image = false; diff --git a/src/lib/util/msdib.cpp b/src/lib/util/msdib.cpp new file mode 100644 index 00000000000..a1ae5eaddf0 --- /dev/null +++ b/src/lib/util/msdib.cpp @@ -0,0 +1,642 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + msdib.h + + Microsoft Device-Independent Bitmap file loading. + +***************************************************************************/ + +#include "msdib.h" + +#include "eminline.h" + +#include +#include +#include + + +#define LOG_GENERAL (1U << 0) +#define LOG_DIB (1U << 1) + +//#define VERBOSE (LOG_GENERAL | LOG_DIB) + +#define LOG_OUTPUT_FUNC osd_printf_verbose + +#ifndef VERBOSE +#define VERBOSE 0 +#endif + +#define LOGMASKED(mask, ...) do { if (VERBOSE & (mask)) (LOG_OUTPUT_FUNC)(__VA_ARGS__); } while (false) + +#define LOG(...) LOGMASKED(LOG_GENERAL, __VA_ARGS__) + + +namespace util { + +namespace { + +// DIB compression schemes +enum : std::uint32_t +{ + DIB_COMP_NONE = 0, + DIB_COMP_RLE8 = 1, + DIB_COMP_RLE4 = 2, + DIB_COMP_BITFIELDS = 3 +}; + + +// file header doesn't use natural alignment +using bitmap_file_header = std::uint8_t [14]; + + +// old-style DIB header +struct bitmap_core_header +{ + std::uint32_t size; // size of the header (12, 16 or 64) + std::int16_t width; // width of bitmap in pixels + std::int16_t height; // height of the image in pixels + std::uint16_t planes; // number of colour planes (must be 1) + std::uint16_t bpp; // bits per pixel +}; + +// new-style DIB header +struct bitmap_info_header +{ + std::uint32_t size; // size of the header + std::int32_t width; // width of bitmap in pixels + std::int32_t height; // height of bitmap in pixels + std::uint16_t planes; // number of colour planes (must be 1) + std::uint16_t bpp; // bits per pixel + std::uint32_t comp; // compression method + std::uint32_t rawsize; // size of bitmap data after decompression or 0 if uncompressed + std::int32_t hres; // horizontal resolution in pixels/metre + std::int32_t vres; // horizontal resolution in pixels/metre + std::uint32_t colors; // number of colours or 0 for 1 << bpp + std::uint32_t important; // number of important colours or 0 if all important + std::uint32_t red; // red field mask - must be contiguous + std::uint32_t green; // green field mask - must be contiguous + std::uint32_t blue; // blue field mask - must be contiguous + std::uint32_t alpha; // alpha field mask - must be contiguous +}; + + +union bitmap_headers +{ + bitmap_core_header core; + bitmap_info_header info; +}; + + +bool dib_parse_mask(uint32_t mask, unsigned &shift, unsigned &bits) +{ + shift = count_leading_zeros(mask); + mask <<= shift; + bits = count_leading_ones(mask); + mask <<= shift; + shift = 32 - shift - bits; + return !mask; +} + + +void dib_truncate_channel(unsigned &shift, unsigned &bits) +{ + if (8U < bits) + { + unsigned const excess(bits - 8); + shift += excess; + bits -= excess; + } +} + + +uint8_t dib_splat_sample(uint8_t val, unsigned bits) +{ + assert(8U >= bits); + for (val <<= (8U - bits); bits && (8U > bits); bits <<= 1) + val |= val >> bits; + return val; +} + + +msdib_error dib_read_file_header(core_file &fp, std::uint32_t &filelen) +{ + // the bitmap file header doesn't use natural alignment + bitmap_file_header file_header; + if (fp.read(file_header, sizeof(file_header)) != sizeof(file_header)) + { + LOG("Error reading DIB file header\n"); + return msdib_error::FILE_TRUNCATED; + } + + // only support Windows bitmaps for now + if ((0x42 != file_header[0]) || (0x4d != file_header[1])) + return msdib_error::BAD_SIGNATURE; + + // do a very basic check on the file length + std::uint32_t const file_length( + (std::uint32_t(file_header[2]) << 0) | + (std::uint32_t(file_header[3]) << 8) | + (std::uint32_t(file_header[4]) << 16) | + (std::uint32_t(file_header[5]) << 24)); + if ((sizeof(file_header) + sizeof(bitmap_core_header)) > file_length) + return msdib_error::FILE_CORRUPT; + + // check that the offset to the pixel data looks half sane + std::uint32_t const pixel_offset( + (std::uint32_t(file_header[10]) << 0) | + (std::uint32_t(file_header[11]) << 8) | + (std::uint32_t(file_header[12]) << 16) | + (std::uint32_t(file_header[13]) << 24)); + if (((sizeof(file_header) + sizeof(bitmap_core_header)) > pixel_offset) || (file_length < pixel_offset)) + return msdib_error::FILE_CORRUPT; + + // looks OK enough + filelen = file_length; + return msdib_error::NONE; +} + + +msdib_error dib_read_bitmap_header( + core_file &fp, + bitmap_headers &header, + unsigned &palette_bytes, + bool &indexed, + std::size_t &palette_entries, + std::size_t &palette_size, + std::size_t &row_bytes, + std::uint32_t length) +{ + // check that these things haven't been padded somehow + static_assert(sizeof(bitmap_core_header) == 12U, "compiler has applied padding to bitmap_core_header"); + static_assert(sizeof(bitmap_info_header) == 56U, "compiler has applied padding to bitmap_info_header"); + + // ensure the header fits in the space for the image data + assert(&header.core.size == &header.info.size); + if (sizeof(header.core) > length) + return msdib_error::FILE_TRUNCATED; + std::memset(&header, 0, sizeof(header)); + if (fp.read(&header.core.size, sizeof(header.core.size)) != sizeof(header.core.size)) + { + LOG("Error reading DIB header size (length %u)\n", length); + return msdib_error::FILE_TRUNCATED; + } + header.core.size = little_endianize_int32(header.core.size); + if (length < header.core.size) + { + LOG("DIB image data (%u bytes) is too small for DIB header (%u bytes)\n", length, header.core.size); + return msdib_error::FILE_CORRUPT; + } + + // identify and read the header - convert OS/2 headers to Windows 3 format + palette_bytes = 4U; + switch (header.core.size) + { + case 16U: + case 64U: + // extended OS/2 bitmap header with support for compression + LOG( + "DIB image data (%u bytes) uses unsupported OS/2 DIB header (size %u)\n", + length, + header.core.size); + return msdib_error::UNSUPPORTED_FORMAT; + + case 12U: + // introduced in OS/2 and Windows 2.0 + { + palette_bytes = 3U; + std::uint32_t const header_read(std::min(header.core.size, sizeof(header.core)) - sizeof(header.core.size)); + if (fp.read(&header.core.width, header_read) != header_read) + { + LOG("Error reading DIB core header from image data (%u bytes)\n", length); + return msdib_error::FILE_TRUNCATED; + } + fp.seek(header.core.size - sizeof(header.core.size) - header_read, SEEK_CUR); + header.core.width = little_endianize_int16(header.core.width); + header.core.height = little_endianize_int16(header.core.height); + header.core.planes = little_endianize_int16(header.core.planes); + header.core.bpp = little_endianize_int16(header.core.bpp); + LOGMASKED( + LOG_DIB, + "Read DIB core header from image data : %d*%d, %u planes, %u bpp\n", + header.core.width, + header.core.height, + header.core.planes, + header.core.bpp); + + // this works because the core header only aliases the width/height of the info header + header.info.bpp = header.core.bpp; + header.info.planes = header.core.planes; + header.info.height = header.core.height; + header.info.width = header.core.width; + header.info.size = 40U; + } + break; + + default: + // the next version will be longer + if (124U >= header.core.size) + { + LOG( + "DIB image data (%u bytes) uses unsupported DIB header format (size %u)\n", + length, + header.core.size); + return msdib_error::UNSUPPORTED_FORMAT; + } + // fall through + case 40U: + case 52U: + case 56U: + case 108U: + case 124U: + // the Windows 3 bitmap header with optional extensions + { + palette_bytes = 4U; + std::uint32_t const header_read(std::min(header.info.size, sizeof(header.info)) - sizeof(header.info.size)); + if (fp.read(&header.info.width, header_read) != header_read) + { + LOG("Error reading DIB info header from image data (%u bytes)\n", length); + return msdib_error::FILE_TRUNCATED; + } + fp.seek(header.info.size - sizeof(header.info.size) - header_read, SEEK_CUR); + header.info.width = little_endianize_int32(header.info.width); + header.info.height = little_endianize_int32(header.info.height); + header.info.planes = little_endianize_int16(header.info.planes); + header.info.bpp = little_endianize_int16(header.info.bpp); + header.info.comp = little_endianize_int32(header.info.comp); + header.info.rawsize = little_endianize_int32(header.info.rawsize); + header.info.hres = little_endianize_int32(header.info.hres); + header.info.vres = little_endianize_int32(header.info.vres); + header.info.colors = little_endianize_int32(header.info.colors); + header.info.important = little_endianize_int32(header.info.important); + header.info.red = little_endianize_int32(header.info.red); + header.info.green = little_endianize_int32(header.info.green); + header.info.blue = little_endianize_int32(header.info.blue); + header.info.alpha = little_endianize_int32(header.info.alpha); + LOGMASKED( + LOG_DIB, + "Read DIB info header from image data: %d*%d (%d*%d ppm), %u planes, %u bpp %u/%s%u colors\n", + header.info.width, + header.info.height, + header.info.hres, + header.info.vres, + header.info.planes, + header.info.bpp, + header.info.important, + header.info.colors ? "" : "2^", + header.info.colors ? header.info.colors : header.info.bpp); + } + break; + } + + // check for unsupported planes/bit depth + if ((1U != header.info.planes) || !header.info.bpp || (32U < header.info.bpp) || ((8U < header.info.bpp) ? (header.info.bpp % 8) : (8 % header.info.bpp))) + { + LOG("DIB image data uses unsupported planes/bits per pixel %u*%u\n", header.info.planes, header.info.bpp); + return msdib_error::UNSUPPORTED_FORMAT; + } + + // check dimensions + if ((0 >= header.info.width) || (0 == header.info.height)) + { + LOG("DIB image data has invalid dimensions %u*%u\n", header.info.width, header.info.height); + return msdib_error::FILE_CORRUPT; + } + + // ensure compression scheme is supported + bool no_palette(false); + indexed = true; + switch (header.info.comp) + { + case DIB_COMP_NONE: + // uncompressed - direct colour with implied bitfields if more than eight bits/pixel + indexed = 8U >= header.info.bpp; + if (indexed) + { + if ((1U << header.info.bpp) < header.info.colors) + { + osd_printf_verbose( + "DIB image data has oversized palette with %u entries for %u bits per pixel\n", + header.info.colors, + header.info.bpp); + } + } + if (!indexed) + { + no_palette = true; + switch(header.info.bpp) + { + case 16U: + header.info.red = 0x00007c00; + header.info.green = 0x000003e0; + header.info.blue = 0x0000001f; + header.info.alpha = 0x00000000; + break; + case 24U: + case 32U: + header.info.red = 0x00ff0000; + header.info.green = 0x0000ff00; + header.info.blue = 0x000000ff; + header.info.alpha = 0x00000000; + break; + } + } + break; + + case DIB_COMP_BITFIELDS: + // uncompressed direct colour with explicitly-specified bitfields + indexed = false; + if (offsetof(bitmap_info_header, alpha) > header.info.size) + { + osd_printf_verbose( + "DIB image data specifies bit masks but is too small (size %u)\n", + header.info.size); + return msdib_error::FILE_CORRUPT; + } + break; + + default: + LOG("DIB image data uses unsupported compression scheme %u\n", header.info.comp); + return msdib_error::UNSUPPORTED_FORMAT; + } + + // we can now calculate the size of the palette and row data + palette_entries = + indexed + ? ((1U == header.info.bpp) ? 2U : header.info.colors ? header.info.colors : (1U << header.info.bpp)) + : (no_palette ? 0U : header.info.colors); + palette_size = palette_bytes * palette_entries; + row_bytes = ((31 + (header.info.width * header.info.bpp)) >> 5) << 2; + + // header looks OK + return msdib_error::NONE; +} + +} // anonymous namespace + + + +msdib_error msdib_verify_header(core_file &fp) +{ + msdib_error err; + + // file header + std::uint32_t file_length; + err = dib_read_file_header(fp, file_length); + if (msdib_error::NONE != err) + return err; + + // bitmap header + bitmap_headers header; + unsigned palette_bytes; + bool indexed; + std::size_t palette_entries, palette_size, row_bytes; + err = dib_read_bitmap_header( + fp, + header, + palette_bytes, + indexed, + palette_entries, + palette_size, + row_bytes, + file_length - sizeof(bitmap_file_header)); + if (msdib_error::NONE != err) + return err; + + // check length + std::size_t const required_size( + sizeof(bitmap_file_header) + + header.info.size + + palette_size + + (row_bytes * std::abs(header.info.height))); + if (required_size > file_length) + return msdib_error::FILE_TRUNCATED; + + // good chance this file is supported + return msdib_error::NONE; +} + + +msdib_error msdib_read_bitmap(core_file &fp, bitmap_argb32 &bitmap) +{ + std::uint32_t file_length; + msdib_error const headerr(dib_read_file_header(fp, file_length)); + if (msdib_error::NONE != headerr) + return headerr; + + return msdib_read_bitmap_data(fp, bitmap, file_length - sizeof(bitmap_file_header), 0U); +} + + +msdib_error msdib_read_bitmap_data(core_file &fp, bitmap_argb32 &bitmap, std::uint32_t length, std::uint32_t dirheight) +{ + // read the bitmap header + bitmap_headers header; + unsigned palette_bytes; + bool indexed; + std::size_t palette_entries, palette_size, row_bytes; + msdib_error const head_error(dib_read_bitmap_header(fp, header, palette_bytes, indexed, palette_entries, palette_size, row_bytes, length)); + if (msdib_error::NONE != head_error) + return head_error; + + // check dimensions + bool const top_down(0 > header.info.height); + if (top_down) + header.info.height = -header.info.height; + bool have_and_mask((2 * dirheight) == header.info.height); + if (!have_and_mask && (0 < dirheight) && (dirheight != header.info.height)) + { + osd_printf_verbose( + "DIB image data height %d doesn't match directory height %u with or without AND mask\n", + header.info.height, + dirheight); + return msdib_error::UNSUPPORTED_FORMAT; + } + if (have_and_mask) + header.info.height >>= 1; + + // we can now calculate the size of the image data + std::size_t const mask_row_bytes(((31 + header.info.width) >> 5) << 2); + std::size_t const required_size( + header.info.size + + palette_size + + ((row_bytes + (have_and_mask ? mask_row_bytes : 0U)) * header.info.height)); + if (required_size > length) + { + LOG("DIB image data (%u bytes) smaller than calculated DIB data size (%u bytes)\n", length, required_size); + return msdib_error::FILE_TRUNCATED; + } + + // load the palette for indexed colour formats or the shifts for direct colour formats + unsigned red_shift(0), green_shift(0), blue_shift(0), alpha_shift(0); + unsigned red_bits(0), green_bits(0), blue_bits(0), alpha_bits(0); + std::unique_ptr palette; + if (indexed) + { + // read palette and convert + std::unique_ptr palette_data; + try { palette_data.reset(new std::uint8_t [palette_size]); } + catch (std::bad_alloc const &) { return msdib_error::OUT_OF_MEMORY; } + if (fp.read(palette_data.get(), palette_size) != palette_size) + { + LOG("Error reading palette from DIB image data (%u bytes)\n", length); + return msdib_error::FILE_TRUNCATED; + } + std::size_t const palette_usable(std::min(palette_entries, std::size_t(1) << header.info.bpp)); + try { palette.reset(new rgb_t [palette_usable]); } + catch (std::bad_alloc const &) { return msdib_error::OUT_OF_MEMORY; } + std::uint8_t const *ptr(palette_data.get()); + for (std::size_t i = 0; palette_usable > i; ++i, ptr += palette_bytes) + palette[i] = rgb_t(ptr[2], ptr[1], ptr[0]); + } + else + { + // skip over the palette if necessary + if (palette_entries) + fp.seek(palette_bytes * palette_entries, SEEK_CUR); + + // convert masks to shifts + bool const masks_contiguous( + dib_parse_mask(header.info.red, red_shift, red_bits) && + dib_parse_mask(header.info.green, green_shift, green_bits) && + dib_parse_mask(header.info.blue, blue_shift, blue_bits) && + dib_parse_mask(header.info.alpha, alpha_shift, alpha_bits)); + if (!masks_contiguous) + { + osd_printf_verbose( + "DIB image data specifies non-contiguous channel masks 0x%x | 0x%x | 0x%x | 0x%x\n", + header.info.red, + header.info.green, + header.info.blue, + header.info.alpha); + } + if ((32U != header.info.bpp) && ((header.info.red | header.info.green | header.info.blue | header.info.alpha) >> header.info.bpp)) + { + LOG( + "DIB image data specifies channel masks 0x%x | 0x%x | 0x%x | 0x%x that exceed %u bits per pixel\n", + header.info.red, + header.info.green, + header.info.blue, + header.info.alpha, + header.info.bpp); + return msdib_error::FILE_CORRUPT; + } + LOGMASKED( + LOG_DIB, + "DIB image data using channels: R((x >> %2$u) & 0x%3$0*1$x) G((x >> %4$u) & 0x%5$0*1$x) B((x >> %6$u) & 0x%7$0*1$x) A((x >> %8$u) & 0x%9$0*1$x)\n", + (header.info.bpp + 3) >> 2, + red_shift, + (std::uint32_t(1) << red_bits) - 1, + green_shift, + (std::uint32_t(1) << green_bits) - 1, + blue_shift, + (std::uint32_t(1) << blue_bits) - 1, + alpha_shift, + (std::uint32_t(1) << alpha_bits) - 1); + + // the MAME bitmap only supports 8 bits/sample maximum + dib_truncate_channel(red_shift, red_bits); + dib_truncate_channel(green_shift, green_bits); + dib_truncate_channel(blue_shift, blue_bits); + dib_truncate_channel(alpha_shift, alpha_bits); + } + + // allocate the bitmap and process row data + std::unique_ptr row_data; + try {row_data.reset(new std::uint8_t [row_bytes]); } + catch (std::bad_alloc const &) { return msdib_error::OUT_OF_MEMORY; } + bitmap.allocate(header.info.width, header.info.height); + int const y_inc(top_down ? 1 : -1); + for (std::int32_t i = 0, y = top_down ? 0 : (header.info.height - 1); header.info.height > i; ++i, y += y_inc) + { + if (fp.read(row_data.get(), row_bytes) != row_bytes) + { + LOG("Error reading DIB row %d data from image data\n", i); + return msdib_error::FILE_TRUNCATED; + } + std::uint8_t *src(row_data.get()); + std::uint32_t *dest(&bitmap.pix(y)); + unsigned shift(0U); + for (std::int32_t x = 0; header.info.width > x; ++x, ++dest) + { + // extract or compose a pixel + std::uint32_t pix(0U); + if (8U >= header.info.bpp) + { + assert(8U > shift); + pix = *src >> (8U - header.info.bpp); + *src <<= header.info.bpp; + shift += header.info.bpp; + if (8U <= shift) + { + shift = 0U; + ++src; + } + } + else for (shift = 0; header.info.bpp > shift; shift += 8U, ++src) + { + pix |= std::uint32_t(*src) << shift; + } + + // convert to RGB + if (indexed) + { + if (palette_entries > pix) + { + *dest = palette[pix]; + } + else + { + *dest = rgb_t::transparent(); + osd_printf_verbose( + "DIB image data has out-of-range color %u at (%d, %d) with %u palette entries\n", + pix, + x, + y, + palette_entries); + } + } + else + { + std::uint8_t r(dib_splat_sample((pix >> red_shift) & ((std::uint32_t(1) << red_bits) - 1), red_bits)); + std::uint8_t g(dib_splat_sample((pix >> green_shift) & ((std::uint32_t(1) << green_bits) - 1), green_bits)); + std::uint8_t b(dib_splat_sample((pix >> blue_shift) & ((std::uint32_t(1) << blue_bits) - 1), blue_bits)); + std::uint8_t a(dib_splat_sample((pix >> alpha_shift) & ((std::uint32_t(1) << alpha_bits) - 1), alpha_bits)); + *dest = rgb_t(alpha_bits ? a : 255, r, g, b); + } + } + } + + // process the AND mask if present + if (have_and_mask) + { + for (std::int32_t i = 0, y = top_down ? 0 : (header.info.height - 1); header.info.height > i; ++i, y += y_inc) + { + if (fp.read(row_data.get(), mask_row_bytes) != mask_row_bytes) + { + LOG("Error reading DIB mask row %d data from image data\n", i); + return msdib_error::FILE_TRUNCATED; + } + std::uint8_t *src(row_data.get()); + std::uint32_t *dest(&bitmap.pix(y)); + unsigned shift(0U); + for (std::int32_t x = 0; header.info.width > x; ++x, ++dest) + { + assert(8U > shift); + rgb_t pix(*dest); + *dest = pix.set_a(BIT(*src, 7U - shift) ? 0U : pix.a()); + if (8U <= ++shift) + { + shift = 0U; + ++src; + } + } + } + } + + // we're done! + return msdib_error::NONE; +} + +} // namespace util diff --git a/src/lib/util/msdib.h b/src/lib/util/msdib.h new file mode 100644 index 00000000000..54767e216fb --- /dev/null +++ b/src/lib/util/msdib.h @@ -0,0 +1,46 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + msdib.h + + Microsoft Device-Independent Bitmap file loading. + +***************************************************************************/ +#ifndef MAME_LIB_UTIL_MSDIB_H +#define MAME_LIB_UTIL_MSDIB_H + +#pragma once + +#include "bitmap.h" +#include "corefile.h" +#include "osdcore.h" + +#include + + +namespace util { + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +// Error types +enum class msdib_error +{ + NONE, + OUT_OF_MEMORY, + FILE_ERROR, + BAD_SIGNATURE, + FILE_TRUNCATED, + FILE_CORRUPT, + UNSUPPORTED_FORMAT +}; + +msdib_error msdib_verify_header(core_file &fp); +msdib_error msdib_read_bitmap(core_file &fp, bitmap_argb32 &bitmap); +msdib_error msdib_read_bitmap_data(core_file &fp, bitmap_argb32 &bitmap, std::uint32_t length, std::uint32_t dirheight = 0U); + +} // namespace util + +#endif // MAME_LIB_UTIL_MSDIB_H diff --git a/src/lib/util/nanosvg.h b/src/lib/util/nanosvg.h new file mode 100644 index 00000000000..6be55cdae32 --- /dev/null +++ b/src/lib/util/nanosvg.h @@ -0,0 +1,33 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +/*************************************************************************** + + nanosvg.h + + NanoSVG helpers. + +***************************************************************************/ +#ifndef MAME_LIB_UTIL_NANOSVG_H +#define MAME_LIB_UTIL_NANOSVG_H + +#include +#include + +#include + + +namespace util { + +struct nsvg_deleter +{ + void operator()(NSVGimage *ptr) { nsvgDelete(ptr); } + void operator()(NSVGrasterizer *ptr) { nsvgDeleteRasterizer(ptr); } +}; + + +using nsvg_image_ptr = std::unique_ptr; +using nsvg_rasterizer_ptr = std::unique_ptr; + +} // namespace util + +#endif // MAME_LIB_UTIL_NANOSVG_H diff --git a/src/lib/util/png.cpp b/src/lib/util/png.cpp index 69835e54051..50aeb28c5fe 100644 --- a/src/lib/util/png.cpp +++ b/src/lib/util/png.cpp @@ -2,7 +2,7 @@ // copyright-holders:Aaron Giles, Vas Crabb /********************************************************************* - png.c + png.cpp PNG reading functions. @@ -16,34 +16,13 @@ #include #include -#include -#include -#include -#include - #include #include +#include +#include - -/*************************************************************************** - GLOBAL VARIABLES -***************************************************************************/ - -static const int samples[] = { 1, 0, 3, 1, 2, 0, 4 }; - - - -/*************************************************************************** - INLINE FUNCTIONS -***************************************************************************/ - -static inline int compute_rowbytes(const png_info &pnginfo) -{ - return (pnginfo.width * samples[pnginfo.color_type] * pnginfo.bit_depth + 7) / 8; -} - - +namespace util { /*************************************************************************** GENERAL FUNCTIONS @@ -66,6 +45,12 @@ void png_info::free_data() namespace { +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +constexpr int samples[] = { 1, 0, 3, 1, 2, 0, 4 }; + constexpr std::uint8_t PNG_SIGNATURE[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a }; #define MNG_Signature "\x8A\x4D\x4E\x47\x0D\x0A\x1A\x0A" @@ -101,6 +86,12 @@ constexpr std::uint8_t PNG_PF_Average = 3; constexpr std::uint8_t PNG_PF_Paeth = 4; +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +inline int compute_rowbytes(const png_info &pnginfo) { return (pnginfo.width * samples[pnginfo.color_type] * pnginfo.bit_depth + 7) / 8; } + inline uint8_t fetch_8bit(uint8_t const *v) { return *v; } inline uint16_t fetch_16bit(uint8_t const *v) { return big_endianize_int16(*reinterpret_cast(v)); } inline uint32_t fetch_32bit(uint8_t const *v) { return big_endianize_int32(*reinterpret_cast(v)); } @@ -132,11 +123,11 @@ private: { // do some basic checks for unsupported images if (!pnginfo.bit_depth || (ARRAY_LENGTH(samples) <= pnginfo.color_type) || !samples[pnginfo.color_type]) - return PNGERR_UNSUPPORTED_FORMAT; // unknown colour format + return png_error::UNSUPPORTED_FORMAT; // unknown colour format if ((0 != pnginfo.interlace_method) && (1 != pnginfo.interlace_method)) - return PNGERR_UNSUPPORTED_FORMAT; // unknown interlace method + return png_error::UNSUPPORTED_FORMAT; // unknown interlace method if ((3 == pnginfo.color_type) && (!pnginfo.num_palette || !pnginfo.palette)) - return PNGERR_FILE_CORRUPT; // indexed colour with no palette + return png_error::FILE_CORRUPT; // indexed colour with no palette // calculate the offset for each pass of the interlace unsigned const pass_count(get_pass_count()); @@ -146,13 +137,13 @@ private: // allocate memory for the filtered image try { pnginfo.image.reset(new std::uint8_t [pass_offset[pass_count]]); } - catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; } + catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; } // decompress image data - png_error error = PNGERR_NONE; + png_error error = png_error::NONE; error = decompress(idata, pass_offset[pass_count]); std::uint32_t const bpp(get_bytes_per_pixel()); - for (unsigned pass = 0; (pass_count > pass) && (PNGERR_NONE == error); ++pass) + for (unsigned pass = 0; (pass_count > pass) && (png_error::NONE == error); ++pass) { // compute some basic parameters std::pair const dimensions(get_pass_dimensions(pass)); @@ -161,7 +152,7 @@ private: // we de-filter in place, stripping the filter bytes off the rows uint8_t *dst(&pnginfo.image[pass_offset[pass]]); uint8_t const *src(dst); - for (std::uint32_t y = 0; (dimensions.second > y) && (PNGERR_NONE == error); ++y) + for (std::uint32_t y = 0; (dimensions.second > y) && (png_error::NONE == error); ++y) { // first byte of each row is the filter type uint8_t const filter(*src++); @@ -172,7 +163,7 @@ private: } // if we errored, free the image data - if (error != PNGERR_NONE) + if (error != png_error::NONE) pnginfo.image.reset(); return error; @@ -182,7 +173,7 @@ private: { // only deflate is permitted if (0 != pnginfo.compression_method) - return PNGERR_DECOMPRESS_ERROR; + return png_error::DECOMPRESS_ERROR; // allocate zlib stream z_stream stream; @@ -195,7 +186,7 @@ private: stream.next_in = Z_NULL; zerr = inflateInit(&stream); if (Z_OK != zerr) - return PNGERR_DECOMPRESS_ERROR; + return png_error::DECOMPRESS_ERROR; // decompress IDAT blocks stream.next_out = pnginfo.image.get(); @@ -217,28 +208,28 @@ private: // it's all good if we got end-of-stream or we have with no data remaining if ((Z_OK == inflateEnd(&stream)) && ((Z_STREAM_END == zerr) || ((Z_OK == zerr) && (idata.end() == it) && !stream.avail_in))) - return PNGERR_NONE; + return png_error::NONE; else - return PNGERR_DECOMPRESS_ERROR; + return png_error::DECOMPRESS_ERROR; } png_error unfilter_row(std::uint8_t type, uint8_t const *src, uint8_t *dst, uint8_t const *dstprev, int bpp, std::uint32_t rowbytes) { if (0 != pnginfo.filter_method) - return PNGERR_UNKNOWN_FILTER; + return png_error::UNKNOWN_FILTER; switch (type) { case PNG_PF_None: // no filter, just copy std::copy_n(src, rowbytes, dst); - return PNGERR_NONE; + return png_error::NONE; case PNG_PF_Sub: // SUB = previous pixel dst = std::copy_n(src, bpp, dst); src += bpp; for (std::uint32_t x = bpp; rowbytes > x; ++x, ++src, ++dst) *dst = *src + dst[-bpp]; - return PNGERR_NONE; + return png_error::NONE; case PNG_PF_Up: // UP = pixel above if (dstprev) @@ -250,7 +241,7 @@ private: { std::copy_n(src, rowbytes, dst); } - return PNGERR_NONE; + return png_error::NONE; case PNG_PF_Average: // AVERAGE = average of pixel above and previous pixel if (dstprev) @@ -267,7 +258,7 @@ private: for (std::uint32_t x = bpp; rowbytes > x; ++x, ++src, ++dst) *dst = *src + (dst[-bpp] >> 1); } - return PNGERR_NONE; + return png_error::NONE; case PNG_PF_Paeth: // PAETH = special filter for (std::uint32_t x = 0; rowbytes > x; ++x, ++src, ++dst) @@ -281,10 +272,10 @@ private: int32_t const dc(std::abs(prediction - pc)); *dst = ((da <= db) && (da <= dc)) ? (*src + pa) : (db <= dc) ? (*src + pb) : (*src + pc); } - return PNGERR_NONE; + return png_error::NONE; default: // unknown filter type - return PNGERR_UNKNOWN_FILTER; + return png_error::UNKNOWN_FILTER; } } @@ -294,7 +285,7 @@ private: { case PNG_CN_IHDR: // image header if (13 > length) - return PNGERR_FILE_CORRUPT; + return png_error::FILE_CORRUPT; pnginfo.width = fetch_32bit(&data[0]); pnginfo.height = fetch_32bit(&data[4]); pnginfo.bit_depth = fetch_8bit(&data[8]); @@ -307,31 +298,31 @@ private: case PNG_CN_PLTE: // palette pnginfo.num_palette = length / 3; if ((length % 3) || ((3 == pnginfo.color_type) && ((1 << pnginfo.bit_depth) < pnginfo.num_palette))) - return PNGERR_FILE_CORRUPT; + return png_error::FILE_CORRUPT; pnginfo.palette = std::move(data); break; case PNG_CN_tRNS: // transparency information if (((0 == pnginfo.color_type) && (2 > length)) || ((2 == pnginfo.color_type) && (6 > length))) - return PNGERR_FILE_CORRUPT; + return png_error::FILE_CORRUPT; pnginfo.num_trans = length; pnginfo.trans = std::move(data); break; case PNG_CN_IDAT: // image data try { idata.emplace_back(length, std::move(data)); } - catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; } + catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; } break; case PNG_CN_gAMA: // gamma if (4 > length) - return PNGERR_FILE_CORRUPT; + return png_error::FILE_CORRUPT; pnginfo.source_gamma = fetch_32bit(data.get()) / 100000.0; break; case PNG_CN_pHYs: // physical information if (9 > length) - return PNGERR_FILE_CORRUPT; + return png_error::FILE_CORRUPT; pnginfo.xres = fetch_32bit(&data[0]); pnginfo.yres = fetch_32bit(&data[4]); pnginfo.resolution_unit = fetch_8bit(&data[8]); @@ -361,17 +352,17 @@ private: } catch (std::bad_alloc const &) { - return PNGERR_OUT_OF_MEMORY; + return png_error::OUT_OF_MEMORY; } break; /* anything else */ default: if ((type & 0x20000000) == 0) - return PNGERR_UNKNOWN_CHUNK; + return png_error::UNKNOWN_CHUNK; break; } - return PNGERR_NONE; + return png_error::NONE; } unsigned get_pass_count() const @@ -413,23 +404,23 @@ private: return ((samples[pnginfo.color_type] * pnginfo.bit_depth) + 7) >> 3; } - static png_error read_chunk(util::core_file &fp, std::unique_ptr &data, std::uint32_t &type, std::uint32_t &length) + static png_error read_chunk(core_file &fp, std::unique_ptr &data, std::uint32_t &type, std::uint32_t &length) { std::uint8_t tempbuff[4]; /* fetch the length of this chunk */ if (fp.read(tempbuff, 4) != 4) - return PNGERR_FILE_TRUNCATED; + return png_error::FILE_TRUNCATED; length = fetch_32bit(tempbuff); /* fetch the type of this chunk */ if (fp.read(tempbuff, 4) != 4) - return PNGERR_FILE_TRUNCATED; + return png_error::FILE_TRUNCATED; type = fetch_32bit(tempbuff); /* stop when we hit an IEND chunk */ if (type == PNG_CN_IEND) - return PNGERR_NONE; + return png_error::NONE; /* start the CRC with the chunk type (but not the length) */ std::uint32_t crc = crc32(0, tempbuff, 4); @@ -439,13 +430,13 @@ private: { /* allocate memory for this chunk */ try { data.reset(new std::uint8_t [length]); } - catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; } + catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; } /* read the data from the file */ if (fp.read(data.get(), length) != length) { data.reset(); - return PNGERR_FILE_TRUNCATED; + return png_error::FILE_TRUNCATED; } /* update the CRC */ @@ -456,7 +447,7 @@ private: if (fp.read(tempbuff, 4) != 4) { data.reset(); - return PNGERR_FILE_TRUNCATED; + return png_error::FILE_TRUNCATED; } std::uint32_t const chunk_crc = fetch_32bit(tempbuff); @@ -464,10 +455,10 @@ private: if (crc != chunk_crc) { data.reset(); - return PNGERR_FILE_CORRUPT; + return png_error::FILE_CORRUPT; } - return PNGERR_NONE; + return png_error::NONE; } png_info & pnginfo; @@ -481,13 +472,13 @@ public: { // do some basic checks for unsupported images if ((8 > pnginfo.bit_depth) || (pnginfo.bit_depth % 8)) - return PNGERR_UNSUPPORTED_FORMAT; // only do multiples of 8bps here - expand lower bit depth first + return png_error::UNSUPPORTED_FORMAT; // only do multiples of 8bps here - expand lower bit depth first if ((ARRAY_LENGTH(samples) <= pnginfo.color_type) || !samples[pnginfo.color_type]) - return PNGERR_UNSUPPORTED_FORMAT; // unknown colour sample format + return png_error::UNSUPPORTED_FORMAT; // unknown colour sample format if ((0 != pnginfo.interlace_method) && (1 != pnginfo.interlace_method)) - return PNGERR_UNSUPPORTED_FORMAT; // unknown interlace method + return png_error::UNSUPPORTED_FORMAT; // unknown interlace method if ((3 == pnginfo.color_type) && (8 != pnginfo.bit_depth)) - return PNGERR_UNSUPPORTED_FORMAT; // indexed colour must be exactly 8bpp + return png_error::UNSUPPORTED_FORMAT; // indexed colour must be exactly 8bpp // everything looks sane, allocate the bitmap and deinterlace into it bitmap.allocate(pnginfo.width, pnginfo.height); @@ -603,22 +594,22 @@ public: // set hasalpha flag and return hasalpha = 0xffU != accumalpha; - return PNGERR_NONE; + return png_error::NONE; } png_error expand_buffer_8bit() { // nothing to do if we're at 8 or greater already if (pnginfo.bit_depth >= 8) - return PNGERR_NONE; + return png_error::NONE; // do some basic checks for unsupported images if (!pnginfo.bit_depth || (8 % pnginfo.bit_depth)) - return PNGERR_UNSUPPORTED_FORMAT; // bit depth must be a factor of eight + return png_error::UNSUPPORTED_FORMAT; // bit depth must be a factor of eight if ((0 != pnginfo.color_type) && (3 != pnginfo.color_type)) - return PNGERR_UNSUPPORTED_FORMAT; // only upsample monochrome and indexed colour + return png_error::UNSUPPORTED_FORMAT; // only upsample monochrome and indexed colour if ((0 != pnginfo.interlace_method) && (1 != pnginfo.interlace_method)) - return PNGERR_UNSUPPORTED_FORMAT; // unknown interlace method + return png_error::UNSUPPORTED_FORMAT; // unknown interlace method // calculate the offset for each pass of the interlace on the input and output unsigned const pass_count(get_pass_count()); @@ -633,7 +624,7 @@ public: // allocate a new buffer at 8-bit std::unique_ptr outbuf; try { outbuf.reset(new std::uint8_t [outp_offset[pass_count]]); } - catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; } + catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; } // upsample bitmap std::uint8_t const bytesamples(8 / pnginfo.bit_depth); @@ -687,13 +678,13 @@ public: pnginfo.image = std::move(outbuf); pnginfo.bit_depth = 8; - return PNGERR_NONE; + return png_error::NONE; } - png_error read_file(util::core_file &fp) + png_error read_file(core_file &fp) { // initialize the data structures - png_error error = PNGERR_NONE; + png_error error = png_error::NONE; pnginfo.reset(); std::list idata; @@ -701,13 +692,13 @@ public: error = verify_header(fp); // loop until we hit an IEND chunk - while (PNGERR_NONE == error) + while (png_error::NONE == error) { // read a chunk std::unique_ptr chunk_data; std::uint32_t chunk_type = 0, chunk_length; error = read_chunk(fp, chunk_data, chunk_type, chunk_length); - if (PNGERR_NONE == error) + if (png_error::NONE == error) { if (chunk_type == PNG_CN_IEND) break; // stop when we hit an IEND chunk @@ -717,29 +708,29 @@ public: } // finish processing the image - if (PNGERR_NONE == error) + if (png_error::NONE == error) error = process(idata); // if we have an error, free all the output data - if (error != PNGERR_NONE) + if (error != png_error::NONE) pnginfo.reset(); return error; } - static png_error verify_header(util::core_file &fp) + static png_error verify_header(core_file &fp) { EQUIVALENT_ARRAY(PNG_SIGNATURE, std::uint8_t) signature; // read 8 bytes if (fp.read(signature, sizeof(signature)) != sizeof(signature)) - return PNGERR_FILE_TRUNCATED; + return png_error::FILE_TRUNCATED; // return an error if we don't match if (std::memcmp(signature, PNG_SIGNATURE, sizeof(PNG_SIGNATURE))) - return PNGERR_BAD_SIGNATURE; + return png_error::BAD_SIGNATURE; - return PNGERR_NONE; + return png_error::NONE; } }; @@ -760,7 +751,7 @@ constexpr unsigned png_private::ADAM7_Y_OFFS[7]; core stream -------------------------------------------------*/ -png_error png_info::verify_header(util::core_file &fp) +png_error png_info::verify_header(core_file &fp) { return png_private::verify_header(fp); } @@ -770,7 +761,7 @@ png_error png_info::verify_header(util::core_file &fp) read_file - read a PNG from a core stream -------------------------------------------------*/ -png_error png_info::read_file(util::core_file &fp) +png_error png_info::read_file(core_file &fp) { return png_private(*this).read_file(fp); } @@ -781,7 +772,7 @@ png_error png_info::read_file(util::core_file &fp) bitmap -------------------------------------------------*/ -png_error png_read_bitmap(util::core_file &fp, bitmap_argb32 &bitmap) +png_error png_read_bitmap(core_file &fp, bitmap_argb32 &bitmap) { png_error result; png_info pnginfo; @@ -789,12 +780,12 @@ png_error png_read_bitmap(util::core_file &fp, bitmap_argb32 &bitmap) // read the PNG data result = png.read_file(fp); - if (PNGERR_NONE != result) + if (png_error::NONE != result) return result; // resample to 8bpp if necessary result = png.expand_buffer_8bit(); - if (PNGERR_NONE != result) + if (png_error::NONE != result) { pnginfo.free_data(); return result; @@ -848,13 +839,13 @@ png_error png_info::add_text(const char *keyword, const char *text) char32_t ch; int const len(uchar_from_utf8(&ch, ptr, kwend - ptr)); if ((0 >= len) || (32 > ch) || (255 < ch) || ((126 < ch) && (161 > ch)) || (((32 == prev) || (keyword == ptr)) && (32 == ch))) - return PNGERR_UNSUPPORTED_FORMAT; + return png_error::UNSUPPORTED_FORMAT; prev = ch; ++cnt; ptr += len; } if ((32 == prev) || (1 > cnt) || (79 < cnt)) - return PNGERR_UNSUPPORTED_FORMAT; + return png_error::UNSUPPORTED_FORMAT; // apply rules to text char const *const textend(text + std::strlen(text)); @@ -863,14 +854,14 @@ png_error png_info::add_text(const char *keyword, const char *text) char32_t ch; int const len(uchar_from_utf8(&ch, ptr, textend - ptr)); if ((0 >= len) || (1 > ch) || (255 < ch)) - return PNGERR_UNSUPPORTED_FORMAT; + return png_error::UNSUPPORTED_FORMAT; ptr += len; } // allocate a new text element try { textlist.emplace_back(std::piecewise_construct, std::forward_as_tuple(keyword, kwend), std::forward_as_tuple(text, textend)); } - catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; } - return PNGERR_NONE; + catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; } + return png_error::NONE; } @@ -879,7 +870,7 @@ png_error png_info::add_text(const char *keyword, const char *text) the given file -------------------------------------------------*/ -static png_error write_chunk(util::core_file &fp, const uint8_t *data, uint32_t type, uint32_t length) +static png_error write_chunk(core_file &fp, const uint8_t *data, uint32_t type, uint32_t length) { uint8_t tempbuff[8]; uint32_t crc; @@ -891,22 +882,22 @@ static png_error write_chunk(util::core_file &fp, const uint8_t *data, uint32_t /* write that data */ if (fp.write(tempbuff, 8) != 8) - return PNGERR_FILE_ERROR; + return png_error::FILE_ERROR; /* append the actual data */ if (length > 0) { if (fp.write(data, length) != length) - return PNGERR_FILE_ERROR; + return png_error::FILE_ERROR; crc = crc32(crc, data, length); } /* write the CRC */ put_32bit(tempbuff, crc); if (fp.write(tempbuff, 4) != 4) - return PNGERR_FILE_ERROR; + return png_error::FILE_ERROR; - return PNGERR_NONE; + return png_error::NONE; } @@ -915,7 +906,7 @@ static png_error write_chunk(util::core_file &fp, const uint8_t *data, uint32_t chunk to the given file by deflating it -------------------------------------------------*/ -static png_error write_deflated_chunk(util::core_file &fp, uint8_t *data, uint32_t type, uint32_t length) +static png_error write_deflated_chunk(core_file &fp, uint8_t *data, uint32_t type, uint32_t length) { uint64_t lengthpos = fp.tell(); uint8_t tempbuff[8192]; @@ -931,7 +922,7 @@ static png_error write_deflated_chunk(util::core_file &fp, uint8_t *data, uint32 /* write that data */ if (fp.write(tempbuff, 8) != 8) - return PNGERR_FILE_ERROR; + return png_error::FILE_ERROR; /* initialize the stream */ memset(&stream, 0, sizeof(stream)); @@ -939,7 +930,7 @@ static png_error write_deflated_chunk(util::core_file &fp, uint8_t *data, uint32 stream.avail_in = length; zerr = deflateInit(&stream, Z_DEFAULT_COMPRESSION); if (zerr != Z_OK) - return PNGERR_COMPRESS_ERROR; + return png_error::COMPRESS_ERROR; /* now loop until we run out of data */ for ( ; ; ) @@ -956,7 +947,7 @@ static png_error write_deflated_chunk(util::core_file &fp, uint8_t *data, uint32 if (fp.write(tempbuff, bytes) != bytes) { deflateEnd(&stream); - return PNGERR_FILE_ERROR; + return png_error::FILE_ERROR; } crc = crc32(crc, tempbuff, bytes); zlength += bytes; @@ -970,29 +961,29 @@ static png_error write_deflated_chunk(util::core_file &fp, uint8_t *data, uint32 if (zerr != Z_OK) { deflateEnd(&stream); - return PNGERR_COMPRESS_ERROR; + return png_error::COMPRESS_ERROR; } } /* clean up deflater(maus) */ zerr = deflateEnd(&stream); if (zerr != Z_OK) - return PNGERR_COMPRESS_ERROR; + return png_error::COMPRESS_ERROR; /* write the CRC */ put_32bit(tempbuff, crc); if (fp.write(tempbuff, 4) != 4) - return PNGERR_FILE_ERROR; + return png_error::FILE_ERROR; /* seek back and update the length */ fp.seek(lengthpos, SEEK_SET); put_32bit(tempbuff + 0, zlength); if (fp.write(tempbuff, 4) != 4) - return PNGERR_FILE_ERROR; + return png_error::FILE_ERROR; /* return to the end */ fp.seek(lengthpos + 8 + zlength + 4, SEEK_SET); - return PNGERR_NONE; + return png_error::NONE; } @@ -1016,7 +1007,7 @@ static png_error convert_bitmap_to_image_palette(png_info &pnginfo, const bitmap /* allocate memory for the palette */ try { pnginfo.palette.reset(new std::uint8_t [3 * 256]); } - catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; } + catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; } /* build the palette */ std::fill_n(pnginfo.palette.get(), 3 * 256, 0); @@ -1036,7 +1027,7 @@ static png_error convert_bitmap_to_image_palette(png_info &pnginfo, const bitmap catch (std::bad_alloc const &) { pnginfo.palette.reset(); - return PNGERR_OUT_OF_MEMORY; + return png_error::OUT_OF_MEMORY; } /* copy in the pixels, specifying a nullptr filter */ @@ -1051,7 +1042,7 @@ static png_error convert_bitmap_to_image_palette(png_info &pnginfo, const bitmap *dst++ = *src++; } - return PNGERR_NONE; + return png_error::NONE; } @@ -1075,7 +1066,7 @@ static png_error convert_bitmap_to_image_rgb(png_info &pnginfo, const bitmap_t & /* allocate memory for the image */ try { pnginfo.image.reset(new std::uint8_t [pnginfo.height * (rowbytes + 1)]); } - catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; } + catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; } /* copy in the pixels, specifying a nullptr filter */ for (y = 0; y < pnginfo.height; y++) @@ -1127,10 +1118,10 @@ static png_error convert_bitmap_to_image_rgb(png_info &pnginfo, const bitmap_t & /* unsupported format */ else - return PNGERR_UNSUPPORTED_FORMAT; + return png_error::UNSUPPORTED_FORMAT; } - return PNGERR_NONE; + return png_error::NONE; } @@ -1139,7 +1130,7 @@ static png_error convert_bitmap_to_image_rgb(png_info &pnginfo, const bitmap_t & chunks to the given file -------------------------------------------------*/ -static png_error write_png_stream(util::core_file &fp, png_info &pnginfo, const bitmap_t &bitmap, int palette_length, const rgb_t *palette) +static png_error write_png_stream(core_file &fp, png_info &pnginfo, const bitmap_t &bitmap, int palette_length, const rgb_t *palette) { uint8_t tempbuff[16]; png_error error; @@ -1149,7 +1140,7 @@ static png_error write_png_stream(util::core_file &fp, png_info &pnginfo, const error = convert_bitmap_to_image_palette(pnginfo, bitmap, palette_length, palette); else error = convert_bitmap_to_image_rgb(pnginfo, bitmap, palette_length, palette); - if (error != PNGERR_NONE) + if (error != png_error::NONE) return error; // if we wanted to get clever and do filtering, we would do it here @@ -1163,18 +1154,18 @@ static png_error write_png_stream(util::core_file &fp, png_info &pnginfo, const put_8bit(tempbuff + 11, pnginfo.filter_method); put_8bit(tempbuff + 12, pnginfo.interlace_method); error = write_chunk(fp, tempbuff, PNG_CN_IHDR, 13); - if (error != PNGERR_NONE) + if (error != png_error::NONE) return error; // write the PLTE chunk if (pnginfo.num_palette > 0) error = write_chunk(fp, pnginfo.palette.get(), PNG_CN_PLTE, pnginfo.num_palette * 3); - if (error != PNGERR_NONE) + if (error != png_error::NONE) return error; - // write a single IDAT chunk */ + // write a single IDAT chunk error = write_deflated_chunk(fp, pnginfo.image.get(), PNG_CN_IDAT, pnginfo.height * (compute_rowbytes(pnginfo) + 1)); - if (error != PNGERR_NONE) + if (error != png_error::NONE) return error; // write TEXT chunks @@ -1182,7 +1173,7 @@ static png_error write_png_stream(util::core_file &fp, png_info &pnginfo, const for (png_info::png_text const &text : pnginfo.textlist) { try { textbuf.resize(text.first.length() + 1 + text.second.length()); } - catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; } + catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; } std::uint8_t *dst(&textbuf[0]); // convert keyword to ISO-8859-1 @@ -1213,7 +1204,7 @@ static png_error write_png_stream(util::core_file &fp, png_info &pnginfo, const } error = write_chunk(fp, &textbuf[0], PNG_CN_tEXt, dst - &textbuf[0]); - if (error != PNGERR_NONE) + if (error != png_error::NONE) return error; } @@ -1222,7 +1213,7 @@ static png_error write_png_stream(util::core_file &fp, png_info &pnginfo, const } -png_error png_write_bitmap(util::core_file &fp, png_info *info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette) +png_error png_write_bitmap(core_file &fp, png_info *info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette) { // use a dummy pnginfo if none passed to us png_info pnginfo; @@ -1231,7 +1222,7 @@ png_error png_write_bitmap(util::core_file &fp, png_info *info, bitmap_t const & // write the PNG signature if (fp.write(PNG_SIGNATURE, sizeof(PNG_SIGNATURE)) != sizeof(PNG_SIGNATURE)) - return PNGERR_FILE_ERROR; + return png_error::FILE_ERROR; /* write the rest of the PNG data */ return write_png_stream(fp, *info, bitmap, palette_length, palette); @@ -1246,7 +1237,7 @@ png_error png_write_bitmap(util::core_file &fp, png_info *info, bitmap_t const & ********************************************************************************/ /** - * @fn png_error mng_capture_start(util::core_file &fp, bitmap_t &bitmap, unsigned rate) + * @fn png_error mng_capture_start(core_file &fp, bitmap_t &bitmap, unsigned rate) * * @brief Mng capture start. * @@ -1257,12 +1248,12 @@ png_error png_write_bitmap(util::core_file &fp, png_info *info, bitmap_t const & * @return A png_error. */ -png_error mng_capture_start(util::core_file &fp, bitmap_t &bitmap, unsigned rate) +png_error mng_capture_start(core_file &fp, bitmap_t &bitmap, unsigned rate) { uint8_t mhdr[28]; if (fp.write(MNG_Signature, 8) != 8) - return PNGERR_FILE_ERROR; + return png_error::FILE_ERROR; memset(mhdr, 0, 28); put_32bit(mhdr + 0, bitmap.width()); @@ -1273,7 +1264,7 @@ png_error mng_capture_start(util::core_file &fp, bitmap_t &bitmap, unsigned rate } /** - * @fn png_error mng_capture_frame(util::core_file &fp, png_info *info, bitmap_t &bitmap, int palette_length, const rgb_t *palette) + * @fn png_error mng_capture_frame(core_file &fp, png_info *info, bitmap_t &bitmap, int palette_length, const rgb_t *palette) * * @brief Mng capture frame. * @@ -1286,13 +1277,13 @@ png_error mng_capture_start(util::core_file &fp, bitmap_t &bitmap, unsigned rate * @return A png_error. */ -png_error mng_capture_frame(util::core_file &fp, png_info &info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette) +png_error mng_capture_frame(core_file &fp, png_info &info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette) { return write_png_stream(fp, info, bitmap, palette_length, palette); } /** - * @fn png_error mng_capture_stop(util::core_file &fp) + * @fn png_error mng_capture_stop(core_file &fp) * * @brief Mng capture stop. * @@ -1301,7 +1292,9 @@ png_error mng_capture_frame(util::core_file &fp, png_info &info, bitmap_t const * @return A png_error. */ -png_error mng_capture_stop(util::core_file &fp) +png_error mng_capture_stop(core_file &fp) { return write_chunk(fp, nullptr, MNG_CN_MEND, 0); } + +} // namespace util diff --git a/src/lib/util/png.h b/src/lib/util/png.h index 5f852fd8c16..784c92646e6 100644 --- a/src/lib/util/png.h +++ b/src/lib/util/png.h @@ -17,31 +17,33 @@ #include "corefile.h" #include "osdcore.h" +#include #include #include #include #include +namespace util { /*************************************************************************** CONSTANTS ***************************************************************************/ /* Error types */ -enum png_error +enum class png_error { - PNGERR_NONE, - PNGERR_OUT_OF_MEMORY, - PNGERR_UNKNOWN_FILTER, - PNGERR_FILE_ERROR, - PNGERR_BAD_SIGNATURE, - PNGERR_DECOMPRESS_ERROR, - PNGERR_FILE_TRUNCATED, - PNGERR_FILE_CORRUPT, - PNGERR_UNKNOWN_CHUNK, - PNGERR_COMPRESS_ERROR, - PNGERR_UNSUPPORTED_FORMAT + NONE, + OUT_OF_MEMORY, + UNKNOWN_FILTER, + FILE_ERROR, + BAD_SIGNATURE, + DECOMPRESS_ERROR, + FILE_TRUNCATED, + FILE_CORRUPT, + UNKNOWN_CHUNK, + COMPRESS_ERROR, + UNSUPPORTED_FORMAT }; @@ -57,7 +59,7 @@ public: ~png_info() { free_data(); } - png_error read_file(util::core_file &fp); + png_error read_file(core_file &fp); png_error copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha); png_error expand_buffer_8bit(); @@ -66,7 +68,7 @@ public: void free_data(); void reset() { free_data(); operator=(png_info()); } - static png_error verify_header(util::core_file &fp); + static png_error verify_header(core_file &fp); std::unique_ptr image; std::uint32_t width, height; @@ -99,12 +101,14 @@ private: FUNCTION PROTOTYPES ***************************************************************************/ -png_error png_read_bitmap(util::core_file &fp, bitmap_argb32 &bitmap); +png_error png_read_bitmap(core_file &fp, bitmap_argb32 &bitmap); -png_error png_write_bitmap(util::core_file &fp, png_info *info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette); +png_error png_write_bitmap(core_file &fp, png_info *info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette); -png_error mng_capture_start(util::core_file &fp, bitmap_t &bitmap, unsigned rate); -png_error mng_capture_frame(util::core_file &fp, png_info &info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette); -png_error mng_capture_stop(util::core_file &fp); +png_error mng_capture_start(core_file &fp, bitmap_t &bitmap, unsigned rate); +png_error mng_capture_frame(core_file &fp, png_info &info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette); +png_error mng_capture_stop(core_file &fp); + +} // namespace util #endif // MAME_LIB_UTIL_PNG_H diff --git a/src/mame/drivers/alg.cpp b/src/mame/drivers/alg.cpp index 73189abc79e..9e42a1ce7e5 100644 --- a/src/mame/drivers/alg.cpp +++ b/src/mame/drivers/alg.cpp @@ -30,7 +30,6 @@ #include "machine/ldp1450.h" #include "machine/nvram.h" #include "machine/amigafdc.h" -#include "render.h" #include "speaker.h" diff --git a/src/mame/drivers/by17.cpp b/src/mame/drivers/by17.cpp index b8832806c8d..33074d0e79b 100644 --- a/src/mame/drivers/by17.cpp +++ b/src/mame/drivers/by17.cpp @@ -26,7 +26,6 @@ ToDo: #include "by17.lh" #include "by17_pwerplay.lh" #include "by17_matahari.lh" -#include "render.h" class by17_state : public genpin_class { @@ -980,10 +979,6 @@ void by17_state::machine_reset() { genpin_class::machine_reset(); - render_target *target = machine().render().first_target(); - - target->set_view(0); - m_u10a = 0; m_u10b = 0; m_u11a = 0; diff --git a/src/mame/drivers/by35.cpp b/src/mame/drivers/by35.cpp index e4f3aab38a6..70d15834ce1 100644 --- a/src/mame/drivers/by35.cpp +++ b/src/mame/drivers/by35.cpp @@ -73,7 +73,6 @@ ToDo: #include "machine/6821pia.h" #include "machine/timer.h" #include "audio/bally.h" -#include "render.h" #include "speaker.h" //#define VERBOSE 1 @@ -1441,10 +1440,6 @@ void by35_state::machine_reset() { genpin_class::machine_reset(); - render_target *target = machine().render().first_target(); - - target->set_view(0); - m_u10a = 0; m_u10b = 0; m_u11a = 0; diff --git a/src/mame/drivers/cliffhgr.cpp b/src/mame/drivers/cliffhgr.cpp index 3761c0548b7..339866ab13c 100644 --- a/src/mame/drivers/cliffhgr.cpp +++ b/src/mame/drivers/cliffhgr.cpp @@ -83,7 +83,6 @@ Side 2 = 0x8F7DDD (or 0x880000 | ( 0x77 << 12 ) | 0x0DDD) #include "video/tms9928a.h" #include "machine/nvram.h" -#include "render.h" #include "speaker.h" diff --git a/src/mame/drivers/decwritr.cpp b/src/mame/drivers/decwritr.cpp index b5b32b2ee71..84133c2e05c 100644 --- a/src/mame/drivers/decwritr.cpp +++ b/src/mame/drivers/decwritr.cpp @@ -19,7 +19,7 @@ #include "machine/i8251.h" #include "machine/input_merger.h" #include "sound/beep.h" -#include "render.h" +#include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/dlair.cpp b/src/mame/drivers/dlair.cpp index b914c58b35c..496ad516322 100644 --- a/src/mame/drivers/dlair.cpp +++ b/src/mame/drivers/dlair.cpp @@ -46,7 +46,6 @@ #include "sound/ay8910.h" #include "sound/spkrdev.h" #include "emupal.h" -#include "render.h" #include "speaker.h" #include "dlair.lh" diff --git a/src/mame/drivers/istellar.cpp b/src/mame/drivers/istellar.cpp index 687be6f0e79..1f546d3be55 100644 --- a/src/mame/drivers/istellar.cpp +++ b/src/mame/drivers/istellar.cpp @@ -26,7 +26,6 @@ Todo: #include "machine/gen_latch.h" #include "machine/ldv1000.h" #include "emupal.h" -#include "render.h" #include "speaker.h" diff --git a/src/mame/drivers/lgp.cpp b/src/mame/drivers/lgp.cpp index 67bc14514f8..9b76c5469de 100644 --- a/src/mame/drivers/lgp.cpp +++ b/src/mame/drivers/lgp.cpp @@ -69,7 +69,6 @@ Dumping Notes: #include "cpu/z80/z80.h" #include "machine/ldv1000.h" #include "emupal.h" -#include "render.h" #include "speaker.h" diff --git a/src/mame/layout/unkeinv.lay b/src/mame/layout/unkeinv.lay index 12b42eef798..b5fde8e2ac8 100644 --- a/src/mame/layout/unkeinv.lay +++ b/src/mame/layout/unkeinv.lay @@ -6,24 +6,6 @@ license:CC0 - - - - - - - - - - - - - - - - - - @@ -32,6 +14,11 @@ license:CC0 + + + + + @@ -41,21 +28,24 @@ license:CC0 - + - - + + + + + + + + + + + + + + + - - - - - - - - - - diff --git a/src/mame/video/midtunit.cpp b/src/mame/video/midtunit.cpp index 94594884e38..4a74af2b188 100644 --- a/src/mame/video/midtunit.cpp +++ b/src/mame/video/midtunit.cpp @@ -1018,7 +1018,7 @@ void midtunit_video_device::log_bitmap(int command, int bpp, bool Skip) } } - png_write_bitmap(file, nullptr, m_log_bitmap, 0, nullptr); + util::png_write_bitmap(file, nullptr, m_log_bitmap, 0, nullptr); if (m_log_json) { diff --git a/src/mame/video/ssv.cpp b/src/mame/video/ssv.cpp index 47f9f031654..c8629284845 100644 --- a/src/mame/video/ssv.cpp +++ b/src/mame/video/ssv.cpp @@ -135,7 +135,6 @@ #include "emu.h" #include "includes/ssv.h" -#include "render.h" void ssv_state::drawgfx_line(bitmap_ind16 &bitmap, const rectangle &cliprect, int gfx, uint32_t code, uint32_t color, int flipx, int flipy, int base_sx, int base_sy, int shadow, int realline, int line) diff --git a/src/mame/video/st0020.cpp b/src/mame/video/st0020.cpp index 6702174635a..f378dbbc302 100644 --- a/src/mame/video/st0020.cpp +++ b/src/mame/video/st0020.cpp @@ -16,7 +16,9 @@ #include "emu.h" #include "st0020.h" -#include "render.h" + +#include "screen.h" + DEFINE_DEVICE_TYPE(ST0020_SPRITES, st0020_device, "st0020", "Seta ST0020 Sprites") diff --git a/src/mame/video/tx1.cpp b/src/mame/video/tx1.cpp index 306da618218..08b74e80462 100644 --- a/src/mame/video/tx1.cpp +++ b/src/mame/video/tx1.cpp @@ -7,10 +7,10 @@ ****************************************************************************/ #include "emu.h" -#include "render.h" +#include "includes/tx1.h" + #include "video/resnet.h" #include "cpu/i86/i86.h" -#include "includes/tx1.h" #define OBJ_FRAC 16 diff --git a/src/osd/modules/render/bgfx/texturemanager.cpp b/src/osd/modules/render/bgfx/texturemanager.cpp index a03a6b46125..d539d1aae46 100644 --- a/src/osd/modules/render/bgfx/texturemanager.cpp +++ b/src/osd/modules/render/bgfx/texturemanager.cpp @@ -59,7 +59,11 @@ bgfx_texture* texture_manager::create_png_texture(std::string path, std::string { bitmap_argb32 bitmap; emu_file file(path.c_str(), OPEN_FLAG_READ); - render_load_png(bitmap, file, nullptr, file_name.c_str()); + if (file.open(file_name) == osd_file::error::NONE) + { + render_load_png(bitmap, file); + file.close(); + } if (bitmap.width() == 0 || bitmap.height() == 0) { diff --git a/src/osd/modules/render/d3d/d3dhlsl.cpp b/src/osd/modules/render/d3d/d3dhlsl.cpp index e19f8882232..57ab4943adb 100644 --- a/src/osd/modules/render/d3d/d3dhlsl.cpp +++ b/src/osd/modules/render/d3d/d3dhlsl.cpp @@ -359,14 +359,14 @@ void shaders::render_snapshot(IDirect3DSurface9 *surface) // add two text entries describing the image std::string text1 = std::string(emulator_info::get_appname()).append(" ").append(emulator_info::get_build_version()); std::string text2 = std::string(machine->system().manufacturer).append(" ").append(machine->system().type.fullname()); - png_info pnginfo; + util::png_info pnginfo; pnginfo.add_text("Software", text1.c_str()); pnginfo.add_text("System", text2.c_str()); // now do the actual work - png_error error = png_write_bitmap(file, &pnginfo, snapshot, 1 << 24, nullptr); - if (error != PNGERR_NONE) - osd_printf_error("Error generating PNG for HLSL snapshot: png_error = %d\n", error); + util::png_error error = util::png_write_bitmap(file, &pnginfo, snapshot, 1 << 24, nullptr); + if (error != util::png_error::NONE) + osd_printf_error("Error generating PNG for HLSL snapshot: png_error = %d\n", std::underlying_type_t(error)); result = snap_copy_target->UnlockRect(); if (FAILED(result)) @@ -721,7 +721,11 @@ int shaders::create_resources() osd_printf_verbose("Direct3D: Error %08lX during device SetRenderTarget call\n", result); emu_file file(machine->options().art_path(), OPEN_FLAG_READ); - render_load_png(shadow_bitmap, file, nullptr, options->shadow_mask_texture); + if (file.open(options->shadow_mask_texture) == osd_file::error::NONE) + { + render_load_png(shadow_bitmap, file); + file.close(); + } // experimental: if we have a shadow bitmap, create a texture for it if (shadow_bitmap.valid()) @@ -742,7 +746,11 @@ int shaders::create_resources() d3d->get_texture_manager()->m_texture_list.push_back(std::move(tex)); } - render_load_png(lut_bitmap, file, nullptr, options->lut_texture); + if (file.open(options->lut_texture) == osd_file::error::NONE) + { + render_load_png(lut_bitmap, file); + file.close(); + } if (lut_bitmap.valid()) { render_texinfo texture; @@ -761,7 +769,11 @@ int shaders::create_resources() d3d->get_texture_manager()->m_texture_list.push_back(std::move(tex)); } - render_load_png(ui_lut_bitmap, file, nullptr, options->ui_lut_texture); + if (file.open(options->ui_lut_texture) == osd_file::error::NONE) + { + render_load_png(ui_lut_bitmap, file); + file.close(); + } if (ui_lut_bitmap.valid()) { render_texinfo texture; diff --git a/src/tools/pngcmp.cpp b/src/tools/pngcmp.cpp index e490d1c6811..714700a3824 100644 --- a/src/tools/pngcmp.cpp +++ b/src/tools/pngcmp.cpp @@ -74,7 +74,7 @@ static int generate_png_diff(const std::string& imgfile1, const std::string& img int width, height, maxwidth; util::core_file::ptr file; osd_file::error filerr; - png_error pngerr; + util::png_error pngerr; int error = 100; /* open the source image */ @@ -86,11 +86,11 @@ static int generate_png_diff(const std::string& imgfile1, const std::string& img } /* load the source image */ - pngerr = png_read_bitmap(*file, bitmap1); + pngerr = util::png_read_bitmap(*file, bitmap1); file.reset(); - if (pngerr != PNGERR_NONE) + if (pngerr != util::png_error::NONE) { - printf("Could not read %s (%d)\n", imgfile1.c_str(), pngerr); + printf("Could not read %s (%d)\n", imgfile1.c_str(), int(pngerr)); goto error; } @@ -103,11 +103,11 @@ static int generate_png_diff(const std::string& imgfile1, const std::string& img } /* load the source image */ - pngerr = png_read_bitmap(*file, bitmap2); + pngerr = util::png_read_bitmap(*file, bitmap2); file.reset(); - if (pngerr != PNGERR_NONE) + if (pngerr != util::png_error::NONE) { - printf("Could not read %s (%d)\n", imgfile2.c_str(), pngerr); + printf("Could not read %s (%d)\n", imgfile2.c_str(), int(pngerr)); goto error; } @@ -176,11 +176,11 @@ static int generate_png_diff(const std::string& imgfile1, const std::string& img printf("Could not open %s (%d)\n", outfilename.c_str(), int(filerr)); goto error; } - pngerr = png_write_bitmap(*file, nullptr, finalbitmap, 0, nullptr); + pngerr = util::png_write_bitmap(*file, nullptr, finalbitmap, 0, nullptr); file.reset(); - if (pngerr != PNGERR_NONE) + if (pngerr != util::png_error::NONE) { - printf("Could not write %s (%d)\n", outfilename.c_str(), pngerr); + printf("Could not write %s (%d)\n", outfilename.c_str(), int(pngerr)); goto error; } } diff --git a/src/tools/regrep.cpp b/src/tools/regrep.cpp index 5bebc733197..49637c2eb16 100644 --- a/src/tools/regrep.cpp +++ b/src/tools/regrep.cpp @@ -748,7 +748,7 @@ static int compare_screenshots(summary_file *curfile) /* if that worked, load the file */ if (filerr == osd_file::error::NONE) { - png_read_bitmap(*file, bitmaps[listnum]); + util::png_read_bitmap(*file, bitmaps[listnum]); file.reset(); } } @@ -835,7 +835,7 @@ static int generate_png_diff(const summary_file *curfile, std::string &destdir, int bitmapcount = 0; util::core_file::ptr file; osd_file::error filerr; - png_error pngerr; + util::png_error pngerr; int error = -1; int starty; @@ -855,9 +855,9 @@ static int generate_png_diff(const summary_file *curfile, std::string &destdir, goto error; /* load the source image */ - pngerr = png_read_bitmap(*file, bitmaps[bitmapcount++]); + pngerr = util::png_read_bitmap(*file, bitmaps[bitmapcount++]); file.reset(); - if (pngerr != PNGERR_NONE) + if (pngerr != util::png_error::NONE) goto error; } @@ -924,9 +924,9 @@ static int generate_png_diff(const summary_file *curfile, std::string &destdir, filerr = util::core_file::open(dstfilename, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file); if (filerr != osd_file::error::NONE) goto error; - pngerr = png_write_bitmap(*file, nullptr, finalbitmap, 0, nullptr); + pngerr = util::png_write_bitmap(*file, nullptr, finalbitmap, 0, nullptr); file.reset(); - if (pngerr != PNGERR_NONE) + if (pngerr != util::png_error::NONE) goto error; /* if we get here, we are error free */ -- cgit v1.2.3 From 3f0bf278b3bd174e6aedabcb09b50b152bf1089c Mon Sep 17 00:00:00 2001 From: Aaron Giles Date: Wed, 7 Oct 2020 08:39:24 -0700 Subject: Fix MSVC build --- src/emu/rendutil.cpp | 4 +- src/mame/audio/tranqgun.cpp | 150 -------------------------------------------- 2 files changed, 2 insertions(+), 152 deletions(-) delete mode 100644 src/mame/audio/tranqgun.cpp diff --git a/src/emu/rendutil.cpp b/src/emu/rendutil.cpp index c66492a8895..64887d111bd 100644 --- a/src/emu/rendutil.cpp +++ b/src/emu/rendutil.cpp @@ -726,7 +726,7 @@ void render_load_jpeg(bitmap_argb32 &bitmap, util::core_file &file) // finish decompression and frees the memory jpeg_finish_decompress(&cinfo); } - catch (jpeg_error_mgr *err) + catch (jpeg_error_mgr *) { char msg[1024]; (cinfo.err->format_message)(reinterpret_cast(&cinfo), msg); @@ -937,7 +937,7 @@ ru_imgformat render_detect_image(util::core_file &file) file.seek(0, SEEK_SET); return RENDUTIL_IMGFORMAT_JPEG; } - catch (jpeg_error_mgr *err) + catch (jpeg_error_mgr *) { jpeg_destroy_decompress(&cinfo); file.seek(0, SEEK_SET); diff --git a/src/mame/audio/tranqgun.cpp b/src/mame/audio/tranqgun.cpp deleted file mode 100644 index afa28fe3591..00000000000 --- a/src/mame/audio/tranqgun.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Zsolt Vasvari, Jim Hernandez -/* - * Tranquillizer Gun audio routines - */ - -#include "emu.h" -#include "includes/vicdual.h" - -/* output port 0x01 definitions - sound effect drive outputs */ -#define OUT_PORT_1_ANIMAL 0x01 -#define OUT_PORT_1_CRY 0x02 -#define OUT_PORT_1_WALK 0x04 -#define OUT_PORT_1_EMAR 0x08 -#define OUT_PORT_1_ANIMALHIT 0x10 -#define OUT_PORT_1_POINT 0x20 -#define OUT_PORT_1_JEEP 0x40 -#define OUT_PORT_1_GUN 0x80 - - -#define PLAY(samp,id,loop) samp->start( id, id, loop ) -#define STOP(samp,id) samp->stop( id ) - - -/* sample file names */ -static const char *const tranqgun_sample_names[] = -{ - "*tranqgun", - "cry", - "walk", - "emar", - "animalhit", - "point", - "jeep", - "gun", - "animal", - nullptr -}; - - -/* sample IDs - must match sample file name table above */ -enum -{ - SND_CRY = 0, - SND_WALK, - SND_EMAR, - SND_ANIMALHIT, - SND_POINT, - SND_JEEP, - SND_GUN, - SND_ANIMAL, -}; - - -void vicdual_state::tranqgun_audio_w(uint8_t data) -{ - int bitsChanged; - int bitsGoneHigh; - int bitsGoneLow; - - bitsChanged = m_port1State ^ data; - bitsGoneHigh = bitsChanged & data; - bitsGoneLow = bitsChanged & ~data; - - m_port1State = data; - - if ( bitsGoneHigh & OUT_PORT_1_ANIMAL ) - { - PLAY( m_samples, SND_ANIMAL, 0 ); - } - if ( bitsGoneLow & OUT_PORT_1_ANIMAL ) - { - STOP( m_samples, SND_ANIMAL ); - } - - if ( bitsGoneHigh & OUT_PORT_1_CRY ) - { - PLAY( m_samples, SND_CRY, 0 ); - } - if ( bitsGoneLow & OUT_PORT_1_CRY ) - { - STOP( m_samples, SND_CRY ); - } - - if ( bitsGoneHigh & OUT_PORT_1_WALK ) - { - PLAY( m_samples, SND_WALK, 0 ); - } - if ( bitsGoneLow & OUT_PORT_1_WALK ) - { - STOP( m_samples, SND_WALK ); - } - - if ( bitsGoneLow & OUT_PORT_1_ANIMAL ) - { - PLAY( m_samples, SND_ANIMAL,0 ); - } - - if ( bitsGoneHigh & OUT_PORT_1_ANIMALHIT ) - { - PLAY( m_samples, SND_ANIMALHIT, 0 ); - } - if ( bitsGoneLow & OUT_PORT_1_ANIMALHIT ) - { - STOP( m_samples, SND_ANIMALHIT ); - } - - if ( bitsGoneHigh & OUT_PORT_1_POINT ) - { - PLAY( m_samples, SND_POINT, 0 ); - } - if ( bitsGoneLow & OUT_PORT_1_POINT ) - { - STOP( m_samples, SND_POINT ); - } - - if ( bitsGoneLow & OUT_PORT_1_JEEP ) - { - PLAY( m_samples, SND_JEEP, 1 ); - - } - if ( bitsGoneHigh & OUT_PORT_1_JEEP ) - { - STOP( m_samples, SND_JEEP ); - } - - if ( bitsGoneLow & OUT_PORT_1_EMAR ) - { - PLAY( m_samples, SND_EMAR, 0 ); - } - - if ( bitsGoneHigh & OUT_PORT_1_GUN ) - { - PLAY( m_samples, SND_GUN, 0 ); - } - if ( bitsGoneLow & OUT_PORT_1_GUN ) - { - STOP( m_samples, SND_GUN ); - } -} - - -void vicdual_state::tranqgun_audio(machine_config &config) -{ - /* samples */ - SAMPLES(config, m_samples); - m_samples->set_channels(8); - m_samples->set_samples_names(tranqgun_sample_names); - m_samples->add_route(ALL_OUTPUTS, "mono", 0.5); -} -- cgit v1.2.3 From 51be063c4bfb763ae1e631f89667cd255a9188f3 Mon Sep 17 00:00:00 2001 From: Aaron Giles Date: Wed, 7 Oct 2020 08:40:17 -0700 Subject: vicdual: Tranquilizer Gun uses the same sound board as Borderline --- scripts/target/mame/arcade.lua | 2 -- scripts/target/mame/nl.lua | 2 -- src/devices/machine/netlist.h | 2 +- src/mame/drivers/vicdual.cpp | 12 ++++++------ src/mame/includes/vicdual.h | 4 ---- src/mame/nl.lst | 1 + 6 files changed, 8 insertions(+), 15 deletions(-) diff --git a/scripts/target/mame/arcade.lua b/scripts/target/mame/arcade.lua index abe23d59dd0..9ba7c1dd7fb 100644 --- a/scripts/target/mame/arcade.lua +++ b/scripts/target/mame/arcade.lua @@ -3625,8 +3625,6 @@ files { MAME_DIR .. "src/mame/audio/invinco.h", MAME_DIR .. "src/mame/audio/pulsar.cpp", MAME_DIR .. "src/mame/audio/pulsar.h", - MAME_DIR .. "src/mame/audio/tranqgun.cpp", - MAME_DIR .. "src/mame/audio/tranqgun.h", MAME_DIR .. "src/mame/drivers/zaxxon.cpp", MAME_DIR .. "src/mame/includes/zaxxon.h", MAME_DIR .. "src/mame/audio/zaxxon.cpp", diff --git a/scripts/target/mame/nl.lua b/scripts/target/mame/nl.lua index f5c8e88d1e5..15306b6d881 100644 --- a/scripts/target/mame/nl.lua +++ b/scripts/target/mame/nl.lua @@ -309,8 +309,6 @@ files{ MAME_DIR .. "src/mame/audio/invinco.h", MAME_DIR .. "src/mame/audio/pulsar.cpp", MAME_DIR .. "src/mame/audio/pulsar.h", - MAME_DIR .. "src/mame/audio/tranqgun.cpp", - MAME_DIR .. "src/mame/audio/tranqgun.h", MAME_DIR .. "src/mame/machine/segacrpt_device.cpp", MAME_DIR .. "src/mame/machine/segacrpt_device.h", diff --git a/src/devices/machine/netlist.h b/src/devices/machine/netlist.h index 8b9e156adba..0a8dd00618d 100644 --- a/src/devices/machine/netlist.h +++ b/src/devices/machine/netlist.h @@ -17,7 +17,7 @@ #include "../../lib/netlist/nltypes.h" #ifndef NETLIST_CREATE_CSV -#define NETLIST_CREATE_CSV (0) +#define NETLIST_CREATE_CSV (1) #endif class netlist_mame_stream_output_device; diff --git a/src/mame/drivers/vicdual.cpp b/src/mame/drivers/vicdual.cpp index be4c5516c87..e768f20dc2c 100644 --- a/src/mame/drivers/vicdual.cpp +++ b/src/mame/drivers/vicdual.cpp @@ -35,7 +35,7 @@ switch between them. * There existed a vertical version of Head On as well. * According to the manuals, Borderline has the same sound - board as Tranquillizer Gun. + board as Tranquilizer Gun. Known issues/to-do's: * Analog sound missing in many games @@ -1093,7 +1093,7 @@ void vicdual_state::digger(machine_config &config) * Invinco / Head On 2 * Invinco / Deap Scan * Space Attack / Head On - * Tranquillizer Gun + * Tranquilizer Gun * Space Trek * Carnival * Borderline @@ -1140,7 +1140,7 @@ void vicdual_state::sspacaho_io_w(offs_t offset, uint8_t data) void vicdual_state::tranqgun_io_w(offs_t offset, uint8_t data) { - if (offset & 0x01) tranqgun_audio_w(data); + if (offset & 0x01) m_vicdual_sound->write(data); if (offset & 0x02) palette_bank_w(data); if (offset & 0x08) assert_coin_status(); } @@ -2260,7 +2260,7 @@ void vicdual_state::tranqgun(machine_config &config) /* audio hardware */ SPEAKER(config, "mono").front_center(); - tranqgun_audio(config); + BORDERLINE_AUDIO(config, m_vicdual_sound, 0).add_route(ALL_OUTPUTS, "mono", 1.0); } @@ -4017,7 +4017,7 @@ GAME( 1980, samurai, 0, samurai, samurai, vicdual_state, empty_in GAME( 1979, invinco, 0, invinco, invinco, vicdual_state, empty_init, ROT270, "Sega", "Invinco", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) GAME( 1979, invds, 0, invds, invds, vicdual_state, empty_init, ROT270, "Sega", "Invinco / Deep Scan", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) GAME( 1979, carhntds, 0, carhntds, carhntds, vicdual_state, empty_init, ROT270, "Sega", "Car Hunt / Deep Scan (France)", MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE ) -GAME( 1980, tranqgun, 0, tranqgun, tranqgun, vicdual_state, empty_init, ROT270, "Sega", "Tranquillizer Gun", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) +GAME( 1980, tranqgun, 0, tranqgun, tranqgun, vicdual_state, empty_init, ROT270, "Sega", "Tranquilizer Gun", MACHINE_SUPPORTS_SAVE ) GAME( 1980, spacetrk, 0, spacetrk, spacetrk, vicdual_state, empty_init, ROT270, "Sega", "Space Trek (upright)", MACHINE_IMPERFECT_GRAPHICS | MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE ) GAME( 1980, spacetrkc, spacetrk, spacetrk, spacetrkc, vicdual_state, empty_init, ROT270, "Sega", "Space Trek (cocktail)", MACHINE_IMPERFECT_GRAPHICS |MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE ) GAME( 1980, carnival, 0, carnival, carnival, carnival_state, empty_init, ROT270, "Sega", "Carnival (upright, AY8912 music)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) @@ -4030,7 +4030,7 @@ GAME( 1981, brdrline, 0, brdrline, brdrline, vicdual_state, empty_in GAME( 1981, starrkr, brdrline, brdrline, starrkr, vicdual_state, empty_init, ROT270, "Sega", "Star Raker", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) GAME( 1981, brdrlins, brdrline, brdrline, brdrline, vicdual_state, empty_init, ROT270, "bootleg (Sidam)", "Borderline (Sidam bootleg)", MACHINE_SUPPORTS_SAVE ) GAME( 1981, brdrlinb, brdrline, brdrline, brdrline, vicdual_state, empty_init, ROT270, "bootleg (Karateco)", "Borderline (Karateco bootleg)", MACHINE_SUPPORTS_SAVE ) -GAME( 1981, brdrlinet, brdrline, tranqgun, tranqgun, vicdual_state, empty_init, ROT270, "Sega", "Borderline (Tranquillizer Gun conversion)", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) // official factory conversion +GAME( 1981, brdrlinet, brdrline, tranqgun, tranqgun, vicdual_state, empty_init, ROT270, "Sega", "Borderline (Tranquilizer Gun conversion)", MACHINE_NOT_WORKING | MACHINE_SUPPORTS_SAVE ) // official factory conversion GAME( 198?, startrks, 0, headons, headons, vicdual_state, empty_init, ROT0, "bootleg (Sidam)", "Star Trek (Head On hardware)", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) GAME( 1980, digger, 0, digger, digger, vicdual_state, empty_init, ROT270, "Sega", "Digger", MACHINE_NO_SOUND | MACHINE_SUPPORTS_SAVE ) GAME( 1981, pulsar, 0, pulsar, pulsar, vicdual_state, empty_init, ROT270, "Sega", "Pulsar", MACHINE_IMPERFECT_SOUND | MACHINE_SUPPORTS_SAVE ) diff --git a/src/mame/includes/vicdual.h b/src/mame/includes/vicdual.h index 94edc7e6884..d26932a55e9 100644 --- a/src/mame/includes/vicdual.h +++ b/src/mame/includes/vicdual.h @@ -70,7 +70,6 @@ public: void carhntds(machine_config &config); void alphaho(machine_config &config); void tranqgun(machine_config &config); - void tranqgun_audio(machine_config &config); DECLARE_READ_LINE_MEMBER(coin_status_r); DECLARE_READ_LINE_MEMBER(get_64v); @@ -160,9 +159,6 @@ protected: void pulsar_audio_1_w(uint8_t data); void pulsar_audio_2_w(uint8_t data); - /*----------- defined in audio/tranqgun.cpp -----------*/ - void tranqgun_audio_w(uint8_t data); - TIMER_DEVICE_CALLBACK_MEMBER(clear_coin_status); DECLARE_MACHINE_START(samurai); diff --git a/src/mame/nl.lst b/src/mame/nl.lst index 0071872f41d..206b9a6d694 100644 --- a/src/mame/nl.lst +++ b/src/mame/nl.lst @@ -370,6 +370,7 @@ brdrline // (c) 1980 Sega brdrlinet // (c) 1981 Sega brdrlins // Sidam bootleg starrkr // (c) 1981 Sega +tranqgun // 413-428 (c) 1980 Sega frogs // 112-119 [1978 Gremlin?] @source:zaxxon.cpp -- cgit v1.2.3