From 0ee0c559b3ea1afa8db9fb95af5f07a328a899f3 Mon Sep 17 00:00:00 2001 From: CharlesThobe Date: Tue, 4 Jan 2022 05:27:00 +0200 Subject: [PATCH] Sync source to mame0238 --- src/emu/addrmap.cpp | 6 +- src/emu/addrmap.h | 6 +- src/emu/emumem.cpp | 106 +- src/emu/emumem.h | 867 +- src/emu/emumem_aspace.cpp | 487 +- src/emu/emumem_hedp.cpp | 22 - src/emu/emumem_hedp.h | 14 +- src/emu/emumem_hedr.h | 1 - src/emu/emumem_hedr.ipp | 5 - src/emu/emumem_hedw.h | 1 - src/emu/emumem_hedw.ipp | 5 - src/emu/emumem_hem.cpp | 36 - src/emu/emumem_hem.h | 12 +- src/emu/emumem_het.cpp | 22 - src/emu/emumem_het.h | 2 - src/emu/emumem_heu.cpp | 62 - src/emu/emumem_heu.h | 2 - src/emu/emumem_heun.cpp | 35 - src/emu/emumem_heun.h | 12 +- src/emu/emumem_mview.cpp | 370 +- src/emu/inpttype.ipp | 816 +- src/emu/ioport.cpp | 98 +- src/emu/ioport.h | 8 +- src/emu/machine.cpp | 70 +- src/emu/mconfig.cpp | 21 +- src/emu/render.cpp | 135 +- src/emu/render.h | 23 +- src/emu/rendersw.hxx | 250 +- src/emu/rendlay.cpp | 58 +- src/emu/rendlay.h | 1 + src/emu/romload.cpp | 2 +- src/emu/screen.cpp | 10 +- src/emu/validity.cpp | 22 +- src/emu/xtal.cpp | 2 - src/frontend/mame/cheat.cpp | 7 - src/frontend/mame/infoxml.cpp | 14 +- src/frontend/mame/luaengine_debug.cpp | 2 +- src/frontend/mame/ui/auditmenu.cpp | 6 +- src/frontend/mame/ui/custui.cpp | 44 +- src/frontend/mame/ui/custui.h | 10 +- src/frontend/mame/ui/devopt.cpp | 14 +- src/frontend/mame/ui/filesel.h | 5 +- src/frontend/mame/ui/info.cpp | 14 +- src/frontend/mame/ui/inputmap.cpp | 42 +- src/frontend/mame/ui/inputmap.h | 2 +- src/frontend/mame/ui/mainmenu.cpp | 6 +- src/frontend/mame/ui/menu.cpp | 2 - src/frontend/mame/ui/selmenu.cpp | 10 +- src/frontend/mame/ui/sndmenu.cpp | 18 +- src/frontend/mame/ui/sndmenu.h | 2 +- src/frontend/mame/ui/submenu.cpp | 12 +- src/frontend/mame/ui/textbox.cpp | 1 - src/frontend/mame/ui/ui.cpp | 2 +- src/lib/formats/cbm_tap.cpp | 2 +- src/lib/formats/dfi_dsk.cpp | 2 +- src/lib/formats/flopimg.h | 8 +- src/lib/formats/sorc_dsk.cpp | 8 +- src/lib/formats/tzx_cas.cpp | 2 +- src/lib/formats/wavfile.cpp | 33 +- src/osd/modules/opengl/gl_shader_mgr.cpp | 14 +- src/osd/modules/sound/sdl_sound.cpp | 5 +- src/tools/aueffectutil-Info.plist | 45 + src/tools/aueffectutil.mm | 1076 +++ src/tools/castool.cpp | 227 + src/tools/chdman.cpp | 2 +- src/tools/discrepancy-fixer.py | 82 + src/tools/discrepancy-spotter.py | 51 + src/tools/floptool.cpp | 711 ++ src/tools/image_handler.cpp | 409 + src/tools/image_handler.h | 119 + src/tools/imgtool/charconv.cpp | 107 + src/tools/imgtool/charconv.h | 82 + src/tools/imgtool/filtbas.cpp | 3123 +++++++ src/tools/imgtool/filteoln.cpp | 108 + src/tools/imgtool/filter.cpp | 83 + src/tools/imgtool/filter.h | 63 + src/tools/imgtool/formats/coco_dsk.cpp | 1187 +++ src/tools/imgtool/formats/coco_dsk.h | 24 + src/tools/imgtool/formats/pc_dsk_legacy.cpp | 140 + src/tools/imgtool/formats/pc_dsk_legacy.h | 21 + src/tools/imgtool/formats/vt_dsk_legacy.cpp | 58 + src/tools/imgtool/formats/vt_dsk_legacy.h | 18 + src/tools/imgtool/iflopimg.cpp | 324 + src/tools/imgtool/iflopimg.h | 44 + src/tools/imgtool/imghd.cpp | 250 + src/tools/imgtool/imghd.h | 50 + src/tools/imgtool/imgterrs.cpp | 56 + src/tools/imgtool/imgterrs.h | 70 + src/tools/imgtool/imgtool.cpp | 2375 ++++++ src/tools/imgtool/imgtool.h | 265 + src/tools/imgtool/library.cpp | 314 + src/tools/imgtool/library.h | 551 ++ src/tools/imgtool/main.cpp | 966 +++ src/tools/imgtool/main.h | 20 + src/tools/imgtool/modules.cpp | 104 + src/tools/imgtool/modules.h | 18 + src/tools/imgtool/modules/amiga.cpp | 2420 ++++++ src/tools/imgtool/modules/bml3.cpp | 932 +++ src/tools/imgtool/modules/concept.cpp | 499 ++ src/tools/imgtool/modules/cybiko.cpp | 583 ++ src/tools/imgtool/modules/cybikoxt.cpp | 529 ++ src/tools/imgtool/modules/dgndos.cpp | 1256 +++ src/tools/imgtool/modules/fat.cpp | 2111 +++++ src/tools/imgtool/modules/fat.h | 6 + src/tools/imgtool/modules/hp48.cpp | 741 ++ src/tools/imgtool/modules/hp85_tape.cpp | 1164 +++ src/tools/imgtool/modules/hp9845_tape.cpp | 1540 ++++ src/tools/imgtool/modules/mac.cpp | 6443 +++++++++++++++ src/tools/imgtool/modules/macbin.cpp | 384 + src/tools/imgtool/modules/macutil.cpp | 118 + src/tools/imgtool/modules/macutil.h | 43 + src/tools/imgtool/modules/os9.cpp | 1241 +++ src/tools/imgtool/modules/pc_flop.cpp | 176 + src/tools/imgtool/modules/pc_hard.cpp | 470 ++ src/tools/imgtool/modules/prodos.cpp | 2270 +++++ src/tools/imgtool/modules/psion.cpp | 698 ++ src/tools/imgtool/modules/rsdos.cpp | 666 ++ src/tools/imgtool/modules/rt11.cpp | 690 ++ src/tools/imgtool/modules/thomson.cpp | 2073 +++++ src/tools/imgtool/modules/ti99.cpp | 5368 ++++++++++++ src/tools/imgtool/modules/ti990hd.cpp | 1826 ++++ src/tools/imgtool/modules/vzdos.cpp | 974 +++ src/tools/imgtool/stream.cpp | 694 ++ src/tools/imgtool/stream.h | 95 + src/tools/jedutil.cpp | 8261 +++++++++++++++++++ src/tools/ldresample.cpp | 575 ++ src/tools/ldverify.cpp | 795 ++ src/tools/pngcmp.cpp | 200 + src/tools/regrep.cpp | 1113 +++ src/tools/romcmp.cpp | 778 ++ src/tools/runtest.cmd | 190 + src/tools/split.cpp | 428 + src/tools/srcclean.cpp | 2373 ++++++ src/tools/testkeys.cpp | 346 + src/tools/unidasm.cpp | 1625 ++++ 135 files changed, 67512 insertions(+), 2201 deletions(-) create mode 100644 src/tools/aueffectutil-Info.plist create mode 100644 src/tools/aueffectutil.mm create mode 100644 src/tools/castool.cpp create mode 100755 src/tools/discrepancy-fixer.py create mode 100755 src/tools/discrepancy-spotter.py create mode 100644 src/tools/floptool.cpp create mode 100644 src/tools/image_handler.cpp create mode 100644 src/tools/image_handler.h create mode 100644 src/tools/imgtool/charconv.cpp create mode 100644 src/tools/imgtool/charconv.h create mode 100644 src/tools/imgtool/filtbas.cpp create mode 100644 src/tools/imgtool/filteoln.cpp create mode 100644 src/tools/imgtool/filter.cpp create mode 100644 src/tools/imgtool/filter.h create mode 100644 src/tools/imgtool/formats/coco_dsk.cpp create mode 100644 src/tools/imgtool/formats/coco_dsk.h create mode 100644 src/tools/imgtool/formats/pc_dsk_legacy.cpp create mode 100644 src/tools/imgtool/formats/pc_dsk_legacy.h create mode 100644 src/tools/imgtool/formats/vt_dsk_legacy.cpp create mode 100644 src/tools/imgtool/formats/vt_dsk_legacy.h create mode 100644 src/tools/imgtool/iflopimg.cpp create mode 100644 src/tools/imgtool/iflopimg.h create mode 100644 src/tools/imgtool/imghd.cpp create mode 100644 src/tools/imgtool/imghd.h create mode 100644 src/tools/imgtool/imgterrs.cpp create mode 100644 src/tools/imgtool/imgterrs.h create mode 100644 src/tools/imgtool/imgtool.cpp create mode 100644 src/tools/imgtool/imgtool.h create mode 100644 src/tools/imgtool/library.cpp create mode 100644 src/tools/imgtool/library.h create mode 100644 src/tools/imgtool/main.cpp create mode 100644 src/tools/imgtool/main.h create mode 100644 src/tools/imgtool/modules.cpp create mode 100644 src/tools/imgtool/modules.h create mode 100644 src/tools/imgtool/modules/amiga.cpp create mode 100644 src/tools/imgtool/modules/bml3.cpp create mode 100644 src/tools/imgtool/modules/concept.cpp create mode 100644 src/tools/imgtool/modules/cybiko.cpp create mode 100644 src/tools/imgtool/modules/cybikoxt.cpp create mode 100644 src/tools/imgtool/modules/dgndos.cpp create mode 100644 src/tools/imgtool/modules/fat.cpp create mode 100644 src/tools/imgtool/modules/fat.h create mode 100644 src/tools/imgtool/modules/hp48.cpp create mode 100644 src/tools/imgtool/modules/hp85_tape.cpp create mode 100644 src/tools/imgtool/modules/hp9845_tape.cpp create mode 100644 src/tools/imgtool/modules/mac.cpp create mode 100644 src/tools/imgtool/modules/macbin.cpp create mode 100644 src/tools/imgtool/modules/macutil.cpp create mode 100644 src/tools/imgtool/modules/macutil.h create mode 100644 src/tools/imgtool/modules/os9.cpp create mode 100644 src/tools/imgtool/modules/pc_flop.cpp create mode 100644 src/tools/imgtool/modules/pc_hard.cpp create mode 100644 src/tools/imgtool/modules/prodos.cpp create mode 100644 src/tools/imgtool/modules/psion.cpp create mode 100644 src/tools/imgtool/modules/rsdos.cpp create mode 100644 src/tools/imgtool/modules/rt11.cpp create mode 100644 src/tools/imgtool/modules/thomson.cpp create mode 100644 src/tools/imgtool/modules/ti99.cpp create mode 100644 src/tools/imgtool/modules/ti990hd.cpp create mode 100644 src/tools/imgtool/modules/vzdos.cpp create mode 100644 src/tools/imgtool/stream.cpp create mode 100644 src/tools/imgtool/stream.h create mode 100644 src/tools/jedutil.cpp create mode 100644 src/tools/ldresample.cpp create mode 100644 src/tools/ldverify.cpp create mode 100644 src/tools/pngcmp.cpp create mode 100644 src/tools/regrep.cpp create mode 100644 src/tools/romcmp.cpp create mode 100644 src/tools/runtest.cmd create mode 100644 src/tools/split.cpp create mode 100644 src/tools/srcclean.cpp create mode 100644 src/tools/testkeys.cpp create mode 100644 src/tools/unidasm.cpp diff --git a/src/emu/addrmap.cpp b/src/emu/addrmap.cpp index 8a6294e..0b55df7 100644 --- a/src/emu/addrmap.cpp +++ b/src/emu/addrmap.cpp @@ -38,7 +38,6 @@ address_map_entry::address_map_entry(device_t &device, address_map &map, offs_t , m_addrselect(0) , m_mask(0) , m_cswidth(0) - , m_flags(0) , m_share(nullptr) , m_region(nullptr) , m_rgnoffs(0) @@ -816,7 +815,7 @@ address_map::address_map(device_t &device, address_map_entry *entry) // address_map - constructor dynamic device mapping case //---------------------------------------------------------- -address_map::address_map(const address_space &space, offs_t start, offs_t end, u64 unitmask, int cswidth, u16 flags, device_t &device, address_map_constructor submap_delegate) +address_map::address_map(const address_space &space, offs_t start, offs_t end, u64 unitmask, int cswidth, device_t &device, address_map_constructor submap_delegate) : m_spacenum(space.spacenum()), m_device(&device), m_view(nullptr), @@ -824,7 +823,7 @@ address_map::address_map(const address_space &space, offs_t start, offs_t end, u m_unmapval(space.unmap()), m_globalmask(space.addrmask()) { - (*this)(start, end).m(DEVICE_SELF, submap_delegate).umask64(unitmask).cswidth(cswidth).flags(flags); + (*this)(start, end).m(DEVICE_SELF, submap_delegate).umask64(unitmask).cswidth(cswidth); } @@ -996,7 +995,6 @@ void address_map::import_submaps(running_machine &machine, device_t &owner, int subentry->m_mask = entry->m_mask; subentry->m_cswidth = std::max(subentry->m_cswidth, entry->m_cswidth); - subentry->m_flags = subentry->m_flags | entry->m_flags; if (subentry->m_addrend > max_end) subentry->m_addrend = max_end; diff --git a/src/emu/addrmap.h b/src/emu/addrmap.h index 69e4a9a..76cdc6a 100644 --- a/src/emu/addrmap.h +++ b/src/emu/addrmap.h @@ -156,9 +156,6 @@ public: // chip select width setting address_map_entry &cswidth(int _cswidth) { m_cswidth = _cswidth; return *this; } - // flags setting - address_map_entry &flags(u16 _flags) { m_flags = _flags; return *this; } - // I/O port configuration address_map_entry &portr(const char *tag) { m_read.m_type = AMH_PORT; m_read.m_tag = tag; return *this; } address_map_entry &portw(const char *tag) { m_write.m_type = AMH_PORT; m_write.m_tag = tag; return *this; } @@ -368,7 +365,6 @@ public: offs_t m_addrselect; // select bits u64 m_mask; // mask for which lanes apply int m_cswidth; // chip select width override - u16 m_flags; // user flags map_handler_data m_read; // data for read handler map_handler_data m_write; // data for write handler const char * m_share; // tag of a shared memory block @@ -508,7 +504,7 @@ public: address_map(device_t &device, int spacenum); address_map(memory_view &view); address_map(device_t &device, address_map_entry *entry); - address_map(const address_space &space, offs_t start, offs_t end, u64 unitmask, int cswidth, u16 flags, device_t &device, address_map_constructor submap_delegate); + address_map(const address_space &space, offs_t start, offs_t end, u64 unitmask, int cswidth, device_t &device, address_map_constructor submap_delegate); ~address_map(); // setters diff --git a/src/emu/emumem.cpp b/src/emu/emumem.cpp index 3982deb..76e71e5 100644 --- a/src/emu/emumem.cpp +++ b/src/emu/emumem.cpp @@ -710,33 +710,33 @@ void address_space_installer::populate_map_entry(const address_map_entry &entry, // fall through to the RAM case otherwise [[fallthrough]]; case AMH_RAM: - install_ram_generic(entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, entry.m_flags, readorwrite, entry.m_memory); + install_ram_generic(entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, readorwrite, entry.m_memory); break; case AMH_NOP: - unmap_generic(entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, entry.m_flags, readorwrite, true); + unmap_generic(entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, readorwrite, true); break; case AMH_UNMAP: - unmap_generic(entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, entry.m_flags, readorwrite, false); + unmap_generic(entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, readorwrite, false); break; case AMH_DEVICE_DELEGATE: if (readorwrite == read_or_write::READ) switch (data.m_bits) { - case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto8, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto16, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto32, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto64, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto8, entry.m_mask, entry.m_cswidth); break; + case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto16, entry.m_mask, entry.m_cswidth); break; + case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto32, entry.m_mask, entry.m_cswidth); break; + case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto64, entry.m_mask, entry.m_cswidth); break; } else switch (data.m_bits) { - case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto8, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto16, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto32, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto64, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto8, entry.m_mask, entry.m_cswidth); break; + case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto16, entry.m_mask, entry.m_cswidth); break; + case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto32, entry.m_mask, entry.m_cswidth); break; + case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto64, entry.m_mask, entry.m_cswidth); break; } break; @@ -744,18 +744,18 @@ void address_space_installer::populate_map_entry(const address_map_entry &entry, if (readorwrite == read_or_write::READ) switch (data.m_bits) { - case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto8m, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto16m, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto32m, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto64m, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto8m, entry.m_mask, entry.m_cswidth); break; + case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto16m, entry.m_mask, entry.m_cswidth); break; + case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto32m, entry.m_mask, entry.m_cswidth); break; + case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto64m, entry.m_mask, entry.m_cswidth); break; } else switch (data.m_bits) { - case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto8m, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto16m, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto32m, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto64m, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto8m, entry.m_mask, entry.m_cswidth); break; + case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto16m, entry.m_mask, entry.m_cswidth); break; + case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto32m, entry.m_mask, entry.m_cswidth); break; + case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto64m, entry.m_mask, entry.m_cswidth); break; } break; @@ -763,18 +763,18 @@ void address_space_installer::populate_map_entry(const address_map_entry &entry, if (readorwrite == read_or_write::READ) switch (data.m_bits) { - case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto8s, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto16s, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto32s, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto64s, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto8s, entry.m_mask, entry.m_cswidth); break; + case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto16s, entry.m_mask, entry.m_cswidth); break; + case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto32s, entry.m_mask, entry.m_cswidth); break; + case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto64s, entry.m_mask, entry.m_cswidth); break; } else switch (data.m_bits) { - case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto8s, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto16s, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto32s, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto64s, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto8s, entry.m_mask, entry.m_cswidth); break; + case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto16s, entry.m_mask, entry.m_cswidth); break; + case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto32s, entry.m_mask, entry.m_cswidth); break; + case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto64s, entry.m_mask, entry.m_cswidth); break; } break; @@ -782,18 +782,18 @@ void address_space_installer::populate_map_entry(const address_map_entry &entry, if (readorwrite == read_or_write::READ) switch (data.m_bits) { - case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto8sm, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto16sm, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto32sm, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto64sm, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto8sm, entry.m_mask, entry.m_cswidth); break; + case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto16sm, entry.m_mask, entry.m_cswidth); break; + case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto32sm, entry.m_mask, entry.m_cswidth); break; + case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto64sm, entry.m_mask, entry.m_cswidth); break; } else switch (data.m_bits) { - case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto8sm, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto16sm, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto32sm, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto64sm, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto8sm, entry.m_mask, entry.m_cswidth); break; + case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto16sm, entry.m_mask, entry.m_cswidth); break; + case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto32sm, entry.m_mask, entry.m_cswidth); break; + case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto64sm, entry.m_mask, entry.m_cswidth); break; } break; @@ -801,18 +801,18 @@ void address_space_installer::populate_map_entry(const address_map_entry &entry, if (readorwrite == read_or_write::READ) switch (data.m_bits) { - case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto8mo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto16mo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto32mo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto64mo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto8mo, entry.m_mask, entry.m_cswidth); break; + case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto16mo, entry.m_mask, entry.m_cswidth); break; + case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto32mo, entry.m_mask, entry.m_cswidth); break; + case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto64mo, entry.m_mask, entry.m_cswidth); break; } else switch (data.m_bits) { - case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto8mo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto16mo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto32mo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto64mo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto8mo, entry.m_mask, entry.m_cswidth); break; + case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto16mo, entry.m_mask, entry.m_cswidth); break; + case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto32mo, entry.m_mask, entry.m_cswidth); break; + case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto64mo, entry.m_mask, entry.m_cswidth); break; } break; @@ -820,23 +820,23 @@ void address_space_installer::populate_map_entry(const address_map_entry &entry, if (readorwrite == read_or_write::READ) switch (data.m_bits) { - case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto8smo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto16smo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto32smo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto64smo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 8: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto8smo, entry.m_mask, entry.m_cswidth); break; + case 16: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto16smo, entry.m_mask, entry.m_cswidth); break; + case 32: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto32smo, entry.m_mask, entry.m_cswidth); break; + case 64: install_read_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_rproto64smo, entry.m_mask, entry.m_cswidth); break; } else switch (data.m_bits) { - case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto8smo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto16smo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto32smo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; - case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto64smo, entry.m_mask, entry.m_cswidth, entry.m_flags); break; + case 8: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto8smo, entry.m_mask, entry.m_cswidth); break; + case 16: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto16smo, entry.m_mask, entry.m_cswidth); break; + case 32: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto32smo, entry.m_mask, entry.m_cswidth); break; + case 64: install_write_handler(entry.m_addrstart, entry.m_addrend, entry.m_addrmask, entry.m_addrmirror, entry.m_addrselect, entry.m_wproto64smo, entry.m_mask, entry.m_cswidth); break; } break; case AMH_PORT: - install_readwrite_port(entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, entry.m_flags, + install_readwrite_port(entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, (readorwrite == read_or_write::READ) ? entry.m_devbase.subtag(data.m_tag) : "", (readorwrite == read_or_write::WRITE) ? entry.m_devbase.subtag(data.m_tag) : ""); break; @@ -847,7 +847,7 @@ void address_space_installer::populate_map_entry(const address_map_entry &entry, memory_bank *bank = m_manager.bank_find(tag); if (!bank) bank = m_manager.bank_alloc(entry.m_devbase, tag); - install_bank_generic(entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, entry.m_flags, + install_bank_generic(entry.m_addrstart, entry.m_addrend, entry.m_addrmirror, (readorwrite == read_or_write::READ) ? bank : nullptr, (readorwrite == read_or_write::WRITE) ? bank : nullptr); } diff --git a/src/emu/emumem.h b/src/emu/emumem.h index 3d4d664..7b156be 100644 --- a/src/emu/emumem.h +++ b/src/emu/emumem.h @@ -484,12 +484,12 @@ class handler_entry template friend class address_space_specific; public: - // Typing flags (low 16 bits are for the user) - static constexpr u32 F_UNMAP = 0x00010000; // the unmapped memory accessed handler - static constexpr u32 F_DISPATCH = 0x00020000; // handler that forwards the access to other handlers - static constexpr u32 F_UNITS = 0x00040000; // handler that merges/splits an access among multiple handlers (unitmask support) - static constexpr u32 F_PASSTHROUGH = 0x00080000; // handler that passes through the request to another handler - static constexpr u32 F_VIEW = 0x00100000; // handler for a view (kinda like dispatch except not entirely) + // Typing flags + static constexpr u32 F_UNMAP = 0x00000001; // the unmapped memory accessed handler + static constexpr u32 F_DISPATCH = 0x00000002; // handler that forwards the access to other handlers + static constexpr u32 F_UNITS = 0x00000004; // handler that merges/splits an access among multiple handlers (unitmask support) + static constexpr u32 F_PASSTHROUGH = 0x00000008; // handler that passes through the request to another handler + static constexpr u32 F_VIEW = 0x00000010; // handler for a view (kinda like dispatch except not entirely) // Start/end of range flags static constexpr u8 START = 1; @@ -580,7 +580,6 @@ public: ~handler_entry_read() {} virtual uX read(offs_t offset, uX mem_mask) const = 0; - virtual std::pair read_flags(offs_t offset, uX mem_mask) const = 0; virtual void *get_ptr(offs_t offset) const; virtual void lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_read *&handler) const; @@ -655,7 +654,6 @@ public: virtual ~handler_entry_write() {} virtual void write(offs_t offset, uX data, uX mem_mask) const = 0; - virtual u16 write_flags(offs_t offset, uX data, uX mem_mask) const = 0; virtual void *get_ptr(offs_t offset) const; virtual void lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_write *&handler) const; @@ -995,274 +993,6 @@ template std::pair::uX, u16> memory_read_generic_flags(TF ropf, offs_t address, typename emu::detail::handler_entry_size::uX mask) -{ - using TargetType = typename emu::detail::handler_entry_size::uX; - using NativeType = typename emu::detail::handler_entry_size::uX; - - constexpr u32 TARGET_BYTES = 1 << TargetWidth; - constexpr u32 TARGET_BITS = 8 * TARGET_BYTES; - constexpr u32 NATIVE_BYTES = 1 << Width; - constexpr u32 NATIVE_BITS = 8 * NATIVE_BYTES; - constexpr u32 NATIVE_STEP = AddrShift >= 0 ? NATIVE_BYTES << iabs(AddrShift) : NATIVE_BYTES >> iabs(AddrShift); - constexpr u32 NATIVE_MASK = Width + AddrShift >= 0 ? make_bitmask(Width + AddrShift) : 0; - - // equal to native size and aligned; simple pass-through to the native reader - if (NATIVE_BYTES == TARGET_BYTES && (Aligned || (address & NATIVE_MASK) == 0)) - return ropf(address & ~NATIVE_MASK, mask); - - // if native size is larger, see if we can do a single masked read (guaranteed if we're aligned) - if (NATIVE_BYTES > TARGET_BYTES) - { - u32 offsbits = 8 * (memory_offset_to_byte(address, AddrShift) & (NATIVE_BYTES - (Aligned ? TARGET_BYTES : 1))); - if (Aligned || (offsbits + TARGET_BITS <= NATIVE_BITS)) - { - if (Endian != ENDIANNESS_LITTLE) offsbits = NATIVE_BITS - TARGET_BITS - offsbits; - auto pack = ropf(address & ~NATIVE_MASK, (NativeType)mask << offsbits); - pack.first >>= offsbits; - return pack; - } - } - - // determine our alignment against the native boundaries, and mask the address - u32 offsbits = 8 * (memory_offset_to_byte(address, AddrShift) & (NATIVE_BYTES - 1)); - address &= ~NATIVE_MASK; - - // if we're here, and native size is larger or equal to the target, we need exactly 2 reads - if (NATIVE_BYTES >= TARGET_BYTES) - { - // little-endian case - if (Endian == ENDIANNESS_LITTLE) - { - // read lower bits from lower address - u16 flags = 0; - TargetType result = 0; - NativeType curmask = (NativeType)mask << offsbits; - if (curmask != 0) { auto pack = ropf(address, curmask); result = pack.first >> offsbits; flags = pack.second; } - - // read upper bits from upper address - offsbits = NATIVE_BITS - offsbits; - curmask = mask >> offsbits; - if (curmask != 0) { auto pack = ropf(address + NATIVE_STEP, curmask); result |= pack.first << offsbits; flags |= pack.second; } - return std::pair(result, flags); - } - - // big-endian case - else - { - // left-justify the mask to the target type - constexpr u32 LEFT_JUSTIFY_TARGET_TO_NATIVE_SHIFT = ((NATIVE_BITS >= TARGET_BITS) ? (NATIVE_BITS - TARGET_BITS) : 0); - u16 flags = 0; - NativeType result = 0; - NativeType ljmask = (NativeType)mask << LEFT_JUSTIFY_TARGET_TO_NATIVE_SHIFT; - NativeType curmask = ljmask >> offsbits; - - // read upper bits from lower address - if (curmask != 0) { auto pack = ropf(address, curmask); result = pack.first << offsbits; flags = pack.second; } - offsbits = NATIVE_BITS - offsbits; - - // read lower bits from upper address - curmask = ljmask << offsbits; - if (curmask != 0) { auto pack = ropf(address + NATIVE_STEP, curmask); result |= pack.first >> offsbits; flags |= pack.second; } - - // return the un-justified result - return std::pair(result >> LEFT_JUSTIFY_TARGET_TO_NATIVE_SHIFT, flags); - } - } - - // if we're here, then we have 2 or more reads needed to get our final result - else - { - // compute the maximum number of loops; we do it this way so that there are - // a fixed number of loops for the compiler to unroll if it desires - constexpr u32 MAX_SPLITS_MINUS_ONE = TARGET_BYTES / NATIVE_BYTES - 1; - u16 flags = 0; - TargetType result = 0; - - // little-endian case - if (Endian == ENDIANNESS_LITTLE) - { - // read lowest bits from first address - NativeType curmask = mask << offsbits; - if (curmask != 0) { auto pack = ropf(address, curmask); result = pack.first >> offsbits; flags = pack.second; } - - // read middle bits from subsequent addresses - offsbits = NATIVE_BITS - offsbits; - for (u32 index = 0; index < MAX_SPLITS_MINUS_ONE; index++) - { - address += NATIVE_STEP; - curmask = mask >> offsbits; - if (curmask != 0) { auto pack = ropf(address, curmask); result |= (TargetType)pack.first << offsbits; flags |= pack.second; } - offsbits += NATIVE_BITS; - } - - // if we're not aligned and we still have bits left, read uppermost bits from last address - if (!Aligned && offsbits < TARGET_BITS) - { - curmask = mask >> offsbits; - if (curmask != 0) { auto pack = ropf(address + NATIVE_STEP, curmask); result |= (TargetType)pack.first << offsbits; flags |= pack.second; } - } - } - - // big-endian case - else - { - // read highest bits from first address - offsbits = TARGET_BITS - (NATIVE_BITS - offsbits); - NativeType curmask = mask >> offsbits; - if (curmask != 0) { auto pack = ropf(address, curmask); result = pack.first >> offsbits; flags = pack.second; } - - // read middle bits from subsequent addresses - for (u32 index = 0; index < MAX_SPLITS_MINUS_ONE; index++) - { - offsbits -= NATIVE_BITS; - address += NATIVE_STEP; - curmask = mask >> offsbits; - if (curmask != 0) { auto pack = ropf(address, curmask); result |= (TargetType)pack.first >> offsbits; flags |= pack.second; } - } - - // if we're not aligned and we still have bits left, read lowermost bits from the last address - if (!Aligned && offsbits != 0) - { - offsbits = NATIVE_BITS - offsbits; - curmask = mask << offsbits; - if (curmask != 0) { auto pack = ropf(address + NATIVE_STEP, curmask); result |= (TargetType)pack.first >> offsbits; flags |= pack.second; } - } - } - return std::pair(result, flags); - } -} - -// generic direct write with flags -template u16 memory_write_generic_flags(TF wopf, offs_t address, typename emu::detail::handler_entry_size::uX data, typename emu::detail::handler_entry_size::uX mask) -{ - using NativeType = typename emu::detail::handler_entry_size::uX; - - constexpr u32 TARGET_BYTES = 1 << TargetWidth; - constexpr u32 TARGET_BITS = 8 * TARGET_BYTES; - constexpr u32 NATIVE_BYTES = 1 << Width; - constexpr u32 NATIVE_BITS = 8 * NATIVE_BYTES; - constexpr u32 NATIVE_STEP = AddrShift >= 0 ? NATIVE_BYTES << iabs(AddrShift) : NATIVE_BYTES >> iabs(AddrShift); - constexpr u32 NATIVE_MASK = Width + AddrShift >= 0 ? (1 << (Width + AddrShift)) - 1 : 0; - - // equal to native size and aligned; simple pass-through to the native writer - if (NATIVE_BYTES == TARGET_BYTES && (Aligned || (address & NATIVE_MASK) == 0)) - return wopf(address & ~NATIVE_MASK, data, mask); - - // if native size is larger, see if we can do a single masked write (guaranteed if we're aligned) - if (NATIVE_BYTES > TARGET_BYTES) - { - u32 offsbits = 8 * (memory_offset_to_byte(address, AddrShift) & (NATIVE_BYTES - (Aligned ? TARGET_BYTES : 1))); - if (Aligned || (offsbits + TARGET_BITS <= NATIVE_BITS)) - { - if (Endian != ENDIANNESS_LITTLE) offsbits = NATIVE_BITS - TARGET_BITS - offsbits; - return wopf(address & ~NATIVE_MASK, (NativeType)data << offsbits, (NativeType)mask << offsbits); - } - } - - // determine our alignment against the native boundaries, and mask the address - u32 offsbits = 8 * (memory_offset_to_byte(address, AddrShift) & (NATIVE_BYTES - 1)); - address &= ~NATIVE_MASK; - - // if we're here, and native size is larger or equal to the target, we need exactly 2 writes - if (NATIVE_BYTES >= TARGET_BYTES) - { - // little-endian case - if (Endian == ENDIANNESS_LITTLE) - { - // write lower bits to lower address - u16 flags = 0; - NativeType curmask = (NativeType)mask << offsbits; - if (curmask != 0) flags |= wopf(address, (NativeType)data << offsbits, curmask); - - // write upper bits to upper address - offsbits = NATIVE_BITS - offsbits; - curmask = mask >> offsbits; - if (curmask != 0) flags |= wopf(address + NATIVE_STEP, data >> offsbits, curmask); - return flags; - } - - // big-endian case - else - { - // left-justify the mask and data to the target type - u16 flags = 0; - constexpr u32 LEFT_JUSTIFY_TARGET_TO_NATIVE_SHIFT = ((NATIVE_BITS >= TARGET_BITS) ? (NATIVE_BITS - TARGET_BITS) : 0); - NativeType ljdata = (NativeType)data << LEFT_JUSTIFY_TARGET_TO_NATIVE_SHIFT; - NativeType ljmask = (NativeType)mask << LEFT_JUSTIFY_TARGET_TO_NATIVE_SHIFT; - // write upper bits to lower address - NativeType curmask = ljmask >> offsbits; - if (curmask != 0) flags |= wopf(address, ljdata >> offsbits, curmask); - // write lower bits to upper address - offsbits = NATIVE_BITS - offsbits; - curmask = ljmask << offsbits; - if (curmask != 0) flags |= wopf(address + NATIVE_STEP, ljdata << offsbits, curmask); - return flags; - } - } - - // if we're here, then we have 2 or more writes needed to get our final result - else - { - // compute the maximum number of loops; we do it this way so that there are - // a fixed number of loops for the compiler to unroll if it desires - constexpr u32 MAX_SPLITS_MINUS_ONE = TARGET_BYTES / NATIVE_BYTES - 1; - u16 flags = 0; - - // little-endian case - if (Endian == ENDIANNESS_LITTLE) - { - // write lowest bits to first address - NativeType curmask = mask << offsbits; - if (curmask != 0) flags |= wopf(address, data << offsbits, curmask); - - // write middle bits to subsequent addresses - offsbits = NATIVE_BITS - offsbits; - for (u32 index = 0; index < MAX_SPLITS_MINUS_ONE; index++) - { - address += NATIVE_STEP; - curmask = mask >> offsbits; - if (curmask != 0) flags |= wopf(address, data >> offsbits, curmask); - offsbits += NATIVE_BITS; - } - - // if we're not aligned and we still have bits left, write uppermost bits to last address - if (!Aligned && offsbits < TARGET_BITS) - { - curmask = mask >> offsbits; - if (curmask != 0) flags |= wopf(address + NATIVE_STEP, data >> offsbits, curmask); - } - } - - // big-endian case - else - { - // write highest bits to first address - offsbits = TARGET_BITS - (NATIVE_BITS - offsbits); - NativeType curmask = mask >> offsbits; - if (curmask != 0) flags |= wopf(address, data >> offsbits, curmask); - - // write middle bits to subsequent addresses - for (u32 index = 0; index < MAX_SPLITS_MINUS_ONE; index++) - { - offsbits -= NATIVE_BITS; - address += NATIVE_STEP; - curmask = mask >> offsbits; - if (curmask != 0) flags |= wopf(address, data >> offsbits, curmask); - } - - // if we're not aligned and we still have bits left, write lowermost bits to the last address - if (!Aligned && offsbits != 0) - { - offsbits = NATIVE_BITS - offsbits; - curmask = mask << offsbits; - if (curmask != 0) flags |= wopf(address + NATIVE_STEP, data << offsbits, curmask); - } - } - return flags; - } -} // ======================> Direct dispatching @@ -1280,20 +1010,6 @@ template void dispatch_write(offs_t mask, o } -template std::pair::uX, u16> dispatch_read_flags(offs_t mask, offs_t offset, typename emu::detail::handler_entry_size::uX mem_mask, const handler_entry_read *const *dispatch) -{ - static constexpr u32 LowBits = emu::detail::handler_entry_dispatch_level_to_lowbits(Level, Width, AddrShift); - return dispatch[(offset & mask) >> LowBits]->read_flags(offset, mem_mask); -} - - -template u16 dispatch_write_flags(offs_t mask, offs_t offset, typename emu::detail::handler_entry_size::uX data, typename emu::detail::handler_entry_size::uX mem_mask, const handler_entry_write *const *dispatch) -{ - static constexpr u32 LowBits = emu::detail::handler_entry_dispatch_level_to_lowbits(Level, Width, AddrShift); - return dispatch[(offset & mask) >> LowBits]->write_flags(offset, data, mem_mask); -} - - // ======================> memory_access_specific // memory_access_specific does uncached but faster accesses by shortcutting the address_space virtual call @@ -1322,67 +1038,33 @@ public: return *m_space; } - auto rop() { return [this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }; } - auto ropf() { return [this](offs_t offset, NativeType mask) -> std::pair { return read_native_flags(offset, mask); }; } - auto wop() { return [this](offs_t offset, NativeType data, NativeType mask) -> void { write_native(offset, data, mask); }; } - auto wopf() { return [this](offs_t offset, NativeType data, NativeType mask) -> u16 { return write_native_flags(offset, data, mask); }; } + u8 read_byte(offs_t address) { return Width == 0 ? read_native(address & ~NATIVE_MASK) : memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xff); } + u16 read_word(offs_t address) { return Width == 1 ? read_native(address & ~NATIVE_MASK) : memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffff); } + u16 read_word(offs_t address, u16 mask) { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } + u16 read_word_unaligned(offs_t address) { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffff); } + u16 read_word_unaligned(offs_t address, u16 mask) { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } + u32 read_dword(offs_t address) { return Width == 2 ? read_native(address & ~NATIVE_MASK) : memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffff); } + u32 read_dword(offs_t address, u32 mask) { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } + u32 read_dword_unaligned(offs_t address) { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffff); } + u32 read_dword_unaligned(offs_t address, u32 mask) { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } + u64 read_qword(offs_t address) { return Width == 3 ? read_native(address & ~NATIVE_MASK) : memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffffffffffffU); } + u64 read_qword(offs_t address, u64 mask) { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } + u64 read_qword_unaligned(offs_t address) { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffffffffffffU); } + u64 read_qword_unaligned(offs_t address, u64 mask) { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } - u8 read_byte(offs_t address) { if constexpr(Width == 0) return read_native(address & ~NATIVE_MASK); else return memory_read_generic(rop(), address, 0xff); } - u16 read_word(offs_t address) { if constexpr(Width == 1) return read_native(address & ~NATIVE_MASK); else return memory_read_generic(rop(), address, 0xffff); } - u16 read_word(offs_t address, u16 mask) { return memory_read_generic(rop(), address, mask); } - u16 read_word_unaligned(offs_t address) { return memory_read_generic(rop(), address, 0xffff); } - u16 read_word_unaligned(offs_t address, u16 mask) { return memory_read_generic(rop(), address, mask); } - u32 read_dword(offs_t address) { if constexpr(Width == 2) return read_native(address & ~NATIVE_MASK); else return memory_read_generic(rop(), address, 0xffffffff); } - u32 read_dword(offs_t address, u32 mask) { return memory_read_generic(rop(), address, mask); } - u32 read_dword_unaligned(offs_t address) { return memory_read_generic(rop(), address, 0xffffffff); } - u32 read_dword_unaligned(offs_t address, u32 mask) { return memory_read_generic(rop(), address, mask); } - u64 read_qword(offs_t address) { if constexpr(Width == 3) return read_native(address & ~NATIVE_MASK); else return memory_read_generic(rop(), address, 0xffffffffffffffffU); } - u64 read_qword(offs_t address, u64 mask) { return memory_read_generic(rop(), address, mask); } - u64 read_qword_unaligned(offs_t address) { return memory_read_generic(rop(), address, 0xffffffffffffffffU); } - u64 read_qword_unaligned(offs_t address, u64 mask) { return memory_read_generic(rop(), address, mask); } - - void write_byte(offs_t address, u8 data) { if constexpr(Width == 0) write_native(address & ~NATIVE_MASK, data); else memory_write_generic(wop(), address, data, 0xff); } - void write_word(offs_t address, u16 data) { if constexpr(Width == 1) write_native(address & ~NATIVE_MASK, data); else memory_write_generic(wop(), address, data, 0xffff); } - void write_word(offs_t address, u16 data, u16 mask) { memory_write_generic(wop(), address, data, mask); } - void write_word_unaligned(offs_t address, u16 data) { memory_write_generic(wop(), address, data, 0xffff); } - void write_word_unaligned(offs_t address, u16 data, u16 mask) { memory_write_generic(wop(), address, data, mask); } - void write_dword(offs_t address, u32 data) { if constexpr(Width == 2) write_native(address & ~NATIVE_MASK, data); else memory_write_generic(wop(), address, data, 0xffffffff); } - void write_dword(offs_t address, u32 data, u32 mask) { memory_write_generic(wop(), address, data, mask); } - void write_dword_unaligned(offs_t address, u32 data) { memory_write_generic(wop(), address, data, 0xffffffff); } - void write_dword_unaligned(offs_t address, u32 data, u32 mask) { memory_write_generic(wop(), address, data, mask); } - void write_qword(offs_t address, u64 data) { if constexpr(Width == 3) write_native(address & ~NATIVE_MASK, data); else memory_write_generic(wop(), address, data, 0xffffffffffffffffU); } - void write_qword(offs_t address, u64 data, u64 mask) { memory_write_generic(wop(), address, data, mask); } - void write_qword_unaligned(offs_t address, u64 data) { memory_write_generic(wop(), address, data, 0xffffffffffffffffU); } - void write_qword_unaligned(offs_t address, u64 data, u64 mask) { memory_write_generic(wop(), address, data, mask); } - - - std::pair read_byte_flags(offs_t address) { if constexpr(Width == 0) return read_native_flags(address & ~NATIVE_MASK); else return memory_read_generic_flags(ropf(), address, 0xff); } - std::pair read_word_flags(offs_t address) { if constexpr(Width == 1) return read_native_flags(address & ~NATIVE_MASK); else return memory_read_generic_flags(ropf(), address, 0xffff); } - std::pair read_word_flags(offs_t address, u16 mask) { return memory_read_generic_flags(ropf(), address, mask); } - std::pair read_word_unaligned_flags(offs_t address) { return memory_read_generic_flags(ropf(), address, 0xffff); } - std::pair read_word_unaligned_flags(offs_t address, u16 mask) { return memory_read_generic_flags(ropf(), address, mask); } - std::pair read_dword_flags(offs_t address) { if constexpr(Width == 2) return read_native_flags(address & ~NATIVE_MASK); else return memory_read_generic_flags(ropf(), address, 0xffffffff); } - std::pair read_dword_flags(offs_t address, u32 mask) { return memory_read_generic_flags(ropf(), address, mask); } - std::pair read_dword_unaligned_flags(offs_t address) { return memory_read_generic_flags(ropf(), address, 0xffffffff); } - std::pair read_dword_unaligned_flags(offs_t address, u32 mask) { return memory_read_generic_flags(ropf(), address, mask); } - std::pair read_qword_flags(offs_t address) { if constexpr(Width == 3) return read_native_flags(address & ~NATIVE_MASK); else return memory_read_generic_flags(ropf(), address, 0xffffffffffffffffU); } - std::pair read_qword_flags(offs_t address, u64 mask) { return memory_read_generic_flags(ropf(), address, mask); } - std::pair read_qword_unaligned_flags(offs_t address) { return memory_read_generic_flags(ropf(), address, 0xffffffffffffffffU); } - std::pair read_qword_unaligned_flags(offs_t address, u64 mask) { return memory_read_generic_flags(ropf(), address, mask); } - - u16 write_byte_flags(offs_t address, u8 data) { if constexpr(Width == 0) return write_native_flags(address & ~NATIVE_MASK, data); else return memory_write_generic_flags(wopf(), address, data, 0xff); } - u16 write_word_flags(offs_t address, u16 data) { if constexpr(Width == 1) return write_native_flags(address & ~NATIVE_MASK, data); else return memory_write_generic_flags(wopf(), address, data, 0xffff); } - u16 write_word_flags(offs_t address, u16 data, u16 mask) { return memory_write_generic_flags(wopf(), address, data, mask); } - u16 write_word_unaligned_flags(offs_t address, u16 data) { return memory_write_generic_flags(wopf(), address, data, 0xffff); } - u16 write_word_unaligned_flags(offs_t address, u16 data, u16 mask) { return memory_write_generic_flags(wopf(), address, data, mask); } - u16 write_dword_flags(offs_t address, u32 data) { if constexpr(Width == 2) return write_native_flags(address & ~NATIVE_MASK, data); else return memory_write_generic_flags(wopf(), address, data, 0xffffffff); } - u16 write_dword_flags(offs_t address, u32 data, u32 mask) { return memory_write_generic_flags(wopf(), address, data, mask); } - u16 write_dword_unaligned_flags(offs_t address, u32 data) { return memory_write_generic_flags(wopf(), address, data, 0xffffffff); } - u16 write_dword_unaligned_flags(offs_t address, u32 data, u32 mask) { return memory_write_generic_flags(wopf(), address, data, mask); } - u16 write_qword_flags(offs_t address, u64 data) { if constexpr(Width == 3) return write_native_flags(address & ~NATIVE_MASK, data); else return memory_write_generic_flags(wopf(), address, data, 0xffffffffffffffffU); } - u16 write_qword_flags(offs_t address, u64 data, u64 mask) { return memory_write_generic_flags(wop(), address, data, mask); } - u16 write_qword_unaligned_flags(offs_t address, u64 data) { return memory_write_generic_flags(wopf(), address, data, 0xffffffffffffffffU); } - u16 write_qword_unaligned_flags(offs_t address, u64 data, u64 mask) { return memory_write_generic_flags(wopf(), address, data, mask); } + void write_byte(offs_t address, u8 data) { if (Width == 0) write_native(address & ~NATIVE_MASK, data); else memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xff); } + void write_word(offs_t address, u16 data) { if (Width == 1) write_native(address & ~NATIVE_MASK, data); else memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffff); } + void write_word(offs_t address, u16 data, u16 mask) { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } + void write_word_unaligned(offs_t address, u16 data) { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffff); } + void write_word_unaligned(offs_t address, u16 data, u16 mask) { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } + void write_dword(offs_t address, u32 data) { if (Width == 2) write_native(address & ~NATIVE_MASK, data); else memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffff); } + void write_dword(offs_t address, u32 data, u32 mask) { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } + void write_dword_unaligned(offs_t address, u32 data) { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffff); } + void write_dword_unaligned(offs_t address, u32 data, u32 mask) { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } + void write_qword(offs_t address, u64 data) { if (Width == 3) write_native(address & ~NATIVE_MASK, data); else memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffffffffffffU); } + void write_qword(offs_t address, u64 data, u64 mask) { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } + void write_qword_unaligned(offs_t address, u64 data) { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffffffffffffU); } + void write_qword_unaligned(offs_t address, u64 data, u64 mask) { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } private: address_space * m_space; @@ -1400,14 +1082,6 @@ private: dispatch_write(offs_t(-1), address & m_addrmask, data, mask, m_dispatch_write); } - std::pair read_native_flags(offs_t address, NativeType mask = ~NativeType(0)) { - return dispatch_read_flags(offs_t(-1), address & m_addrmask, mask, m_dispatch_read); - } - - u16 write_native_flags(offs_t address, NativeType data, NativeType mask = ~NativeType(0)) { - return dispatch_write_flags(offs_t(-1), address & m_addrmask, data, mask, m_dispatch_write); - } - void set(address_space *space, std::pair rw); }; @@ -1467,67 +1141,33 @@ public: return m_cache_r->get_ptr(address); } - auto rop() { return [this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }; } - auto ropf() { return [this](offs_t offset, NativeType mask) -> std::pair { return read_native_flags(offset, mask); }; } - auto wop() { return [this](offs_t offset, NativeType data, NativeType mask) -> void { write_native(offset, data, mask); }; } - auto wopf() { return [this](offs_t offset, NativeType data, NativeType mask) -> u16 { return write_native_flags(offset, data, mask); }; } + u8 read_byte(offs_t address) { return Width == 0 ? read_native(address & ~NATIVE_MASK) : memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xff); } + u16 read_word(offs_t address) { return Width == 1 ? read_native(address & ~NATIVE_MASK) : memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffff); } + u16 read_word(offs_t address, u16 mask) { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } + u16 read_word_unaligned(offs_t address) { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffff); } + u16 read_word_unaligned(offs_t address, u16 mask) { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } + u32 read_dword(offs_t address) { return Width == 2 ? read_native(address & ~NATIVE_MASK) : memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffff); } + u32 read_dword(offs_t address, u32 mask) { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } + u32 read_dword_unaligned(offs_t address) { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffff); } + u32 read_dword_unaligned(offs_t address, u32 mask) { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } + u64 read_qword(offs_t address) { return Width == 3 ? read_native(address & ~NATIVE_MASK) : memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffffffffffffU); } + u64 read_qword(offs_t address, u64 mask) { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } + u64 read_qword_unaligned(offs_t address) { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffffffffffffU); } + u64 read_qword_unaligned(offs_t address, u64 mask) { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } - u8 read_byte(offs_t address) { if constexpr(Width == 0) return read_native(address & ~NATIVE_MASK); else return memory_read_generic(rop(), address, 0xff); } - u16 read_word(offs_t address) { if constexpr(Width == 1) return read_native(address & ~NATIVE_MASK); else return memory_read_generic(rop(), address, 0xffff); } - u16 read_word(offs_t address, u16 mask) { return memory_read_generic(rop(), address, mask); } - u16 read_word_unaligned(offs_t address) { return memory_read_generic(rop(), address, 0xffff); } - u16 read_word_unaligned(offs_t address, u16 mask) { return memory_read_generic(rop(), address, mask); } - u32 read_dword(offs_t address) { if constexpr(Width == 2) return read_native(address & ~NATIVE_MASK); else return memory_read_generic(rop(), address, 0xffffffff); } - u32 read_dword(offs_t address, u32 mask) { return memory_read_generic(rop(), address, mask); } - u32 read_dword_unaligned(offs_t address) { return memory_read_generic(rop(), address, 0xffffffff); } - u32 read_dword_unaligned(offs_t address, u32 mask) { return memory_read_generic(rop(), address, mask); } - u64 read_qword(offs_t address) { if constexpr(Width == 3) return read_native(address & ~NATIVE_MASK); else return memory_read_generic(rop(), address, 0xffffffffffffffffU); } - u64 read_qword(offs_t address, u64 mask) { return memory_read_generic(rop(), address, mask); } - u64 read_qword_unaligned(offs_t address) { return memory_read_generic(rop(), address, 0xffffffffffffffffU); } - u64 read_qword_unaligned(offs_t address, u64 mask) { return memory_read_generic(rop(), address, mask); } - - void write_byte(offs_t address, u8 data) { if constexpr(Width == 0) write_native(address & ~NATIVE_MASK, data); else memory_write_generic(wop(), address, data, 0xff); } - void write_word(offs_t address, u16 data) { if constexpr(Width == 1) write_native(address & ~NATIVE_MASK, data); else memory_write_generic(wop(), address, data, 0xffff); } - void write_word(offs_t address, u16 data, u16 mask) { memory_write_generic(wop(), address, data, mask); } - void write_word_unaligned(offs_t address, u16 data) { memory_write_generic(wop(), address, data, 0xffff); } - void write_word_unaligned(offs_t address, u16 data, u16 mask) { memory_write_generic(wop(), address, data, mask); } - void write_dword(offs_t address, u32 data) { if constexpr(Width == 2) write_native(address & ~NATIVE_MASK, data); else memory_write_generic(wop(), address, data, 0xffffffff); } - void write_dword(offs_t address, u32 data, u32 mask) { memory_write_generic(wop(), address, data, mask); } - void write_dword_unaligned(offs_t address, u32 data) { memory_write_generic(wop(), address, data, 0xffffffff); } - void write_dword_unaligned(offs_t address, u32 data, u32 mask) { memory_write_generic(wop(), address, data, mask); } - void write_qword(offs_t address, u64 data) { if constexpr(Width == 3) write_native(address & ~NATIVE_MASK, data); else memory_write_generic(wop(), address, data, 0xffffffffffffffffU); } - void write_qword(offs_t address, u64 data, u64 mask) { memory_write_generic(wop(), address, data, mask); } - void write_qword_unaligned(offs_t address, u64 data) { memory_write_generic(wop(), address, data, 0xffffffffffffffffU); } - void write_qword_unaligned(offs_t address, u64 data, u64 mask) { memory_write_generic(wop(), address, data, mask); } - - - std::pair read_byte_flags(offs_t address) { if constexpr(Width == 0) return read_native_flags(address & ~NATIVE_MASK); else return memory_read_generic_flags(ropf(), address, 0xff); } - std::pair read_word_flags(offs_t address) { if constexpr(Width == 1) return read_native_flags(address & ~NATIVE_MASK); else return memory_read_generic_flags(ropf(), address, 0xffff); } - std::pair read_word_flags(offs_t address, u16 mask) { return memory_read_generic_flags(ropf(), address, mask); } - std::pair read_word_unaligned_flags(offs_t address) { return memory_read_generic_flags(ropf(), address, 0xffff); } - std::pair read_word_unaligned_flags(offs_t address, u16 mask) { return memory_read_generic_flags(ropf(), address, mask); } - std::pair read_dword_flags(offs_t address) { if constexpr(Width == 2) return read_native_flags(address & ~NATIVE_MASK); else return memory_read_generic_flags(ropf(), address, 0xffffffff); } - std::pair read_dword_flags(offs_t address, u32 mask) { return memory_read_generic_flags(ropf(), address, mask); } - std::pair read_dword_unaligned_flags(offs_t address) { return memory_read_generic_flags(ropf(), address, 0xffffffff); } - std::pair read_dword_unaligned_flags(offs_t address, u32 mask) { return memory_read_generic_flags(ropf(), address, mask); } - std::pair read_qword_flags(offs_t address) { if constexpr(Width == 3) return read_native_flags(address & ~NATIVE_MASK); else return memory_read_generic_flags(ropf(), address, 0xffffffffffffffffU); } - std::pair read_qword_flags(offs_t address, u64 mask) { return memory_read_generic_flags(ropf(), address, mask); } - std::pair read_qword_unaligned_flags(offs_t address) { return memory_read_generic_flags(ropf(), address, 0xffffffffffffffffU); } - std::pair read_qword_unaligned_flags(offs_t address, u64 mask) { return memory_read_generic_flags(ropf(), address, mask); } - - u16 write_byte_flags(offs_t address, u8 data) { if constexpr(Width == 0) return write_native_flags(address & ~NATIVE_MASK, data); else return memory_write_generic_flags(wopf(), address, data, 0xff); } - u16 write_word_flags(offs_t address, u16 data) { if constexpr(Width == 1) return write_native_flags(address & ~NATIVE_MASK, data); else return memory_write_generic_flags(wopf(), address, data, 0xffff); } - u16 write_word_flags(offs_t address, u16 data, u16 mask) { return memory_write_generic_flags(wopf(), address, data, mask); } - u16 write_word_unaligned_flags(offs_t address, u16 data) { return memory_write_generic_flags(wopf(), address, data, 0xffff); } - u16 write_word_unaligned_flags(offs_t address, u16 data, u16 mask) { return memory_write_generic_flags(wopf(), address, data, mask); } - u16 write_dword_flags(offs_t address, u32 data) { if constexpr(Width == 2) return write_native_flags(address & ~NATIVE_MASK, data); else return memory_write_generic_flags(wopf(), address, data, 0xffffffff); } - u16 write_dword_flags(offs_t address, u32 data, u32 mask) { return memory_write_generic_flags(wopf(), address, data, mask); } - u16 write_dword_unaligned_flags(offs_t address, u32 data) { return memory_write_generic_flags(wopf(), address, data, 0xffffffff); } - u16 write_dword_unaligned_flags(offs_t address, u32 data, u32 mask) { return memory_write_generic_flags(wopf(), address, data, mask); } - u16 write_qword_flags(offs_t address, u64 data) { if constexpr(Width == 3) return write_native_flags(address & ~NATIVE_MASK, data); else return memory_write_generic_flags(wopf(), address, data, 0xffffffffffffffffU); } - u16 write_qword_flags(offs_t address, u64 data, u64 mask) { return memory_write_generic_flags(wop(), address, data, mask); } - u16 write_qword_unaligned_flags(offs_t address, u64 data) { return memory_write_generic_flags(wopf(), address, data, 0xffffffffffffffffU); } - u16 write_qword_unaligned_flags(offs_t address, u64 data, u64 mask) { return memory_write_generic_flags(wopf(), address, data, mask); } + void write_byte(offs_t address, u8 data) { if (Width == 0) write_native(address & ~NATIVE_MASK, data); else memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xff); } + void write_word(offs_t address, u16 data) { if (Width == 1) write_native(address & ~NATIVE_MASK, data); else memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffff); } + void write_word(offs_t address, u16 data, u16 mask) { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } + void write_word_unaligned(offs_t address, u16 data) { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffff); } + void write_word_unaligned(offs_t address, u16 data, u16 mask) { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } + void write_dword(offs_t address, u32 data) { if (Width == 2) write_native(address & ~NATIVE_MASK, data); else memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffff); } + void write_dword(offs_t address, u32 data, u32 mask) { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } + void write_dword_unaligned(offs_t address, u32 data) { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffff); } + void write_dword_unaligned(offs_t address, u32 data, u32 mask) { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } + void write_qword(offs_t address, u64 data) { if (Width == 3) write_native(address & ~NATIVE_MASK, data); else memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffffffffffffU); } + void write_qword(offs_t address, u64 data, u64 mask) { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } + void write_qword_unaligned(offs_t address, u64 data) { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffffffffffffU); } + void write_qword_unaligned(offs_t address, u64 data, u64 mask) { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } private: address_space * m_space; @@ -1545,8 +1185,6 @@ private: NativeType read_native(offs_t address, NativeType mask = ~NativeType(0)); void write_native(offs_t address, NativeType data, NativeType mask = ~NativeType(0)); - std::pair read_native_flags(offs_t address, NativeType mask = ~NativeType(0)); - u16 write_native_flags(offs_t address, NativeType data, NativeType mask = ~NativeType(0)); void set(address_space *space, std::pair rw); }; @@ -1638,53 +1276,42 @@ public: u8 logaddrchars() const { return m_logaddrchars; } // unmap ranges (short form) - void unmap_read(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0, u16 flags = 0) { unmap_generic(addrstart, addrend, addrmirror, flags, read_or_write::READ, false); } - void unmap_write(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0, u16 flags = 0) { unmap_generic(addrstart, addrend, addrmirror, flags, read_or_write::WRITE, false); } - void unmap_readwrite(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0, u16 flags = 0) { unmap_generic(addrstart, addrend, addrmirror, flags, read_or_write::READWRITE, false); } - void nop_read(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0, u16 flags = 0) { unmap_generic(addrstart, addrend, addrmirror, flags, read_or_write::READ, true); } - void nop_write(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0, u16 flags = 0) { unmap_generic(addrstart, addrend, addrmirror, flags, read_or_write::WRITE, true); } - void nop_readwrite(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0, u16 flags = 0) { unmap_generic(addrstart, addrend, addrmirror, flags, read_or_write::READWRITE, true); } + void unmap_read(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0) { unmap_generic(addrstart, addrend, addrmirror, read_or_write::READ, false); } + void unmap_write(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0) { unmap_generic(addrstart, addrend, addrmirror, read_or_write::WRITE, false); } + void unmap_readwrite(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0) { unmap_generic(addrstart, addrend, addrmirror, read_or_write::READWRITE, false); } + void nop_read(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0) { unmap_generic(addrstart, addrend, addrmirror, read_or_write::READ, true); } + void nop_write(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0) { unmap_generic(addrstart, addrend, addrmirror, read_or_write::WRITE, true); } + void nop_readwrite(offs_t addrstart, offs_t addrend, offs_t addrmirror = 0) { unmap_generic(addrstart, addrend, addrmirror, read_or_write::READWRITE, true); } // install ports, banks, RAM (short form) - void install_read_port(offs_t addrstart, offs_t addrend, const char *rtag) { install_read_port(addrstart, addrend, 0, 0, rtag); } - void install_write_port(offs_t addrstart, offs_t addrend, const char *wtag) { install_write_port(addrstart, addrend, 0, 0, wtag); } - void install_readwrite_port(offs_t addrstart, offs_t addrend, const char *rtag, const char *wtag) { install_readwrite_port(addrstart, addrend, 0, 0, rtag, wtag); } - void install_read_bank(offs_t addrstart, offs_t addrend, memory_bank *bank) { install_read_bank(addrstart, addrend, 0, 0, bank); } - void install_write_bank(offs_t addrstart, offs_t addrend, memory_bank *bank) { install_write_bank(addrstart, addrend, 0, 0, bank); } - void install_readwrite_bank(offs_t addrstart, offs_t addrend, memory_bank *bank) { install_readwrite_bank(addrstart, addrend, 0, 0, bank); } - void install_rom(offs_t addrstart, offs_t addrend, void *baseptr) { install_rom(addrstart, addrend, 0, 0, baseptr); } - void install_writeonly(offs_t addrstart, offs_t addrend, void *baseptr) { install_writeonly(addrstart, addrend, 0, 0, baseptr); } - void install_ram(offs_t addrstart, offs_t addrend, void *baseptr) { install_ram(addrstart, addrend, 0, 0, baseptr); } + void install_read_port(offs_t addrstart, offs_t addrend, const char *rtag) { install_read_port(addrstart, addrend, 0, rtag); } + void install_write_port(offs_t addrstart, offs_t addrend, const char *wtag) { install_write_port(addrstart, addrend, 0, wtag); } + void install_readwrite_port(offs_t addrstart, offs_t addrend, const char *rtag, const char *wtag) { install_readwrite_port(addrstart, addrend, 0, rtag, wtag); } + void install_read_bank(offs_t addrstart, offs_t addrend, memory_bank *bank) { install_read_bank(addrstart, addrend, 0, bank); } + void install_write_bank(offs_t addrstart, offs_t addrend, memory_bank *bank) { install_write_bank(addrstart, addrend, 0, bank); } + void install_readwrite_bank(offs_t addrstart, offs_t addrend, memory_bank *bank) { install_readwrite_bank(addrstart, addrend, 0, bank); } + void install_rom(offs_t addrstart, offs_t addrend, void *baseptr) { install_rom(addrstart, addrend, 0, baseptr); } + void install_writeonly(offs_t addrstart, offs_t addrend, void *baseptr) { install_writeonly(addrstart, addrend, 0, baseptr); } + void install_ram(offs_t addrstart, offs_t addrend, void *baseptr) { install_ram(addrstart, addrend, 0, baseptr); } // install ports, banks, RAM (with mirror/mask) - void install_read_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *rtag) { install_readwrite_port(addrstart, addrend, addrmirror, 0, rtag, ""); } - void install_write_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *wtag) { install_readwrite_port(addrstart, addrend, addrmirror, 0, "", wtag); } - void install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string rtag, std::string wtag) { install_readwrite_port(addrstart, addrend, addrmirror, 0, rtag, wtag); } - void install_read_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *bank) { install_bank_generic(addrstart, addrend, addrmirror, 0, bank, nullptr); } - void install_write_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *bank) { install_bank_generic(addrstart, addrend, addrmirror, 0, nullptr, bank); } - void install_readwrite_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *bank) { install_bank_generic(addrstart, addrend, addrmirror, 0, bank, bank); } - void install_rom(offs_t addrstart, offs_t addrend, offs_t addrmirror, void *baseptr) { install_ram_generic(addrstart, addrend, addrmirror, 0, read_or_write::READ, baseptr); } - void install_writeonly(offs_t addrstart, offs_t addrend, offs_t addrmirror, void *baseptr) { install_ram_generic(addrstart, addrend, addrmirror, 0, read_or_write::WRITE, baseptr); } - void install_ram(offs_t addrstart, offs_t addrend, offs_t addrmirror, void *baseptr) { install_ram_generic(addrstart, addrend, addrmirror, 0, read_or_write::READWRITE, baseptr); } - - // install ports, banks, RAM (with mirror/mask/flags) - void install_read_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, const char *rtag) { install_readwrite_port(addrstart, addrend, addrmirror, flags, rtag, ""); } - void install_write_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, const char *wtag) { install_readwrite_port(addrstart, addrend, addrmirror, flags, "", wtag); } - virtual void install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, std::string rtag, std::string wtag) = 0; - void install_read_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, memory_bank *bank) { install_bank_generic(addrstart, addrend, addrmirror, flags, bank, nullptr); } - void install_write_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, memory_bank *bank) { install_bank_generic(addrstart, addrend, addrmirror, flags, nullptr, bank); } - void install_readwrite_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, memory_bank *bank) { install_bank_generic(addrstart, addrend, addrmirror, flags, bank, bank); } - void install_rom(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, void *baseptr) { install_ram_generic(addrstart, addrend, addrmirror, flags, read_or_write::READ, baseptr); } - void install_writeonly(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, void *baseptr) { install_ram_generic(addrstart, addrend, addrmirror, flags, read_or_write::WRITE, baseptr); } - void install_ram(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, void *baseptr) { install_ram_generic(addrstart, addrend, addrmirror, flags, read_or_write::READWRITE, baseptr); } + void install_read_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *rtag) { install_readwrite_port(addrstart, addrend, addrmirror, rtag, ""); } + void install_write_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, const char *wtag) { install_readwrite_port(addrstart, addrend, addrmirror, "", wtag); } + virtual void install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string rtag, std::string wtag) = 0; + void install_read_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *bank) { install_bank_generic(addrstart, addrend, addrmirror, bank, nullptr); } + void install_write_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *bank) { install_bank_generic(addrstart, addrend, addrmirror, nullptr, bank); } + void install_readwrite_bank(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *bank) { install_bank_generic(addrstart, addrend, addrmirror, bank, bank); } + void install_rom(offs_t addrstart, offs_t addrend, offs_t addrmirror, void *baseptr) { install_ram_generic(addrstart, addrend, addrmirror, read_or_write::READ, baseptr); } + void install_writeonly(offs_t addrstart, offs_t addrend, offs_t addrmirror, void *baseptr) { install_ram_generic(addrstart, addrend, addrmirror, read_or_write::WRITE, baseptr); } + void install_ram(offs_t addrstart, offs_t addrend, offs_t addrmirror, void *baseptr) { install_ram_generic(addrstart, addrend, addrmirror, read_or_write::READWRITE, baseptr); } // install device memory maps - template void install_device(offs_t addrstart, offs_t addrend, T &device, void (T::*map)(address_map &map), u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { + template void install_device(offs_t addrstart, offs_t addrend, T &device, void (T::*map)(address_map &map), u64 unitmask = 0, int cswidth = 0) { address_map_constructor delegate(map, "dynamic_device_install", &device); - install_device_delegate(addrstart, addrend, device, delegate, unitmask, cswidth, flags); + install_device_delegate(addrstart, addrend, device, delegate, unitmask, cswidth); } - virtual void install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &map, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &map, u64 unitmask = 0, int cswidth = 0) = 0; // install taps without mirroring memory_passthrough_handler *install_read_tap(offs_t addrstart, offs_t addrend, std::string name, std::function tap, memory_passthrough_handler *mph = nullptr) { return install_read_tap(addrstart, addrend, 0, name, tap, mph); } @@ -1719,167 +1346,167 @@ public: virtual void install_view(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_view &view) = 0; // install new-style delegate handlers (short form) - void install_read_handler(offs_t addrstart, offs_t addrend, read8_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read8_delegate rhandler, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } - void install_read_handler(offs_t addrstart, offs_t addrend, read16_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read16_delegate rhandler, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } - void install_read_handler(offs_t addrstart, offs_t addrend, read32_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read32_delegate rhandler, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } - void install_read_handler(offs_t addrstart, offs_t addrend, read64_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read64_delegate rhandler, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } + void install_read_handler(offs_t addrstart, offs_t addrend, read8_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read8_delegate rhandler, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } + void install_read_handler(offs_t addrstart, offs_t addrend, read16_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read16_delegate rhandler, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } + void install_read_handler(offs_t addrstart, offs_t addrend, read32_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read32_delegate rhandler, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } + void install_read_handler(offs_t addrstart, offs_t addrend, read64_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read64_delegate rhandler, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read8m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read8m_delegate rhandler, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } - void install_read_handler(offs_t addrstart, offs_t addrend, read16m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read16m_delegate rhandler, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } - void install_read_handler(offs_t addrstart, offs_t addrend, read32m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read32m_delegate rhandler, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } - void install_read_handler(offs_t addrstart, offs_t addrend, read64m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read64m_delegate rhandler, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } + void install_read_handler(offs_t addrstart, offs_t addrend, read8m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read8m_delegate rhandler, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } + void install_read_handler(offs_t addrstart, offs_t addrend, read16m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read16m_delegate rhandler, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } + void install_read_handler(offs_t addrstart, offs_t addrend, read32m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read32m_delegate rhandler, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } + void install_read_handler(offs_t addrstart, offs_t addrend, read64m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read64m_delegate rhandler, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read8s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read8s_delegate rhandler, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } - void install_read_handler(offs_t addrstart, offs_t addrend, read16s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read16s_delegate rhandler, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } - void install_read_handler(offs_t addrstart, offs_t addrend, read32s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read32s_delegate rhandler, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } - void install_read_handler(offs_t addrstart, offs_t addrend, read64s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read64s_delegate rhandler, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } + void install_read_handler(offs_t addrstart, offs_t addrend, read8s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read8s_delegate rhandler, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } + void install_read_handler(offs_t addrstart, offs_t addrend, read16s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read16s_delegate rhandler, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } + void install_read_handler(offs_t addrstart, offs_t addrend, read32s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read32s_delegate rhandler, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } + void install_read_handler(offs_t addrstart, offs_t addrend, read64s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read64s_delegate rhandler, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read8sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read8sm_delegate rhandler, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } - void install_read_handler(offs_t addrstart, offs_t addrend, read16sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read16sm_delegate rhandler, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } - void install_read_handler(offs_t addrstart, offs_t addrend, read32sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read32sm_delegate rhandler, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } - void install_read_handler(offs_t addrstart, offs_t addrend, read64sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read64sm_delegate rhandler, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } + void install_read_handler(offs_t addrstart, offs_t addrend, read8sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read8sm_delegate rhandler, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } + void install_read_handler(offs_t addrstart, offs_t addrend, read16sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read16sm_delegate rhandler, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } + void install_read_handler(offs_t addrstart, offs_t addrend, read32sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read32sm_delegate rhandler, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } + void install_read_handler(offs_t addrstart, offs_t addrend, read64sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read64sm_delegate rhandler, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read8mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read8mo_delegate rhandler, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } - void install_read_handler(offs_t addrstart, offs_t addrend, read16mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read16mo_delegate rhandler, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } - void install_read_handler(offs_t addrstart, offs_t addrend, read32mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read32mo_delegate rhandler, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } - void install_read_handler(offs_t addrstart, offs_t addrend, read64mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read64mo_delegate rhandler, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } + void install_read_handler(offs_t addrstart, offs_t addrend, read8mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read8mo_delegate rhandler, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } + void install_read_handler(offs_t addrstart, offs_t addrend, read16mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read16mo_delegate rhandler, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } + void install_read_handler(offs_t addrstart, offs_t addrend, read32mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read32mo_delegate rhandler, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } + void install_read_handler(offs_t addrstart, offs_t addrend, read64mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read64mo_delegate rhandler, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } - void install_read_handler(offs_t addrstart, offs_t addrend, read8smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read8smo_delegate rhandler, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } - void install_read_handler(offs_t addrstart, offs_t addrend, read16smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read16smo_delegate rhandler, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } - void install_read_handler(offs_t addrstart, offs_t addrend, read32smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read32smo_delegate rhandler, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } - void install_read_handler(offs_t addrstart, offs_t addrend, read64smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth, flags); } - void install_write_handler(offs_t addrstart, offs_t addrend, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth, flags); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, read64smo_delegate rhandler, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) { install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth, flags); } + void install_read_handler(offs_t addrstart, offs_t addrend, read8smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read8smo_delegate rhandler, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } + void install_read_handler(offs_t addrstart, offs_t addrend, read16smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read16smo_delegate rhandler, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } + void install_read_handler(offs_t addrstart, offs_t addrend, read32smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read32smo_delegate rhandler, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { return install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } + void install_read_handler(offs_t addrstart, offs_t addrend, read64smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) { install_read_handler(addrstart, addrend, 0, 0, 0, rhandler, unitmask, cswidth); } + void install_write_handler(offs_t addrstart, offs_t addrend, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_write_handler(addrstart, addrend, 0, 0, 0, whandler, unitmask, cswidth); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, read64smo_delegate rhandler, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) { install_readwrite_handler(addrstart, addrend, 0, 0, 0, rhandler, whandler, unitmask, cswidth); } // install new-style delegate handlers (with mirror/mask) - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8m_delegate rhandler, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16m_delegate rhandler, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32m_delegate rhandler, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64m_delegate rhandler, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8m_delegate rhandler, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16m_delegate rhandler, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32m_delegate rhandler, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64m_delegate rhandler, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8s_delegate rhandler, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16s_delegate rhandler, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32s_delegate rhandler, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64s_delegate rhandler, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8s_delegate rhandler, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16s_delegate rhandler, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32s_delegate rhandler, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64s_delegate rhandler, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8sm_delegate rhandler, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16sm_delegate rhandler, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32sm_delegate rhandler, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64sm_delegate rhandler, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8sm_delegate rhandler, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16sm_delegate rhandler, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32sm_delegate rhandler, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64sm_delegate rhandler, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8mo_delegate rhandler, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16mo_delegate rhandler, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32mo_delegate rhandler, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64mo_delegate rhandler, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8mo_delegate rhandler, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16mo_delegate rhandler, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32mo_delegate rhandler, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64mo_delegate rhandler, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8smo_delegate rhandler, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16smo_delegate rhandler, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32smo_delegate rhandler, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; - virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64smo_delegate rhandler, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8smo_delegate rhandler, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16smo_delegate rhandler, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32smo_delegate rhandler, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; + virtual void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64smo_delegate rhandler, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) = 0; protected: - virtual void unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, read_or_write readorwrite, bool quiet) = 0; - virtual void install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, read_or_write readorwrite, void *baseptr) = 0; - virtual void install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, memory_bank *rbank, memory_bank *wbank) = 0; + virtual void unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, bool quiet) = 0; + virtual void install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, void *baseptr) = 0; + virtual void install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *rbank, memory_bank *wbank) = 0; void populate_map_entry(const address_map_entry &entry, read_or_write readorwrite); void adjust_addresses(offs_t &start, offs_t &end, offs_t &mask, offs_t &mirror) { @@ -2024,36 +1651,6 @@ public: virtual void write_qword_unaligned(offs_t address, u64 data) = 0; virtual void write_qword_unaligned(offs_t address, u64 data, u64 mask) = 0; - // read accessors with flags - virtual std::pair read_byte_flags(offs_t address) = 0; - virtual std::pair read_word_flags(offs_t address) = 0; - virtual std::pair read_word_flags(offs_t address, u16 mask) = 0; - virtual std::pair read_word_unaligned_flags(offs_t address) = 0; - virtual std::pair read_word_unaligned_flags(offs_t address, u16 mask) = 0; - virtual std::pair read_dword_flags(offs_t address) = 0; - virtual std::pair read_dword_flags(offs_t address, u32 mask) = 0; - virtual std::pair read_dword_unaligned_flags(offs_t address) = 0; - virtual std::pair read_dword_unaligned_flags(offs_t address, u32 mask) = 0; - virtual std::pair read_qword_flags(offs_t address) = 0; - virtual std::pair read_qword_flags(offs_t address, u64 mask) = 0; - virtual std::pair read_qword_unaligned_flags(offs_t address) = 0; - virtual std::pair read_qword_unaligned_flags(offs_t address, u64 mask) = 0; - - // write accessors with flags - virtual u16 write_byte_flags(offs_t address, u8 data) = 0; - virtual u16 write_word_flags(offs_t address, u16 data) = 0; - virtual u16 write_word_flags(offs_t address, u16 data, u16 mask) = 0; - virtual u16 write_word_unaligned_flags(offs_t address, u16 data) = 0; - virtual u16 write_word_unaligned_flags(offs_t address, u16 data, u16 mask) = 0; - virtual u16 write_dword_flags(offs_t address, u32 data) = 0; - virtual u16 write_dword_flags(offs_t address, u32 data, u32 mask) = 0; - virtual u16 write_dword_unaligned_flags(offs_t address, u32 data) = 0; - virtual u16 write_dword_unaligned_flags(offs_t address, u32 data, u32 mask) = 0; - virtual u16 write_qword_flags(offs_t address, u64 data) = 0; - virtual u16 write_qword_flags(offs_t address, u64 data, u64 mask) = 0; - virtual u16 write_qword_unaligned_flags(offs_t address, u64 data) = 0; - virtual u16 write_qword_unaligned_flags(offs_t address, u64 data, u64 mask) = 0; - // setup void prepare_map(); void prepare_device_map(address_map &map); diff --git a/src/emu/emumem_aspace.cpp b/src/emu/emumem_aspace.cpp index 5aeea17..1882544 100644 --- a/src/emu/emumem_aspace.cpp +++ b/src/emu/emumem_aspace.cpp @@ -131,162 +131,162 @@ public: std::string get_handler_string(read_or_write readorwrite, offs_t byteaddress) const override; void dump_maps(std::vector &read_map, std::vector &write_map) const override; - void unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, read_or_write readorwrite, bool quiet) override; - void install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, read_or_write readorwrite, void *baseptr) override; - void install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, memory_bank *rbank, memory_bank *wbank) override; - void install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, std::string rtag, std::string wtag) override; - void install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &map, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override; + void unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, bool quiet) override; + void install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, void *baseptr) override; + void install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *rbank, memory_bank *wbank) override; + void install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string rtag, std::string wtag) override; + void install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &map, u64 unitmask = 0, int cswidth = 0) override; void install_view(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_view &view) override; - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8m_delegate rhandler, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16m_delegate rhandler, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32m_delegate rhandler, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64m_delegate rhandler, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8m_delegate rhandler, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16m_delegate rhandler, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32m_delegate rhandler, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64m_delegate rhandler, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8s_delegate rhandler, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16s_delegate rhandler, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32s_delegate rhandler, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64s_delegate rhandler, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8s_delegate rhandler, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16s_delegate rhandler, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32s_delegate rhandler, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64s_delegate rhandler, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8sm_delegate rhandler, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16sm_delegate rhandler, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32sm_delegate rhandler, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64sm_delegate rhandler, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8sm_delegate rhandler, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16sm_delegate rhandler, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32sm_delegate rhandler, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64sm_delegate rhandler, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8mo_delegate rhandler, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16mo_delegate rhandler, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32mo_delegate rhandler, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64mo_delegate rhandler, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8mo_delegate rhandler, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16mo_delegate rhandler, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32mo_delegate rhandler, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64mo_delegate rhandler, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8smo_delegate rhandler, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16smo_delegate rhandler, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32smo_delegate rhandler, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64smo_delegate rhandler, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8smo_delegate rhandler, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16smo_delegate rhandler, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32smo_delegate rhandler, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64smo_delegate rhandler, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } using address_space::install_read_tap; using address_space::install_write_tap; @@ -300,10 +300,10 @@ public: address_space_specific(memory_manager &manager, device_memory_interface &memory, int spacenum, int address_width) : address_space(manager, memory, spacenum) { - m_unmap_r = new handler_entry_read_unmapped (this, 0); - m_unmap_w = new handler_entry_write_unmapped(this, 0); - m_nop_r = new handler_entry_read_nop (this, 0); - m_nop_w = new handler_entry_write_nop(this, 0); + m_unmap_r = new handler_entry_read_unmapped (this); + m_unmap_w = new handler_entry_write_unmapped(this); + m_nop_r = new handler_entry_read_nop (this); + m_nop_w = new handler_entry_write_nop(this); handler_entry::range r{ 0, 0xffffffff >> (32 - address_width) }; @@ -347,11 +347,6 @@ public: m_dispatch_write = m_root_write->get_dispatch(); } - virtual ~address_space_specific() { - m_root_read ->unref(); - m_root_write->unref(); - } - std::pair get_cache_info() override { std::pair rw; rw.first = m_root_read; @@ -433,106 +428,52 @@ public: return dispatch_read(offs_t(-1), offset & m_addrmask, mask, m_dispatch_read); } - std::pair read_native_flags(offs_t offset, NativeType mask) - { - return dispatch_read_flags(offs_t(-1), offset & m_addrmask, mask, m_dispatch_read); - } - // mask-less native read NativeType read_native(offs_t offset) { return dispatch_read(offs_t(-1), offset & m_addrmask, uX(0xffffffffffffffffU), m_dispatch_read); } - std::pair read_native_flags(offs_t offset) - { - return dispatch_read_flags(offs_t(-1), offset & m_addrmask, uX(0xffffffffffffffffU), m_dispatch_read); - } - // native write void write_native(offs_t offset, NativeType data, NativeType mask) { dispatch_write(offs_t(-1), offset & m_addrmask, data, mask, m_dispatch_write); } - u16 write_native_flags(offs_t offset, NativeType data, NativeType mask) - { - return dispatch_write_flags(offs_t(-1), offset & m_addrmask, data, mask, m_dispatch_write); - } - // mask-less native write void write_native(offs_t offset, NativeType data) { dispatch_write(offs_t(-1), offset & m_addrmask, data, uX(0xffffffffffffffffU), m_dispatch_write); } - u16 write_native_flags(offs_t offset, NativeType data) - { - return dispatch_write_flags(offs_t(-1), offset & m_addrmask, data, uX(0xffffffffffffffffU), m_dispatch_write); - } - - auto rop() { return [this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }; } - auto ropf() { return [this](offs_t offset, NativeType mask) -> std::pair { return read_native_flags(offset, mask); }; } - auto wop() { return [this](offs_t offset, NativeType data, NativeType mask) -> void { write_native(offset, data, mask); }; } - auto wopf() { return [this](offs_t offset, NativeType data, NativeType mask) -> u16 { return write_native_flags(offset, data, mask); }; } - // virtual access to these functions - u8 read_byte(offs_t address) override { if constexpr(Width == 0) return read_native(address & ~NATIVE_MASK); else return memory_read_generic(rop(), address, 0xff); } - u16 read_word(offs_t address) override { if constexpr(Width == 1) return read_native(address & ~NATIVE_MASK); else return memory_read_generic(rop(), address, 0xffff); } - u16 read_word(offs_t address, u16 mask) override { return memory_read_generic(rop(), address, mask); } - u16 read_word_unaligned(offs_t address) override { return memory_read_generic(rop(), address, 0xffff); } - u16 read_word_unaligned(offs_t address, u16 mask) override { return memory_read_generic(rop(), address, mask); } - u32 read_dword(offs_t address) override { if constexpr(Width == 2) return read_native(address & ~NATIVE_MASK); else return memory_read_generic(rop(), address, 0xffffffff); } - u32 read_dword(offs_t address, u32 mask) override { return memory_read_generic(rop(), address, mask); } - u32 read_dword_unaligned(offs_t address) override { return memory_read_generic(rop(), address, 0xffffffff); } - u32 read_dword_unaligned(offs_t address, u32 mask) override { return memory_read_generic(rop(), address, mask); } - u64 read_qword(offs_t address) override { if constexpr(Width == 3) return read_native(address & ~NATIVE_MASK); else return memory_read_generic(rop(), address, 0xffffffffffffffffU); } - u64 read_qword(offs_t address, u64 mask) override { return memory_read_generic(rop(), address, mask); } - u64 read_qword_unaligned(offs_t address) override { return memory_read_generic(rop(), address, 0xffffffffffffffffU); } - u64 read_qword_unaligned(offs_t address, u64 mask) override { return memory_read_generic(rop(), address, mask); } - - void write_byte(offs_t address, u8 data) override { if constexpr(Width == 0) write_native(address & ~NATIVE_MASK, data); else memory_write_generic(wop(), address, data, 0xff); } - void write_word(offs_t address, u16 data) override { if constexpr(Width == 1) write_native(address & ~NATIVE_MASK, data); else memory_write_generic(wop(), address, data, 0xffff); } - void write_word(offs_t address, u16 data, u16 mask) override { memory_write_generic(wop(), address, data, mask); } - void write_word_unaligned(offs_t address, u16 data) override { memory_write_generic(wop(), address, data, 0xffff); } - void write_word_unaligned(offs_t address, u16 data, u16 mask) override { memory_write_generic(wop(), address, data, mask); } - void write_dword(offs_t address, u32 data) override { if constexpr(Width == 2) write_native(address & ~NATIVE_MASK, data); else memory_write_generic(wop(), address, data, 0xffffffff); } - void write_dword(offs_t address, u32 data, u32 mask) override { memory_write_generic(wop(), address, data, mask); } - void write_dword_unaligned(offs_t address, u32 data) override { memory_write_generic(wop(), address, data, 0xffffffff); } - void write_dword_unaligned(offs_t address, u32 data, u32 mask) override { memory_write_generic(wop(), address, data, mask); } - void write_qword(offs_t address, u64 data) override { if constexpr(Width == 3) write_native(address & ~NATIVE_MASK, data); else memory_write_generic(wop(), address, data, 0xffffffffffffffffU); } - void write_qword(offs_t address, u64 data, u64 mask) override { memory_write_generic(wop(), address, data, mask); } - void write_qword_unaligned(offs_t address, u64 data) override { memory_write_generic(wop(), address, data, 0xffffffffffffffffU); } - void write_qword_unaligned(offs_t address, u64 data, u64 mask) override { memory_write_generic(wop(), address, data, mask); } - - std::pair read_byte_flags(offs_t address) override { if constexpr(Width == 0) return read_native_flags(address & ~NATIVE_MASK); else return memory_read_generic_flags(ropf(), address, 0xff); } - std::pair read_word_flags(offs_t address) override { if constexpr(Width == 1) return read_native_flags(address & ~NATIVE_MASK); else return memory_read_generic_flags(ropf(), address, 0xffff); } - std::pair read_word_flags(offs_t address, u16 mask) override { return memory_read_generic_flags(ropf(), address, mask); } - std::pair read_word_unaligned_flags(offs_t address) override { return memory_read_generic_flags(ropf(), address, 0xffff); } - std::pair read_word_unaligned_flags(offs_t address, u16 mask) override { return memory_read_generic_flags(ropf(), address, mask); } - std::pair read_dword_flags(offs_t address) override { if constexpr(Width == 2) return read_native_flags(address & ~NATIVE_MASK); else return memory_read_generic_flags(ropf(), address, 0xffffffff); } - std::pair read_dword_flags(offs_t address, u32 mask) override { return memory_read_generic_flags(ropf(), address, mask); } - std::pair read_dword_unaligned_flags(offs_t address) override { return memory_read_generic_flags(ropf(), address, 0xffffffff); } - std::pair read_dword_unaligned_flags(offs_t address, u32 mask) override { return memory_read_generic_flags(ropf(), address, mask); } - std::pair read_qword_flags(offs_t address) override { if constexpr(Width == 3) return read_native_flags(address & ~NATIVE_MASK); else return memory_read_generic_flags(ropf(), address, 0xffffffffffffffffU); } - std::pair read_qword_flags(offs_t address, u64 mask) override { return memory_read_generic_flags(ropf(), address, mask); } - std::pair read_qword_unaligned_flags(offs_t address) override { return memory_read_generic_flags(ropf(), address, 0xffffffffffffffffU); } - std::pair read_qword_unaligned_flags(offs_t address, u64 mask) override { return memory_read_generic_flags(ropf(), address, mask); } - - u16 write_byte_flags(offs_t address, u8 data) override { if constexpr(Width == 0) return write_native_flags(address & ~NATIVE_MASK, data); else return memory_write_generic_flags(wopf(), address, data, 0xff); } - u16 write_word_flags(offs_t address, u16 data) override { if constexpr(Width == 1) return write_native_flags(address & ~NATIVE_MASK, data); else return memory_write_generic_flags(wopf(), address, data, 0xffff); } - u16 write_word_flags(offs_t address, u16 data, u16 mask) override { return memory_write_generic_flags(wopf(), address, data, mask); } - u16 write_word_unaligned_flags(offs_t address, u16 data) override { return memory_write_generic_flags(wopf(), address, data, 0xffff); } - u16 write_word_unaligned_flags(offs_t address, u16 data, u16 mask) override { return memory_write_generic_flags(wopf(), address, data, mask); } - u16 write_dword_flags(offs_t address, u32 data) override { if constexpr(Width == 2) return write_native_flags(address & ~NATIVE_MASK, data); else return memory_write_generic_flags(wopf(), address, data, 0xffffffff); } - u16 write_dword_flags(offs_t address, u32 data, u32 mask) override { return memory_write_generic_flags(wopf(), address, data, mask); } - u16 write_dword_unaligned_flags(offs_t address, u32 data) override { return memory_write_generic_flags(wopf(), address, data, 0xffffffff); } - u16 write_dword_unaligned_flags(offs_t address, u32 data, u32 mask) override { return memory_write_generic_flags(wopf(), address, data, mask); } - u16 write_qword_flags(offs_t address, u64 data) override { if constexpr(Width == 3) return write_native_flags(address & ~NATIVE_MASK, data); else return memory_write_generic_flags(wopf(), address, data, 0xffffffffffffffffU); } - u16 write_qword_flags(offs_t address, u64 data, u64 mask) override { return memory_write_generic_flags(wopf(), address, data, mask); } - u16 write_qword_unaligned_flags(offs_t address, u64 data) override { return memory_write_generic_flags(wopf(), address, data, 0xffffffffffffffffU); } - u16 write_qword_unaligned_flags(offs_t address, u64 data, u64 mask) override { return memory_write_generic_flags(wopf(), address, data, mask); } + u8 read_byte(offs_t address) override { return Width == 0 ? read_native(address & ~NATIVE_MASK) : memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xff); } + u16 read_word(offs_t address) override { return Width == 1 ? read_native(address & ~NATIVE_MASK) : memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffff); } + u16 read_word(offs_t address, u16 mask) override { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } + u16 read_word_unaligned(offs_t address) override { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffff); } + u16 read_word_unaligned(offs_t address, u16 mask) override { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } + u32 read_dword(offs_t address) override { return Width == 2 ? read_native(address & ~NATIVE_MASK) : memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffff); } + u32 read_dword(offs_t address, u32 mask) override { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } + u32 read_dword_unaligned(offs_t address) override { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffff); } + u32 read_dword_unaligned(offs_t address, u32 mask) override { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } + u64 read_qword(offs_t address) override { return Width == 3 ? read_native(address & ~NATIVE_MASK) : memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffffffffffffU); } + u64 read_qword(offs_t address, u64 mask) override { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } + u64 read_qword_unaligned(offs_t address) override { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, 0xffffffffffffffffU); } + u64 read_qword_unaligned(offs_t address, u64 mask) override { return memory_read_generic([this](offs_t offset, NativeType mask) -> NativeType { return read_native(offset, mask); }, address, mask); } + void write_byte(offs_t address, u8 data) override { if (Width == 0) write_native(address & ~NATIVE_MASK, data); else memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xff); } + void write_word(offs_t address, u16 data) override { if (Width == 1) write_native(address & ~NATIVE_MASK, data); else memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffff); } + void write_word(offs_t address, u16 data, u16 mask) override { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } + void write_word_unaligned(offs_t address, u16 data) override { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffff); } + void write_word_unaligned(offs_t address, u16 data, u16 mask) override { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } + void write_dword(offs_t address, u32 data) override { if (Width == 2) write_native(address & ~NATIVE_MASK, data); else memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffff); } + void write_dword(offs_t address, u32 data, u32 mask) override { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } + void write_dword_unaligned(offs_t address, u32 data) override { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffff); } + void write_dword_unaligned(offs_t address, u32 data, u32 mask) override { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } + void write_qword(offs_t address, u64 data) override { if (Width == 3) write_native(address & ~NATIVE_MASK, data); else memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffffffffffffU); } + void write_qword(offs_t address, u64 data, u64 mask) override { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } + void write_qword_unaligned(offs_t address, u64 data) override { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, 0xffffffffffffffffU); } + void write_qword_unaligned(offs_t address, u64 data, u64 mask) override { memory_write_generic([this](offs_t offset, NativeType data, NativeType mask) { write_native(offset, data, mask); }, address, data, mask); } // static access to these functions static u8 read_byte_static(this_type &space, offs_t address) { return Width == 0 ? space.read_native(address & ~NATIVE_MASK) : memory_read_generic([&space](offs_t offset, NativeType mask) -> NativeType { return space.read_native(offset, mask); }, address, 0xff); } @@ -557,29 +498,29 @@ public: private: template - void install_read_handler_impl(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, u16 flags, READ &handler_r) + void install_read_handler_impl(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, READ &handler_r) { try { handler_r.resolve(); } catch (const binding_type_exception &) { osd_printf_error("Binding error while installing read handler %s for range 0x%X-0x%X mask 0x%X mirror 0x%X select 0x%X umask 0x%X\n", handler_r.name(), addrstart, addrend, addrmask, addrmirror, addrselect, unitmask); throw; } - install_read_handler_helper::value>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, handler_r); + install_read_handler_helper::value>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler_r); } template - void install_write_handler_impl(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, u16 flags, WRITE &handler_w) + void install_write_handler_impl(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, WRITE &handler_w) { try { handler_w.resolve(); } catch (const binding_type_exception &) { osd_printf_error("Binding error while installing write handler %s for range 0x%X-0x%X mask 0x%X mirror 0x%X select 0x%X umask 0x%X\n", handler_w.name(), addrstart, addrend, addrmask, addrmirror, addrselect, unitmask); throw; } - install_write_handler_helper::value>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, handler_w); + install_write_handler_helper::value>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler_w); } template - void install_readwrite_handler_impl(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, u16 flags, READ &handler_r, WRITE &handler_w) + void install_readwrite_handler_impl(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, READ &handler_r, WRITE &handler_w) { static_assert(handler_width::value == handler_width::value, "handler widths do not match"); try { handler_r.resolve(); } @@ -592,11 +533,11 @@ private: osd_printf_error("Binding error while installing write handler %s for range 0x%X-0x%X mask 0x%X mirror 0x%X select 0x%X umask 0x%X\n", handler_w.name(), addrstart, addrend, addrmask, addrmirror, addrselect, unitmask); throw; } - install_readwrite_handler_helper::value>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, handler_r, handler_w); + install_readwrite_handler_helper::value>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler_r, handler_w); } template - void install_read_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, u16 flags, const READ &handler_r) + void install_read_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, const READ &handler_r) { if constexpr (Width < AccessWidth) { fatalerror("install_read_handler: cannot install a %d-wide handler in a %d-wide bus", 8 << AccessWidth, 8 << Width); @@ -613,11 +554,11 @@ private: check_optimize_all("install_read_handler", 8 << AccessWidth, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth); if constexpr (Width == AccessWidth) { - auto hand_r = new handler_entry_read_delegate(this, flags, handler_r); + auto hand_r = new handler_entry_read_delegate(this, handler_r); hand_r->set_address_info(nstart, nmask); m_root_read->populate(nstart, nend, nmirror, hand_r); } else { - auto hand_r = new handler_entry_read_delegate(this, flags, handler_r); + auto hand_r = new handler_entry_read_delegate(this, handler_r); memory_units_descriptor descriptor(AccessWidth, Endian, hand_r, nstart, nend, nmask, nunitmask, ncswidth); hand_r->set_address_info(descriptor.get_handler_start(), descriptor.get_handler_mask()); m_root_read->populate_mismatched(nstart, nend, nmirror, descriptor); @@ -628,7 +569,7 @@ private: } template - void install_write_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, u16 flags, + void install_write_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, const WRITE &handler_w) { if constexpr (Width < AccessWidth) { @@ -646,11 +587,11 @@ private: check_optimize_all("install_write_handler", 8 << AccessWidth, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth); if constexpr (Width == AccessWidth) { - auto hand_w = new handler_entry_write_delegate(this, flags, handler_w); + auto hand_w = new handler_entry_write_delegate(this, handler_w); hand_w->set_address_info(nstart, nmask); m_root_write->populate(nstart, nend, nmirror, hand_w); } else { - auto hand_w = new handler_entry_write_delegate(this, flags, handler_w); + auto hand_w = new handler_entry_write_delegate(this, handler_w); memory_units_descriptor descriptor(AccessWidth, Endian, hand_w, nstart, nend, nmask, nunitmask, ncswidth); hand_w->set_address_info(descriptor.get_handler_start(), descriptor.get_handler_mask()); m_root_write->populate_mismatched(nstart, nend, nmirror, descriptor); @@ -661,7 +602,7 @@ private: } template - void install_readwrite_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, u16 flags, + void install_readwrite_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, const READ &handler_r, const WRITE &handler_w) { @@ -680,21 +621,21 @@ private: check_optimize_all("install_readwrite_handler", 8 << AccessWidth, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth); if constexpr (Width == AccessWidth) { - auto hand_r = new handler_entry_read_delegate (this, flags, handler_r); + auto hand_r = new handler_entry_read_delegate (this, handler_r); hand_r->set_address_info(nstart, nmask); m_root_read ->populate(nstart, nend, nmirror, hand_r); - auto hand_w = new handler_entry_write_delegate(this, flags, handler_w); + auto hand_w = new handler_entry_write_delegate(this, handler_w); hand_w->set_address_info(nstart, nmask); m_root_write->populate(nstart, nend, nmirror, hand_w); } else { - auto hand_r = new handler_entry_read_delegate (this, flags, handler_r); + auto hand_r = new handler_entry_read_delegate (this, handler_r); memory_units_descriptor descriptor(AccessWidth, Endian, hand_r, nstart, nend, nmask, nunitmask, ncswidth); hand_r->set_address_info(descriptor.get_handler_start(), descriptor.get_handler_mask()); m_root_read ->populate_mismatched(nstart, nend, nmirror, descriptor); hand_r->unref(); - auto hand_w = new handler_entry_write_delegate(this, flags, handler_w); + auto hand_w = new handler_entry_write_delegate(this, handler_w); descriptor.set_subunit_handler(hand_w); hand_w->set_address_info(descriptor.get_handler_start(), descriptor.get_handler_mask()); m_root_write->populate_mismatched(nstart, nend, nmirror, descriptor); @@ -1034,7 +975,7 @@ template void address_ // unmap - unmap a section of address space //------------------------------------------------- -template void address_space_specific::unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, read_or_write readorwrite, bool quiet) +template void address_space_specific::unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, bool quiet) { VPRINTF("address_space::unmap(%*x-%*x mirror=%*x, %s, %s)\n", m_addrchars, addrstart, m_addrchars, addrend, @@ -1047,22 +988,14 @@ template void address_ // read space if (readorwrite == read_or_write::READ || readorwrite == read_or_write::READWRITE) { - auto handler = flags ? (quiet ? - static_cast *>(new handler_entry_read_nop(this, flags)) : - static_cast *>(new handler_entry_read_unmapped(this, flags))) - : (quiet ? static_cast *>(m_nop_r) : - static_cast *>(m_unmap_r)); + auto handler = static_cast *>(quiet ? m_nop_r : m_unmap_r); handler->ref(); m_root_read->populate(nstart, nend, nmirror, handler); } // write space if (readorwrite == read_or_write::WRITE || readorwrite == read_or_write::READWRITE) { - auto handler = flags ? (quiet ? - static_cast *>(new handler_entry_write_nop(this, flags)) : - static_cast *>(new handler_entry_write_unmapped(this, flags))) - : (quiet ? static_cast *>(m_nop_w) : - static_cast *>(m_unmap_w)); + auto handler = static_cast *>(quiet ? m_nop_w : m_unmap_w); handler->ref(); m_root_write->populate(nstart, nend, nmirror, handler); } @@ -1162,10 +1095,10 @@ template memory_passth // of a live device into this address space //------------------------------------------------- - template void address_space_specific::install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &delegate, u64 unitmask, int cswidth, u16 flags) +template void address_space_specific::install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &delegate, u64 unitmask, int cswidth) { check_address("install_device_delegate", addrstart, addrend); - address_map map(*this, addrstart, addrend, unitmask, cswidth, flags, m_device, delegate); + address_map map(*this, addrstart, addrend, unitmask, cswidth, m_device, delegate); map.import_submaps(m_manager.machine(), device, data_width(), endianness(), addr_shift()); prepare_device_map(map); populate_from_map(&map); @@ -1178,7 +1111,7 @@ template memory_passth // handler into this address space //------------------------------------------------- - template void address_space_specific::install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, std::string rtag, std::string wtag) +template void address_space_specific::install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string rtag, std::string wtag) { VPRINTF("address_space::install_readwrite_port(%*x-%*x mirror=%*x, read=\"%s\" / write=\"%s\")\n", m_addrchars, addrstart, m_addrchars, addrend, @@ -1197,7 +1130,7 @@ template memory_passth throw emu_fatalerror("Attempted to map non-existent port '%s' for read in space %s of device '%s'\n", rtag, m_name, m_device.tag()); // map the range and set the ioport - auto hand_r = new handler_entry_read_ioport(this, flags, port); + auto hand_r = new handler_entry_read_ioport(this, port); m_root_read->populate(nstart, nend, nmirror, hand_r); } @@ -1209,7 +1142,7 @@ template memory_passth fatalerror("Attempted to map non-existent port '%s' for write in space %s of device '%s'\n", wtag, m_name, m_device.tag()); // map the range and set the ioport - auto hand_w = new handler_entry_write_ioport(this, flags, port); + auto hand_w = new handler_entry_write_ioport(this, port); m_root_write->populate(nstart, nend, nmirror, hand_w); } @@ -1222,7 +1155,7 @@ template memory_passth // mapping to a particular bank //------------------------------------------------- -template void address_space_specific::install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, memory_bank *rbank, memory_bank *wbank) +template void address_space_specific::install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *rbank, memory_bank *wbank) { VPRINTF("address_space::install_readwrite_bank(%*x-%*x mirror=%*x, read=\"%s\" / write=\"%s\")\n", m_addrchars, addrstart, m_addrchars, addrend, @@ -1235,7 +1168,7 @@ template void address_ // map the read bank if (rbank != nullptr) { - auto hand_r = new handler_entry_read_memory_bank(this, flags, *rbank); + auto hand_r = new handler_entry_read_memory_bank(this, *rbank); hand_r->set_address_info(nstart, nmask); m_root_read->populate(nstart, nend, nmirror, hand_r); } @@ -1243,7 +1176,7 @@ template void address_ // map the write bank if (wbank != nullptr) { - auto hand_w = new handler_entry_write_memory_bank(this, flags, *wbank); + auto hand_w = new handler_entry_write_memory_bank(this, *wbank); hand_w->set_address_info(nstart, nmask); m_root_write->populate(nstart, nend, nmirror, hand_w); } @@ -1257,7 +1190,7 @@ template void address_ // RAM region into the given address space //------------------------------------------------- -template void address_space_specific::install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, read_or_write readorwrite, void *baseptr) +template void address_space_specific::install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, void *baseptr) { VPRINTF("address_space::install_ram_generic(%s-%s mirror=%s, %s, %p)\n", m_addrchars, addrstart, m_addrchars, addrend, @@ -1271,7 +1204,7 @@ template void address_ // map for read if (readorwrite == read_or_write::READ || readorwrite == read_or_write::READWRITE) { - auto hand_r = new handler_entry_read_memory(this, flags, baseptr); + auto hand_r = new handler_entry_read_memory(this, baseptr); hand_r->set_address_info(nstart, nmask); m_root_read->populate(nstart, nend, nmirror, hand_r); } @@ -1279,7 +1212,7 @@ template void address_ // map for write if (readorwrite == read_or_write::WRITE || readorwrite == read_or_write::READWRITE) { - auto hand_w = new handler_entry_write_memory(this, flags, baseptr); + auto hand_w = new handler_entry_write_memory(this, baseptr); hand_w->set_address_info(nstart, nmask); m_root_write->populate(nstart, nend, nmirror, hand_w); } diff --git a/src/emu/emumem_hedp.cpp b/src/emu/emumem_hedp.cpp index e3a5838..6f6cfea 100644 --- a/src/emu/emumem_hedp.cpp +++ b/src/emu/emumem_hedp.cpp @@ -70,11 +70,6 @@ template typename emu::detail::handler_ return read_impl(offset, mem_mask); } -template std::pair::uX, u16> handler_entry_read_delegate::read_flags(offs_t offset, uX mem_mask) const -{ - return std::pair(read_impl(offset, mem_mask), this->m_flags); -} - template std::string handler_entry_read_delegate::name() const { return m_delegate.name(); @@ -145,12 +140,6 @@ template void handler_entry_write_dele write_impl(offset, data, mem_mask); } -template u16 handler_entry_write_delegate::write_flags(offs_t offset, uX data, uX mem_mask) const -{ - write_impl(offset, data, mem_mask); - return this->m_flags; -} - template std::string handler_entry_write_delegate::name() const { return m_delegate.name(); @@ -164,11 +153,6 @@ template typename emu::detail::handler_entry_sizeread(); } -template std::pair::uX, u16> handler_entry_read_ioport::read_flags(offs_t offset, uX mem_mask) const -{ - return std::pair(m_port->read(), this->m_flags); -} - template std::string handler_entry_read_ioport::name() const { return m_port->tag(); @@ -179,12 +163,6 @@ template void handler_entry_write_ioportwrite(data, mem_mask); } -template u16 handler_entry_write_ioport::write_flags(offs_t offset, uX data, uX mem_mask) const -{ - m_port->write(data, mem_mask); - return this->m_flags; -} - template std::string handler_entry_write_ioport::name() const { return m_port->tag(); diff --git a/src/emu/emumem_hedp.h b/src/emu/emumem_hedp.h index 8055216..1481829 100644 --- a/src/emu/emumem_hedp.h +++ b/src/emu/emumem_hedp.h @@ -7,18 +7,17 @@ // handler_entry_read_delegate/handler_entry_write_delegate -// Executes an access through a delegate, usually containing a handler or a lambda +// Executes an access through called a delegate, usually containing a handler or a lambda template class handler_entry_read_delegate : public handler_entry_read_address { public: using uX = typename emu::detail::handler_entry_size::uX; - handler_entry_read_delegate(address_space *space, u16 flags, const READ &delegate) : handler_entry_read_address(space, flags), m_delegate(delegate) {} + handler_entry_read_delegate(address_space *space, const READ &delegate) : handler_entry_read_address(space, 0), m_delegate(delegate) {} ~handler_entry_read_delegate() = default; uX read(offs_t offset, uX mem_mask) const override; - std::pair read_flags(offs_t offset, uX mem_mask) const override; std::string name() const override; @@ -73,11 +72,10 @@ template class handler_entry_write_del public: using uX = typename emu::detail::handler_entry_size::uX; - handler_entry_write_delegate(address_space *space, u16 flags, const WRITE &delegate) : handler_entry_write_address(space, flags), m_delegate(delegate) {} + handler_entry_write_delegate(address_space *space, const WRITE &delegate) : handler_entry_write_address(space, 0), m_delegate(delegate) {} ~handler_entry_write_delegate() = default; void write(offs_t offset, uX data, uX mem_mask) const override; - u16 write_flags(offs_t offset, uX data, uX mem_mask) const override; std::string name() const override; @@ -137,11 +135,10 @@ template class handler_entry_read_ioport : public hand public: using uX = typename emu::detail::handler_entry_size::uX; - handler_entry_read_ioport(address_space *space, u16 flags, ioport_port *port) : handler_entry_read(space, flags), m_port(port) {} + handler_entry_read_ioport(address_space *space, ioport_port *port) : handler_entry_read(space, 0), m_port(port) {} ~handler_entry_read_ioport() = default; uX read(offs_t offset, uX mem_mask) const override; - std::pair read_flags(offs_t offset, uX mem_mask) const override; std::string name() const override; @@ -154,11 +151,10 @@ template class handler_entry_write_ioport : public han public: using uX = typename emu::detail::handler_entry_size::uX; - handler_entry_write_ioport(address_space *space, u16 flags, ioport_port *port) : handler_entry_write(space, flags), m_port(port) {} + handler_entry_write_ioport(address_space *space, ioport_port *port) : handler_entry_write(space, 0), m_port(port) {} ~handler_entry_write_ioport() = default; void write(offs_t offset, uX data, uX mem_mask) const override; - u16 write_flags(offs_t offset, uX data, uX mem_mask) const override; std::string name() const override; diff --git a/src/emu/emumem_hedr.h b/src/emu/emumem_hedr.h index 277cb2d..5a85464 100644 --- a/src/emu/emumem_hedr.h +++ b/src/emu/emumem_hedr.h @@ -22,7 +22,6 @@ public: ~handler_entry_read_dispatch(); uX read(offs_t offset, uX mem_mask) const override; - std::pair read_flags(offs_t offset, uX mem_mask) const override; void *get_ptr(offs_t offset) const override; void lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_read *&handler) const override; diff --git a/src/emu/emumem_hedr.ipp b/src/emu/emumem_hedr.ipp index 3a192e3..9e99675 100644 --- a/src/emu/emumem_hedr.ipp +++ b/src/emu/emumem_hedr.ipp @@ -132,11 +132,6 @@ template typename emu::detail::handler_e return dispatch_read(HIGHMASK, offset, mem_mask, m_a_dispatch); } -template std::pair::uX, u16> handler_entry_read_dispatch::read_flags(offs_t offset, uX mem_mask) const -{ - return dispatch_read_flags(HIGHMASK, offset, mem_mask, m_a_dispatch); -} - template void *handler_entry_read_dispatch::get_ptr(offs_t offset) const { return m_a_dispatch[(offset & HIGHMASK) >> LowBits]->get_ptr(offset); diff --git a/src/emu/emumem_hedw.h b/src/emu/emumem_hedw.h index 4c4f0f3..377cb37 100644 --- a/src/emu/emumem_hedw.h +++ b/src/emu/emumem_hedw.h @@ -22,7 +22,6 @@ public: ~handler_entry_write_dispatch(); void write(offs_t offset, uX data, uX mem_mask) const override; - u16 write_flags(offs_t offset, uX data, uX mem_mask) const override; void *get_ptr(offs_t offset) const override; void lookup(offs_t address, offs_t &start, offs_t &end, handler_entry_write *&handler) const override; diff --git a/src/emu/emumem_hedw.ipp b/src/emu/emumem_hedw.ipp index d9c4ced..a9b6981 100644 --- a/src/emu/emumem_hedw.ipp +++ b/src/emu/emumem_hedw.ipp @@ -131,11 +131,6 @@ template void handler_entry_write_dispat dispatch_write(HIGHMASK, offset, data, mem_mask, m_a_dispatch); } -template u16 handler_entry_write_dispatch::write_flags(offs_t offset, uX data, uX mem_mask) const -{ - return dispatch_write_flags(HIGHMASK, offset, data, mem_mask, m_a_dispatch); -} - template void *handler_entry_write_dispatch::get_ptr(offs_t offset) const { return m_a_dispatch[(offset & HIGHMASK) >> LowBits]->get_ptr(offset); diff --git a/src/emu/emumem_hem.cpp b/src/emu/emumem_hem.cpp index b835538..d7a41e1 100644 --- a/src/emu/emumem_hem.cpp +++ b/src/emu/emumem_hem.cpp @@ -10,11 +10,6 @@ template typename emu::detail::handler_entry_sizem_address_base) & this->m_address_mask) >> (Width + AddrShift)]; } -template std::pair::uX, u16> handler_entry_read_memory::read_flags(offs_t offset, uX mem_mask) const -{ - return std::pair(m_base[((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift)], this->m_flags); -} - template void *handler_entry_read_memory::get_ptr(offs_t offset) const { return m_base + (((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift)); @@ -32,24 +27,11 @@ template void handler_entry_write_memory u16 handler_entry_write_memory::write_flags(offs_t offset, uX data, uX mem_mask) const -{ - offs_t off = ((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift); - m_base[off] = (m_base[off] & ~mem_mask) | (data & mem_mask); - return this->m_flags; -} - template<> void handler_entry_write_memory<0, 0>::write(offs_t offset, u8 data, u8 mem_mask) const { m_base[(offset - this->m_address_base) & this->m_address_mask] = data; } -template<> u16 handler_entry_write_memory<0, 0>::write_flags(offs_t offset, u8 data, u8 mem_mask) const -{ - m_base[(offset - this->m_address_base) & this->m_address_mask] = data; - return this->m_flags; -} - template void *handler_entry_write_memory::get_ptr(offs_t offset) const { return m_base + (((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift)); @@ -69,11 +51,6 @@ template typename emu::detail::handler_entry_size(m_bank.base())[((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift)]; } -template std::pair::uX, u16> handler_entry_read_memory_bank::read_flags(offs_t offset, uX mem_mask) const -{ - return std::pair(static_cast(m_bank.base())[((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift)], this->m_flags); -} - template void *handler_entry_read_memory_bank::get_ptr(offs_t offset) const { return static_cast(m_bank.base()) + (((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift)); @@ -91,24 +68,11 @@ template void handler_entry_write_memory_bank(m_bank.base())[off] = (static_cast(m_bank.base())[off] & ~mem_mask) | (data & mem_mask); } -template u16 handler_entry_write_memory_bank::write_flags(offs_t offset, uX data, uX mem_mask) const -{ - offs_t off = ((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift); - static_cast(m_bank.base())[off] = (static_cast(m_bank.base())[off] & ~mem_mask) | (data & mem_mask); - return this->m_flags; -} - template<> void handler_entry_write_memory_bank<0, 0>::write(offs_t offset, u8 data, u8 mem_mask) const { static_cast(m_bank.base())[(offset - this->m_address_base) & this->m_address_mask] = data; } -template<> u16 handler_entry_write_memory_bank<0, 0>::write_flags(offs_t offset, u8 data, u8 mem_mask) const -{ - static_cast(m_bank.base())[(offset - this->m_address_base) & this->m_address_mask] = data; - return this->m_flags; -} - template void *handler_entry_write_memory_bank::get_ptr(offs_t offset) const { return static_cast(m_bank.base()) + (((offset - this->m_address_base) & this->m_address_mask) >> (Width + AddrShift)); diff --git a/src/emu/emumem_hem.h b/src/emu/emumem_hem.h index b5edc2c..a7d4653 100644 --- a/src/emu/emumem_hem.h +++ b/src/emu/emumem_hem.h @@ -14,11 +14,10 @@ template class handler_entry_read_memory : public hand public: using uX = typename emu::detail::handler_entry_size::uX; - handler_entry_read_memory(address_space *space, u16 flags, void *base) : handler_entry_read_address(space, flags), m_base(reinterpret_cast(base)) {} + handler_entry_read_memory(address_space *space, void *base) : handler_entry_read_address(space, 0), m_base(reinterpret_cast(base)) {} ~handler_entry_read_memory() = default; uX read(offs_t offset, uX mem_mask) const override; - std::pair read_flags(offs_t offset, uX mem_mask) const override; void *get_ptr(offs_t offset) const override; std::string name() const override; @@ -32,11 +31,10 @@ template class handler_entry_write_memory : public han public: using uX = typename emu::detail::handler_entry_size::uX; - handler_entry_write_memory(address_space *space, u16 flags, void *base) : handler_entry_write_address(space, flags), m_base(reinterpret_cast(base)) {} + handler_entry_write_memory(address_space *space, void *base) : handler_entry_write_address(space, 0), m_base(reinterpret_cast(base)) {} ~handler_entry_write_memory() = default; void write(offs_t offset, uX data, uX mem_mask) const override; - u16 write_flags(offs_t offset, uX data, uX mem_mask) const override; void *get_ptr(offs_t offset) const override; std::string name() const override; @@ -55,11 +53,10 @@ template class handler_entry_read_memory_bank : public public: using uX = typename emu::detail::handler_entry_size::uX; - handler_entry_read_memory_bank(address_space *space, u16 flags, memory_bank &bank) : handler_entry_read_address(space, flags), m_bank(bank) {} + handler_entry_read_memory_bank(address_space *space, memory_bank &bank) : handler_entry_read_address(space, 0), m_bank(bank) {} ~handler_entry_read_memory_bank() = default; uX read(offs_t offset, uX mem_mask) const override; - std::pair read_flags(offs_t offset, uX mem_mask) const override; void *get_ptr(offs_t offset) const override; std::string name() const override; @@ -73,11 +70,10 @@ template class handler_entry_write_memory_bank : publi public: using uX = typename emu::detail::handler_entry_size::uX; - handler_entry_write_memory_bank(address_space *space, u16 flags, memory_bank &bank) : handler_entry_write_address(space, flags), m_bank(bank) {} + handler_entry_write_memory_bank(address_space *space, memory_bank &bank) : handler_entry_write_address(space, 0), m_bank(bank) {} ~handler_entry_write_memory_bank() = default; void write(offs_t offset, uX data, uX mem_mask) const override; - u16 write_flags(offs_t offset, uX data, uX mem_mask) const override; void *get_ptr(offs_t offset) const override; std::string name() const override; diff --git a/src/emu/emumem_het.cpp b/src/emu/emumem_het.cpp index 1360bad..c936369 100644 --- a/src/emu/emumem_het.cpp +++ b/src/emu/emumem_het.cpp @@ -16,17 +16,6 @@ template typename emu::detail::handler_entry_size std::pair::uX, u16> handler_entry_read_tap::read_flags(offs_t offset, uX mem_mask) const -{ - this->ref(); - - auto pack = this->m_next->read_flags(offset, mem_mask); - m_tap(offset, pack.first, mem_mask); - - this->unref(); - return pack; -} - template std::string handler_entry_read_tap::name() const { return '(' + m_name + ") " + this->m_next->name(); @@ -48,17 +37,6 @@ template void handler_entry_write_tapunref(); } -template u16 handler_entry_write_tap::write_flags(offs_t offset, uX data, uX mem_mask) const -{ - this->ref(); - - m_tap(offset, data, mem_mask); - u16 flags = this->m_next->write_flags(offset, data, mem_mask); - - this->unref(); - return flags; -} - template std::string handler_entry_write_tap::name() const { return '(' + m_name + ") " + this->m_next->name(); diff --git a/src/emu/emumem_het.h b/src/emu/emumem_het.h index 2914605..ac717b8 100644 --- a/src/emu/emumem_het.h +++ b/src/emu/emumem_het.h @@ -18,7 +18,6 @@ public: ~handler_entry_read_tap() = default; uX read(offs_t offset, uX mem_mask) const override; - std::pair read_flags(offs_t offset, uX mem_mask) const override; std::string name() const override; @@ -40,7 +39,6 @@ public: ~handler_entry_write_tap() = default; void write(offs_t offset, uX data, uX mem_mask) const override; - u16 write_flags(offs_t offset, uX data, uX mem_mask) const override; std::string name() const override; diff --git a/src/emu/emumem_heu.cpp b/src/emu/emumem_heu.cpp index 3c05a1e..d78a4a8 100644 --- a/src/emu/emumem_heu.cpp +++ b/src/emu/emumem_heu.cpp @@ -104,39 +104,6 @@ template typename emu::detail::handler_entry_size std::pair::uX, u16> handler_entry_read_units::read_flags(offs_t offset, uX mem_mask) const -{ - this->ref(); - - uX result = m_unmap; - u16 flags = 0; - for (int index = 0; index < m_subunits; index++) { - const subunit_info &si = m_subunit_infos[index]; - if (mem_mask & si.m_amask) { - offs_t aoffset = (si.m_ashift >= 0 ? offset >> si.m_ashift : offset << si.m_ashift) + si.m_offset; - std::pair pack; - switch(si.m_width) { - case 0: - pack = static_cast *>(si.m_handler)->read_flags(aoffset, mem_mask >> si.m_dshift); - break; - case 1: - pack = static_cast *>(si.m_handler)->read_flags(aoffset, mem_mask >> si.m_dshift); - break; - case 2: - pack = static_cast *>(si.m_handler)->read_flags(aoffset, mem_mask >> si.m_dshift); - break; - default: - abort(); - } - result |= uX(pack.first) << si.m_dshift; - flags |= pack.second; - } - } - - this->unref(); - return std::make_pair(result, flags); -} - template std::string handler_entry_read_units::m2r(typename emu::detail::handler_entry_size::uX mask) { constexpr u32 mbits = 8*sizeof(uX); @@ -256,35 +223,6 @@ template void handler_entry_write_unitsunref(); } -template u16 handler_entry_write_units::write_flags(offs_t offset, uX data, uX mem_mask) const -{ - this->ref(); - - u16 flags = 0; - for (int index = 0; index < m_subunits; index++) { - const subunit_info &si = m_subunit_infos[index]; - if (mem_mask & si.m_amask) { - offs_t aoffset = (si.m_ashift >= 0 ? offset >> si.m_ashift : offset << si.m_ashift) + si.m_offset; - switch(si.m_width) { - case 0: - flags |= static_cast *>(si.m_handler)->write_flags(aoffset, data >> si.m_dshift, mem_mask >> si.m_dshift); - break; - case 1: - flags |= static_cast *>(si.m_handler)->write_flags(aoffset, data >> si.m_dshift, mem_mask >> si.m_dshift); - break; - case 2: - flags |= static_cast *>(si.m_handler)->write_flags(aoffset, data >> si.m_dshift, mem_mask >> si.m_dshift); - break; - default: - abort(); - } - } - } - - this->unref(); - return flags; -} - template std::string handler_entry_write_units::m2r(typename emu::detail::handler_entry_size::uX mask) { diff --git a/src/emu/emumem_heu.h b/src/emu/emumem_heu.h index 4b44a72..541f96b 100644 --- a/src/emu/emumem_heu.h +++ b/src/emu/emumem_heu.h @@ -20,7 +20,6 @@ public: ~handler_entry_read_units(); uX read(offs_t offset, uX mem_mask) const override; - std::pair read_flags(offs_t offset, uX mem_mask) const override; std::string name() const override; @@ -62,7 +61,6 @@ public: ~handler_entry_write_units(); void write(offs_t offset, uX data, uX mem_mask) const override; - u16 write_flags(offs_t offset, uX data, uX mem_mask) const override; std::string name() const override; diff --git a/src/emu/emumem_heun.cpp b/src/emu/emumem_heun.cpp index 4caa177..f81b95d 100644 --- a/src/emu/emumem_heun.cpp +++ b/src/emu/emumem_heun.cpp @@ -17,18 +17,6 @@ template typename emu::detail::handler_entry_sizem_space->unmap(); } -template std::pair::uX, u16> handler_entry_read_unmapped::read_flags(offs_t offset, uX mem_mask) const -{ - if (this->m_space->log_unmap() && !this->m_space->m_manager.machine().side_effects_disabled()) - this->m_space->device().logerror(this->m_space->is_octal() - ? "%s: unmapped %s memory read from %0*o & %0*o\n" - : "%s: unmapped %s memory read from %0*X & %0*X\n", - this->m_space->m_manager.machine().describe_context(), this->m_space->name(), - this->m_space->addrchars(), offset, - 2 << Width, mem_mask); - return std::pair(this->m_space->unmap(), this->m_flags); -} - template std::string handler_entry_read_unmapped::name() const { return "unmapped"; @@ -47,19 +35,6 @@ template void handler_entry_write_unmapped u16 handler_entry_write_unmapped::write_flags(offs_t offset, uX data, uX mem_mask)const -{ - if (this->m_space->log_unmap() && !this->m_space->m_manager.machine().side_effects_disabled()) - this->m_space->device().logerror(this->m_space->is_octal() - ? "%s: unmapped %s memory write to %0*o = %0*o & %0*o\n" - : "%s: unmapped %s memory write to %0*X = %0*X & %0*X\n", - this->m_space->m_manager.machine().describe_context(), this->m_space->name(), - this->m_space->addrchars(), offset, - 2 << Width, data, - 2 << Width, mem_mask); - return this->m_flags; -} - template std::string handler_entry_write_unmapped::name() const { return "unmapped"; @@ -73,11 +48,6 @@ template typename emu::detail::handler_entry_sizem_space->unmap(); } -template std::pair::uX, u16> handler_entry_read_nop::read_flags(offs_t offset, uX mem_mask) const -{ - return std::pair(this->m_space->unmap(), this->m_flags); -} - template std::string handler_entry_read_nop::name() const { return "nop"; @@ -88,11 +58,6 @@ template void handler_entry_write_nop u16 handler_entry_write_nop::write_flags(offs_t offset, uX data, uX mem_mask) const -{ - return this->m_flags; -} - template std::string handler_entry_write_nop::name() const { return "nop"; diff --git a/src/emu/emumem_heun.h b/src/emu/emumem_heun.h index e355bbe..bfd099f 100644 --- a/src/emu/emumem_heun.h +++ b/src/emu/emumem_heun.h @@ -14,11 +14,10 @@ template class handler_entry_read_unmapped : public ha public: using uX = typename emu::detail::handler_entry_size::uX; - handler_entry_read_unmapped(address_space *space, u16 flags) : handler_entry_read(space, handler_entry::F_UNMAP | flags) {} + handler_entry_read_unmapped(address_space *space) : handler_entry_read(space, handler_entry::F_UNMAP) {} ~handler_entry_read_unmapped() = default; uX read(offs_t offset, uX mem_mask) const override; - std::pair read_flags(offs_t offset, uX mem_mask) const override; std::string name() const override; }; @@ -28,11 +27,10 @@ template class handler_entry_write_unmapped : public h public: using uX = typename emu::detail::handler_entry_size::uX; - handler_entry_write_unmapped(address_space *space, u16 flags) : handler_entry_write(space, handler_entry::F_UNMAP | flags) {} + handler_entry_write_unmapped(address_space *space) : handler_entry_write(space, handler_entry::F_UNMAP) {} ~handler_entry_write_unmapped() = default; void write(offs_t offset, uX data, uX mem_mask) const override; - u16 write_flags(offs_t offset, uX data, uX mem_mask) const override; std::string name() const override; }; @@ -48,11 +46,10 @@ template class handler_entry_read_nop : public handler public: using uX = typename emu::detail::handler_entry_size::uX; - handler_entry_read_nop(address_space *space, u16 flags) : handler_entry_read(space, flags) {} + handler_entry_read_nop(address_space *space) : handler_entry_read(space, 0) {} ~handler_entry_read_nop() = default; uX read(offs_t offset, uX mem_mask) const override; - std::pair read_flags(offs_t offset, uX mem_mask) const override; std::string name() const override; }; @@ -62,11 +59,10 @@ template class handler_entry_write_nop : public handle public: using uX = typename emu::detail::handler_entry_size::uX; - handler_entry_write_nop(address_space *space, u16 flags) : handler_entry_write(space, flags) {} + handler_entry_write_nop(address_space *space) : handler_entry_write(space, 0) {} ~handler_entry_write_nop() = default; void write(offs_t offset, uX data, uX mem_mask) const override; - u16 write_flags(offs_t offset, uX data, uX mem_mask) const override; std::string name() const override; }; diff --git a/src/emu/emumem_mview.cpp b/src/emu/emumem_mview.cpp index 06007c7..aef50da 100644 --- a/src/emu/emumem_mview.cpp +++ b/src/emu/emumem_mview.cpp @@ -130,188 +130,188 @@ public: virtual memory_passthrough_handler *install_write_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function tap, memory_passthrough_handler *mph) override; virtual memory_passthrough_handler *install_readwrite_tap(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string name, std::function tapr, std::function tapw, memory_passthrough_handler *mph) override; - virtual void unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, read_or_write readorwrite, bool quiet) override; - virtual void install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, read_or_write readorwrite, void *baseptr) override; - virtual void install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, memory_bank *rbank, memory_bank *wbank) override; + virtual void unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, bool quiet) override; + virtual void install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, void *baseptr) override; + virtual void install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *rbank, memory_bank *wbank) override; virtual void install_view(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_view &view) override; - virtual void install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, std::string rtag, std::string wtag) override; - virtual void install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &map, u64 unitmask, int cswidth, u16 flags) override; + virtual void install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string rtag, std::string wtag) override; + virtual void install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &map, u64 unitmask, int cswidth) override; - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8_delegate rhandler, write8_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16_delegate rhandler, write16_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32_delegate rhandler, write32_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64_delegate rhandler, write64_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8m_delegate rhandler, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16m_delegate rhandler, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32m_delegate rhandler, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64m_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64m_delegate rhandler, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8m_delegate rhandler, write8m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16m_delegate rhandler, write16m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32m_delegate rhandler, write32m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64m_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64m_delegate rhandler, write64m_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8s_delegate rhandler, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16s_delegate rhandler, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32s_delegate rhandler, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64s_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64s_delegate rhandler, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8s_delegate rhandler, write8s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16s_delegate rhandler, write16s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32s_delegate rhandler, write32s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64s_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64s_delegate rhandler, write64s_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8sm_delegate rhandler, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16sm_delegate rhandler, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32sm_delegate rhandler, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64sm_delegate rhandler, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8sm_delegate rhandler, write8sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16sm_delegate rhandler, write16sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32sm_delegate rhandler, write32sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64sm_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64sm_delegate rhandler, write64sm_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8mo_delegate rhandler, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16mo_delegate rhandler, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32mo_delegate rhandler, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64mo_delegate rhandler, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8mo_delegate rhandler, write8mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16mo_delegate rhandler, write16mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32mo_delegate rhandler, write32mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64mo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64mo_delegate rhandler, write64mo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8smo_delegate rhandler, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16smo_delegate rhandler, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32smo_delegate rhandler, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } - void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler); } - void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, whandler); } - void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64smo_delegate rhandler, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0, u16 flags = 0) override - { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read8smo_delegate rhandler, write8smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read16smo_delegate rhandler, write16smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read32smo_delegate rhandler, write32smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } + void install_read_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64smo_delegate rhandler, u64 unitmask = 0, int cswidth = 0) override + { install_read_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler); } + void install_write_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_write_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, whandler); } + void install_readwrite_handler(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, read64smo_delegate rhandler, write64smo_delegate whandler, u64 unitmask = 0, int cswidth = 0) override + { install_readwrite_handler_impl(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, rhandler, whandler); } template - void install_read_handler_impl(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, u16 flags, READ &handler_r) + void install_read_handler_impl(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, READ &handler_r) { try { handler_r.resolve(); } catch (const binding_type_exception &) { osd_printf_error("Binding error while installing read handler %s for range 0x%X-0x%X mask 0x%X mirror 0x%X select 0x%X umask 0x%X\n", handler_r.name(), addrstart, addrend, addrmask, addrmirror, addrselect, unitmask); throw; } - install_read_handler_helper::value>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, handler_r); + install_read_handler_helper::value>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler_r); } template - void install_write_handler_impl(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, u16 flags, WRITE &handler_w) + void install_write_handler_impl(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, WRITE &handler_w) { try { handler_w.resolve(); } catch (const binding_type_exception &) { osd_printf_error("Binding error while installing write handler %s for range 0x%X-0x%X mask 0x%X mirror 0x%X select 0x%X umask 0x%X\n", handler_w.name(), addrstart, addrend, addrmask, addrmirror, addrselect, unitmask); throw; } - install_write_handler_helper::value>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, handler_w); + install_write_handler_helper::value>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler_w); } template - void install_readwrite_handler_impl(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, u16 flags, READ &handler_r, WRITE &handler_w) + void install_readwrite_handler_impl(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, READ &handler_r, WRITE &handler_w) { static_assert(handler_width::value == handler_width::value, "handler widths do not match"); try { handler_r.resolve(); } @@ -324,11 +324,11 @@ public: osd_printf_error("Binding error while installing write handler %s for range 0x%X-0x%X mask 0x%X mirror 0x%X select 0x%X umask 0x%X\n", handler_w.name(), addrstart, addrend, addrmask, addrmirror, addrselect, unitmask); throw; } - install_readwrite_handler_helper::value>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, flags, handler_r, handler_w); + install_readwrite_handler_helper::value>(addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, handler_r, handler_w); } template - void install_read_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, u16 flags, const READ &handler_r) + void install_read_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, const READ &handler_r) { if constexpr (Width < AccessWidth) { fatalerror("install_read_handler: cannot install a %d-wide handler in a %d-wide bus", 8 << AccessWidth, 8 << Width); @@ -345,11 +345,11 @@ public: check_optimize_all("install_read_handler", 8 << AccessWidth, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth); if constexpr (Width == AccessWidth) { - auto hand_r = new handler_entry_read_delegate(m_view.m_space, flags, handler_r); + auto hand_r = new handler_entry_read_delegate(m_view.m_space, handler_r); hand_r->set_address_info(nstart, nmask); r()->populate(nstart, nend, nmirror, hand_r); } else { - auto hand_r = new handler_entry_read_delegate(m_view.m_space, flags, handler_r); + auto hand_r = new handler_entry_read_delegate(m_view.m_space, handler_r); memory_units_descriptor descriptor(AccessWidth, endianness(), hand_r, nstart, nend, nmask, nunitmask, ncswidth); hand_r->set_address_info(descriptor.get_handler_start(), descriptor.get_handler_mask()); r()->populate_mismatched(nstart, nend, nmirror, descriptor); @@ -360,7 +360,7 @@ public: } template - void install_write_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, u16 flags, + void install_write_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, const WRITE &handler_w) { if constexpr (Width < AccessWidth) { @@ -378,11 +378,11 @@ public: check_optimize_all("install_write_handler", 8 << AccessWidth, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth); if constexpr (Width == AccessWidth) { - auto hand_w = new handler_entry_write_delegate(m_view.m_space, flags, handler_w); + auto hand_w = new handler_entry_write_delegate(m_view.m_space, handler_w); hand_w->set_address_info(nstart, nmask); w()->populate(nstart, nend, nmirror, hand_w); } else { - auto hand_w = new handler_entry_write_delegate(m_view.m_space, flags, handler_w); + auto hand_w = new handler_entry_write_delegate(m_view.m_space, handler_w); memory_units_descriptor descriptor(AccessWidth, endianness(), hand_w, nstart, nend, nmask, nunitmask, ncswidth); hand_w->set_address_info(descriptor.get_handler_start(), descriptor.get_handler_mask()); w()->populate_mismatched(nstart, nend, nmirror, descriptor); @@ -393,7 +393,7 @@ public: } template - void install_readwrite_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, u16 flags, + void install_readwrite_handler_helper(offs_t addrstart, offs_t addrend, offs_t addrmask, offs_t addrmirror, offs_t addrselect, u64 unitmask, int cswidth, const READ &handler_r, const WRITE &handler_w) { @@ -412,21 +412,21 @@ public: check_optimize_all("install_readwrite_handler", 8 << AccessWidth, addrstart, addrend, addrmask, addrmirror, addrselect, unitmask, cswidth, nstart, nend, nmask, nmirror, nunitmask, ncswidth); if constexpr (Width == AccessWidth) { - auto hand_r = new handler_entry_read_delegate (m_view.m_space, flags, handler_r); + auto hand_r = new handler_entry_read_delegate (m_view.m_space, handler_r); hand_r->set_address_info(nstart, nmask); r() ->populate(nstart, nend, nmirror, hand_r); - auto hand_w = new handler_entry_write_delegate(m_view.m_space, flags, handler_w); + auto hand_w = new handler_entry_write_delegate(m_view.m_space, handler_w); hand_w->set_address_info(nstart, nmask); w()->populate(nstart, nend, nmirror, hand_w); } else { - auto hand_r = new handler_entry_read_delegate (m_view.m_space, flags, handler_r); + auto hand_r = new handler_entry_read_delegate (m_view.m_space, handler_r); memory_units_descriptor descriptor(AccessWidth, endianness(), hand_r, nstart, nend, nmask, nunitmask, ncswidth); hand_r->set_address_info(descriptor.get_handler_start(), descriptor.get_handler_mask()); r() ->populate_mismatched(nstart, nend, nmirror, descriptor); hand_r->unref(); - auto hand_w = new handler_entry_write_delegate(m_view.m_space, flags, handler_w); + auto hand_w = new handler_entry_write_delegate(m_view.m_space, handler_w); descriptor.set_subunit_handler(hand_w); hand_w->set_address_info(descriptor.get_handler_start(), descriptor.get_handler_mask()); w()->populate_mismatched(nstart, nend, nmirror, descriptor); @@ -810,7 +810,7 @@ void memory_view::memory_view_entry::check_range_address(const char *function, o fatalerror("%s: The range %x-%x exceeds the view window boundaries %x-%x.\n", function, addrstart, addrend, m_view.m_addrstart, m_view.m_addrend); } -template void memory_view_entry_specific::install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, read_or_write readorwrite, void *baseptr) +template void memory_view_entry_specific::install_ram_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, void *baseptr) { VPRINTF("memory_view::install_ram_generic(%s-%s mirror=%s, %s, %p)\n", m_addrchars, addrstart, m_addrchars, addrend, @@ -827,7 +827,7 @@ template void memory_view_entry_specific(m_view.m_space, flags, baseptr); + auto hand_r = new handler_entry_read_memory(m_view.m_space, baseptr); hand_r->set_address_info(nstart, nmask); r()->populate(nstart, nend, nmirror, hand_r); } @@ -835,7 +835,7 @@ template void memory_view_entry_specific(m_view.m_space, flags, baseptr); + auto hand_w = new handler_entry_write_memory(m_view.m_space, baseptr); hand_w->set_address_info(nstart, nmask); w()->populate(nstart, nend, nmirror, hand_w); } @@ -843,7 +843,7 @@ template void memory_view_entry_specific void memory_view_entry_specific::unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, read_or_write readorwrite, bool quiet) +template void memory_view_entry_specific::unmap_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, read_or_write readorwrite, bool quiet) { VPRINTF("memory_view::unmap(%*x-%*x mirror=%*x, %s, %s)\n", m_addrchars, addrstart, m_addrchars, addrend, @@ -859,24 +859,14 @@ template void memory_view_entry_specific *>(new handler_entry_read_nop(m_view.m_space, flags)) : - static_cast *>(new handler_entry_read_unmapped(m_view.m_space, flags))) - : (quiet ? - static_cast *>(m_view.m_space->nop_r()) : - static_cast *>(m_view.m_space->unmap_r())); + auto handler = static_cast *>(quiet ? m_view.m_space->nop_r() : m_view.m_space->unmap_r()); handler->ref(); r()->populate(nstart, nend, nmirror, handler); } // write space if (readorwrite == read_or_write::WRITE || readorwrite == read_or_write::READWRITE) { - auto handler = flags ? (quiet ? - static_cast *>(new handler_entry_write_nop(m_view.m_space, flags)) : - static_cast *>(new handler_entry_write_unmapped(m_view.m_space, flags))) - : (quiet ? - static_cast *>(m_view.m_space->nop_w()) : - static_cast *>(m_view.m_space->unmap_w())); + auto handler = static_cast *>(quiet ? m_view.m_space->nop_w() : m_view.m_space->unmap_w()); handler->ref(); w()->populate(nstart, nend, nmirror, handler); } @@ -959,16 +949,16 @@ template memory_passthrough_handler *memory return mph; } -template void memory_view_entry_specific::install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &delegate, u64 unitmask, int cswidth, u16 flags) +template void memory_view_entry_specific::install_device_delegate(offs_t addrstart, offs_t addrend, device_t &device, address_map_constructor &delegate, u64 unitmask, int cswidth) { check_range_address("install_device_delegate", addrstart, addrend); - address_map map(*m_view.m_space, addrstart, addrend, unitmask, cswidth, flags, m_view.m_device, delegate); + address_map map(*m_view.m_space, addrstart, addrend, unitmask, cswidth, m_view.m_device, delegate); map.import_submaps(m_manager.machine(), device, data_width(), endianness(), addr_shift()); prepare_device_map(map); populate_from_map(&map); } -template void memory_view_entry_specific::install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, std::string rtag, std::string wtag) +template void memory_view_entry_specific::install_readwrite_port(offs_t addrstart, offs_t addrend, offs_t addrmirror, std::string rtag, std::string wtag) { VPRINTF("memory_view::install_readwrite_port(%*x-%*x mirror=%*x, read=\"%s\" / write=\"%s\")\n", m_addrchars, addrstart, m_addrchars, addrend, @@ -990,7 +980,7 @@ template void memory_view_entry_specific(m_view.m_space, flags, port); + auto hand_r = new handler_entry_read_ioport(m_view.m_space, port); r()->populate(nstart, nend, nmirror, hand_r); } @@ -1002,13 +992,13 @@ template void memory_view_entry_specific(m_view.m_space, flags, port); + auto hand_w = new handler_entry_write_ioport(m_view.m_space, port); w()->populate(nstart, nend, nmirror, hand_w); } invalidate_caches(rtag != "" ? wtag != "" ? read_or_write::READWRITE : read_or_write::READ : read_or_write::WRITE); } -template void memory_view_entry_specific::install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, u16 flags, memory_bank *rbank, memory_bank *wbank) +template void memory_view_entry_specific::install_bank_generic(offs_t addrstart, offs_t addrend, offs_t addrmirror, memory_bank *rbank, memory_bank *wbank) { VPRINTF("memory_view::install_readwrite_bank(%*x-%*x mirror=%*x, read=\"%s\" / write=\"%s\")\n", m_addrchars, addrstart, m_addrchars, addrend, @@ -1024,7 +1014,7 @@ template void memory_view_entry_specific(m_view.m_space, flags, *rbank); + auto hand_r = new handler_entry_read_memory_bank(m_view.m_space, *rbank); hand_r->set_address_info(nstart, nmask); r()->populate(nstart, nend, nmirror, hand_r); } @@ -1032,7 +1022,7 @@ template void memory_view_entry_specific(m_view.m_space, flags, *wbank); + auto hand_w = new handler_entry_write_memory_bank(m_view.m_space, *wbank); hand_w->set_address_info(nstart, nmask); w()->populate(nstart, nend, nmirror, hand_w); } diff --git a/src/emu/inpttype.ipp b/src/emu/inpttype.ipp index 59c6a45..aca3973 100644 --- a/src/emu/inpttype.ipp +++ b/src/emu/inpttype.ipp @@ -19,83 +19,83 @@ namespace { #define CORE_INPUT_TYPES_P1 \ CORE_INPUT_TYPES_BEGIN(p1) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICK_UP, N_p("input-name", "%p Up"), input_seq(KEYCODE_UP, input_seq::or_code, JOYCODE_Y_UP_SWITCH_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICK_DOWN, N_p("input-name", "%p Down"), input_seq(KEYCODE_DOWN, input_seq::or_code, JOYCODE_Y_DOWN_SWITCH_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICK_LEFT, N_p("input-name", "%p Left"), input_seq(KEYCODE_LEFT, input_seq::or_code, JOYCODE_X_LEFT_SWITCH_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICK_RIGHT, N_p("input-name", "%p Right"), input_seq(KEYCODE_RIGHT, input_seq::or_code, JOYCODE_X_RIGHT_SWITCH_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKRIGHT_UP, N_p("input-name", "%p Right Stick/Up"), input_seq(KEYCODE_I, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKRIGHT_DOWN, N_p("input-name", "%p Right Stick/Down"), input_seq(KEYCODE_K, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKRIGHT_LEFT, N_p("input-name", "%p Right Stick/Left"), input_seq(KEYCODE_J, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKRIGHT_RIGHT, N_p("input-name", "%p Right Stick/Right"), input_seq(KEYCODE_L, input_seq::or_code, JOYCODE_BUTTON4_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKLEFT_UP, N_p("input-name", "%p Left Stick/Up"), input_seq(KEYCODE_E, input_seq::or_code, JOYCODE_Y_UP_SWITCH_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKLEFT_DOWN, N_p("input-name", "%p Left Stick/Down"), input_seq(KEYCODE_D, input_seq::or_code, JOYCODE_Y_DOWN_SWITCH_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKLEFT_LEFT, N_p("input-name", "%p Left Stick/Left"), input_seq(KEYCODE_S, input_seq::or_code, JOYCODE_X_LEFT_SWITCH_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKLEFT_RIGHT, N_p("input-name", "%p Left Stick/Right"), input_seq(KEYCODE_F, input_seq::or_code, JOYCODE_X_RIGHT_SWITCH_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON1, N_p("input-name", "%p Button 1"), input_seq(KEYCODE_LCONTROL, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(0), input_seq::or_code, MOUSECODE_BUTTON1_INDEXED(0), input_seq::or_code, GUNCODE_BUTTON1_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON2, N_p("input-name", "%p Button 2"), input_seq(KEYCODE_LALT, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(0), input_seq::or_code, MOUSECODE_BUTTON3_INDEXED(0), input_seq::or_code, GUNCODE_BUTTON2_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON3, N_p("input-name", "%p Button 3"), input_seq(KEYCODE_SPACE, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(0), input_seq::or_code, MOUSECODE_BUTTON2_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON4, N_p("input-name", "%p Button 4"), input_seq(KEYCODE_LSHIFT, input_seq::or_code, JOYCODE_BUTTON4_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON5, N_p("input-name", "%p Button 5"), input_seq(KEYCODE_Z, input_seq::or_code, JOYCODE_BUTTON5_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON6, N_p("input-name", "%p Button 6"), input_seq(KEYCODE_X, input_seq::or_code, JOYCODE_BUTTON6_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON7, N_p("input-name", "%p Button 7"), input_seq(KEYCODE_C, input_seq::or_code, JOYCODE_BUTTON7_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON8, N_p("input-name", "%p Button 8"), input_seq(KEYCODE_V, input_seq::or_code, JOYCODE_BUTTON8_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON9, N_p("input-name", "%p Button 9"), input_seq(KEYCODE_B, input_seq::or_code, JOYCODE_BUTTON9_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON10, N_p("input-name", "%p Button 10"), input_seq(KEYCODE_N, input_seq::or_code, JOYCODE_BUTTON10_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON11, N_p("input-name", "%p Button 11"), input_seq(KEYCODE_M, input_seq::or_code, JOYCODE_BUTTON11_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON12, N_p("input-name", "%p Button 12"), input_seq(KEYCODE_COMMA, input_seq::or_code, JOYCODE_BUTTON12_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON13, N_p("input-name", "%p Button 13"), input_seq(KEYCODE_STOP, input_seq::or_code, JOYCODE_BUTTON13_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON14, N_p("input-name", "%p Button 14"), input_seq(KEYCODE_SLASH, input_seq::or_code, JOYCODE_BUTTON14_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON15, N_p("input-name", "%p Button 15"), input_seq(KEYCODE_RSHIFT, input_seq::or_code, JOYCODE_BUTTON15_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON16, N_p("input-name", "%p Button 16"), input_seq(JOYCODE_BUTTON16_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, START, N_p("input-name", "%p Start"), input_seq(KEYCODE_1, input_seq::or_code, JOYCODE_START_INDEXED(0)) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, SELECT, N_p("input-name", "%p Select"), input_seq(KEYCODE_5, input_seq::or_code, JOYCODE_SELECT_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICK_UP, N_p("input-name", "P1 Up"), input_seq(KEYCODE_UP, input_seq::or_code, JOYCODE_Y_UP_SWITCH_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICK_DOWN, N_p("input-name", "P1 Down"), input_seq(KEYCODE_DOWN, input_seq::or_code, JOYCODE_Y_DOWN_SWITCH_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICK_LEFT, N_p("input-name", "P1 Left"), input_seq(KEYCODE_LEFT, input_seq::or_code, JOYCODE_X_LEFT_SWITCH_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICK_RIGHT, N_p("input-name", "P1 Right"), input_seq(KEYCODE_RIGHT, input_seq::or_code, JOYCODE_X_RIGHT_SWITCH_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKRIGHT_UP, N_p("input-name", "P1 Right Stick/Up"), input_seq(KEYCODE_I, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKRIGHT_DOWN, N_p("input-name", "P1 Right Stick/Down"), input_seq(KEYCODE_K, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKRIGHT_LEFT, N_p("input-name", "P1 Right Stick/Left"), input_seq(KEYCODE_J, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKRIGHT_RIGHT, N_p("input-name", "P1 Right Stick/Right"), input_seq(KEYCODE_L, input_seq::or_code, JOYCODE_BUTTON4_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKLEFT_UP, N_p("input-name", "P1 Left Stick/Up"), input_seq(KEYCODE_E, input_seq::or_code, JOYCODE_Y_UP_SWITCH_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKLEFT_DOWN, N_p("input-name", "P1 Left Stick/Down"), input_seq(KEYCODE_D, input_seq::or_code, JOYCODE_Y_DOWN_SWITCH_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKLEFT_LEFT, N_p("input-name", "P1 Left Stick/Left"), input_seq(KEYCODE_S, input_seq::or_code, JOYCODE_X_LEFT_SWITCH_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, JOYSTICKLEFT_RIGHT, N_p("input-name", "P1 Left Stick/Right"), input_seq(KEYCODE_F, input_seq::or_code, JOYCODE_X_RIGHT_SWITCH_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON1, N_p("input-name", "P1 Button 1"), input_seq(KEYCODE_LCONTROL, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(0), input_seq::or_code, MOUSECODE_BUTTON1_INDEXED(0), input_seq::or_code, GUNCODE_BUTTON1_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON2, N_p("input-name", "P1 Button 2"), input_seq(KEYCODE_LALT, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(0), input_seq::or_code, MOUSECODE_BUTTON3_INDEXED(0), input_seq::or_code, GUNCODE_BUTTON2_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON3, N_p("input-name", "P1 Button 3"), input_seq(KEYCODE_SPACE, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(0), input_seq::or_code, MOUSECODE_BUTTON2_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON4, N_p("input-name", "P1 Button 4"), input_seq(KEYCODE_LSHIFT, input_seq::or_code, JOYCODE_BUTTON4_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON5, N_p("input-name", "P1 Button 5"), input_seq(KEYCODE_Z, input_seq::or_code, JOYCODE_BUTTON5_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON6, N_p("input-name", "P1 Button 6"), input_seq(KEYCODE_X, input_seq::or_code, JOYCODE_BUTTON6_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON7, N_p("input-name", "P1 Button 7"), input_seq(KEYCODE_C, input_seq::or_code, JOYCODE_BUTTON7_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON8, N_p("input-name", "P1 Button 8"), input_seq(KEYCODE_V, input_seq::or_code, JOYCODE_BUTTON8_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON9, N_p("input-name", "P1 Button 9"), input_seq(KEYCODE_B, input_seq::or_code, JOYCODE_BUTTON9_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON10, N_p("input-name", "P1 Button 10"), input_seq(KEYCODE_N, input_seq::or_code, JOYCODE_BUTTON10_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON11, N_p("input-name", "P1 Button 11"), input_seq(KEYCODE_M, input_seq::or_code, JOYCODE_BUTTON11_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON12, N_p("input-name", "P1 Button 12"), input_seq(KEYCODE_COMMA, input_seq::or_code, JOYCODE_BUTTON12_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON13, N_p("input-name", "P1 Button 13"), input_seq(KEYCODE_STOP, input_seq::or_code, JOYCODE_BUTTON13_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON14, N_p("input-name", "P1 Button 14"), input_seq(KEYCODE_SLASH, input_seq::or_code, JOYCODE_BUTTON14_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON15, N_p("input-name", "P1 Button 15"), input_seq(KEYCODE_RSHIFT, input_seq::or_code, JOYCODE_BUTTON15_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, BUTTON16, N_p("input-name", "P1 Button 16"), input_seq(JOYCODE_BUTTON16_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, START, N_p("input-name", "P1 Start"), input_seq(KEYCODE_1, input_seq::or_code, JOYCODE_START_INDEXED(0)) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, SELECT, N_p("input-name", "P1 Select"), input_seq(KEYCODE_5, input_seq::or_code, JOYCODE_SELECT_INDEXED(0)) ) \ CORE_INPUT_TYPES_END() #define CORE_INPUT_TYPES_P1_MAHJONG \ CORE_INPUT_TYPES_BEGIN(p1_mahjong) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_A, N_p("input-name", "%p Mahjong A"), input_seq(KEYCODE_A) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_B, N_p("input-name", "%p Mahjong B"), input_seq(KEYCODE_B) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_C, N_p("input-name", "%p Mahjong C"), input_seq(KEYCODE_C) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_D, N_p("input-name", "%p Mahjong D"), input_seq(KEYCODE_D) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_E, N_p("input-name", "%p Mahjong E"), input_seq(KEYCODE_E) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_F, N_p("input-name", "%p Mahjong F"), input_seq(KEYCODE_F) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_G, N_p("input-name", "%p Mahjong G"), input_seq(KEYCODE_G) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_H, N_p("input-name", "%p Mahjong H"), input_seq(KEYCODE_H) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_I, N_p("input-name", "%p Mahjong I"), input_seq(KEYCODE_I) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_J, N_p("input-name", "%p Mahjong J"), input_seq(KEYCODE_J) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_K, N_p("input-name", "%p Mahjong K"), input_seq(KEYCODE_K) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_L, N_p("input-name", "%p Mahjong L"), input_seq(KEYCODE_L) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_M, N_p("input-name", "%p Mahjong M"), input_seq(KEYCODE_M) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_N, N_p("input-name", "%p Mahjong N"), input_seq(KEYCODE_N) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_O, N_p("input-name", "%p Mahjong O"), input_seq(KEYCODE_O) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_P, N_p("input-name", "%p Mahjong P"), input_seq(KEYCODE_COLON) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_Q, N_p("input-name", "%p Mahjong Q"), input_seq(KEYCODE_Q) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_KAN, N_p("input-name", "%p Mahjong Kan"), input_seq(KEYCODE_LCONTROL) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_PON, N_p("input-name", "%p Mahjong Pon"), input_seq(KEYCODE_LALT) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_CHI, N_p("input-name", "%p Mahjong Chi"), input_seq(KEYCODE_SPACE) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_REACH, N_p("input-name", "%p Mahjong Reach"), input_seq(KEYCODE_LSHIFT) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_RON, N_p("input-name", "%p Mahjong Ron"), input_seq(KEYCODE_Z) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_FLIP_FLOP, N_p("input-name", "%p Mahjong Flip Flop"), input_seq(KEYCODE_Y) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_BET, N_p("input-name", "%p Mahjong Bet"), input_seq(KEYCODE_3) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_SCORE, N_p("input-name", "%p Mahjong Take Score"), input_seq(KEYCODE_RCONTROL) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_DOUBLE_UP, N_p("input-name", "%p Mahjong Double Up"), input_seq(KEYCODE_RSHIFT) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_BIG, N_p("input-name", "%p Mahjong Big"), input_seq(KEYCODE_ENTER) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_SMALL, N_p("input-name", "%p Mahjong Small"), input_seq(KEYCODE_BACKSPACE) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_LAST_CHANCE, N_p("input-name", "%p Mahjong Last Chance"), input_seq(KEYCODE_RALT) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_A, N_p("input-name", "P1 Mahjong A"), input_seq(KEYCODE_A) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_B, N_p("input-name", "P1 Mahjong B"), input_seq(KEYCODE_B) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_C, N_p("input-name", "P1 Mahjong C"), input_seq(KEYCODE_C) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_D, N_p("input-name", "P1 Mahjong D"), input_seq(KEYCODE_D) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_E, N_p("input-name", "P1 Mahjong E"), input_seq(KEYCODE_E) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_F, N_p("input-name", "P1 Mahjong F"), input_seq(KEYCODE_F) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_G, N_p("input-name", "P1 Mahjong G"), input_seq(KEYCODE_G) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_H, N_p("input-name", "P1 Mahjong H"), input_seq(KEYCODE_H) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_I, N_p("input-name", "P1 Mahjong I"), input_seq(KEYCODE_I) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_J, N_p("input-name", "P1 Mahjong J"), input_seq(KEYCODE_J) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_K, N_p("input-name", "P1 Mahjong K"), input_seq(KEYCODE_K) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_L, N_p("input-name", "P1 Mahjong L"), input_seq(KEYCODE_L) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_M, N_p("input-name", "P1 Mahjong M"), input_seq(KEYCODE_M) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_N, N_p("input-name", "P1 Mahjong N"), input_seq(KEYCODE_N) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_O, N_p("input-name", "P1 Mahjong O"), input_seq(KEYCODE_O) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_P, N_p("input-name", "P1 Mahjong P"), input_seq(KEYCODE_COLON) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_Q, N_p("input-name", "P1 Mahjong Q"), input_seq(KEYCODE_Q) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_KAN, N_p("input-name", "P1 Mahjong Kan"), input_seq(KEYCODE_LCONTROL) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_PON, N_p("input-name", "P1 Mahjong Pon"), input_seq(KEYCODE_LALT) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_CHI, N_p("input-name", "P1 Mahjong Chi"), input_seq(KEYCODE_SPACE) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_REACH, N_p("input-name", "P1 Mahjong Reach"), input_seq(KEYCODE_LSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_RON, N_p("input-name", "P1 Mahjong Ron"), input_seq(KEYCODE_Z) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_FLIP_FLOP, N_p("input-name", "P1 Mahjong Flip Flop"), input_seq(KEYCODE_Y) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_BET, N_p("input-name", "P1 Mahjong Bet"), input_seq(KEYCODE_3) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_SCORE, N_p("input-name", "P1 Mahjong Take Score"), input_seq(KEYCODE_RCONTROL) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_DOUBLE_UP, N_p("input-name", "P1 Mahjong Double Up"), input_seq(KEYCODE_RSHIFT) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_BIG, N_p("input-name", "P1 Mahjong Big"), input_seq(KEYCODE_ENTER) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_SMALL, N_p("input-name", "P1 Mahjong Small"), input_seq(KEYCODE_BACKSPACE) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, MAHJONG_LAST_CHANCE, N_p("input-name", "P1 Mahjong Last Chance"), input_seq(KEYCODE_RALT) ) \ CORE_INPUT_TYPES_END() #define CORE_INPUT_TYPES_P1_HANAFUDA \ CORE_INPUT_TYPES_BEGIN(p1_hanafuda) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_A, N_p("input-name", "%p Hanafuda A/1"), input_seq(KEYCODE_A) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_B, N_p("input-name", "%p Hanafuda B/2"), input_seq(KEYCODE_B) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_C, N_p("input-name", "%p Hanafuda C/3"), input_seq(KEYCODE_C) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_D, N_p("input-name", "%p Hanafuda D/4"), input_seq(KEYCODE_D) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_E, N_p("input-name", "%p Hanafuda E/5"), input_seq(KEYCODE_E) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_F, N_p("input-name", "%p Hanafuda F/6"), input_seq(KEYCODE_F) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_G, N_p("input-name", "%p Hanafuda G/7"), input_seq(KEYCODE_G) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_H, N_p("input-name", "%p Hanafuda H/8"), input_seq(KEYCODE_H) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_YES, N_p("input-name", "%p Hanafuda Yes"), input_seq(KEYCODE_M) ) \ - INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_NO, N_p("input-name", "%p Hanafuda No"), input_seq(KEYCODE_N) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_A, N_p("input-name", "P1 Hanafuda A/1"), input_seq(KEYCODE_A) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_B, N_p("input-name", "P1 Hanafuda B/2"), input_seq(KEYCODE_B) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_C, N_p("input-name", "P1 Hanafuda C/3"), input_seq(KEYCODE_C) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_D, N_p("input-name", "P1 Hanafuda D/4"), input_seq(KEYCODE_D) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_E, N_p("input-name", "P1 Hanafuda E/5"), input_seq(KEYCODE_E) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_F, N_p("input-name", "P1 Hanafuda F/6"), input_seq(KEYCODE_F) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_G, N_p("input-name", "P1 Hanafuda G/7"), input_seq(KEYCODE_G) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_H, N_p("input-name", "P1 Hanafuda H/8"), input_seq(KEYCODE_H) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_YES, N_p("input-name", "P1 Hanafuda Yes"), input_seq(KEYCODE_M) ) \ + INPUT_PORT_DIGITAL_TYPE( 1, PLAYER1, HANAFUDA_NO, N_p("input-name", "P1 Hanafuda No"), input_seq(KEYCODE_N) ) \ CORE_INPUT_TYPES_END() #define CORE_INPUT_TYPES_GAMBLE \ @@ -137,355 +137,355 @@ namespace { #define CORE_INPUT_TYPES_P2 \ CORE_INPUT_TYPES_BEGIN(p2) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICK_UP, N_p("input-name", "%p Up"), input_seq(KEYCODE_R, input_seq::or_code, JOYCODE_Y_UP_SWITCH_INDEXED(1)) ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICK_DOWN, N_p("input-name", "%p Down"), input_seq(KEYCODE_F, input_seq::or_code, JOYCODE_Y_DOWN_SWITCH_INDEXED(1)) ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICK_LEFT, N_p("input-name", "%p Left"), input_seq(KEYCODE_D, input_seq::or_code, JOYCODE_X_LEFT_SWITCH_INDEXED(1)) ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICK_RIGHT, N_p("input-name", "%p Right"), input_seq(KEYCODE_G, input_seq::or_code, JOYCODE_X_RIGHT_SWITCH_INDEXED(1)) ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKRIGHT_UP, N_p("input-name", "%p Right Stick/Up"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKRIGHT_DOWN, N_p("input-name", "%p Right Stick/Down"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKRIGHT_LEFT, N_p("input-name", "%p Right Stick/Left"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKRIGHT_RIGHT, N_p("input-name", "%p Right Stick/Right"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKLEFT_UP, N_p("input-name", "%p Left Stick/Up"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKLEFT_DOWN, N_p("input-name", "%p Left Stick/Down"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKLEFT_LEFT, N_p("input-name", "%p Left Stick/Left"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKLEFT_RIGHT, N_p("input-name", "%p Left Stick/Right"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON1, N_p("input-name", "%p Button 1"), input_seq(KEYCODE_A, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(1), input_seq::or_code, MOUSECODE_BUTTON1_INDEXED(1), input_seq::or_code, GUNCODE_BUTTON1_INDEXED(1)) ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON2, N_p("input-name", "%p Button 2"), input_seq(KEYCODE_S, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(1), input_seq::or_code, MOUSECODE_BUTTON3_INDEXED(1), input_seq::or_code, GUNCODE_BUTTON2_INDEXED(1)) ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON3, N_p("input-name", "%p Button 3"), input_seq(KEYCODE_Q, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(1), input_seq::or_code, MOUSECODE_BUTTON2_INDEXED(1)) ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON4, N_p("input-name", "%p Button 4"), input_seq(KEYCODE_W, input_seq::or_code, JOYCODE_BUTTON4_INDEXED(1)) ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON5, N_p("input-name", "%p Button 5"), input_seq(KEYCODE_E, input_seq::or_code, JOYCODE_BUTTON5_INDEXED(1)) ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON6, N_p("input-name", "%p Button 6"), input_seq(JOYCODE_BUTTON6_INDEXED(1)) ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON7, N_p("input-name", "%p Button 7"), input_seq(JOYCODE_BUTTON7_INDEXED(1)) ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON8, N_p("input-name", "%p Button 8"), input_seq(JOYCODE_BUTTON8_INDEXED(1)) ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON9, N_p("input-name", "%p Button 9"), input_seq(JOYCODE_BUTTON9_INDEXED(1)) ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON10, N_p("input-name", "%p Button 10"), input_seq(JOYCODE_BUTTON10_INDEXED(1)) ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON11, N_p("input-name", "%p Button 11"), input_seq(JOYCODE_BUTTON11_INDEXED(1)) ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON12, N_p("input-name", "%p Button 12"), input_seq(JOYCODE_BUTTON12_INDEXED(1)) ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON13, N_p("input-name", "%p Button 13"), input_seq(JOYCODE_BUTTON13_INDEXED(1)) ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON14, N_p("input-name", "%p Button 14"), input_seq(JOYCODE_BUTTON14_INDEXED(1)) ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON15, N_p("input-name", "%p Button 15"), input_seq(JOYCODE_BUTTON15_INDEXED(1)) ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON16, N_p("input-name", "%p Button 16"), input_seq(JOYCODE_BUTTON16_INDEXED(1)) ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, START, N_p("input-name", "%p Start"), input_seq(KEYCODE_2, input_seq::or_code, JOYCODE_START_INDEXED(1)) ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, SELECT, N_p("input-name", "%p Select"), input_seq(KEYCODE_6, input_seq::or_code, JOYCODE_SELECT_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICK_UP, N_p("input-name", "P2 Up"), input_seq(KEYCODE_R, input_seq::or_code, JOYCODE_Y_UP_SWITCH_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICK_DOWN, N_p("input-name", "P2 Down"), input_seq(KEYCODE_F, input_seq::or_code, JOYCODE_Y_DOWN_SWITCH_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICK_LEFT, N_p("input-name", "P2 Left"), input_seq(KEYCODE_D, input_seq::or_code, JOYCODE_X_LEFT_SWITCH_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICK_RIGHT, N_p("input-name", "P2 Right"), input_seq(KEYCODE_G, input_seq::or_code, JOYCODE_X_RIGHT_SWITCH_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKRIGHT_UP, N_p("input-name", "P2 Right Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKRIGHT_DOWN, N_p("input-name", "P2 Right Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKRIGHT_LEFT, N_p("input-name", "P2 Right Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKRIGHT_RIGHT, N_p("input-name", "P2 Right Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKLEFT_UP, N_p("input-name", "P2 Left Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKLEFT_DOWN, N_p("input-name", "P2 Left Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKLEFT_LEFT, N_p("input-name", "P2 Left Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, JOYSTICKLEFT_RIGHT, N_p("input-name", "P2 Left Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON1, N_p("input-name", "P2 Button 1"), input_seq(KEYCODE_A, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(1), input_seq::or_code, MOUSECODE_BUTTON1_INDEXED(1), input_seq::or_code, GUNCODE_BUTTON1_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON2, N_p("input-name", "P2 Button 2"), input_seq(KEYCODE_S, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(1), input_seq::or_code, MOUSECODE_BUTTON3_INDEXED(1), input_seq::or_code, GUNCODE_BUTTON2_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON3, N_p("input-name", "P2 Button 3"), input_seq(KEYCODE_Q, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(1), input_seq::or_code, MOUSECODE_BUTTON2_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON4, N_p("input-name", "P2 Button 4"), input_seq(KEYCODE_W, input_seq::or_code, JOYCODE_BUTTON4_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON5, N_p("input-name", "P2 Button 5"), input_seq(KEYCODE_E, input_seq::or_code, JOYCODE_BUTTON5_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON6, N_p("input-name", "P2 Button 6"), input_seq(JOYCODE_BUTTON6_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON7, N_p("input-name", "P2 Button 7"), input_seq(JOYCODE_BUTTON7_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON8, N_p("input-name", "P2 Button 8"), input_seq(JOYCODE_BUTTON8_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON9, N_p("input-name", "P2 Button 9"), input_seq(JOYCODE_BUTTON9_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON10, N_p("input-name", "P2 Button 10"), input_seq(JOYCODE_BUTTON10_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON11, N_p("input-name", "P2 Button 11"), input_seq(JOYCODE_BUTTON11_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON12, N_p("input-name", "P2 Button 12"), input_seq(JOYCODE_BUTTON12_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON13, N_p("input-name", "P2 Button 13"), input_seq(JOYCODE_BUTTON13_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON14, N_p("input-name", "P2 Button 14"), input_seq(JOYCODE_BUTTON14_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON15, N_p("input-name", "P2 Button 15"), input_seq(JOYCODE_BUTTON15_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, BUTTON16, N_p("input-name", "P2 Button 16"), input_seq(JOYCODE_BUTTON16_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, START, N_p("input-name", "P2 Start"), input_seq(KEYCODE_2, input_seq::or_code, JOYCODE_START_INDEXED(1)) ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, SELECT, N_p("input-name", "P2 Select"), input_seq(KEYCODE_6, input_seq::or_code, JOYCODE_SELECT_INDEXED(1)) ) \ CORE_INPUT_TYPES_END() #define CORE_INPUT_TYPES_P2_MAHJONG \ CORE_INPUT_TYPES_BEGIN(p2_mahjong) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_A, N_p("input-name", "%p Mahjong A"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_B, N_p("input-name", "%p Mahjong B"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_C, N_p("input-name", "%p Mahjong C"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_D, N_p("input-name", "%p Mahjong D"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_E, N_p("input-name", "%p Mahjong E"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_F, N_p("input-name", "%p Mahjong F"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_G, N_p("input-name", "%p Mahjong G"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_H, N_p("input-name", "%p Mahjong H"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_I, N_p("input-name", "%p Mahjong I"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_J, N_p("input-name", "%p Mahjong J"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_K, N_p("input-name", "%p Mahjong K"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_L, N_p("input-name", "%p Mahjong L"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_M, N_p("input-name", "%p Mahjong M"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_N, N_p("input-name", "%p Mahjong N"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_O, N_p("input-name", "%p Mahjong O"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_P, N_p("input-name", "%p Mahjong P"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_Q, N_p("input-name", "%p Mahjong Q"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_KAN, N_p("input-name", "%p Mahjong Kan"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_PON, N_p("input-name", "%p Mahjong Pon"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_CHI, N_p("input-name", "%p Mahjong Chi"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_REACH, N_p("input-name", "%p Mahjong Reach"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_RON, N_p("input-name", "%p Mahjong Ron"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_FLIP_FLOP, N_p("input-name", "%p Mahjong Flip Flop"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_BET, N_p("input-name", "%p Mahjong Bet"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_SCORE, N_p("input-name", "%p Mahjong Take Score"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_DOUBLE_UP, N_p("input-name", "%p Mahjong Double Up"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_BIG, N_p("input-name", "%p Mahjong Big"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_SMALL, N_p("input-name", "%p Mahjong Small"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_LAST_CHANCE, N_p("input-name", "%p Mahjong Last Chance"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_A, N_p("input-name", "P2 Mahjong A"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_B, N_p("input-name", "P2 Mahjong B"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_C, N_p("input-name", "P2 Mahjong C"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_D, N_p("input-name", "P2 Mahjong D"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_E, N_p("input-name", "P2 Mahjong E"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_F, N_p("input-name", "P2 Mahjong F"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_G, N_p("input-name", "P2 Mahjong G"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_H, N_p("input-name", "P2 Mahjong H"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_I, N_p("input-name", "P2 Mahjong I"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_J, N_p("input-name", "P2 Mahjong J"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_K, N_p("input-name", "P2 Mahjong K"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_L, N_p("input-name", "P2 Mahjong L"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_M, N_p("input-name", "P2 Mahjong M"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_N, N_p("input-name", "P2 Mahjong N"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_O, N_p("input-name", "P2 Mahjong O"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_P, N_p("input-name", "P2 Mahjong P"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_Q, N_p("input-name", "P2 Mahjong Q"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_KAN, N_p("input-name", "P2 Mahjong Kan"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_PON, N_p("input-name", "P2 Mahjong Pon"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_CHI, N_p("input-name", "P2 Mahjong Chi"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_REACH, N_p("input-name", "P2 Mahjong Reach"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_RON, N_p("input-name", "P2 Mahjong Ron"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_BET, N_p("input-name", "P2 Mahjong Bet"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_LAST_CHANCE, N_p("input-name", "P2 Mahjong Last Chance"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_SCORE, N_p("input-name", "P2 Mahjong Take Score"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_DOUBLE_UP, N_p("input-name", "P2 Mahjong Double Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_FLIP_FLOP, N_p("input-name", "P2 Mahjong Flip Flop"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_BIG, N_p("input-name", "P2 Mahjong Big"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, MAHJONG_SMALL, N_p("input-name", "P2 Mahjong Small"), input_seq() ) \ CORE_INPUT_TYPES_END() #define CORE_INPUT_TYPES_P2_HANAFUDA \ CORE_INPUT_TYPES_BEGIN(p2_hanafuda) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_A, N_p("input-name", "%p Hanafuda A/1"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_B, N_p("input-name", "%p Hanafuda B/2"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_C, N_p("input-name", "%p Hanafuda C/3"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_D, N_p("input-name", "%p Hanafuda D/4"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_E, N_p("input-name", "%p Hanafuda E/5"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_F, N_p("input-name", "%p Hanafuda F/6"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_G, N_p("input-name", "%p Hanafuda G/7"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_H, N_p("input-name", "%p Hanafuda H/8"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_YES, N_p("input-name", "%p Hanafuda Yes"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_NO, N_p("input-name", "%p Hanafuda No"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_A, N_p("input-name", "P2 Hanafuda A/1"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_B, N_p("input-name", "P2 Hanafuda B/2"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_C, N_p("input-name", "P2 Hanafuda C/3"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_D, N_p("input-name", "P2 Hanafuda D/4"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_E, N_p("input-name", "P2 Hanafuda E/5"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_F, N_p("input-name", "P2 Hanafuda F/6"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_G, N_p("input-name", "P2 Hanafuda G/7"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_H, N_p("input-name", "P2 Hanafuda H/8"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_YES, N_p("input-name", "P2 Hanafuda Yes"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 2, PLAYER2, HANAFUDA_NO, N_p("input-name", "P2 Hanafuda No"), input_seq() ) \ CORE_INPUT_TYPES_END() #define CORE_INPUT_TYPES_P3 \ CORE_INPUT_TYPES_BEGIN(p3) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICK_UP, N_p("input-name", "%p Up"), input_seq(KEYCODE_I, input_seq::or_code, JOYCODE_Y_UP_SWITCH_INDEXED(2)) ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICK_DOWN, N_p("input-name", "%p Down"), input_seq(KEYCODE_K, input_seq::or_code, JOYCODE_Y_DOWN_SWITCH_INDEXED(2)) ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICK_LEFT, N_p("input-name", "%p Left"), input_seq(KEYCODE_J, input_seq::or_code, JOYCODE_X_LEFT_SWITCH_INDEXED(2)) ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICK_RIGHT, N_p("input-name", "%p Right"), input_seq(KEYCODE_L, input_seq::or_code, JOYCODE_X_RIGHT_SWITCH_INDEXED(2)) ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKRIGHT_UP, N_p("input-name", "%p Right Stick/Up"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKRIGHT_DOWN, N_p("input-name", "%p Right Stick/Down"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKRIGHT_LEFT, N_p("input-name", "%p Right Stick/Left"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKRIGHT_RIGHT, N_p("input-name", "%p Right Stick/Right"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKLEFT_UP, N_p("input-name", "%p Left Stick/Up"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKLEFT_DOWN, N_p("input-name", "%p Left Stick/Down"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKLEFT_LEFT, N_p("input-name", "%p Left Stick/Left"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKLEFT_RIGHT, N_p("input-name", "%p Left Stick/Right"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON1, N_p("input-name", "%p Button 1"), input_seq(KEYCODE_RCONTROL, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(2), input_seq::or_code, GUNCODE_BUTTON1_INDEXED(2)) ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON2, N_p("input-name", "%p Button 2"), input_seq(KEYCODE_RSHIFT, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(2), input_seq::or_code, GUNCODE_BUTTON2_INDEXED(2)) ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON3, N_p("input-name", "%p Button 3"), input_seq(KEYCODE_ENTER, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(2)) ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON4, N_p("input-name", "%p Button 4"), input_seq(JOYCODE_BUTTON4_INDEXED(2)) ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON5, N_p("input-name", "%p Button 5"), input_seq(JOYCODE_BUTTON5_INDEXED(2)) ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON6, N_p("input-name", "%p Button 6"), input_seq(JOYCODE_BUTTON6_INDEXED(2)) ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON7, N_p("input-name", "%p Button 7"), input_seq(JOYCODE_BUTTON7_INDEXED(2)) ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON8, N_p("input-name", "%p Button 8"), input_seq(JOYCODE_BUTTON8_INDEXED(2)) ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON9, N_p("input-name", "%p Button 9"), input_seq(JOYCODE_BUTTON9_INDEXED(2)) ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON10, N_p("input-name", "%p Button 10"), input_seq(JOYCODE_BUTTON10_INDEXED(2)) ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON11, N_p("input-name", "%p Button 11"), input_seq(JOYCODE_BUTTON11_INDEXED(2)) ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON12, N_p("input-name", "%p Button 12"), input_seq(JOYCODE_BUTTON12_INDEXED(2)) ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON13, N_p("input-name", "%p Button 13"), input_seq(JOYCODE_BUTTON13_INDEXED(2)) ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON14, N_p("input-name", "%p Button 14"), input_seq(JOYCODE_BUTTON14_INDEXED(2)) ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON15, N_p("input-name", "%p Button 15"), input_seq(JOYCODE_BUTTON15_INDEXED(2)) ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON16, N_p("input-name", "%p Button 16"), input_seq(JOYCODE_BUTTON16_INDEXED(2)) ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, START, N_p("input-name", "%p Start"), input_seq(KEYCODE_3, input_seq::or_code, JOYCODE_START_INDEXED(2)) ) \ - INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, SELECT, N_p("input-name", "%p Select"), input_seq(KEYCODE_7, input_seq::or_code, JOYCODE_SELECT_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICK_UP, N_p("input-name", "P3 Up"), input_seq(KEYCODE_I, input_seq::or_code, JOYCODE_Y_UP_SWITCH_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICK_DOWN, N_p("input-name", "P3 Down"), input_seq(KEYCODE_K, input_seq::or_code, JOYCODE_Y_DOWN_SWITCH_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICK_LEFT, N_p("input-name", "P3 Left"), input_seq(KEYCODE_J, input_seq::or_code, JOYCODE_X_LEFT_SWITCH_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICK_RIGHT, N_p("input-name", "P3 Right"), input_seq(KEYCODE_L, input_seq::or_code, JOYCODE_X_RIGHT_SWITCH_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKRIGHT_UP, N_p("input-name", "P3 Right Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKRIGHT_DOWN, N_p("input-name", "P3 Right Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKRIGHT_LEFT, N_p("input-name", "P3 Right Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKRIGHT_RIGHT, N_p("input-name", "P3 Right Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKLEFT_UP, N_p("input-name", "P3 Left Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKLEFT_DOWN, N_p("input-name", "P3 Left Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKLEFT_LEFT, N_p("input-name", "P3 Left Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, JOYSTICKLEFT_RIGHT, N_p("input-name", "P3 Left Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON1, N_p("input-name", "P3 Button 1"), input_seq(KEYCODE_RCONTROL, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(2), input_seq::or_code, GUNCODE_BUTTON1_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON2, N_p("input-name", "P3 Button 2"), input_seq(KEYCODE_RSHIFT, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(2), input_seq::or_code, GUNCODE_BUTTON2_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON3, N_p("input-name", "P3 Button 3"), input_seq(KEYCODE_ENTER, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON4, N_p("input-name", "P3 Button 4"), input_seq(JOYCODE_BUTTON4_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON5, N_p("input-name", "P3 Button 5"), input_seq(JOYCODE_BUTTON5_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON6, N_p("input-name", "P3 Button 6"), input_seq(JOYCODE_BUTTON6_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON7, N_p("input-name", "P3 Button 7"), input_seq(JOYCODE_BUTTON7_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON8, N_p("input-name", "P3 Button 8"), input_seq(JOYCODE_BUTTON8_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON9, N_p("input-name", "P3 Button 9"), input_seq(JOYCODE_BUTTON9_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON10, N_p("input-name", "P3 Button 10"), input_seq(JOYCODE_BUTTON10_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON11, N_p("input-name", "P3 Button 11"), input_seq(JOYCODE_BUTTON11_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON12, N_p("input-name", "P3 Button 12"), input_seq(JOYCODE_BUTTON12_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON13, N_p("input-name", "P3 Button 13"), input_seq(JOYCODE_BUTTON13_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON14, N_p("input-name", "P3 Button 14"), input_seq(JOYCODE_BUTTON14_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON15, N_p("input-name", "P3 Button 15"), input_seq(JOYCODE_BUTTON15_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, BUTTON16, N_p("input-name", "P3 Button 16"), input_seq(JOYCODE_BUTTON16_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, START, N_p("input-name", "P3 Start"), input_seq(KEYCODE_3, input_seq::or_code, JOYCODE_START_INDEXED(2)) ) \ + INPUT_PORT_DIGITAL_TYPE( 3, PLAYER3, SELECT, N_p("input-name", "P3 Select"), input_seq(KEYCODE_7, input_seq::or_code, JOYCODE_SELECT_INDEXED(2)) ) \ CORE_INPUT_TYPES_END() #define CORE_INPUT_TYPES_P4 \ CORE_INPUT_TYPES_BEGIN(p4) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICK_UP, N_p("input-name", "%p Up"), input_seq(KEYCODE_8_PAD, input_seq::or_code, JOYCODE_Y_UP_SWITCH_INDEXED(3)) ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICK_DOWN, N_p("input-name", "%p Down"), input_seq(KEYCODE_2_PAD, input_seq::or_code, JOYCODE_Y_DOWN_SWITCH_INDEXED(3)) ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICK_LEFT, N_p("input-name", "%p Left"), input_seq(KEYCODE_4_PAD, input_seq::or_code, JOYCODE_X_LEFT_SWITCH_INDEXED(3)) ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICK_RIGHT, N_p("input-name", "%p Right"), input_seq(KEYCODE_6_PAD, input_seq::or_code, JOYCODE_X_RIGHT_SWITCH_INDEXED(3)) ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKRIGHT_UP, N_p("input-name", "%p Right Stick/Up"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKRIGHT_DOWN, N_p("input-name", "%p Right Stick/Down"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKRIGHT_LEFT, N_p("input-name", "%p Right Stick/Left"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKRIGHT_RIGHT, N_p("input-name", "%p Right Stick/Right"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKLEFT_UP, N_p("input-name", "%p Left Stick/Up"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKLEFT_DOWN, N_p("input-name", "%p Left Stick/Down"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKLEFT_LEFT, N_p("input-name", "%p Left Stick/Left"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKLEFT_RIGHT, N_p("input-name", "%p Left Stick/Right"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON1, N_p("input-name", "%p Button 1"), input_seq(KEYCODE_0_PAD, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(3)) ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON2, N_p("input-name", "%p Button 2"), input_seq(KEYCODE_DEL_PAD, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(3)) ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON3, N_p("input-name", "%p Button 3"), input_seq(KEYCODE_ENTER_PAD, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(3)) ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON4, N_p("input-name", "%p Button 4"), input_seq(JOYCODE_BUTTON4_INDEXED(3)) ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON5, N_p("input-name", "%p Button 5"), input_seq(JOYCODE_BUTTON5_INDEXED(3)) ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON6, N_p("input-name", "%p Button 6"), input_seq(JOYCODE_BUTTON6_INDEXED(3)) ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON7, N_p("input-name", "%p Button 7"), input_seq(JOYCODE_BUTTON7_INDEXED(3)) ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON8, N_p("input-name", "%p Button 8"), input_seq(JOYCODE_BUTTON8_INDEXED(3)) ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON9, N_p("input-name", "%p Button 9"), input_seq(JOYCODE_BUTTON9_INDEXED(3)) ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON10, N_p("input-name", "%p Button 10"), input_seq(JOYCODE_BUTTON10_INDEXED(3)) ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON11, N_p("input-name", "%p Button 11"), input_seq(JOYCODE_BUTTON11_INDEXED(3)) ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON12, N_p("input-name", "%p Button 12"), input_seq(JOYCODE_BUTTON12_INDEXED(3)) ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON13, N_p("input-name", "%p Button 13"), input_seq(JOYCODE_BUTTON13_INDEXED(3)) ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON14, N_p("input-name", "%p Button 14"), input_seq(JOYCODE_BUTTON14_INDEXED(3)) ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON15, N_p("input-name", "%p Button 15"), input_seq(JOYCODE_BUTTON15_INDEXED(3)) ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON16, N_p("input-name", "%p Button 16"), input_seq(JOYCODE_BUTTON16_INDEXED(3)) ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, START, N_p("input-name", "%p Start"), input_seq(KEYCODE_4, input_seq::or_code, JOYCODE_START_INDEXED(3)) ) \ - INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, SELECT, N_p("input-name", "%p Select"), input_seq(KEYCODE_8, input_seq::or_code, JOYCODE_SELECT_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICK_UP, N_p("input-name", "P4 Up"), input_seq(KEYCODE_8_PAD, input_seq::or_code, JOYCODE_Y_UP_SWITCH_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICK_DOWN, N_p("input-name", "P4 Down"), input_seq(KEYCODE_2_PAD, input_seq::or_code, JOYCODE_Y_DOWN_SWITCH_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICK_LEFT, N_p("input-name", "P4 Left"), input_seq(KEYCODE_4_PAD, input_seq::or_code, JOYCODE_X_LEFT_SWITCH_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICK_RIGHT, N_p("input-name", "P4 Right"), input_seq(KEYCODE_6_PAD, input_seq::or_code, JOYCODE_X_RIGHT_SWITCH_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKRIGHT_UP, N_p("input-name", "P4 Right Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKRIGHT_DOWN, N_p("input-name", "P4 Right Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKRIGHT_LEFT, N_p("input-name", "P4 Right Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKRIGHT_RIGHT, N_p("input-name", "P4 Right Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKLEFT_UP, N_p("input-name", "P4 Left Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKLEFT_DOWN, N_p("input-name", "P4 Left Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKLEFT_LEFT, N_p("input-name", "P4 Left Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, JOYSTICKLEFT_RIGHT, N_p("input-name", "P4 Left Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON1, N_p("input-name", "P4 Button 1"), input_seq(KEYCODE_0_PAD, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON2, N_p("input-name", "P4 Button 2"), input_seq(KEYCODE_DEL_PAD, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON3, N_p("input-name", "P4 Button 3"), input_seq(KEYCODE_ENTER_PAD, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON4, N_p("input-name", "P4 Button 4"), input_seq(JOYCODE_BUTTON4_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON5, N_p("input-name", "P4 Button 5"), input_seq(JOYCODE_BUTTON5_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON6, N_p("input-name", "P4 Button 6"), input_seq(JOYCODE_BUTTON6_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON7, N_p("input-name", "P4 Button 7"), input_seq(JOYCODE_BUTTON7_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON8, N_p("input-name", "P4 Button 8"), input_seq(JOYCODE_BUTTON8_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON9, N_p("input-name", "P4 Button 9"), input_seq(JOYCODE_BUTTON9_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON10, N_p("input-name", "P4 Button 10"), input_seq(JOYCODE_BUTTON10_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON11, N_p("input-name", "P4 Button 11"), input_seq(JOYCODE_BUTTON11_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON12, N_p("input-name", "P4 Button 12"), input_seq(JOYCODE_BUTTON12_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON13, N_p("input-name", "P4 Button 13"), input_seq(JOYCODE_BUTTON13_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON14, N_p("input-name", "P4 Button 14"), input_seq(JOYCODE_BUTTON14_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON15, N_p("input-name", "P4 Button 15"), input_seq(JOYCODE_BUTTON15_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, BUTTON16, N_p("input-name", "P4 Button 16"), input_seq(JOYCODE_BUTTON16_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, START, N_p("input-name", "P4 Start"), input_seq(KEYCODE_4, input_seq::or_code, JOYCODE_START_INDEXED(3)) ) \ + INPUT_PORT_DIGITAL_TYPE( 4, PLAYER4, SELECT, N_p("input-name", "P4 Select"), input_seq(KEYCODE_8, input_seq::or_code, JOYCODE_SELECT_INDEXED(3)) ) \ CORE_INPUT_TYPES_END() #define CORE_INPUT_TYPES_P5 \ CORE_INPUT_TYPES_BEGIN(p5) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICK_UP, N_p("input-name", "%p Up"), input_seq(JOYCODE_Y_UP_SWITCH_INDEXED(4)) ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICK_DOWN, N_p("input-name", "%p Down"), input_seq(JOYCODE_Y_DOWN_SWITCH_INDEXED(4)) ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICK_LEFT, N_p("input-name", "%p Left"), input_seq(JOYCODE_X_LEFT_SWITCH_INDEXED(4)) ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICK_RIGHT, N_p("input-name", "%p Right"), input_seq(JOYCODE_X_RIGHT_SWITCH_INDEXED(4)) ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKRIGHT_UP, N_p("input-name", "%p Right Stick/Up"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKRIGHT_DOWN, N_p("input-name", "%p Right Stick/Down"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKRIGHT_LEFT, N_p("input-name", "%p Right Stick/Left"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKRIGHT_RIGHT, N_p("input-name", "%p Right Stick/Right"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKLEFT_UP, N_p("input-name", "%p Left Stick/Up"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKLEFT_DOWN, N_p("input-name", "%p Left Stick/Down"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKLEFT_LEFT, N_p("input-name", "%p Left Stick/Left"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKLEFT_RIGHT, N_p("input-name", "%p Left Stick/Right"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON1, N_p("input-name", "%p Button 1"), input_seq(JOYCODE_BUTTON1_INDEXED(4)) ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON2, N_p("input-name", "%p Button 2"), input_seq(JOYCODE_BUTTON2_INDEXED(4)) ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON3, N_p("input-name", "%p Button 3"), input_seq(JOYCODE_BUTTON3_INDEXED(4)) ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON4, N_p("input-name", "%p Button 4"), input_seq(JOYCODE_BUTTON4_INDEXED(4)) ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON5, N_p("input-name", "%p Button 5"), input_seq(JOYCODE_BUTTON5_INDEXED(4)) ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON6, N_p("input-name", "%p Button 6"), input_seq(JOYCODE_BUTTON6_INDEXED(4)) ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON7, N_p("input-name", "%p Button 7"), input_seq(JOYCODE_BUTTON7_INDEXED(4)) ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON8, N_p("input-name", "%p Button 8"), input_seq(JOYCODE_BUTTON8_INDEXED(4)) ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON9, N_p("input-name", "%p Button 9"), input_seq(JOYCODE_BUTTON9_INDEXED(4)) ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON10, N_p("input-name", "%p Button 10"), input_seq(JOYCODE_BUTTON10_INDEXED(4)) ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON11, N_p("input-name", "%p Button 11"), input_seq(JOYCODE_BUTTON11_INDEXED(4)) ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON12, N_p("input-name", "%p Button 12"), input_seq(JOYCODE_BUTTON12_INDEXED(4)) ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON13, N_p("input-name", "%p Button 13"), input_seq(JOYCODE_BUTTON13_INDEXED(4)) ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON14, N_p("input-name", "%p Button 14"), input_seq(JOYCODE_BUTTON14_INDEXED(4)) ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON15, N_p("input-name", "%p Button 15"), input_seq(JOYCODE_BUTTON15_INDEXED(4)) ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON16, N_p("input-name", "%p Button 16"), input_seq(JOYCODE_BUTTON16_INDEXED(4)) ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, START, N_p("input-name", "%p Start"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, SELECT, N_p("input-name", "%p Select"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICK_UP, N_p("input-name", "P5 Up"), input_seq(JOYCODE_Y_UP_SWITCH_INDEXED(4)) ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICK_DOWN, N_p("input-name", "P5 Down"), input_seq(JOYCODE_Y_DOWN_SWITCH_INDEXED(4)) ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICK_LEFT, N_p("input-name", "P5 Left"), input_seq(JOYCODE_X_LEFT_SWITCH_INDEXED(4)) ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICK_RIGHT, N_p("input-name", "P5 Right"), input_seq(JOYCODE_X_RIGHT_SWITCH_INDEXED(4)) ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKRIGHT_UP, N_p("input-name", "P5 Right Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKRIGHT_DOWN, N_p("input-name", "P5 Right Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKRIGHT_LEFT, N_p("input-name", "P5 Right Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKRIGHT_RIGHT, N_p("input-name", "P5 Right Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKLEFT_UP, N_p("input-name", "P5 Left Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKLEFT_DOWN, N_p("input-name", "P5 Left Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKLEFT_LEFT, N_p("input-name", "P5 Left Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, JOYSTICKLEFT_RIGHT, N_p("input-name", "P5 Left Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON1, N_p("input-name", "P5 Button 1"), input_seq(JOYCODE_BUTTON1_INDEXED(4)) ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON2, N_p("input-name", "P5 Button 2"), input_seq(JOYCODE_BUTTON2_INDEXED(4)) ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON3, N_p("input-name", "P5 Button 3"), input_seq(JOYCODE_BUTTON3_INDEXED(4)) ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON4, N_p("input-name", "P5 Button 4"), input_seq(JOYCODE_BUTTON4_INDEXED(4)) ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON5, N_p("input-name", "P5 Button 5"), input_seq(JOYCODE_BUTTON5_INDEXED(4)) ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON6, N_p("input-name", "P5 Button 6"), input_seq(JOYCODE_BUTTON6_INDEXED(4)) ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON7, N_p("input-name", "P5 Button 7"), input_seq(JOYCODE_BUTTON7_INDEXED(4)) ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON8, N_p("input-name", "P5 Button 8"), input_seq(JOYCODE_BUTTON8_INDEXED(4)) ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON9, N_p("input-name", "P5 Button 9"), input_seq(JOYCODE_BUTTON9_INDEXED(4)) ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON10, N_p("input-name", "P5 Button 10"), input_seq(JOYCODE_BUTTON10_INDEXED(4)) ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON11, N_p("input-name", "P5 Button 11"), input_seq(JOYCODE_BUTTON11_INDEXED(4)) ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON12, N_p("input-name", "P5 Button 12"), input_seq(JOYCODE_BUTTON12_INDEXED(4)) ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON13, N_p("input-name", "P5 Button 13"), input_seq(JOYCODE_BUTTON13_INDEXED(4)) ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON14, N_p("input-name", "P5 Button 14"), input_seq(JOYCODE_BUTTON14_INDEXED(4)) ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON15, N_p("input-name", "P5 Button 15"), input_seq(JOYCODE_BUTTON15_INDEXED(4)) ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, BUTTON16, N_p("input-name", "P5 Button 16"), input_seq(JOYCODE_BUTTON16_INDEXED(4)) ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, START, N_p("input-name", "P5 Start"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 5, PLAYER5, SELECT, N_p("input-name", "P5 Select"), input_seq() ) \ CORE_INPUT_TYPES_END() #define CORE_INPUT_TYPES_P6 \ CORE_INPUT_TYPES_BEGIN(p6) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICK_UP, N_p("input-name", "%p Up"), input_seq(JOYCODE_Y_UP_SWITCH_INDEXED(5)) ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICK_DOWN, N_p("input-name", "%p Down"), input_seq(JOYCODE_Y_DOWN_SWITCH_INDEXED(5)) ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICK_LEFT, N_p("input-name", "%p Left"), input_seq(JOYCODE_X_LEFT_SWITCH_INDEXED(5)) ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICK_RIGHT, N_p("input-name", "%p Right"), input_seq(JOYCODE_X_RIGHT_SWITCH_INDEXED(5)) ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKRIGHT_UP, N_p("input-name", "%p Right Stick/Up"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKRIGHT_DOWN, N_p("input-name", "%p Right Stick/Down"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKRIGHT_LEFT, N_p("input-name", "%p Right Stick/Left"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKRIGHT_RIGHT, N_p("input-name", "%p Right Stick/Right"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKLEFT_UP, N_p("input-name", "%p Left Stick/Up"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKLEFT_DOWN, N_p("input-name", "%p Left Stick/Down"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKLEFT_LEFT, N_p("input-name", "%p Left Stick/Left"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKLEFT_RIGHT, N_p("input-name", "%p Left Stick/Right"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON1, N_p("input-name", "%p Button 1"), input_seq(JOYCODE_BUTTON1_INDEXED(5)) ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON2, N_p("input-name", "%p Button 2"), input_seq(JOYCODE_BUTTON2_INDEXED(5)) ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON3, N_p("input-name", "%p Button 3"), input_seq(JOYCODE_BUTTON3_INDEXED(5)) ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON4, N_p("input-name", "%p Button 4"), input_seq(JOYCODE_BUTTON4_INDEXED(5)) ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON5, N_p("input-name", "%p Button 5"), input_seq(JOYCODE_BUTTON5_INDEXED(5)) ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON6, N_p("input-name", "%p Button 6"), input_seq(JOYCODE_BUTTON6_INDEXED(5)) ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON7, N_p("input-name", "%p Button 7"), input_seq(JOYCODE_BUTTON7_INDEXED(5)) ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON8, N_p("input-name", "%p Button 8"), input_seq(JOYCODE_BUTTON8_INDEXED(5)) ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON9, N_p("input-name", "%p Button 9"), input_seq(JOYCODE_BUTTON9_INDEXED(5)) ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON10, N_p("input-name", "%p Button 10"), input_seq(JOYCODE_BUTTON10_INDEXED(5)) ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON11, N_p("input-name", "%p Button 11"), input_seq(JOYCODE_BUTTON11_INDEXED(5)) ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON12, N_p("input-name", "%p Button 12"), input_seq(JOYCODE_BUTTON12_INDEXED(5)) ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON13, N_p("input-name", "%p Button 13"), input_seq(JOYCODE_BUTTON13_INDEXED(5)) ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON14, N_p("input-name", "%p Button 14"), input_seq(JOYCODE_BUTTON14_INDEXED(5)) ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON15, N_p("input-name", "%p Button 15"), input_seq(JOYCODE_BUTTON15_INDEXED(5)) ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON16, N_p("input-name", "%p Button 16"), input_seq(JOYCODE_BUTTON16_INDEXED(5)) ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, START, N_p("input-name", "%p Start"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, SELECT, N_p("input-name", "%p Select"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICK_UP, N_p("input-name", "P6 Up"), input_seq(JOYCODE_Y_UP_SWITCH_INDEXED(5)) ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICK_DOWN, N_p("input-name", "P6 Down"), input_seq(JOYCODE_Y_DOWN_SWITCH_INDEXED(5)) ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICK_LEFT, N_p("input-name", "P6 Left"), input_seq(JOYCODE_X_LEFT_SWITCH_INDEXED(5)) ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICK_RIGHT, N_p("input-name", "P6 Right"), input_seq(JOYCODE_X_RIGHT_SWITCH_INDEXED(5)) ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKRIGHT_UP, N_p("input-name", "P6 Right Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKRIGHT_DOWN, N_p("input-name", "P6 Right Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKRIGHT_LEFT, N_p("input-name", "P6 Right Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKRIGHT_RIGHT, N_p("input-name", "P6 Right Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKLEFT_UP, N_p("input-name", "P6 Left Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKLEFT_DOWN, N_p("input-name", "P6 Left Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKLEFT_LEFT, N_p("input-name", "P6 Left Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, JOYSTICKLEFT_RIGHT, N_p("input-name", "P6 Left Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON1, N_p("input-name", "P6 Button 1"), input_seq(JOYCODE_BUTTON1_INDEXED(5)) ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON2, N_p("input-name", "P6 Button 2"), input_seq(JOYCODE_BUTTON2_INDEXED(5)) ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON3, N_p("input-name", "P6 Button 3"), input_seq(JOYCODE_BUTTON3_INDEXED(5)) ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON4, N_p("input-name", "P6 Button 4"), input_seq(JOYCODE_BUTTON4_INDEXED(5)) ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON5, N_p("input-name", "P6 Button 5"), input_seq(JOYCODE_BUTTON5_INDEXED(5)) ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON6, N_p("input-name", "P6 Button 6"), input_seq(JOYCODE_BUTTON6_INDEXED(5)) ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON7, N_p("input-name", "P6 Button 7"), input_seq(JOYCODE_BUTTON7_INDEXED(5)) ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON8, N_p("input-name", "P6 Button 8"), input_seq(JOYCODE_BUTTON8_INDEXED(5)) ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON9, N_p("input-name", "P6 Button 9"), input_seq(JOYCODE_BUTTON9_INDEXED(5)) ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON10, N_p("input-name", "P6 Button 10"), input_seq(JOYCODE_BUTTON10_INDEXED(5)) ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON11, N_p("input-name", "P6 Button 11"), input_seq(JOYCODE_BUTTON11_INDEXED(5)) ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON12, N_p("input-name", "P6 Button 12"), input_seq(JOYCODE_BUTTON12_INDEXED(5)) ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON13, N_p("input-name", "P6 Button 13"), input_seq(JOYCODE_BUTTON13_INDEXED(5)) ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON14, N_p("input-name", "P6 Button 14"), input_seq(JOYCODE_BUTTON14_INDEXED(5)) ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON15, N_p("input-name", "P6 Button 15"), input_seq(JOYCODE_BUTTON15_INDEXED(5)) ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, BUTTON16, N_p("input-name", "P6 Button 16"), input_seq(JOYCODE_BUTTON16_INDEXED(5)) ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, START, N_p("input-name", "P6 Start"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 6, PLAYER6, SELECT, N_p("input-name", "P6 Select"), input_seq() ) \ CORE_INPUT_TYPES_END() #define CORE_INPUT_TYPES_P7 \ CORE_INPUT_TYPES_BEGIN(p7) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICK_UP, N_p("input-name", "%p Up"), input_seq(JOYCODE_Y_UP_SWITCH_INDEXED(6)) ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICK_DOWN, N_p("input-name", "%p Down"), input_seq(JOYCODE_Y_DOWN_SWITCH_INDEXED(6)) ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICK_LEFT, N_p("input-name", "%p Left"), input_seq(JOYCODE_X_LEFT_SWITCH_INDEXED(6)) ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICK_RIGHT, N_p("input-name", "%p Right"), input_seq(JOYCODE_X_RIGHT_SWITCH_INDEXED(6)) ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKRIGHT_UP, N_p("input-name", "%p Right Stick/Up"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKRIGHT_DOWN, N_p("input-name", "%p Right Stick/Down"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKRIGHT_LEFT, N_p("input-name", "%p Right Stick/Left"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKRIGHT_RIGHT, N_p("input-name", "%p Right Stick/Right"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKLEFT_UP, N_p("input-name", "%p Left Stick/Up"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKLEFT_DOWN, N_p("input-name", "%p Left Stick/Down"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKLEFT_LEFT, N_p("input-name", "%p Left Stick/Left"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKLEFT_RIGHT, N_p("input-name", "%p Left Stick/Right"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON1, N_p("input-name", "%p Button 1"), input_seq(JOYCODE_BUTTON1_INDEXED(6)) ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON2, N_p("input-name", "%p Button 2"), input_seq(JOYCODE_BUTTON2_INDEXED(6)) ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON3, N_p("input-name", "%p Button 3"), input_seq(JOYCODE_BUTTON3_INDEXED(6)) ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON4, N_p("input-name", "%p Button 4"), input_seq(JOYCODE_BUTTON4_INDEXED(6)) ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON5, N_p("input-name", "%p Button 5"), input_seq(JOYCODE_BUTTON5_INDEXED(6)) ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON6, N_p("input-name", "%p Button 6"), input_seq(JOYCODE_BUTTON6_INDEXED(6)) ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON7, N_p("input-name", "%p Button 7"), input_seq(JOYCODE_BUTTON7_INDEXED(6)) ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON8, N_p("input-name", "%p Button 8"), input_seq(JOYCODE_BUTTON8_INDEXED(6)) ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON9, N_p("input-name", "%p Button 9"), input_seq(JOYCODE_BUTTON9_INDEXED(6)) ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON10, N_p("input-name", "%p Button 10"), input_seq(JOYCODE_BUTTON10_INDEXED(6)) ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON11, N_p("input-name", "%p Button 11"), input_seq(JOYCODE_BUTTON11_INDEXED(6)) ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON12, N_p("input-name", "%p Button 12"), input_seq(JOYCODE_BUTTON12_INDEXED(6)) ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON13, N_p("input-name", "%p Button 13"), input_seq(JOYCODE_BUTTON13_INDEXED(6)) ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON14, N_p("input-name", "%p Button 14"), input_seq(JOYCODE_BUTTON14_INDEXED(6)) ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON15, N_p("input-name", "%p Button 15"), input_seq(JOYCODE_BUTTON15_INDEXED(6)) ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON16, N_p("input-name", "%p Button 16"), input_seq(JOYCODE_BUTTON16_INDEXED(6)) ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, START, N_p("input-name", "%p Start"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, SELECT, N_p("input-name", "%p Select"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICK_UP, N_p("input-name", "P7 Up"), input_seq(JOYCODE_Y_UP_SWITCH_INDEXED(6)) ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICK_DOWN, N_p("input-name", "P7 Down"), input_seq(JOYCODE_Y_DOWN_SWITCH_INDEXED(6)) ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICK_LEFT, N_p("input-name", "P7 Left"), input_seq(JOYCODE_X_LEFT_SWITCH_INDEXED(6)) ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICK_RIGHT, N_p("input-name", "P7 Right"), input_seq(JOYCODE_X_RIGHT_SWITCH_INDEXED(6)) ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKRIGHT_UP, N_p("input-name", "P7 Right Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKRIGHT_DOWN, N_p("input-name", "P7 Right Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKRIGHT_LEFT, N_p("input-name", "P7 Right Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKRIGHT_RIGHT, N_p("input-name", "P7 Right Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKLEFT_UP, N_p("input-name", "P7 Left Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKLEFT_DOWN, N_p("input-name", "P7 Left Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKLEFT_LEFT, N_p("input-name", "P7 Left Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, JOYSTICKLEFT_RIGHT, N_p("input-name", "P7 Left Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON1, N_p("input-name", "P7 Button 1"), input_seq(JOYCODE_BUTTON1_INDEXED(6)) ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON2, N_p("input-name", "P7 Button 2"), input_seq(JOYCODE_BUTTON2_INDEXED(6)) ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON3, N_p("input-name", "P7 Button 3"), input_seq(JOYCODE_BUTTON3_INDEXED(6)) ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON4, N_p("input-name", "P7 Button 4"), input_seq(JOYCODE_BUTTON4_INDEXED(6)) ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON5, N_p("input-name", "P7 Button 5"), input_seq(JOYCODE_BUTTON5_INDEXED(6)) ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON6, N_p("input-name", "P7 Button 6"), input_seq(JOYCODE_BUTTON6_INDEXED(6)) ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON7, N_p("input-name", "P7 Button 7"), input_seq(JOYCODE_BUTTON7_INDEXED(6)) ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON8, N_p("input-name", "P7 Button 8"), input_seq(JOYCODE_BUTTON8_INDEXED(6)) ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON9, N_p("input-name", "P7 Button 9"), input_seq(JOYCODE_BUTTON9_INDEXED(6)) ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON10, N_p("input-name", "P7 Button 10"), input_seq(JOYCODE_BUTTON10_INDEXED(6)) ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON11, N_p("input-name", "P7 Button 11"), input_seq(JOYCODE_BUTTON11_INDEXED(6)) ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON12, N_p("input-name", "P7 Button 12"), input_seq(JOYCODE_BUTTON12_INDEXED(6)) ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON13, N_p("input-name", "P7 Button 13"), input_seq(JOYCODE_BUTTON13_INDEXED(6)) ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON14, N_p("input-name", "P7 Button 14"), input_seq(JOYCODE_BUTTON14_INDEXED(6)) ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON15, N_p("input-name", "P7 Button 15"), input_seq(JOYCODE_BUTTON15_INDEXED(6)) ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, BUTTON16, N_p("input-name", "P7 Button 16"), input_seq(JOYCODE_BUTTON16_INDEXED(6)) ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, START, N_p("input-name", "P7 Start"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 7, PLAYER7, SELECT, N_p("input-name", "P7 Select"), input_seq() ) \ CORE_INPUT_TYPES_END() #define CORE_INPUT_TYPES_P8 \ CORE_INPUT_TYPES_BEGIN(p8) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICK_UP, N_p("input-name", "%p Up"), input_seq(JOYCODE_Y_UP_SWITCH_INDEXED(7)) ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICK_DOWN, N_p("input-name", "%p Down"), input_seq(JOYCODE_Y_DOWN_SWITCH_INDEXED(7)) ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICK_LEFT, N_p("input-name", "%p Left"), input_seq(JOYCODE_X_LEFT_SWITCH_INDEXED(7)) ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICK_RIGHT, N_p("input-name", "%p Right"), input_seq(JOYCODE_X_RIGHT_SWITCH_INDEXED(7)) ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKRIGHT_UP, N_p("input-name", "%p Right Stick/Up"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKRIGHT_DOWN, N_p("input-name", "%p Right Stick/Down"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKRIGHT_LEFT, N_p("input-name", "%p Right Stick/Left"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKRIGHT_RIGHT, N_p("input-name", "%p Right Stick/Right"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKLEFT_UP, N_p("input-name", "%p Left Stick/Up"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKLEFT_DOWN, N_p("input-name", "%p Left Stick/Down"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKLEFT_LEFT, N_p("input-name", "%p Left Stick/Left"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKLEFT_RIGHT, N_p("input-name", "%p Left Stick/Right"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON1, N_p("input-name", "%p Button 1"), input_seq(JOYCODE_BUTTON1_INDEXED(7)) ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON2, N_p("input-name", "%p Button 2"), input_seq(JOYCODE_BUTTON2_INDEXED(7)) ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON3, N_p("input-name", "%p Button 3"), input_seq(JOYCODE_BUTTON3_INDEXED(7)) ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON4, N_p("input-name", "%p Button 4"), input_seq(JOYCODE_BUTTON4_INDEXED(7)) ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON5, N_p("input-name", "%p Button 5"), input_seq(JOYCODE_BUTTON5_INDEXED(7)) ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON6, N_p("input-name", "%p Button 6"), input_seq(JOYCODE_BUTTON6_INDEXED(7)) ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON7, N_p("input-name", "%p Button 7"), input_seq(JOYCODE_BUTTON7_INDEXED(7)) ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON8, N_p("input-name", "%p Button 8"), input_seq(JOYCODE_BUTTON8_INDEXED(7)) ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON9, N_p("input-name", "%p Button 9"), input_seq(JOYCODE_BUTTON9_INDEXED(7)) ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON10, N_p("input-name", "%p Button 10"), input_seq(JOYCODE_BUTTON10_INDEXED(7)) ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON11, N_p("input-name", "%p Button 11"), input_seq(JOYCODE_BUTTON11_INDEXED(7)) ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON12, N_p("input-name", "%p Button 12"), input_seq(JOYCODE_BUTTON12_INDEXED(7)) ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON13, N_p("input-name", "%p Button 13"), input_seq(JOYCODE_BUTTON13_INDEXED(7)) ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON14, N_p("input-name", "%p Button 14"), input_seq(JOYCODE_BUTTON14_INDEXED(7)) ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON15, N_p("input-name", "%p Button 15"), input_seq(JOYCODE_BUTTON15_INDEXED(7)) ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON16, N_p("input-name", "%p Button 16"), input_seq(JOYCODE_BUTTON16_INDEXED(7)) ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, START, N_p("input-name", "%p Start"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, SELECT, N_p("input-name", "%p Select"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICK_UP, N_p("input-name", "P8 Up"), input_seq(JOYCODE_Y_UP_SWITCH_INDEXED(7)) ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICK_DOWN, N_p("input-name", "P8 Down"), input_seq(JOYCODE_Y_DOWN_SWITCH_INDEXED(7)) ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICK_LEFT, N_p("input-name", "P8 Left"), input_seq(JOYCODE_X_LEFT_SWITCH_INDEXED(7)) ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICK_RIGHT, N_p("input-name", "P8 Right"), input_seq(JOYCODE_X_RIGHT_SWITCH_INDEXED(7)) ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKRIGHT_UP, N_p("input-name", "P8 Right Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKRIGHT_DOWN, N_p("input-name", "P8 Right Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKRIGHT_LEFT, N_p("input-name", "P8 Right Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKRIGHT_RIGHT, N_p("input-name", "P8 Right Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKLEFT_UP, N_p("input-name", "P8 Left Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKLEFT_DOWN, N_p("input-name", "P8 Left Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKLEFT_LEFT, N_p("input-name", "P8 Left Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, JOYSTICKLEFT_RIGHT, N_p("input-name", "P8 Left Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON1, N_p("input-name", "P8 Button 1"), input_seq(JOYCODE_BUTTON1_INDEXED(7)) ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON2, N_p("input-name", "P8 Button 2"), input_seq(JOYCODE_BUTTON2_INDEXED(7)) ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON3, N_p("input-name", "P8 Button 3"), input_seq(JOYCODE_BUTTON3_INDEXED(7)) ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON4, N_p("input-name", "P8 Button 4"), input_seq(JOYCODE_BUTTON4_INDEXED(7)) ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON5, N_p("input-name", "P8 Button 5"), input_seq(JOYCODE_BUTTON5_INDEXED(7)) ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON6, N_p("input-name", "P8 Button 6"), input_seq(JOYCODE_BUTTON6_INDEXED(7)) ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON7, N_p("input-name", "P8 Button 7"), input_seq(JOYCODE_BUTTON7_INDEXED(7)) ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON8, N_p("input-name", "P8 Button 8"), input_seq(JOYCODE_BUTTON8_INDEXED(7)) ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON9, N_p("input-name", "P8 Button 9"), input_seq(JOYCODE_BUTTON9_INDEXED(7)) ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON10, N_p("input-name", "P8 Button 10"), input_seq(JOYCODE_BUTTON10_INDEXED(7)) ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON11, N_p("input-name", "P8 Button 11"), input_seq(JOYCODE_BUTTON11_INDEXED(7)) ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON12, N_p("input-name", "P8 Button 12"), input_seq(JOYCODE_BUTTON12_INDEXED(7)) ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON13, N_p("input-name", "P8 Button 13"), input_seq(JOYCODE_BUTTON13_INDEXED(7)) ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON14, N_p("input-name", "P8 Button 14"), input_seq(JOYCODE_BUTTON14_INDEXED(7)) ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON15, N_p("input-name", "P8 Button 15"), input_seq(JOYCODE_BUTTON15_INDEXED(7)) ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, BUTTON16, N_p("input-name", "P8 Button 16"), input_seq(JOYCODE_BUTTON16_INDEXED(7)) ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, START, N_p("input-name", "P8 Start"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 8, PLAYER8, SELECT, N_p("input-name", "P8 Select"), input_seq() ) \ CORE_INPUT_TYPES_END() #define CORE_INPUT_TYPES_P9 \ CORE_INPUT_TYPES_BEGIN(p9) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICK_UP, N_p("input-name", "%p Up"), input_seq(JOYCODE_Y_UP_SWITCH_INDEXED(8)) ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICK_DOWN, N_p("input-name", "%p Down"), input_seq(JOYCODE_Y_DOWN_SWITCH_INDEXED(8)) ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICK_LEFT, N_p("input-name", "%p Left"), input_seq(JOYCODE_X_LEFT_SWITCH_INDEXED(8)) ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICK_RIGHT, N_p("input-name", "%p Right"), input_seq(JOYCODE_X_RIGHT_SWITCH_INDEXED(8)) ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKRIGHT_UP, N_p("input-name", "%p Right Stick/Up"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKRIGHT_DOWN, N_p("input-name", "%p Right Stick/Down"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKRIGHT_LEFT, N_p("input-name", "%p Right Stick/Left"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKRIGHT_RIGHT, N_p("input-name", "%p Right Stick/Right"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKLEFT_UP, N_p("input-name", "%p Left Stick/Up"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKLEFT_DOWN, N_p("input-name", "%p Left Stick/Down"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKLEFT_LEFT, N_p("input-name", "%p Left Stick/Left"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKLEFT_RIGHT, N_p("input-name", "%p Left Stick/Right"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON1, N_p("input-name", "%p Button 1"), input_seq(JOYCODE_BUTTON1_INDEXED(8)) ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON2, N_p("input-name", "%p Button 2"), input_seq(JOYCODE_BUTTON2_INDEXED(8)) ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON3, N_p("input-name", "%p Button 3"), input_seq(JOYCODE_BUTTON3_INDEXED(8)) ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON4, N_p("input-name", "%p Button 4"), input_seq(JOYCODE_BUTTON4_INDEXED(8)) ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON5, N_p("input-name", "%p Button 5"), input_seq(JOYCODE_BUTTON5_INDEXED(8)) ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON6, N_p("input-name", "%p Button 6"), input_seq(JOYCODE_BUTTON6_INDEXED(8)) ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON7, N_p("input-name", "%p Button 7"), input_seq(JOYCODE_BUTTON7_INDEXED(8)) ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON8, N_p("input-name", "%p Button 8"), input_seq(JOYCODE_BUTTON8_INDEXED(8)) ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON9, N_p("input-name", "%p Button 9"), input_seq(JOYCODE_BUTTON9_INDEXED(8)) ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON10, N_p("input-name", "%p Button 10"), input_seq(JOYCODE_BUTTON10_INDEXED(8)) ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON11, N_p("input-name", "%p Button 11"), input_seq(JOYCODE_BUTTON11_INDEXED(8)) ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON12, N_p("input-name", "%p Button 12"), input_seq(JOYCODE_BUTTON12_INDEXED(8)) ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON13, N_p("input-name", "%p Button 13"), input_seq(JOYCODE_BUTTON13_INDEXED(8)) ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON14, N_p("input-name", "%p Button 14"), input_seq(JOYCODE_BUTTON14_INDEXED(8)) ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON15, N_p("input-name", "%p Button 15"), input_seq(JOYCODE_BUTTON15_INDEXED(8)) ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON16, N_p("input-name", "%p Button 16"), input_seq(JOYCODE_BUTTON16_INDEXED(8)) ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, START, N_p("input-name", "%p Start"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, SELECT, N_p("input-name", "%p Select"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICK_UP, N_p("input-name", "P9 Up"), input_seq(JOYCODE_Y_UP_SWITCH_INDEXED(8)) ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICK_DOWN, N_p("input-name", "P9 Down"), input_seq(JOYCODE_Y_DOWN_SWITCH_INDEXED(8)) ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICK_LEFT, N_p("input-name", "P9 Left"), input_seq(JOYCODE_X_LEFT_SWITCH_INDEXED(8)) ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICK_RIGHT, N_p("input-name", "P9 Right"), input_seq(JOYCODE_X_RIGHT_SWITCH_INDEXED(8)) ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKRIGHT_UP, N_p("input-name", "P9 Right Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKRIGHT_DOWN, N_p("input-name", "P9 Right Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKRIGHT_LEFT, N_p("input-name", "P9 Right Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKRIGHT_RIGHT, N_p("input-name", "P9 Right Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKLEFT_UP, N_p("input-name", "P9 Left Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKLEFT_DOWN, N_p("input-name", "P9 Left Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKLEFT_LEFT, N_p("input-name", "P9 Left Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, JOYSTICKLEFT_RIGHT, N_p("input-name", "P9 Left Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON1, N_p("input-name", "P9 Button 1"), input_seq(JOYCODE_BUTTON1_INDEXED(8)) ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON2, N_p("input-name", "P9 Button 2"), input_seq(JOYCODE_BUTTON2_INDEXED(8)) ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON3, N_p("input-name", "P9 Button 3"), input_seq(JOYCODE_BUTTON3_INDEXED(8)) ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON4, N_p("input-name", "P9 Button 4"), input_seq(JOYCODE_BUTTON4_INDEXED(8)) ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON5, N_p("input-name", "P9 Button 5"), input_seq(JOYCODE_BUTTON5_INDEXED(8)) ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON6, N_p("input-name", "P9 Button 6"), input_seq(JOYCODE_BUTTON6_INDEXED(8)) ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON7, N_p("input-name", "P9 Button 7"), input_seq(JOYCODE_BUTTON7_INDEXED(8)) ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON8, N_p("input-name", "P9 Button 8"), input_seq(JOYCODE_BUTTON8_INDEXED(8)) ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON9, N_p("input-name", "P9 Button 9"), input_seq(JOYCODE_BUTTON9_INDEXED(8)) ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON10, N_p("input-name", "P9 Button 10"), input_seq(JOYCODE_BUTTON10_INDEXED(8)) ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON11, N_p("input-name", "P9 Button 11"), input_seq(JOYCODE_BUTTON11_INDEXED(8)) ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON12, N_p("input-name", "P9 Button 12"), input_seq(JOYCODE_BUTTON12_INDEXED(8)) ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON13, N_p("input-name", "P9 Button 13"), input_seq(JOYCODE_BUTTON13_INDEXED(8)) ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON14, N_p("input-name", "P9 Button 14"), input_seq(JOYCODE_BUTTON14_INDEXED(8)) ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON15, N_p("input-name", "P9 Button 15"), input_seq(JOYCODE_BUTTON15_INDEXED(8)) ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, BUTTON16, N_p("input-name", "P9 Button 16"), input_seq(JOYCODE_BUTTON16_INDEXED(8)) ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, START, N_p("input-name", "P9 Start"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 9, PLAYER9, SELECT, N_p("input-name", "P9 Select"), input_seq() ) \ CORE_INPUT_TYPES_END() #define CORE_INPUT_TYPES_P10 \ CORE_INPUT_TYPES_BEGIN(p10) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICK_UP, N_p("input-name", "%p Up"), input_seq(JOYCODE_Y_UP_SWITCH_INDEXED(9)) ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICK_DOWN, N_p("input-name", "%p Down"), input_seq(JOYCODE_Y_DOWN_SWITCH_INDEXED(9)) ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICK_LEFT, N_p("input-name", "%p Left"), input_seq(JOYCODE_X_LEFT_SWITCH_INDEXED(9)) ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICK_RIGHT, N_p("input-name", "%p Right"), input_seq(JOYCODE_X_RIGHT_SWITCH_INDEXED(9)) ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKRIGHT_UP, N_p("input-name", "%p Right Stick/Up"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKRIGHT_DOWN, N_p("input-name", "%p Right Stick/Down"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKRIGHT_LEFT, N_p("input-name", "%p Right Stick/Left"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKRIGHT_RIGHT, N_p("input-name", "%p Right Stick/Right"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKLEFT_UP, N_p("input-name", "%p Left Stick/Up"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKLEFT_DOWN, N_p("input-name", "%p Left Stick/Down"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKLEFT_LEFT, N_p("input-name", "%p Left Stick/Left"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKLEFT_RIGHT, N_p("input-name", "%p Left Stick/Right"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON1, N_p("input-name", "%p Button 1"), input_seq(JOYCODE_BUTTON1_INDEXED(9)) ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON2, N_p("input-name", "%p Button 2"), input_seq(JOYCODE_BUTTON2_INDEXED(9)) ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON3, N_p("input-name", "%p Button 3"), input_seq(JOYCODE_BUTTON3_INDEXED(9)) ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON4, N_p("input-name", "%p Button 4"), input_seq(JOYCODE_BUTTON4_INDEXED(9)) ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON5, N_p("input-name", "%p Button 5"), input_seq(JOYCODE_BUTTON5_INDEXED(9)) ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON6, N_p("input-name", "%p Button 6"), input_seq(JOYCODE_BUTTON6_INDEXED(9)) ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON7, N_p("input-name", "%p Button 7"), input_seq(JOYCODE_BUTTON7_INDEXED(9)) ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON8, N_p("input-name", "%p Button 8"), input_seq(JOYCODE_BUTTON8_INDEXED(9)) ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON9, N_p("input-name", "%p Button 9"), input_seq(JOYCODE_BUTTON9_INDEXED(9)) ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON10, N_p("input-name", "%p Button 10"), input_seq(JOYCODE_BUTTON10_INDEXED(9)) ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON11, N_p("input-name", "%p Button 11"), input_seq(JOYCODE_BUTTON11_INDEXED(9)) ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON12, N_p("input-name", "%p Button 12"), input_seq(JOYCODE_BUTTON12_INDEXED(9)) ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON13, N_p("input-name", "%p Button 13"), input_seq(JOYCODE_BUTTON13_INDEXED(9)) ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON14, N_p("input-name", "%p Button 14"), input_seq(JOYCODE_BUTTON14_INDEXED(9)) ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON15, N_p("input-name", "%p Button 15"), input_seq(JOYCODE_BUTTON15_INDEXED(9)) ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON16, N_p("input-name", "%p Button 16"), input_seq(JOYCODE_BUTTON16_INDEXED(9)) ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, START, N_p("input-name", "%p Start"), input_seq() ) \ - INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, SELECT, N_p("input-name", "%p Select"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICK_UP, N_p("input-name", "P10 Up"), input_seq(JOYCODE_Y_UP_SWITCH_INDEXED(9)) ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICK_DOWN, N_p("input-name", "P10 Down"), input_seq(JOYCODE_Y_DOWN_SWITCH_INDEXED(9)) ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICK_LEFT, N_p("input-name", "P10 Left"), input_seq(JOYCODE_X_LEFT_SWITCH_INDEXED(9)) ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICK_RIGHT, N_p("input-name", "P10 Right"), input_seq(JOYCODE_X_RIGHT_SWITCH_INDEXED(9)) ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKRIGHT_UP, N_p("input-name", "P10 Right Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKRIGHT_DOWN, N_p("input-name", "P10 Right Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKRIGHT_LEFT, N_p("input-name", "P10 Right Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKRIGHT_RIGHT, N_p("input-name", "P10 Right Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKLEFT_UP, N_p("input-name", "P10 Left Stick/Up"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKLEFT_DOWN, N_p("input-name", "P10 Left Stick/Down"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKLEFT_LEFT, N_p("input-name", "P10 Left Stick/Left"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, JOYSTICKLEFT_RIGHT, N_p("input-name", "P10 Left Stick/Right"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON1, N_p("input-name", "P10 Button 1"), input_seq(JOYCODE_BUTTON1_INDEXED(9)) ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON2, N_p("input-name", "P10 Button 2"), input_seq(JOYCODE_BUTTON2_INDEXED(9)) ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON3, N_p("input-name", "P10 Button 3"), input_seq(JOYCODE_BUTTON3_INDEXED(9)) ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON4, N_p("input-name", "P10 Button 4"), input_seq(JOYCODE_BUTTON4_INDEXED(9)) ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON5, N_p("input-name", "P10 Button 5"), input_seq(JOYCODE_BUTTON5_INDEXED(9)) ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON6, N_p("input-name", "P10 Button 6"), input_seq(JOYCODE_BUTTON6_INDEXED(9)) ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON7, N_p("input-name", "P10 Button 7"), input_seq(JOYCODE_BUTTON7_INDEXED(9)) ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON8, N_p("input-name", "P10 Button 8"), input_seq(JOYCODE_BUTTON8_INDEXED(9)) ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON9, N_p("input-name", "P10 Button 9"), input_seq(JOYCODE_BUTTON9_INDEXED(9)) ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON10, N_p("input-name", "P10 Button 10"), input_seq(JOYCODE_BUTTON10_INDEXED(9)) ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON11, N_p("input-name", "P10 Button 11"), input_seq(JOYCODE_BUTTON11_INDEXED(9)) ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON12, N_p("input-name", "P10 Button 12"), input_seq(JOYCODE_BUTTON12_INDEXED(9)) ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON13, N_p("input-name", "P10 Button 13"), input_seq(JOYCODE_BUTTON13_INDEXED(9)) ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON14, N_p("input-name", "P10 Button 14"), input_seq(JOYCODE_BUTTON14_INDEXED(9)) ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON15, N_p("input-name", "P10 Button 15"), input_seq(JOYCODE_BUTTON15_INDEXED(9)) ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, BUTTON16, N_p("input-name", "P10 Button 16"), input_seq(JOYCODE_BUTTON16_INDEXED(9)) ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, START, N_p("input-name", "P10 Start"), input_seq() ) \ + INPUT_PORT_DIGITAL_TYPE( 10, PLAYER10, SELECT, N_p("input-name", "P10 Select"), input_seq() ) \ CORE_INPUT_TYPES_END() #define CORE_INPUT_TYPES_START \ @@ -547,44 +547,44 @@ namespace { #define CORE_INPUT_TYPES_PEDAL \ CORE_INPUT_TYPES_BEGIN(pedal) \ - INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, PEDAL, N_p("input-name", "%p Pedal 1"), input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(0)), input_seq(), input_seq(KEYCODE_LCONTROL, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(0)) ) \ - INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, PEDAL, N_p("input-name", "%p Pedal 1"), input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(1)), input_seq(), input_seq(KEYCODE_A, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(1)) ) \ - INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, PEDAL, N_p("input-name", "%p Pedal 1"), input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(2)), input_seq(), input_seq(KEYCODE_RCONTROL, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(2)) ) \ - INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, PEDAL, N_p("input-name", "%p Pedal 1"), input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(3)), input_seq(), input_seq(KEYCODE_0_PAD, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(3)) ) \ - INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, PEDAL, N_p("input-name", "%p Pedal 1"), input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(4)), input_seq(), input_seq(JOYCODE_BUTTON1_INDEXED(4)) ) \ - INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, PEDAL, N_p("input-name", "%p Pedal 1"), input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(5)), input_seq(), input_seq(JOYCODE_BUTTON1_INDEXED(5)) ) \ - INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, PEDAL, N_p("input-name", "%p Pedal 1"), input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(6)), input_seq(), input_seq(JOYCODE_BUTTON1_INDEXED(6)) ) \ - INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, PEDAL, N_p("input-name", "%p Pedal 1"), input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(7)), input_seq(), input_seq(JOYCODE_BUTTON1_INDEXED(7)) ) \ - INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, PEDAL, N_p("input-name", "%p Pedal 1"), input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(8)), input_seq(), input_seq(JOYCODE_BUTTON1_INDEXED(8)) ) \ - INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, PEDAL, N_p("input-name", "%p Pedal 1"), input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(9)), input_seq(), input_seq(JOYCODE_BUTTON1_INDEXED(9)) ) \ + INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, PEDAL, N_p("input-name", "P1 Pedal 1"), input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(0)), input_seq(), input_seq(KEYCODE_LCONTROL, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(0)) ) \ + INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, PEDAL, N_p("input-name", "P2 Pedal 1"), input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(1)), input_seq(), input_seq(KEYCODE_A, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(1)) ) \ + INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, PEDAL, N_p("input-name", "P3 Pedal 1"), input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(2)), input_seq(), input_seq(KEYCODE_RCONTROL, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(2)) ) \ + INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, PEDAL, N_p("input-name", "P4 Pedal 1"), input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(3)), input_seq(), input_seq(KEYCODE_0_PAD, input_seq::or_code, JOYCODE_BUTTON1_INDEXED(3)) ) \ + INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, PEDAL, N_p("input-name", "P5 Pedal 1"), input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(4)), input_seq(), input_seq(JOYCODE_BUTTON1_INDEXED(4)) ) \ + INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, PEDAL, N_p("input-name", "P6 Pedal 1"), input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(5)), input_seq(), input_seq(JOYCODE_BUTTON1_INDEXED(5)) ) \ + INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, PEDAL, N_p("input-name", "P7 Pedal 1"), input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(6)), input_seq(), input_seq(JOYCODE_BUTTON1_INDEXED(6)) ) \ + INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, PEDAL, N_p("input-name", "P8 Pedal 1"), input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(7)), input_seq(), input_seq(JOYCODE_BUTTON1_INDEXED(7)) ) \ + INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, PEDAL, N_p("input-name", "P9 Pedal 1"), input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(8)), input_seq(), input_seq(JOYCODE_BUTTON1_INDEXED(8)) ) \ + INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, PEDAL, N_p("input-name", "P10 Pedal 1"), input_seq(JOYCODE_Z_NEG_ABSOLUTE_INDEXED(9)), input_seq(), input_seq(JOYCODE_BUTTON1_INDEXED(9)) ) \ CORE_INPUT_TYPES_END() #define CORE_INPUT_TYPES_PEDAL2 \ CORE_INPUT_TYPES_BEGIN(pedal2) \ - INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, PEDAL2, N_p("input-name", "%p Pedal 2"), input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(0)), input_seq(), input_seq(KEYCODE_LALT, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(0)) ) \ - INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, PEDAL2, N_p("input-name", "%p Pedal 2"), input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(1)), input_seq(), input_seq(KEYCODE_S, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(1)) ) \ - INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, PEDAL2, N_p("input-name", "%p Pedal 2"), input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(2)), input_seq(), input_seq(KEYCODE_RSHIFT, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(2)) ) \ - INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, PEDAL2, N_p("input-name", "%p Pedal 2"), input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(3)), input_seq(), input_seq(KEYCODE_DEL_PAD, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(3)) ) \ - INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, PEDAL2, N_p("input-name", "%p Pedal 2"), input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(4)), input_seq(), input_seq(JOYCODE_BUTTON2_INDEXED(4)) ) \ - INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, PEDAL2, N_p("input-name", "%p Pedal 2"), input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(5)), input_seq(), input_seq(JOYCODE_BUTTON2_INDEXED(5)) ) \ - INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, PEDAL2, N_p("input-name", "%p Pedal 2"), input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(6)), input_seq(), input_seq(JOYCODE_BUTTON2_INDEXED(6)) ) \ - INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, PEDAL2, N_p("input-name", "%p Pedal 2"), input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(7)), input_seq(), input_seq(JOYCODE_BUTTON2_INDEXED(7)) ) \ - INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, PEDAL2, N_p("input-name", "%p Pedal 2"), input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(8)), input_seq(), input_seq(JOYCODE_BUTTON2_INDEXED(8)) ) \ - INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, PEDAL2, N_p("input-name", "%p Pedal 2"), input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(9)), input_seq(), input_seq(JOYCODE_BUTTON2_INDEXED(9)) ) \ + INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, PEDAL2, N_p("input-name", "P1 Pedal 2"), input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(0)), input_seq(), input_seq(KEYCODE_LALT, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(0)) ) \ + INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, PEDAL2, N_p("input-name", "P2 Pedal 2"), input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(1)), input_seq(), input_seq(KEYCODE_S, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(1)) ) \ + INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, PEDAL2, N_p("input-name", "P3 Pedal 2"), input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(2)), input_seq(), input_seq(KEYCODE_RSHIFT, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(2)) ) \ + INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, PEDAL2, N_p("input-name", "P4 Pedal 2"), input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(3)), input_seq(), input_seq(KEYCODE_DEL_PAD, input_seq::or_code, JOYCODE_BUTTON2_INDEXED(3)) ) \ + INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, PEDAL2, N_p("input-name", "P5 Pedal 2"), input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(4)), input_seq(), input_seq(JOYCODE_BUTTON2_INDEXED(4)) ) \ + INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, PEDAL2, N_p("input-name", "P6 Pedal 2"), input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(5)), input_seq(), input_seq(JOYCODE_BUTTON2_INDEXED(5)) ) \ + INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, PEDAL2, N_p("input-name", "P7 Pedal 2"), input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(6)), input_seq(), input_seq(JOYCODE_BUTTON2_INDEXED(6)) ) \ + INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, PEDAL2, N_p("input-name", "P8 Pedal 2"), input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(7)), input_seq(), input_seq(JOYCODE_BUTTON2_INDEXED(7)) ) \ + INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, PEDAL2, N_p("input-name", "P9 Pedal 2"), input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(8)), input_seq(), input_seq(JOYCODE_BUTTON2_INDEXED(8)) ) \ + INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, PEDAL2, N_p("input-name", "P10 Pedal 2"), input_seq(JOYCODE_W_NEG_ABSOLUTE_INDEXED(9)), input_seq(), input_seq(JOYCODE_BUTTON2_INDEXED(9)) ) \ CORE_INPUT_TYPES_END() #define CORE_INPUT_TYPES_PEDAL3 \ CORE_INPUT_TYPES_BEGIN(pedal3) \ - INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, PEDAL3, N_p("input-name", "%p Pedal 3"), input_seq(), input_seq(), input_seq(KEYCODE_SPACE, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(0)) ) \ - INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, PEDAL3, N_p("input-name", "%p Pedal 3"), input_seq(), input_seq(), input_seq(KEYCODE_Q, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(1)) ) \ - INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, PEDAL3, N_p("input-name", "%p Pedal 3"), input_seq(), input_seq(), input_seq(KEYCODE_ENTER, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(2)) ) \ - INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, PEDAL3, N_p("input-name", "%p Pedal 3"), input_seq(), input_seq(), input_seq(KEYCODE_ENTER_PAD, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(3)) ) \ - INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, PEDAL3, N_p("input-name", "%p Pedal 3"), input_seq(), input_seq(), input_seq(JOYCODE_BUTTON3_INDEXED(4)) ) \ - INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, PEDAL3, N_p("input-name", "%p Pedal 3"), input_seq(), input_seq(), input_seq(JOYCODE_BUTTON3_INDEXED(5)) ) \ - INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, PEDAL3, N_p("input-name", "%p Pedal 3"), input_seq(), input_seq(), input_seq(JOYCODE_BUTTON3_INDEXED(6)) ) \ - INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, PEDAL3, N_p("input-name", "%p Pedal 3"), input_seq(), input_seq(), input_seq(JOYCODE_BUTTON3_INDEXED(7)) ) \ - INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, PEDAL3, N_p("input-name", "%p Pedal 3"), input_seq(), input_seq(), input_seq(JOYCODE_BUTTON3_INDEXED(8)) ) \ - INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, PEDAL3, N_p("input-name", "%p Pedal 3"), input_seq(), input_seq(), input_seq(JOYCODE_BUTTON3_INDEXED(9)) ) \ + INPUT_PORT_ANALOG_TYPE( 1, PLAYER1, PEDAL3, N_p("input-name", "P1 Pedal 3"), input_seq(), input_seq(), input_seq(KEYCODE_SPACE, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(0)) ) \ + INPUT_PORT_ANALOG_TYPE( 2, PLAYER2, PEDAL3, N_p("input-name", "P2 Pedal 3"), input_seq(), input_seq(), input_seq(KEYCODE_Q, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(1)) ) \ + INPUT_PORT_ANALOG_TYPE( 3, PLAYER3, PEDAL3, N_p("input-name", "P3 Pedal 3"), input_seq(), input_seq(), input_seq(KEYCODE_ENTER, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(2)) ) \ + INPUT_PORT_ANALOG_TYPE( 4, PLAYER4, PEDAL3, N_p("input-name", "P4 Pedal 3"), input_seq(), input_seq(), input_seq(KEYCODE_ENTER_PAD, input_seq::or_code, JOYCODE_BUTTON3_INDEXED(3)) ) \ + INPUT_PORT_ANALOG_TYPE( 5, PLAYER5, PEDAL3, N_p("input-name", "P5 Pedal 3"), input_seq(), input_seq(), input_seq(JOYCODE_BUTTON3_INDEXED(4)) ) \ + INPUT_PORT_ANALOG_TYPE( 6, PLAYER6, PEDAL3, N_p("input-name", "P6 Pedal 3"), input_seq(), input_seq(), input_seq(JOYCODE_BUTTON3_INDEXED(5)) ) \ + INPUT_PORT_ANALOG_TYPE( 7, PLAYER7, PEDAL3, N_p("input-name", "P7 Pedal 3"), input_seq(), input_seq(), input_seq(JOYCODE_BUTTON3_INDEXED(6)) ) \ + INPUT_PORT_ANALOG_TYPE( 8, PLAYER8, PEDAL3, N_p("input-name", "P8 Pedal 3"), input_seq(), input_seq(), input_seq(JOYCODE_BUTTON3_INDEXED(7)) ) \ + INPUT_PORT_ANALOG_TYPE( 9, PLAYER9, PEDAL3, N_p("input-name", "P9 Pedal 3"), input_seq(), input_seq(), input_seq(JOYCODE_BUTTON3_INDEXED(8)) ) \ + INPUT_PORT_ANALOG_TYPE( 10, PLAYER10, PEDAL3, N_p("input-name", "P10 Pedal 3"), input_seq(), input_seq(), input_seq(JOYCODE_BUTTON3_INDEXED(9)) ) \ CORE_INPUT_TYPES_END() #define CORE_INPUT_TYPES_PADDLE \ diff --git a/src/emu/ioport.cpp b/src/emu/ioport.cpp index a9b8677..0ea7974 100644 --- a/src/emu/ioport.cpp +++ b/src/emu/ioport.cpp @@ -101,7 +101,6 @@ #include "util/corestr.h" #include "util/ioprocsfilter.h" -#include "util/language.h" #include "util/unicode.h" #include "osdepend.h" @@ -308,37 +307,6 @@ inline bool input_seq_good(running_machine &machine, input_seq const &seq) return input_seq::end_code != machine.input().seq_clean(seq)[0]; } - -std::string substitute_player(std::string_view name, u8 player) -{ - using util::lang_translate; - - std::string result; - while (!name.empty()) - { - auto const found = name.find('%'); - if ((std::string_view::npos == found) || (name.length() == found + 1)) - { - result.append(name); - break; - } - switch (name[found + 1]) - { - case '%': - result.append(name.substr(0, found + 1)); - break; - case 'p': - result.append(name.substr(0, found)); - result.append(util::string_format(_("input-name", "P%1$u"), player + 1)); - break; - default: - result.append(name.substr(0, found + 2)); - } - name.remove_prefix(found + 2); - } - return result; -} - } // anonymous namespace @@ -422,24 +390,6 @@ input_type_entry::input_type_entry(ioport_type type, ioport_group group, int pla } -//------------------------------------------------- -// name - gets the display name for the input -// type -//------------------------------------------------- - -std::string input_type_entry::name() const -{ - using util::lang_translate; - - if (!m_name) - return std::string(); - else if ((group() < IPG_PLAYER1) || (group() > IPG_PLAYER10)) - return _("input-name", m_name); - else - return substitute_player(_("input-name", m_name), player()); -} - - //------------------------------------------------- // replace_code - replace all instances of // oldcodewith newcode in all sequences @@ -735,25 +685,16 @@ ioport_field::~ioport_field() // field (this must never return nullptr) //------------------------------------------------- -std::string ioport_field::name() const +const char *ioport_field::name() const { - using util::lang_translate; - - // if we have an overridden name, use that - if (m_live && !m_live->name.empty()) - return m_live->name; - - // if no specific name, use the generic name for the type - if (!m_name) - return manager().type_name(m_type, m_player); - - // return name for non-controller fields as-is - ioport_group const group = manager().type_group(m_type, m_player); - if ((group < IPG_PLAYER1) || (group > IPG_PLAYER10)) + // if we have a non-default name, use that + if (m_live != nullptr && !m_live->name.empty()) + return m_live->name.c_str(); + if (m_name != nullptr) return m_name; - // substitute the player number in if necessary - return substitute_player(m_name, m_player); + // otherwise, return the name associated with the type + return manager().type_name(m_type, m_player); } @@ -809,8 +750,8 @@ void ioport_field::set_defseq(input_seq_type seqtype, const input_seq &newseq) ioport_type_class ioport_field::type_class() const noexcept { // inputs associated with specific players - ioport_group const group = manager().type_group(m_type, m_player); - if ((group >= IPG_PLAYER1) && (group <= IPG_PLAYER10)) + ioport_group group = manager().type_group(m_type, m_player); + if (group >= IPG_PLAYER1 && group <= IPG_PLAYER10) return INPUT_CLASS_CONTROLLER; // keys (names derived from character codes) @@ -1761,14 +1702,12 @@ time_t ioport_manager::initialize() if (&port.second->device() == &device) { for (ioport_field &field : port.second->fields()) - { if (field.type_class() == INPUT_CLASS_CONTROLLER) { if (players < field.player() + 1) players = field.player() + 1; field.set_player(field.player() + player_offset); } - } } } player_offset += players; @@ -1907,21 +1846,15 @@ ioport_manager::~ioport_manager() // type/player //------------------------------------------------- -std::string ioport_manager::type_name(ioport_type type, u8 player) const +const char *ioport_manager::type_name(ioport_type type, u8 player) const noexcept { - using util::lang_translate; - // if we have a machine, use the live state and quick lookup - input_type_entry const *const entry = m_type_to_entry[type][player]; - if (entry) - { - std::string name = entry->name(); - if (!name.empty()) - return name; - } + input_type_entry *entry = m_type_to_entry[type][player]; + if (entry != nullptr && entry->name() != nullptr) + return entry->name(); // if we find nothing, return a default string (not a null pointer) - return _("input-name", "???"); + return "???"; } @@ -2380,10 +2313,7 @@ bool ioport_manager::load_controller_config( for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) { if (input_seq_good(machine(), newseq[seqtype].first)) - { - field.live().seq[seqtype] = newseq[seqtype].first; field.set_defseq(seqtype, newseq[seqtype].first); - } } // fetch configurable attributes diff --git a/src/emu/ioport.h b/src/emu/ioport.h index e81cce1..59f14b0 100644 --- a/src/emu/ioport.h +++ b/src/emu/ioport.h @@ -798,7 +798,7 @@ public: ioport_group group() const noexcept { return m_group; } u8 player() const noexcept { return m_player; } const char *token() const noexcept { return m_token; } - std::string name() const; + const char *name() const noexcept { return m_name; } input_seq &defseq(input_seq_type seqtype = SEQ_TYPE_STANDARD) noexcept { return m_defseq[seqtype]; } const input_seq &defseq(input_seq_type seqtype = SEQ_TYPE_STANDARD) const noexcept { return m_defseq[seqtype]; } const input_seq &seq(input_seq_type seqtype = SEQ_TYPE_STANDARD) const noexcept { return m_seq[seqtype]; } @@ -914,7 +914,6 @@ public: { m_condition = condition; m_tag = tag; - m_port = nullptr; m_mask = mask; m_value = value; } @@ -1037,7 +1036,7 @@ public: bool analog_invert() const { return ((m_flags & ANALOG_FLAG_INVERT) != 0); } u8 impulse() const noexcept { return m_impulse; } - std::string name() const; + const char *name() const; const char *specific_name() const noexcept { return m_name; } const input_seq &seq(input_seq_type seqtype = SEQ_TYPE_STANDARD) const noexcept; const input_seq &defseq(input_seq_type seqtype = SEQ_TYPE_STANDARD) const noexcept; @@ -1382,7 +1381,7 @@ public: // type helpers const std::vector &types() const noexcept { return m_typelist; } bool type_pressed(ioport_type type, int player = 0); - std::string type_name(ioport_type type, u8 player) const; + const char *type_name(ioport_type type, u8 player) const noexcept; ioport_group type_group(ioport_type type, int player) const noexcept; const input_seq &type_seq(ioport_type type, int player = 0, input_seq_type seqtype = SEQ_TYPE_STANDARD) const noexcept; void set_type_seq(ioport_type type, int player, input_seq_type seqtype, const input_seq &newseq) noexcept; @@ -1489,7 +1488,6 @@ public: ioport_configurer& field_set_toggle() { m_curfield->m_flags |= ioport_field::FIELD_FLAG_TOGGLE; return *this; } ioport_configurer& field_set_impulse(u8 impulse) { m_curfield->m_impulse = impulse; return *this; } ioport_configurer& field_set_analog_reverse() { m_curfield->m_flags |= ioport_field::ANALOG_FLAG_REVERSE; return *this; } - [[deprecated("PORT_RESET is deprecated; manage counter state explicitly")]] ioport_configurer& field_set_analog_reset() { m_curfield->m_flags |= ioport_field::ANALOG_FLAG_RESET; return *this; } ioport_configurer& field_set_optional() { m_curfield->m_flags |= ioport_field::FIELD_FLAG_OPTIONAL; return *this; } ioport_configurer& field_set_min_max(ioport_value minval, ioport_value maxval) { m_curfield->m_min = minval; m_curfield->m_max = maxval; return *this; } diff --git a/src/emu/machine.cpp b/src/emu/machine.cpp index b70ffb6..7e9d809 100644 --- a/src/emu/machine.cpp +++ b/src/emu/machine.cpp @@ -6,10 +6,69 @@ Controls execution of the core MAME system. +**************************************************************************** + + Since there has been confusion in the past over the order of + initialization and other such things, here it is, all spelled out + as of January, 2008: + + main() + - does platform-specific init + - calls mame_execute() [mame.c] + + mame_execute() [mame.c] + - calls mame_validitychecks() [validity.c] to perform validity checks on all compiled drivers + - begins resource tracking (level 1) + - calls create_machine [mame.c] to initialize the running_machine structure + - calls init_machine() [mame.c] + + init_machine() [mame.c] + - calls fileio_init() [fileio.c] to initialize file I/O info + - calls config_init() [config.c] to initialize configuration system + - calls input_init() [input.c] to initialize the input system + - calls output_init() [output.c] to initialize the output system + - calls state_init() [state.c] to initialize save state system + - calls state_save_allow_registration() [state.c] to allow registrations + - calls palette_init() [palette.c] to initialize palette system + - calls render_init() [render.c] to initialize the rendering system + - calls ui_init() [ui.c] to initialize the user interface + - calls generic_machine_init() [machine/generic.c] to initialize generic machine structures + - calls timer_init() [timer.c] to reset the timer system + - calls osd_init() [osdepend.h] to do platform-specific initialization + - calls input_port_init() [inptport.c] to set up the input ports + - calls rom_init() [romload.c] to load the game's ROMs + - calls memory_init() [memory.c] to process the game's memory maps + - calls the driver's DRIVER_INIT callback + - calls device_list_start() [devintrf.c] to start any devices + - calls video_init() [video.c] to start the video system + - calls tilemap_init() [tilemap.c] to start the tilemap system + - calls crosshair_init() [crsshair.c] to configure the crosshairs + - calls sound_init() [sound.c] to start the audio system + - calls debugger_init() [debugger.c] to set up the debugger + - calls the driver's MACHINE_START, SOUND_START, and VIDEO_START callbacks + - calls cheat_init() [cheat.c] to initialize the cheat system + - calls image_init() [image.c] to initialize the image system + + - calls config_load_settings() [config.c] to load the configuration file + - calls nvram_load [machine/generic.c] to load NVRAM + - calls ui_display_startup_screens() [ui.c] to display the startup screens + - begins resource tracking (level 2) + - calls soft_reset() [mame.c] to reset all systems + + -------------------( at this point, we're up and running )---------------------- + + - calls scheduler->timeslice() [schedule.c] over and over until we exit + - ends resource tracking (level 2), freeing all auto_mallocs and timers + - calls the nvram_save() [machine/generic.c] to save NVRAM + - calls config_save_settings() [config.c] to save the game's configuration + - calls all registered exit routines [mame.c] + - ends resource tracking (level 1), freeing all auto_mallocs and timers + + - exits the program + ***************************************************************************/ #include "emu.h" - #include "emuopts.h" #include "osdepend.h" #include "config.h" @@ -17,6 +76,7 @@ #include "render.h" #include "uiinput.h" #include "crsshair.h" +#include "unzip.h" #include "debug/debugvw.h" #include "debug/debugcpu.h" #include "dirtc.h" @@ -26,15 +86,11 @@ #include "tilemap.h" #include "natkeyboard.h" #include "ui/uimain.h" - #include "corestr.h" -#include "unzip.h" - +#include #include #include -#include - #if defined(__EMSCRIPTEN__) #include #endif @@ -441,7 +497,7 @@ void running_machine::schedule_soft_reset() //------------------------------------------------- // get_statename - allow to specify a subfolder of // the state directory for state loading/saving, -// very useful for consoles or computers +// very useful for MESS and consoles or computers // where you can have separate folders for diff // software //------------------------------------------------- diff --git a/src/emu/mconfig.cpp b/src/emu/mconfig.cpp index 2c8c1aa..fb97c31 100644 --- a/src/emu/mconfig.cpp +++ b/src/emu/mconfig.cpp @@ -257,23 +257,24 @@ std::pair machine_config::resolve_owner(const char *ta device_t *owner(m_current_device); // if the device path is absolute, start from the root - if (!*tag || (':' == *tag) || ('^' == *tag)) - throw emu_fatalerror("Attempting to add device with tag containing parent references '%s'\n", orig_tag); + if (tag[0] == ':') + { + tag++; + owner = m_root_device.get(); + } // go down the path until we're done with it - char const *next; - while ((next = strchr(tag, ':')) != nullptr) + while (strchr(tag, ':')) { + const char *next = strchr(tag, ':'); assert(next != tag); std::string_view part(tag, next - tag); owner = owner->subdevices().find(part); if (!owner) - throw emu_fatalerror("Could not find '%s' when looking up path for device '%s'\n", part, orig_tag); - tag = next + 1; - if ('^' == *tag) - throw emu_fatalerror("Attempting to add device with tag containing parent references '%s'\n", orig_tag); + throw emu_fatalerror("Could not find %s when looking up path for device %s\n", part, orig_tag); + tag = next+1; } - assert(*tag != '\0'); + assert(tag[0] != '\0'); return std::make_pair(tag, owner); } @@ -295,7 +296,7 @@ std::tuple machine_config::prepare_replace if (old_device) remove_references(*old_device); else - throw emu_fatalerror("Attempting to replace non-existent device '%s'\n", tag); + osd_printf_warning("Warning: attempting to replace non-existent device '%s'\n", tag); return std::make_tuple(owner.first, owner.second, old_device); } diff --git a/src/emu/render.cpp b/src/emu/render.cpp index 1f99185..2790c21 100644 --- a/src/emu/render.cpp +++ b/src/emu/render.cpp @@ -536,7 +536,8 @@ const rgb_t *render_texture::get_adjusted_palette(render_container &container, u //------------------------------------------------- render_container::render_container(render_manager &manager, screen_device *screen) - : m_manager(manager) + : m_next(nullptr) + , m_manager(manager) , m_screen(screen) , m_overlaybitmap(nullptr) , m_overlaytexture(nullptr) @@ -893,6 +894,7 @@ render_target::render_target(render_manager &manager, util::xml::data_node const template render_target::render_target(render_manager &manager, T &&layout, u32 flags, constructor_impl_t) : m_next(nullptr) , m_manager(manager) + , m_filelist(std::make_unique>()) , m_curview(0U) , m_flags(flags) , m_listindex(0) @@ -958,7 +960,7 @@ template render_target::render_target(render_manager &manager, T && // load the layout files load_layout_files(std::forward(layout), flags & RENDER_CREATE_SINGLE_FILE); - for (layout_file &file : m_filelist) + for (layout_file &file : *m_filelist) for (layout_view &view : file.views()) if (!(m_flags & RENDER_CREATE_NO_ART) || !view.has_art()) m_views.emplace_back(view, view.default_visibility_mask()); @@ -1415,6 +1417,23 @@ render_primitive_list &render_target::get_primitives() } } + // process the debug containers + for (render_container &debug : m_debug_containers) + { + object_transform ui_xform; + ui_xform.xoffs = 0; + ui_xform.yoffs = 0; + ui_xform.xscale = (float)m_width; + ui_xform.yscale = (float)m_height; + ui_xform.color.r = ui_xform.color.g = ui_xform.color.b = 1.0f; + ui_xform.color.a = 0.9f; + ui_xform.orientation = m_orientation; + ui_xform.no_center = true; + + // add UI elements + add_container_primitives(list, root_xform, ui_xform, debug, BLENDMODE_ALPHA); + } + // process the UI if we are the UI target if (is_ui_target()) { @@ -1584,13 +1603,45 @@ void render_target::invalidate_all(void *refptr) } +//------------------------------------------------- +// debug_alloc - allocate a container for a debug +// view +//------------------------------------------------- + +render_container *render_target::debug_alloc() +{ + return &m_debug_containers.append(*m_manager.container_alloc()); +} + + +//------------------------------------------------- +// debug_free - free a container for a debug view +//------------------------------------------------- + +void render_target::debug_free(render_container &container) +{ + m_debug_containers.remove(container); +} + + +//------------------------------------------------- +// debug_append - move a debug view container to +// the end of the list +//------------------------------------------------- + +void render_target::debug_append(render_container &container) +{ + m_debug_containers.append(m_debug_containers.detach(container)); +} + + //------------------------------------------------- // resolve_tags - resolve tag lookups //------------------------------------------------- void render_target::resolve_tags() { - for (layout_file &file : m_filelist) + for (layout_file &file : *m_filelist) file.resolve_tags(); current_view().recompute(visibility_mask(), m_layerconfig.zoom_to_screen()); @@ -1757,7 +1808,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have auto const nth_view = [this] (unsigned n) -> layout_view * { - for (layout_file &file : m_filelist) + for (layout_file &file : *m_filelist) for (layout_view &view : file.views()) if (!(m_flags & RENDER_CREATE_NO_ART) || !view.has_art()) if (n-- == 0) @@ -1770,7 +1821,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have if (!nth_view(0)) { load_layout_file(nullptr, layout_noscreens); - if (m_filelist.empty()) + if (m_filelist->empty()) throw emu_fatalerror("Couldn't parse default layout??"); } } @@ -2175,7 +2226,7 @@ bool render_target::load_layout_file(device_t &device, util::xml::data_node cons // parse and catch any errors try { - m_filelist.emplace_back(device, rootnode, searchpath, dirname); + m_filelist->emplace_back(device, rootnode, searchpath, dirname); } catch (emu_fatalerror &err) { @@ -3056,7 +3107,7 @@ render_manager::render_manager(running_machine &machine) , m_ui_target(nullptr) , m_live_textures(0) , m_texture_id(0) - , m_ui_container(std::make_unique(*this)) + , m_ui_container(new render_container(*this)) { // register callbacks machine.configuration().config_register( @@ -3066,7 +3117,7 @@ render_manager::render_manager(running_machine &machine) // create one container per screen for (screen_device &screen : screen_device_enumerator(machine.root_device())) - screen.set_container(m_screen_container_list.emplace_back(*this, &screen)); + screen.set_container(*container_alloc(&screen)); } @@ -3077,8 +3128,8 @@ render_manager::render_manager(running_machine &machine) render_manager::~render_manager() { // free all the containers since they may own textures - m_ui_container.reset(); - m_screen_container_list.clear(); + container_free(m_ui_container); + m_screen_container_list.reset(); // better not be any outstanding textures when we die assert(m_live_textures == 0); @@ -3178,7 +3229,7 @@ float render_manager::ui_aspect(render_container *rc) int orient; float aspect; - if (rc == m_ui_container.get() || rc == nullptr) { + if (rc == m_ui_container || rc == nullptr) { // ui container, aggregated multi-screen target orient = orientation_add(m_ui_target->orientation(), m_ui_container->orientation()); @@ -3289,6 +3340,29 @@ void render_manager::resolve_tags() } +//------------------------------------------------- +// container_alloc - allocate a new container +//------------------------------------------------- + +render_container *render_manager::container_alloc(screen_device *screen) +{ + auto container = new render_container(*this, screen); + if (screen != nullptr) + m_screen_container_list.append(*container); + return container; +} + + +//------------------------------------------------- +// container_free - release a container +//------------------------------------------------- + +void render_manager::container_free(render_container *container) +{ + m_screen_container_list.remove(*container); +} + + //------------------------------------------------- // config_load - read and apply data from the // configuration file @@ -3321,29 +3395,24 @@ void render_manager::config_load(config_type cfg_type, config_level cfg_level, u for (util::xml::data_node const *screennode = parentnode->get_child("screen"); screennode; screennode = screennode->get_next_sibling("screen")) { int const index = screennode->get_attribute_int("index", -1); - render_container *container = nullptr; - if (index >= 0 && index < m_screen_container_list.size()) - container = &*std::next(m_screen_container_list.begin(), index); + render_container *container = m_screen_container_list.find(index); - if (container != nullptr) - { - // fetch current settings - render_container::user_settings settings = container->get_user_settings(); + // fetch current settings + render_container::user_settings settings = container->get_user_settings(); - // fetch color controls - settings.m_brightness = screennode->get_attribute_float("brightness", settings.m_brightness); - settings.m_contrast = screennode->get_attribute_float("contrast", settings.m_contrast); - settings.m_gamma = screennode->get_attribute_float("gamma", settings.m_gamma); + // fetch color controls + settings.m_brightness = screennode->get_attribute_float("brightness", settings.m_brightness); + settings.m_contrast = screennode->get_attribute_float("contrast", settings.m_contrast); + settings.m_gamma = screennode->get_attribute_float("gamma", settings.m_gamma); - // fetch positioning controls - settings.m_xoffset = screennode->get_attribute_float("hoffset", settings.m_xoffset); - settings.m_xscale = screennode->get_attribute_float("hstretch", settings.m_xscale); - settings.m_yoffset = screennode->get_attribute_float("voffset", settings.m_yoffset); - settings.m_yscale = screennode->get_attribute_float("vstretch", settings.m_yscale); + // fetch positioning controls + settings.m_xoffset = screennode->get_attribute_float("hoffset", settings.m_xoffset); + settings.m_xscale = screennode->get_attribute_float("hstretch", settings.m_xscale); + settings.m_yoffset = screennode->get_attribute_float("voffset", settings.m_yoffset); + settings.m_yscale = screennode->get_attribute_float("vstretch", settings.m_yscale); - // set the new values - container->set_user_settings(settings); - } + // set the new values + container->set_user_settings(settings); } } @@ -3388,7 +3457,7 @@ void render_manager::config_save(config_type cfg_type, util::xml::data_node *par // iterate over screen containers int scrnum = 0; - for (render_container &container : m_screen_container_list) + for (render_container *container = m_screen_container_list.first(); container != nullptr; container = container->next(), scrnum++) { // create a node util::xml::data_node *const screennode = parentnode->add_child("screen", nullptr); @@ -3399,7 +3468,7 @@ void render_manager::config_save(config_type cfg_type, util::xml::data_node *par // output the basics screennode->set_attribute_int("index", scrnum); - render_container::user_settings const settings = container.get_user_settings(); + render_container::user_settings settings = container->get_user_settings(); // output the color controls if (settings.m_brightness != machine().options().brightness()) @@ -3449,7 +3518,5 @@ void render_manager::config_save(config_type cfg_type, util::xml::data_node *par if (!changed) screennode->delete_node(); } - - scrnum++; } } diff --git a/src/emu/render.h b/src/emu/render.h index d4f9937..d1d8987 100644 --- a/src/emu/render.h +++ b/src/emu/render.h @@ -377,14 +377,15 @@ private: // a render_container holds a list of items and an orientation for the entire collection class render_container { + friend class simple_list; friend class render_manager; friend class render_target; -public: // construction/destruction render_container(render_manager &manager, screen_device *screen = nullptr); ~render_container(); +public: // user settings describes the collected user-controllable settings struct user_settings { @@ -403,6 +404,7 @@ public: }; // getters + render_container *next() const { return m_next; } screen_device *screen() const { return m_screen; } render_manager &manager() const { return m_manager; } render_texture *overlay() const { return m_overlaytexture; } @@ -476,6 +478,7 @@ private: void update_palette(); // internal state + render_container * m_next; // the next container in the list render_manager & m_manager; // reference back to the owning manager simple_list m_itemlist; // head of the item list fixed_allocator m_item_allocator; // free container items @@ -561,6 +564,11 @@ public: // reference tracking void invalidate_all(void *refptr); + // debug containers + render_container *debug_alloc(); + void debug_free(render_container &container); + void debug_append(render_container &container); + // resolve tag lookups void resolve_tags(); @@ -606,7 +614,7 @@ private: // internal state render_target * m_next; // link to next target render_manager & m_manager; // reference to our owning manager - std::list m_filelist; // list of layout files + std::unique_ptr> m_filelist; // list of layout files view_mask_vector m_views; // views we consider unsigned m_curview; // current view index u32 m_flags; // creation flags @@ -630,6 +638,7 @@ private: render_layer_config m_base_layerconfig; // the layer configuration at the time of first frame int m_maxtexwidth; // maximum width of a texture int m_maxtexheight; // maximum height of a texture + simple_list m_debug_containers; // list of debug containers s32 m_clear_extent_count; // number of clear extents s32 m_clear_extents[MAX_CLEAR_EXTENTS]; // array of clear extents bool m_transform_container; // determines whether the screen container is transformed by the core renderer, @@ -662,7 +671,7 @@ public: render_target *target_alloc(util::xml::data_node const &layout, u32 flags = 0); void target_free(render_target *target); const simple_list &targets() const { return m_targetlist; } - render_target *first_target() { return m_targetlist.first(); } + render_target *first_target() const { return m_targetlist.first(); } render_target *target_by_index(int index) const; // UI targets @@ -687,6 +696,10 @@ public: void resolve_tags(); private: + // containers + render_container *container_alloc(screen_device *screen = nullptr); + void container_free(render_container *container); + // config callbacks void config_load(config_type cfg_type, config_level cfg_lvl, util::xml::data_node const *parentnode); void config_save(config_type cfg_type, util::xml::data_node *parentnode); @@ -704,8 +717,8 @@ private: fixed_allocator m_texture_allocator;// texture allocator // containers for the UI and for screens - std::unique_ptr m_ui_container; // UI container - std::list m_screen_container_list; // list of containers for the screen + render_container * m_ui_container; // UI container + simple_list m_screen_container_list; // list of containers for the screen }; #endif // MAME_EMU_RENDER_H diff --git a/src/emu/rendersw.hxx b/src/emu/rendersw.hxx index aa7b225..840f7fa 100644 --- a/src/emu/rendersw.hxx +++ b/src/emu/rendersw.hxx @@ -130,7 +130,7 @@ private: static inline u32 get_texel_palette16(const render_texinfo &texture, s32 curu, s32 curv) { - rgb_t const *const palbase = texture.palette; + const rgb_t *palbase = texture.palette; if constexpr (BilinearFilter) { s32 u0 = curu >> 16; @@ -142,7 +142,7 @@ private: if (v0 < 0) v0 = v1 = 0; else if (v0 + 1 >= texture.height) v0 = texture.height - 1, v1 = 0; - u16 const *texbase = reinterpret_cast(texture.base); + const u16 *texbase = reinterpret_cast(texture.base); texbase += v0 * texture.rowpixels + u0; u32 pix00 = palbase[texbase[0]]; @@ -153,7 +153,7 @@ private: } else { - u16 const *const texbase = reinterpret_cast(texture.base) + (curv >> 16) * texture.rowpixels + (curu >> 16); + const u16 *texbase = reinterpret_cast(texture.base) + (curv >> 16) * texture.rowpixels + (curu >> 16); return palbase[texbase[0]]; } } @@ -166,7 +166,7 @@ private: static inline u32 get_texel_palette16a(const render_texinfo &texture, s32 curu, s32 curv) { - rgb_t const *const palbase = texture.palette; + const rgb_t *palbase = texture.palette; if constexpr (BilinearFilter) { s32 u0 = curu >> 16; @@ -178,14 +178,14 @@ private: if (v0 < 0) v0 = v1 = 0; else if (v0 + 1 >= texture.height) v0 = texture.height - 1, v1 = 0; - u16 const *texbase = reinterpret_cast(texture.base); + const u16 *texbase = reinterpret_cast(texture.base); texbase += v0 * texture.rowpixels + u0; return rgbaint_t::bilinear_filter(palbase[texbase[0]], palbase[texbase[u1]], palbase[texbase[v1]], palbase[texbase[u1 + v1]], curu >> 8, curv >> 8); } else { - u16 const *const texbase = reinterpret_cast(texture.base) + (curv >> 16) * texture.rowpixels + (curu >> 16); + const u16 *texbase = reinterpret_cast(texture.base) + (curv >> 16) * texture.rowpixels + (curu >> 16); return palbase[texbase[0]]; } } @@ -415,7 +415,7 @@ private: // draw_line - draw a line or point //------------------------------------------------- - static void draw_line(render_primitive const &prim, PixelType *dstdata, s32 width, s32 height, u32 pitch) + static void draw_line(const render_primitive &prim, PixelType *dstdata, s32 width, s32 height, u32 pitch) { // internal tables static u32 s_cosine_table[2049]; @@ -576,35 +576,49 @@ private: // draw_rect - draw a solid rectangle //------------------------------------------------- - static void draw_rect(render_primitive const &prim, PixelType *dstdata, s32 width, s32 height, u32 pitch) + static void draw_rect(const render_primitive &prim, PixelType *dstdata, s32 width, s32 height, u32 pitch) { - render_bounds const fpos = prim.bounds; + render_bounds fpos = prim.bounds; assert(fpos.x0 <= fpos.x1); assert(fpos.y0 <= fpos.y1); - // clamp to integers and ensure we fit - s32 const startx = std::clamp(round_nearest(fpos.x0), 0, width); - s32 const starty = std::clamp(round_nearest(fpos.y0), 0, height); - s32 const endx = std::clamp(round_nearest(fpos.x1), 0, width); - s32 const endy = std::clamp(round_nearest(fpos.y1), 0, height); + // clamp to integers + s32 startx = round_nearest(fpos.x0); + s32 starty = round_nearest(fpos.y0); + s32 endx = round_nearest(fpos.x1); + s32 endy = round_nearest(fpos.y1); + + // ensure we fit + if (startx < 0) startx = 0; + if (startx >= width) startx = width; + if (endx < 0) endx = 0; + if (endx >= width) endx = width; + if (starty < 0) starty = 0; + if (starty >= height) starty = height; + if (endy < 0) endy = 0; + if (endy >= height) endy = height; // bail if nothing left - if ((startx > endx) || (starty > endy)) + if (fpos.x0 > fpos.x1 || fpos.y0 > fpos.y1) return; // only support alpha and "none" blendmodes assert(PRIMFLAG_GET_BLENDMODE(prim.flags) == BLENDMODE_NONE || PRIMFLAG_GET_BLENDMODE(prim.flags) == BLENDMODE_ALPHA); - if ((PRIMFLAG_GET_BLENDMODE(prim.flags) == BLENDMODE_NONE) || is_opaque(prim.color.a)) + // fast case: no alpha + if (PRIMFLAG_GET_BLENDMODE(prim.flags) == BLENDMODE_NONE || is_opaque(prim.color.a)) { - // fast case: no alpha + u32 r = u32(256.0f * prim.color.r); + u32 g = u32(256.0f * prim.color.g); + u32 b = u32(256.0f * prim.color.b); + u32 pix; // clamp R,G,B to 0-256 range - u32 const r = u32(std::clamp(256.0f * prim.color.r, 0.0f, 255.0f)); - u32 const g = u32(std::clamp(256.0f * prim.color.g, 0.0f, 255.0f)); - u32 const b = u32(std::clamp(256.0f * prim.color.b, 0.0f, 255.0f)); - u32 const pix = dest_rgb_to_pixel(r, g, b); + if (r > 0xff) { if (s32(r) < 0) r = 0; else r = 0xff; } + if (g > 0xff) { if (s32(g) < 0) g = 0; else g = 0xff; } + if (b > 0xff) { if (s32(b) < 0) b = 0; else b = 0xff; } + pix = dest_rgb_to_pixel(r, g, b); // loop over rows for (s32 y = starty; y < endy; y++) @@ -616,18 +630,23 @@ private: *dest++ = pix; } } + + // alpha and/or coloring case else if (!is_transparent(prim.color.a)) { - // alpha and/or coloring case - u32 const rmask = dest_rgb_to_pixel(0xff,0x00,0x00); - u32 const gmask = dest_rgb_to_pixel(0x00,0xff,0x00); - u32 const bmask = dest_rgb_to_pixel(0x00,0x00,0xff); + u32 rmask = dest_rgb_to_pixel(0xff,0x00,0x00); + u32 gmask = dest_rgb_to_pixel(0x00,0xff,0x00); + u32 bmask = dest_rgb_to_pixel(0x00,0x00,0xff); + u32 r = u32(256.0f * prim.color.r * prim.color.a); + u32 g = u32(256.0f * prim.color.g * prim.color.a); + u32 b = u32(256.0f * prim.color.b * prim.color.a); + u32 inva = u32(256.0f * (1.0f - prim.color.a)); // clamp R,G,B and inverse A to 0-256 range - u32 r = u32(std::clamp(256.0f * prim.color.r * prim.color.a, 0.0f, 255.0f)); - u32 g = u32(std::clamp(256.0f * prim.color.g * prim.color.a, 0.0f, 255.0f)); - u32 b = u32(std::clamp(256.0f * prim.color.b * prim.color.a, 0.0f, 255.0f)); - u32 const inva = u32(std::clamp(256.0f * (1.0f - prim.color.a), 0.0f, 256.0f)); + if (r > 0xff) { if (s32(r) < 0) r = 0; else r = 0xff; } + if (g > 0xff) { if (s32(g) < 0) g = 0; else g = 0xff; } + if (b > 0xff) { if (s32(b) < 0) b = 0; else b = 0xff; } + if (inva > 0x100) { if (s32(inva) < 0) inva = 0; else inva = 0x100; } // pre-shift the RGBA pieces r = dest_rgb_to_pixel(r, 0, 0) << 8; @@ -662,15 +681,14 @@ private: // rasterization of a 16bpp palettized texture //------------------------------------------------- - static void draw_quad_palette16_none(render_primitive const &prim, PixelType *dstdata, u32 pitch, quad_setup_data const &setup) + static void draw_quad_palette16_none(const render_primitive &prim, PixelType *dstdata, u32 pitch, const quad_setup_data &setup) { // ensure all parameters are valid assert(prim.texture.palette != nullptr); + // fast case: no coloring, no alpha if (prim.color.r >= 1.0f && prim.color.g >= 1.0f && prim.color.b >= 1.0f && is_opaque(prim.color.a)) { - // fast case: no coloring, no alpha - // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) { @@ -681,21 +699,25 @@ private: // loop over cols for (s32 x = setup.startx; x < setup.endx; x++) { - u32 const pix = get_texel_palette16(prim.texture, curu, curv); + const u32 pix = get_texel_palette16(prim.texture, curu, curv); *dest++ = source32_to_dest(pix); curu += setup.dudx; curv += setup.dvdx; } } } + + // coloring-only case else if (is_opaque(prim.color.a)) { - // coloring-only case + u32 sr = u32(256.0f * prim.color.r); + u32 sg = u32(256.0f * prim.color.g); + u32 sb = u32(256.0f * prim.color.b); // clamp R,G,B to 0-256 range - u32 const sr = u32(std::clamp(256.0f * prim.color.r, 0.0f, 256.0f)); - u32 const sg = u32(std::clamp(256.0f * prim.color.g, 0.0f, 256.0f)); - u32 const sb = u32(std::clamp(256.0f * prim.color.b, 0.0f, 256.0f)); + if (sr > 0x100) { if (s32(sr) < 0) sr = 0; else sr = 0x100; } + if (sg > 0x100) { if (s32(sg) < 0) sg = 0; else sg = 0x100; } + if (sb > 0x100) { if (s32(sb) < 0) sb = 0; else sb = 0x100; } // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) @@ -707,10 +729,10 @@ private: // loop over cols for (s32 x = setup.startx; x < setup.endx; x++) { - u32 const pix = get_texel_palette16(prim.texture, curu, curv); - u32 const r = (source32_r(pix) * sr) >> 8; - u32 const g = (source32_g(pix) * sg) >> 8; - u32 const b = (source32_b(pix) * sb) >> 8; + const u32 pix = get_texel_palette16(prim.texture, curu, curv); + const u32 r = (source32_r(pix) * sr) >> 8; + const u32 g = (source32_g(pix) * sg) >> 8; + const u32 b = (source32_b(pix) * sb) >> 8; *dest++ = dest_assemble_rgb(r, g, b); curu += setup.dudx; @@ -718,15 +740,20 @@ private: } } } + + // alpha and/or coloring case else if (!is_transparent(prim.color.a)) { - // alpha and/or coloring case + u32 sr = u32(256.0f * prim.color.r * prim.color.a); + u32 sg = u32(256.0f * prim.color.g * prim.color.a); + u32 sb = u32(256.0f * prim.color.b * prim.color.a); + u32 invsa = u32(256.0f * (1.0f - prim.color.a)); // clamp R,G,B and inverse A to 0-256 range - u32 const sr = u32(std::clamp(256.0f * prim.color.r * prim.color.a, 0.0f, 256.0f)); - u32 const sg = u32(std::clamp(256.0f * prim.color.g * prim.color.a, 0.0f, 256.0f)); - u32 const sb = u32(std::clamp(256.0f * prim.color.b * prim.color.a, 0.0f, 256.0f)); - u32 const invsa = u32(std::clamp(256.0f * (1.0f - prim.color.a), 0.0f, 256.0f)); + if (sr > 0x100) { if (s32(sr) < 0) sr = 0; else sr = 0x100; } + if (sg > 0x100) { if (s32(sg) < 0) sg = 0; else sg = 0x100; } + if (sb > 0x100) { if (s32(sb) < 0) sb = 0; else sb = 0x100; } + if (invsa > 0x100) { if (s32(invsa) < 0) invsa = 0; else invsa = 0x100; } // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) @@ -738,11 +765,11 @@ private: // loop over cols for (s32 x = setup.startx; x < setup.endx; x++) { - u32 const pix = get_texel_palette16(prim.texture, curu, curv); - u32 const dpix = NoDestRead ? 0 : *dest; - u32 const r = (source32_r(pix) * sr + dest_r(dpix) * invsa) >> 8; - u32 const g = (source32_g(pix) * sg + dest_g(dpix) * invsa) >> 8; - u32 const b = (source32_b(pix) * sb + dest_b(dpix) * invsa) >> 8; + const u32 pix = get_texel_palette16(prim.texture, curu, curv); + const u32 dpix = NoDestRead ? 0 : *dest; + const u32 r = (source32_r(pix) * sr + dest_r(dpix) * invsa) >> 8; + const u32 g = (source32_g(pix) * sg + dest_g(dpix) * invsa) >> 8; + const u32 b = (source32_b(pix) * sb + dest_b(dpix) * invsa) >> 8; *dest++ = dest_assemble_rgb(r, g, b); curu += setup.dudx; @@ -758,15 +785,14 @@ private: // rasterization of a 16bpp palettized texture //------------------------------------------------- - static void draw_quad_palette16_add(render_primitive const &prim, PixelType *dstdata, u32 pitch, quad_setup_data const &setup) + static void draw_quad_palette16_add(const render_primitive &prim, PixelType *dstdata, u32 pitch, const quad_setup_data&setup) { // ensure all parameters are valid assert(prim.texture.palette != nullptr); + // fast case: no coloring, no alpha if (prim.color.r >= 1.0f && prim.color.g >= 1.0f && prim.color.b >= 1.0f && is_opaque(prim.color.a)) { - // fast case: no coloring, no alpha - // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) { @@ -780,7 +806,7 @@ private: const u32 pix = get_texel_palette16(prim.texture, curu, curv); if ((pix & 0xffffff) != 0) { - u32 const dpix = NoDestRead ? 0 : *dest; + const u32 dpix = NoDestRead ? 0 : *dest; u32 r = source32_r(pix) + dest_r(dpix); u32 g = source32_g(pix) + dest_g(dpix); u32 b = source32_b(pix) + dest_b(dpix); @@ -795,14 +821,18 @@ private: } } } + + // alpha and/or coloring case else { - // alpha and/or coloring case + u32 sr = u32(256.0f * prim.color.r * prim.color.a); + u32 sg = u32(256.0f * prim.color.g * prim.color.a); + u32 sb = u32(256.0f * prim.color.b * prim.color.a); // clamp R,G,B and inverse A to 0-256 range - u32 const sr = u32(std::clamp(256.0f * prim.color.r * prim.color.a, 0.0f, 256.0f)); - u32 const sg = u32(std::clamp(256.0f * prim.color.g * prim.color.a, 0.0f, 256.0f)); - u32 const sb = u32(std::clamp(256.0f * prim.color.b * prim.color.a, 0.0f, 256.0f)); + if (sr > 0x100) { if (s32(sr) < 0) sr = 0; else sr = 0x100; } + if (sg > 0x100) { if (s32(sg) < 0) sg = 0; else sg = 0x100; } + if (sb > 0x100) { if (s32(sb) < 0) sb = 0; else sb = 0x100; } // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) @@ -814,10 +844,10 @@ private: // loop over cols for (s32 x = setup.startx; x < setup.endx; x++) { - u32 const pix = get_texel_palette16(prim.texture, curu, curv); + const u32 pix = get_texel_palette16(prim.texture, curu, curv); if ((pix & 0xffffff) != 0) { - u32 const dpix = NoDestRead ? 0 : *dest; + const u32 dpix = NoDestRead ? 0 : *dest; u32 r = ((source32_r(pix) * sr) >> 8) + dest_r(dpix); u32 g = ((source32_g(pix) * sg) >> 8) + dest_g(dpix); u32 b = ((source32_b(pix) * sb) >> 8) + dest_b(dpix); @@ -844,12 +874,11 @@ private: // rasterization of a 16bpp YUY image //------------------------------------------------- - static void draw_quad_yuy16_none(render_primitive const &prim, PixelType *dstdata, u32 pitch, quad_setup_data const &setup) + static void draw_quad_yuy16_none(const render_primitive &prim, PixelType *dstdata, u32 pitch, const quad_setup_data&setup) { + // fast case: no coloring, no alpha if (prim.color.r >= 1.0f && prim.color.g >= 1.0f && prim.color.b >= 1.0f && is_opaque(prim.color.a)) { - // fast case: no coloring, no alpha - // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) { @@ -860,21 +889,25 @@ private: // loop over cols for (s32 x = setup.startx; x < setup.endx; x++) { - u32 const pix = ycc_to_rgb(get_texel_yuy16(prim.texture, curu, curv)); + const u32 pix = ycc_to_rgb(get_texel_yuy16(prim.texture, curu, curv)); *dest++ = source32_to_dest(pix); curu += setup.dudx; curv += setup.dvdx; } } } + + // coloring-only case else if (is_opaque(prim.color.a)) { - // coloring-only case + u32 sr = u32(256.0f * prim.color.r); + u32 sg = u32(256.0f * prim.color.g); + u32 sb = u32(256.0f * prim.color.b); // clamp R,G,B to 0-256 range - u32 const sr = u32(std::clamp(256.0f * prim.color.r, 0.0f, 256.0f)); - u32 const sg = u32(std::clamp(256.0f * prim.color.g, 0.0f, 256.0f)); - u32 const sb = u32(std::clamp(256.0f * prim.color.b, 0.0f, 256.0f)); + if (sr > 0x100) { if (s32(sr) < 0) sr = 0; else sr = 0x100; } + if (sg > 0x100) { if (s32(sg) < 0) sg = 0; else sg = 0x100; } + if (sb > 0x100) { if (s32(sb) < 0) sb = 0; else sb = 0x100; } // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) @@ -886,10 +919,10 @@ private: // loop over cols for (s32 x = setup.startx; x < setup.endx; x++) { - u32 const pix = ycc_to_rgb(get_texel_yuy16(prim.texture, curu, curv)); - u32 const r = (source32_r(pix) * sr) >> 8; - u32 const g = (source32_g(pix) * sg) >> 8; - u32 const b = (source32_b(pix) * sb) >> 8; + const u32 pix = ycc_to_rgb(get_texel_yuy16(prim.texture, curu, curv)); + const u32 r = (source32_r(pix) * sr) >> 8; + const u32 g = (source32_g(pix) * sg) >> 8; + const u32 b = (source32_b(pix) * sb) >> 8; *dest++ = dest_assemble_rgb(r, g, b); curu += setup.dudx; @@ -897,15 +930,20 @@ private: } } } + + // alpha and/or coloring case else if (!is_transparent(prim.color.a)) { - // alpha and/or coloring case + u32 sr = u32(256.0f * prim.color.r * prim.color.a); + u32 sg = u32(256.0f * prim.color.g * prim.color.a); + u32 sb = u32(256.0f * prim.color.b * prim.color.a); + u32 invsa = u32(256.0f * (1.0f - prim.color.a)); // clamp R,G,B and inverse A to 0-256 range - u32 const sr = u32(std::clamp(256.0f * prim.color.r * prim.color.a, 0.0f, 256.0f)); - u32 const sg = u32(std::clamp(256.0f * prim.color.g * prim.color.a, 0.0f, 256.0f)); - u32 const sb = u32(std::clamp(256.0f * prim.color.b * prim.color.a, 0.0f, 256.0f)); - u32 const invsa = u32(std::clamp(256.0f * (1.0f - prim.color.a), 0.0f, 256.0f)); + if (sr > 0x100) { if (s32(sr) < 0) sr = 0; else sr = 0x100; } + if (sg > 0x100) { if (s32(sg) < 0) sg = 0; else sg = 0x100; } + if (sb > 0x100) { if (s32(sb) < 0) sb = 0; else sb = 0x100; } + if (invsa > 0x100) { if (s32(invsa) < 0) invsa = 0; else invsa = 0x100; } // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) @@ -917,11 +955,11 @@ private: // loop over cols for (s32 x = setup.startx; x < setup.endx; x++) { - u32 const pix = ycc_to_rgb(get_texel_yuy16(prim.texture, curu, curv)); - u32 const dpix = NoDestRead ? 0 : *dest; - u32 const r = (source32_r(pix) * sr + dest_r(dpix) * invsa) >> 8; - u32 const g = (source32_g(pix) * sg + dest_g(dpix) * invsa) >> 8; - u32 const b = (source32_b(pix) * sb + dest_b(dpix) * invsa) >> 8; + const u32 pix = ycc_to_rgb(get_texel_yuy16(prim.texture, curu, curv)); + const u32 dpix = NoDestRead ? 0 : *dest; + const u32 r = (source32_r(pix) * sr + dest_r(dpix) * invsa) >> 8; + const u32 g = (source32_g(pix) * sg + dest_g(dpix) * invsa) >> 8; + const u32 b = (source32_b(pix) * sb + dest_b(dpix) * invsa) >> 8; *dest++ = dest_assemble_rgb(r, g, b); curu += setup.dudx; @@ -938,16 +976,15 @@ private: // conversion //------------------------------------------------- - static void draw_quad_yuy16_add(render_primitive const &prim, PixelType *dstdata, u32 pitch, quad_setup_data const &setup) + static void draw_quad_yuy16_add(const render_primitive &prim, PixelType *dstdata, u32 pitch, const quad_setup_data&setup) { // simply can't do this without reading from the dest if constexpr (NoDestRead) return; + // fast case: no coloring, no alpha if (prim.color.r >= 1.0f && prim.color.g >= 1.0f && prim.color.b >= 1.0f && is_opaque(prim.color.a)) { - // fast case: no coloring, no alpha - // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) { @@ -958,8 +995,8 @@ private: // loop over cols for (s32 x = setup.startx; x < setup.endx; x++) { - u32 const pix = ycc_to_rgb(get_texel_yuy16(prim.texture, curu, curv)); - u32 const dpix = NoDestRead ? 0 : *dest; + const u32 pix = ycc_to_rgb(get_texel_yuy16(prim.texture, curu, curv)); + const u32 dpix = NoDestRead ? 0 : *dest; u32 r = source32_r(pix) + dest_r(dpix); u32 g = source32_g(pix) + dest_g(dpix); u32 b = source32_b(pix) + dest_b(dpix); @@ -972,15 +1009,20 @@ private: } } } + + // alpha and/or coloring case else { - // alpha and/or coloring case + u32 sr = u32(256.0f * prim.color.r); + u32 sg = u32(256.0f * prim.color.g); + u32 sb = u32(256.0f * prim.color.b); + u32 sa = u32(256.0f * prim.color.a); // clamp R,G,B and inverse A to 0-256 range - u32 const sr = u32(std::clamp(256.0f * prim.color.r, 0.0f, 256.0f)); - u32 const sg = u32(std::clamp(256.0f * prim.color.g, 0.0f, 256.0f)); - u32 const sb = u32(std::clamp(256.0f * prim.color.b, 0.0f, 256.0f)); - u32 const sa = u32(std::clamp(256.0f * prim.color.a, 0.0f, 256.0f)); + if (sr > 0x100) { if (s32(sr) < 0) sr = 0; else sr = 0x100; } + if (sg > 0x100) { if (s32(sg) < 0) sg = 0; else sg = 0x100; } + if (sb > 0x100) { if (s32(sb) < 0) sb = 0; else sb = 0x100; } + if (sa > 0x100) { if (s32(sa) < 0) sa = 0; else sa = 0x100; } // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) @@ -1122,10 +1164,10 @@ private: // alpha and/or coloring case // clamp R,G,B and inverse A to 0-256 range - u32 const sr = u32(std::clamp(256.0f * prim.color.r * prim.color.a, 0.0f, 256.0f)); - u32 const sg = u32(std::clamp(256.0f * prim.color.g * prim.color.a, 0.0f, 256.0f)); - u32 const sb = u32(std::clamp(256.0f * prim.color.b * prim.color.a, 0.0f, 256.0f)); - u32 const invsa = u32(std::clamp(256.0f * (1.0f - prim.color.a), 0.0f, 256.0f)); + u32 sr = u32(std::clamp(256.0f * prim.color.r * prim.color.a, 0.0f, 256.0f)); + u32 sg = u32(std::clamp(256.0f * prim.color.g * prim.color.a, 0.0f, 256.0f)); + u32 sb = u32(std::clamp(256.0f * prim.color.b * prim.color.a, 0.0f, 256.0f)); + u32 invsa = u32(std::clamp(256.0f * (1.0f - prim.color.a), 0.0f, 256.0f)); // loop over rows for (s32 y = setup.starty; y < setup.endy; y++) @@ -1727,16 +1769,16 @@ private: // drawing routine //------------------------------------------------- - static void setup_and_draw_textured_quad(render_primitive const &prim, PixelType *dstdata, s32 width, s32 height, u32 pitch) + static void setup_and_draw_textured_quad(const render_primitive &prim, PixelType *dstdata, s32 width, s32 height, u32 pitch) { assert(prim.bounds.x0 <= prim.bounds.x1); assert(prim.bounds.y0 <= prim.bounds.y1); // determine U/V deltas - float const fdudx = (prim.texcoords.tr.u - prim.texcoords.tl.u) / (prim.bounds.x1 - prim.bounds.x0); - float const fdvdx = (prim.texcoords.tr.v - prim.texcoords.tl.v) / (prim.bounds.x1 - prim.bounds.x0); - float const fdudy = (prim.texcoords.bl.u - prim.texcoords.tl.u) / (prim.bounds.y1 - prim.bounds.y0); - float const fdvdy = (prim.texcoords.bl.v - prim.texcoords.tl.v) / (prim.bounds.y1 - prim.bounds.y0); + float fdudx = (prim.texcoords.tr.u - prim.texcoords.tl.u) / (prim.bounds.x1 - prim.bounds.x0); + float fdvdx = (prim.texcoords.tr.v - prim.texcoords.tl.v) / (prim.bounds.x1 - prim.bounds.x0); + float fdudy = (prim.texcoords.bl.u - prim.texcoords.tl.u) / (prim.bounds.y1 - prim.bounds.y0); + float fdvdy = (prim.texcoords.bl.v - prim.texcoords.tl.v) / (prim.bounds.y1 - prim.bounds.y0); // clamp to integers quad_setup_data setup; @@ -1850,10 +1892,10 @@ private: //------------------------------------------------- public: - static void draw_primitives(render_primitive_list const &primlist, void *dstdata, u32 width, u32 height, u32 pitch) + static void draw_primitives(const render_primitive_list &primlist, void *dstdata, u32 width, u32 height, u32 pitch) { // loop over the list and render each element - for (render_primitive const *prim = primlist.first(); prim != nullptr; prim = prim->next()) + for (const render_primitive *prim = primlist.first(); prim != nullptr; prim = prim->next()) switch (prim->type) { case render_primitive::LINE: diff --git a/src/emu/rendlay.cpp b/src/emu/rendlay.cpp index 28011b8..41075c2 100644 --- a/src/emu/rendlay.cpp +++ b/src/emu/rendlay.cpp @@ -1246,6 +1246,9 @@ int get_blend_mode(emu::render::detail::view_environment &env, util::xml::data_n layout_element::make_component_map const layout_element::s_make_component{ { "image", &make_component }, { "text", &make_component }, + { "dotmatrix", &make_dotmatrix_component<8> }, + { "dotmatrix5dot", &make_dotmatrix_component<5> }, + { "dotmatrixdot", &make_dotmatrix_component<1> }, { "simplecounter", &make_component }, { "reel", &make_component }, { "led7seg", &make_component }, @@ -3076,6 +3079,47 @@ protected: }; +// row of dots for a dotmatrix +class layout_element::dotmatrix_component : public component +{ +public: + // construction/destruction + dotmatrix_component(int dots, environment &env, util::xml::data_node const &compnode) + : component(env, compnode) + , m_dots(dots) + { + } + +protected: + // overrides + virtual int maxstate() const override { return (1 << m_dots) - 1; } + + virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override + { + const rgb_t onpen = rgb_t(0xff, 0xff, 0xff, 0xff); + const rgb_t offpen = rgb_t(0xff, 0x20, 0x20, 0x20); + + // sizes for computation + int bmheight = 300; + int dotwidth = 250; + + // allocate a temporary bitmap for drawing + bitmap_argb32 tempbitmap(dotwidth*m_dots, bmheight); + tempbitmap.fill(rgb_t(0xff, 0x00, 0x00, 0x00)); + + for (int i = 0; i < m_dots; i++) + draw_segment_decimal(tempbitmap, ((dotwidth / 2) + (i * dotwidth)), bmheight / 2, dotwidth, BIT(state, i) ? onpen : offpen); + + // resample to the target size + render_resample_argb_bitmap_hq(dest, tempbitmap, color(state)); + } + +private: + // internal state + int m_dots; +}; + + // simple counter class layout_element::simplecounter_component : public component { @@ -3119,8 +3163,6 @@ public: , m_searchpath(env.search_path() ? env.search_path() : "") , m_dirname(env.directory_name() ? env.directory_name() : "") { - osd_printf_warning("Warning: layout file contains deprecated reel component\n"); - std::string_view symbollist = env.get_attribute_string(compnode, "symbollist", "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15"); // split out position names from string and figure out our number of symbols @@ -3516,6 +3558,18 @@ layout_element::component::ptr layout_element::make_component(environment &env, } +//------------------------------------------------- +// make_component - create dotmatrix component +// with given vertical resolution +//------------------------------------------------- + +template +layout_element::component::ptr layout_element::make_dotmatrix_component(environment &env, util::xml::data_node const &compnode) +{ + return std::make_unique(D, env, compnode); +} + + //************************************************************************** // LAYOUT ELEMENT TEXTURE diff --git a/src/emu/rendlay.h b/src/emu/rendlay.h index da9edfc..befae56 100644 --- a/src/emu/rendlay.h +++ b/src/emu/rendlay.h @@ -146,6 +146,7 @@ private: class led16seg_component; class led14segsc_component; class led16segsc_component; + class dotmatrix_component; class simplecounter_component; class reel_component; diff --git a/src/emu/romload.cpp b/src/emu/romload.cpp index bdff58a..c4c3101 100644 --- a/src/emu/romload.cpp +++ b/src/emu/romload.cpp @@ -1200,7 +1200,7 @@ void rom_load_manager::normalize_flags_for_device(std::string_view rgntag, u8 &w /*------------------------------------------------- load_software_part_region - load a software part - This is used by MAME when loading a piece of + This is used by MESS when loading a piece of software. The code should be merged with process_region_list or updated to use a slight more general process_region_list. diff --git a/src/emu/screen.cpp b/src/emu/screen.cpp index 19b0d05..e3ca748 100644 --- a/src/emu/screen.cpp +++ b/src/emu/screen.cpp @@ -683,9 +683,9 @@ void screen_device::device_validity_check(validity_checker &valid) const osd_printf_error("Non-raster display cannot have a variable width\n"); } - // check for invalid frame rate - if (m_refresh == 0 || m_refresh > ATTOSECONDS_PER_SECOND) - osd_printf_error("Invalid (under 1Hz) refresh rate\n"); + // check for zero frame rate + if (m_refresh == 0) + osd_printf_error("Invalid (zero) refresh rate\n"); texture_format texformat = !m_screen_update_ind16.isnull() ? TEXFORMAT_PALETTE16 : TEXFORMAT_RGB32; if (m_palette.finder_tag() != finder_base::DUMMY_TAG) @@ -1108,7 +1108,7 @@ void screen_device::realloc_screen_bitmaps() s32 effwidth = std::max(per_scanline ? m_max_width : m_width, m_visarea.right() + 1); s32 effheight = std::max(m_height, m_visarea.bottom() + 1); - // resize all registered screen bitmaps + // reize all registered screen bitmaps for (auto &item : m_auto_bitmap_list) item->m_bitmap.resize(effwidth, effheight); @@ -1870,7 +1870,7 @@ void screen_device::finalize_burnin() m_visarea.top() * m_burnin.height() / m_height, m_visarea.bottom() * m_burnin.height() / m_height); - // wrap a bitmap around the subregion we care about + // wrap a bitmap around the memregion we care about bitmap_argb32 finalmap(scaledvis.width(), scaledvis.height()); int srcwidth = m_burnin.width(); int srcheight = m_burnin.height(); diff --git a/src/emu/validity.cpp b/src/emu/validity.cpp index e4cf086..18ba2c6 100644 --- a/src/emu/validity.cpp +++ b/src/emu/validity.cpp @@ -2292,9 +2292,8 @@ void validity_checker::validate_analog_input_field(const ioport_field &field) void validity_checker::validate_dip_settings(const ioport_field &field) { - char const *const demo_sounds = ioport_string_from_index(INPUT_STRING_Demo_Sounds); - char const *const flipscreen = ioport_string_from_index(INPUT_STRING_Flip_Screen); - char const *const name = field.specific_name(); + const char *demo_sounds = ioport_string_from_index(INPUT_STRING_Demo_Sounds); + const char *flipscreen = ioport_string_from_index(INPUT_STRING_Flip_Screen); u8 coin_list[__input_string_coinage_end + 1 - __input_string_coinage_start] = { 0 }; bool coin_error = false; @@ -2307,15 +2306,15 @@ void validity_checker::validate_dip_settings(const ioport_field &field) coin_list[strindex - __input_string_coinage_start] = 1; // make sure demo sounds default to on - if (name == demo_sounds && strindex == INPUT_STRING_On && field.defvalue() != setting->value()) + if (field.name() == demo_sounds && strindex == INPUT_STRING_On && field.defvalue() != setting->value()) osd_printf_error("Demo Sounds must default to On\n"); // check for bad demo sounds options - if (name == demo_sounds && (strindex == INPUT_STRING_Yes || strindex == INPUT_STRING_No)) + if (field.name() == demo_sounds && (strindex == INPUT_STRING_Yes || strindex == INPUT_STRING_No)) osd_printf_error("Demo Sounds option must be Off/On, not %s\n", setting->name()); // check for bad flip screen options - if (name == flipscreen && (strindex == INPUT_STRING_Yes || strindex == INPUT_STRING_No)) + if (field.name() == flipscreen && (strindex == INPUT_STRING_Yes || strindex == INPUT_STRING_No)) osd_printf_error("Flip Screen option must be Off/On, not %s\n", setting->name()); // if we have a neighbor, compare ourselves to him @@ -2325,21 +2324,21 @@ void validity_checker::validate_dip_settings(const ioport_field &field) // check for inverted off/on DIP switch order int next_strindex = get_defstr_index(nextsetting->name(), true); if (strindex == INPUT_STRING_On && next_strindex == INPUT_STRING_Off) - osd_printf_error("%s option must have Off/On options in the order: Off, On\n", name); + osd_printf_error("%s option must have Off/On options in the order: Off, On\n", field.name()); // check for inverted yes/no DIP switch order else if (strindex == INPUT_STRING_Yes && next_strindex == INPUT_STRING_No) - osd_printf_error("%s option must have Yes/No options in the order: No, Yes\n", name); + osd_printf_error("%s option must have Yes/No options in the order: No, Yes\n", field.name()); // check for inverted upright/cocktail DIP switch order else if (strindex == INPUT_STRING_Cocktail && next_strindex == INPUT_STRING_Upright) - osd_printf_error("%s option must have Upright/Cocktail options in the order: Upright, Cocktail\n", name); + osd_printf_error("%s option must have Upright/Cocktail options in the order: Upright, Cocktail\n", field.name()); // check for proper coin ordering else if (strindex >= __input_string_coinage_start && strindex <= __input_string_coinage_end && next_strindex >= __input_string_coinage_start && next_strindex <= __input_string_coinage_end && strindex >= next_strindex && setting->condition() == nextsetting->condition()) { - osd_printf_error("%s option has unsorted coinage %s > %s\n", name, setting->name(), nextsetting->name()); + osd_printf_error("%s option has unsorted coinage %s > %s\n", field.name(), setting->name(), nextsetting->name()); coin_error = true; } } @@ -2468,6 +2467,9 @@ void validity_checker::validate_inputs(device_t &root) // check for invalid UTF-8 if (!utf8_is_valid_string(name)) osd_printf_error("Field '%s' has invalid characters\n", name); + + // look up the string and print an error if default strings are not used + /*strindex =get_defstr_index(defstr_map, name, driver, &error);*/ } // verify conditions on the field diff --git a/src/emu/xtal.cpp b/src/emu/xtal.cpp index f36d0fe..16178a2 100644 --- a/src/emu/xtal.cpp +++ b/src/emu/xtal.cpp @@ -327,7 +327,6 @@ const double XTAL::known_xtals[] = { 22'464'000, /* 22.464_MHz_XTAL CIT-101 132-column display clock */ 22'579'000, /* 22.579_MHz_XTAL Sega System H1 SCSP clock */ 22'656'000, /* 22.656_MHz_XTAL Super Pinball Action (~1440x NTSC line rate) */ - 22'680'000, /* 22.680_MHz_XTAL HDS200 80-columns display clock */ 22'896'000, /* 22.896_MHz_XTAL DEC VT220 132-column display clock */ 23'200'000, /* 23.2_MHz_XTAL Roland JV-80 & JV-880 PCM clock */ 23'814'000, /* 23.814_MHz_XTAL TeleVideo TVI-912, 920 & 950 */ @@ -403,7 +402,6 @@ const double XTAL::known_xtals[] = { 34'000'000, /* 34_MHz_XTAL Gaelco PCBs */ 34'291'712, /* 34.291712_MHz_XTAL Fairlight CMI master card */ 34'846'000, /* 34.846_MHz_XTAL Visual 550 */ - 35'640'000, /* 35.640_MHz_XTAL HDS200 132-column display clock */ 35'834'400, /* 35.8344_MHz_XTAL Tab Products E-22 132-column display clock */ 35'840'000, /* 35.84_MHz_XTAL Akai MPC 60 voice PCB */ 35'904'000, /* 35.904_MHz_XTAL Used on HP98543 graphics board */ diff --git a/src/frontend/mame/cheat.cpp b/src/frontend/mame/cheat.cpp index dbbd0ed..2946d2c 100644 --- a/src/frontend/mame/cheat.cpp +++ b/src/frontend/mame/cheat.cpp @@ -418,10 +418,6 @@ cheat_script::script_entry::script_entry( if (!expression || !expression[0]) throw emu_fatalerror("%s.xml(%d): missing expression in action tag\n", filename, entrynode.line); m_expression.parse(expression); - - // initialise these to defautlt values - m_line = 0; - m_justify = ui::text_layout::text_justify::LEFT; } else { @@ -1060,9 +1056,6 @@ constexpr int cheat_manager::CHEAT_VERSION; cheat_manager::cheat_manager(running_machine &machine) : m_machine(machine) - , m_framecount(0) - , m_numlines(0) - , m_lastline(0) , m_disabled(true) , m_symtable(machine) { diff --git a/src/frontend/mame/infoxml.cpp b/src/frontend/mame/infoxml.cpp index 6adb5f5..8ff0331 100644 --- a/src/frontend/mame/infoxml.cpp +++ b/src/frontend/mame/infoxml.cpp @@ -110,7 +110,7 @@ void output_footer(std::ostream &out); void output_one(std::ostream &out, driver_enumerator &drivlist, const game_driver &driver, device_type_set *devtypes); void output_sampleof(std::ostream &out, device_t &device); void output_bios(std::ostream &out, device_t const &device); -void output_rom(std::ostream &out, machine_config &config, driver_list const *drivlist, const game_driver *driver, device_t &device); +void output_rom(std::ostream &out, machine_config &config, driver_enumerator *drivlist, const game_driver *driver, device_t &device); void output_device_refs(std::ostream &out, device_t &root); void output_sample(std::ostream &out, device_t &device); void output_chips(std::ostream &out, device_t &device, const char *root_tag); @@ -131,7 +131,7 @@ void output_ramoptions(std::ostream &out, device_t &root); void output_one_device(std::ostream &out, machine_config &config, device_t &device, const char *devtag); void output_devices(std::ostream &out, emu_options &lookup_options, device_type_set const *filter); -char const *get_merge_name(driver_list const &drivlist, game_driver const &driver, util::hash_collection const &romhashes); +char const *get_merge_name(driver_enumerator &drivlist, game_driver const &driver, util::hash_collection const &romhashes); char const *get_merge_name(machine_config &config, device_t const &device, util::hash_collection const &romhashes); char const *get_merge_name(tiny_rom_entry const *roms, util::hash_collection const &romhashes); @@ -982,7 +982,7 @@ void output_bios(std::ostream &out, device_t const &device) // the XML output //------------------------------------------------- -void output_rom(std::ostream &out, machine_config &config, driver_list const *drivlist, const game_driver *driver, device_t &device) +void output_rom(std::ostream &out, machine_config &config, driver_enumerator *drivlist, const game_driver *driver, device_t &device) { enum class type { BIOS, NORMAL, DISK }; std::map biosnames; @@ -1053,7 +1053,7 @@ void output_rom(std::ostream &out, machine_config &config, driver_list const *dr if ((type::DISK == pass) != is_disk) continue; - // BIOS ROMs only apply to BIOSes + // BIOS ROMs only apply to bioses // FIXME: disk images associated with a system BIOS will never be listed u32 const biosno(ROM_GETBIOSFLAGS(rom)); if ((type::BIOS == pass) != bool(biosno)) @@ -1824,7 +1824,7 @@ void output_switches(std::ostream &out, const ioport_list &portlist, const char newtag = newtag.substr(newtag.find(oldtag.append(root_tag)) + oldtag.length()); // output the switch name information - std::string const normalized_field_name(normalize_string(field.specific_name())); + std::string const normalized_field_name(normalize_string(field.name())); std::string const normalized_newtag(normalize_string(newtag)); util::stream_format(out, "\t\t<%s name=\"%s\" tag=\"%s\" mask=\"%u\">\n", outertag, normalized_field_name, normalized_newtag, field.mask()); if (!field.condition().none()) @@ -1894,7 +1894,7 @@ void output_adjusters(std::ostream &out, const ioport_list &portlist) for (ioport_field const &field : port.second->fields()) if (field.type() == IPT_ADJUSTER) { - util::stream_format(out, "\t\t\n", normalize_string(field.specific_name()), field.defvalue()); + util::stream_format(out, "\t\t\n", normalize_string(field.name()), field.defvalue()); } } @@ -2169,7 +2169,7 @@ void output_ramoptions(std::ostream &out, device_t &root) // parent set //------------------------------------------------- -char const *get_merge_name(driver_list const &drivlist, game_driver const &driver, util::hash_collection const &romhashes) +char const *get_merge_name(driver_enumerator &drivlist, game_driver const &driver, util::hash_collection const &romhashes) { char const *result = nullptr; diff --git a/src/frontend/mame/luaengine_debug.cpp b/src/frontend/mame/luaengine_debug.cpp index 8349cbd..e3817d9 100644 --- a/src/frontend/mame/luaengine_debug.cpp +++ b/src/frontend/mame/luaengine_debug.cpp @@ -100,7 +100,7 @@ void lua_engine::initialize_debug(sol::table &emu) debugger_type["consolelog"] = sol::property([] (debugger_manager &debug) { return wrap_textbuf(debug.console().get_console_textbuf()); }); debugger_type["errorlog"] = sol::property([](debugger_manager &debug) { return wrap_textbuf(debug.console().get_errorlog_textbuf()); }); debugger_type["visible_cpu"] = sol::property( - [](debugger_manager &debug) { return debug.console().get_visible_cpu(); }, + [](debugger_manager &debug) { debug.console().get_visible_cpu(); }, [](debugger_manager &debug, device_t &dev) { debug.console().set_visible_cpu(&dev); }); debugger_type["execution_state"] = sol::property( [] (debugger_manager &debug) { return debug.cpu().is_stopped() ? "stop" : "run"; }, diff --git a/src/frontend/mame/ui/auditmenu.cpp b/src/frontend/mame/ui/auditmenu.cpp index a5cb46f..aaad096 100644 --- a/src/frontend/mame/ui/auditmenu.cpp +++ b/src/frontend/mame/ui/auditmenu.cpp @@ -95,7 +95,7 @@ void menu_audit::custom_render(void *selectedref, float top, float bottom, float std::size_t const total(m_fast ? m_unavailable : m_availablesorted.size()); std::ostringstream text; util::stream_format(text, - _("Auditing media for machine %2$u of %3$u...\n%1$s"), + _("Auditing ROMs for machine %2$u of %3$u...\n%1$s"), system ? std::string_view(system->description) : std::string_view(), (std::min)(audited + 1, total), total); @@ -133,8 +133,8 @@ bool menu_audit::custom_ui_cancel() void menu_audit::populate(float &customtop, float &custombottom) { if (m_unavailable && (m_availablesorted.size() != m_unavailable)) - item_append(util::string_format(_("Audit media for %1$u machines marked unavailable"), m_unavailable), 0, ITEMREF_START_FAST); - item_append(util::string_format(_("Audit media for all %1$u machines"), m_availablesorted.size()), 0, ITEMREF_START_FULL); + item_append(util::string_format(_("Audit ROMs for %1$u machines marked unavailable"), m_unavailable), 0, ITEMREF_START_FAST); + item_append(util::string_format(_("Audit ROMs for all %1$u machines"), m_availablesorted.size()), 0, ITEMREF_START_FULL); item_append(menu_item_type::SEPARATOR, 0); custombottom = (ui().get_line_height() * 1.0f) + (ui().box_tb_border() * 3.0f); } diff --git a/src/frontend/mame/ui/custui.cpp b/src/frontend/mame/ui/custui.cpp index 02da931..62396d7 100644 --- a/src/frontend/mame/ui/custui.cpp +++ b/src/frontend/mame/ui/custui.cpp @@ -83,10 +83,10 @@ menu_custom_ui::menu_custom_ui(mame_ui_manager &mui, render_container &container } //------------------------------------------------- -// menu dismissed +// dtor //------------------------------------------------- -void menu_custom_ui::menu_dismissed() +menu_custom_ui::~menu_custom_ui() { ui().options().set_value(OPTION_HIDE_PANELS, ui_globals::panels_status, OPTION_PRIORITY_CMDLINE); @@ -375,10 +375,10 @@ void menu_font_ui::list() } //------------------------------------------------- -// menu dismissed +// dtor //------------------------------------------------- -void menu_font_ui::menu_dismissed() +menu_font_ui::~menu_font_ui() { if (m_changed) { @@ -580,10 +580,10 @@ menu_colors_ui::menu_colors_ui(mame_ui_manager &mui, render_container &container } //------------------------------------------------- -// menu dismissed +// dtor //------------------------------------------------- -void menu_colors_ui::menu_dismissed() +menu_colors_ui::~menu_colors_ui() { std::string dec_color; for (int index = 1; index < MUI_RESTORE; index++) @@ -607,7 +607,7 @@ void menu_colors_ui::handle(event const *ev) { if ((uintptr_t)ev->itemref != MUI_RESTORE) { - menu::stack_push(ui(), container(), &m_color_table[(uintptr_t)ev->itemref].color, std::string(selected_item().text())); + menu::stack_push(ui(), container(), &m_color_table[(uintptr_t)ev->itemref].color, selected_item().text()); } else { @@ -803,17 +803,25 @@ void menu_colors_ui::restore_colors() // ctor //------------------------------------------------- -menu_rgb_ui::menu_rgb_ui(mame_ui_manager &mui, render_container &container, rgb_t *color, std::string &&title) - : menu(mui, container) - , m_color(color) - , m_search() - , m_key_active(false) - , m_lock_ref(0) - , m_title(std::move(title)) +menu_rgb_ui::menu_rgb_ui(mame_ui_manager &mui, render_container &container, rgb_t *_color, std::string _title) + : menu(mui, container), + m_color(_color), + m_search(), + m_key_active(false), + m_lock_ref(0), + m_title(_title) { set_process_flags(PROCESS_LR_REPEAT); } +//------------------------------------------------- +// dtor +//------------------------------------------------- + +menu_rgb_ui::~menu_rgb_ui() +{ +} + //------------------------------------------------- // handle //------------------------------------------------- @@ -1103,6 +1111,14 @@ menu_palette_sel::menu_palette_sel(mame_ui_manager &mui, render_container &conta { } +//------------------------------------------------- +// dtor +//------------------------------------------------- + +menu_palette_sel::~menu_palette_sel() +{ +} + //------------------------------------------------- // handle //------------------------------------------------- diff --git a/src/frontend/mame/ui/custui.h b/src/frontend/mame/ui/custui.h index 168934b..34d417c 100644 --- a/src/frontend/mame/ui/custui.h +++ b/src/frontend/mame/ui/custui.h @@ -28,10 +28,10 @@ class menu_custom_ui : public menu { public: menu_custom_ui(mame_ui_manager &mui, render_container &container, std::function &&handler); + virtual ~menu_custom_ui() override; protected: virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; - virtual void menu_dismissed() override; private: virtual void populate(float &customtop, float &custombottom) override; @@ -55,10 +55,10 @@ class menu_font_ui : public menu { public: menu_font_ui(mame_ui_manager &mui, render_container &container, std::function &&handler); + virtual ~menu_font_ui() override; protected: virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; - virtual void menu_dismissed() override; private: virtual void populate(float &customtop, float &custombottom) override; @@ -89,10 +89,10 @@ class menu_colors_ui : public menu { public: menu_colors_ui(mame_ui_manager &mui, render_container &container); + virtual ~menu_colors_ui() override; protected: virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; - virtual void menu_dismissed() override; private: enum @@ -136,7 +136,8 @@ private: class menu_rgb_ui : public menu { public: - menu_rgb_ui(mame_ui_manager &mui, render_container &container, rgb_t *color, std::string &&title); + menu_rgb_ui(mame_ui_manager &mui, render_container &container, rgb_t *_color, std::string _title); + virtual ~menu_rgb_ui() override; protected: virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; @@ -171,6 +172,7 @@ class menu_palette_sel : public menu { public: menu_palette_sel(mame_ui_manager &mui, render_container &container, rgb_t &_color); + virtual ~menu_palette_sel() override; private: virtual void populate(float &customtop, float &custombottom) override; diff --git a/src/frontend/mame/ui/devopt.cpp b/src/frontend/mame/ui/devopt.cpp index c9a2c7f..9885e69 100644 --- a/src/frontend/mame/ui/devopt.cpp +++ b/src/frontend/mame/ui/devopt.cpp @@ -120,16 +120,10 @@ void menu_device_config::populate_text(std::optional &layout, float } else { - const u32 rate = u32(screen.frame_period().as_hz() * 1'000'000 + 0.5); - const bool valid = rate >= 1'000'000; - std::string hz(valid ? std::to_string(rate) : "?"); - if (valid) - { - size_t dpos = hz.length() - 6; - hz.insert(dpos, "."); - size_t last = hz.find_last_not_of('0'); - hz = hz.substr(0, last + (last != dpos ? 1 : 0)); - } + std::string hz(std::to_string(float(screen.frame_period().as_hz()))); + size_t last = hz.find_last_not_of('0'); + size_t dpos = hz.find_last_of('.'); + hz = hz.substr(0, last + (last != dpos ? 1 : 0)); const rectangle &visarea = screen.visible_area(); layout->add_text( diff --git a/src/frontend/mame/ui/filesel.h b/src/frontend/mame/ui/filesel.h index 11e5643..9f7eea7 100644 --- a/src/frontend/mame/ui/filesel.h +++ b/src/frontend/mame/ui/filesel.h @@ -61,11 +61,10 @@ private: struct file_selector_entry { - file_selector_entry() = default; + file_selector_entry() { } file_selector_entry(file_selector_entry &&) = default; file_selector_entry &operator=(file_selector_entry &&) = default; - - file_selector_entry_type type = SELECTOR_ENTRY_TYPE_EMPTY; + file_selector_entry_type type; std::string basename; std::string fullpath; }; diff --git a/src/frontend/mame/ui/info.cpp b/src/frontend/mame/ui/info.cpp index d7ed210..1d1e975 100644 --- a/src/frontend/mame/ui/info.cpp +++ b/src/frontend/mame/ui/info.cpp @@ -464,16 +464,10 @@ std::string machine_info::game_info_string() const detail = _("Vector"); else { - const u32 rate = u32(screen.frame_period().as_hz() * 1'000'000 + 0.5); - const bool valid = rate >= 1'000'000; - std::string hz(valid ? std::to_string(rate) : "?"); - if (valid) - { - size_t dpos = hz.length() - 6; - hz.insert(dpos, "."); - size_t last = hz.find_last_not_of('0'); - hz = hz.substr(0, last + (last != dpos ? 1 : 0)); - } + std::string hz(std::to_string(float(screen.frame_period().as_hz()))); + size_t last = hz.find_last_not_of('0'); + size_t dpos = hz.find_last_of('.'); + hz = hz.substr(0, last + (last != dpos ? 1 : 0)); const rectangle &visarea = screen.visible_area(); detail = string_format("%d " UTF8_MULTIPLY " %d (%s) %s" UTF8_NBSP "Hz", diff --git a/src/frontend/mame/ui/inputmap.cpp b/src/frontend/mame/ui/inputmap.cpp index 38dd731..ada0d08 100644 --- a/src/frontend/mame/ui/inputmap.cpp +++ b/src/frontend/mame/ui/inputmap.cpp @@ -78,30 +78,26 @@ void menu_input_general::populate(float &customtop, float &custombottom) for (const input_type_entry &entry : machine().ioport().types()) { // add if we match the group and we have a valid name - if (entry.group() == group) + if ((entry.group() == group) && entry.name() && entry.name()[0]) { - std::string name = entry.name(); - if (!name.empty()) + // loop over all sequence types + for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) { - // loop over all sequence types - for (input_seq_type seqtype = SEQ_TYPE_STANDARD; seqtype < SEQ_TYPE_TOTAL; ++seqtype) - { - // build an entry for the standard sequence - input_item_data &item(data.emplace_back()); - item.ref = &entry; - item.seqtype = seqtype; - item.seq = machine().ioport().type_seq(entry.type(), entry.player(), seqtype); - item.defseq = &entry.defseq(seqtype); - item.group = entry.group(); - item.type = ioport_manager::type_is_analog(entry.type()) ? (INPUT_TYPE_ANALOG + seqtype) : INPUT_TYPE_DIGITAL; - item.is_optional = false; - item.name = name; - item.owner = nullptr; + // build an entry for the standard sequence + input_item_data &item(data.emplace_back()); + item.ref = &entry; + item.seqtype = seqtype; + item.seq = machine().ioport().type_seq(entry.type(), entry.player(), seqtype); + item.defseq = &entry.defseq(seqtype); + item.group = entry.group(); + item.type = ioport_manager::type_is_analog(entry.type()) ? (INPUT_TYPE_ANALOG + seqtype) : INPUT_TYPE_DIGITAL; + item.is_optional = false; + item.name = _("input-name", entry.name()); + item.owner = nullptr; - // stop after one, unless we're analog - if (item.type == INPUT_TYPE_DIGITAL) - break; - } + // stop after one, unless we're analog + if (item.type == INPUT_TYPE_DIGITAL) + break; } } } @@ -169,7 +165,7 @@ void menu_input_specific::populate(float &customtop, float &custombottom) item.group = machine().ioport().type_group(field.type(), field.player()); item.type = field.is_analog() ? (INPUT_TYPE_ANALOG + seqtype) : INPUT_TYPE_DIGITAL; item.is_optional = field.optional(); - item.name = field.name(); + item.name = _("input-name", field.name()); item.owner = &field.device(); // stop after one, unless we're analog @@ -207,7 +203,7 @@ void menu_input_specific::populate(float &customtop, float &custombottom) return true; if (!codes2.empty() && (codes1.empty() || codes1[0] > codes2[0])) return false; - cmp = i1.name.compare(i2.name); + cmp = strcmp(i1.name, i2.name); if (cmp < 0) return true; if (cmp > 0) diff --git a/src/frontend/mame/ui/inputmap.h b/src/frontend/mame/ui/inputmap.h index a43a682..841382d 100644 --- a/src/frontend/mame/ui/inputmap.h +++ b/src/frontend/mame/ui/inputmap.h @@ -54,7 +54,7 @@ protected: input_seq_type seqtype = SEQ_TYPE_INVALID; // sequence type input_seq seq; // copy of the live sequence const input_seq * defseq = nullptr; // pointer to the default sequence - std::string name; // base name of the item + const char * name = nullptr; // pointer to the base name of the item const device_t * owner = nullptr; // pointer to the owner of the item ioport_group group = IPG_INVALID; // group type uint8_t type = 0U; // type of port diff --git a/src/frontend/mame/ui/mainmenu.cpp b/src/frontend/mame/ui/mainmenu.cpp index 74f0060..0de7d7c 100644 --- a/src/frontend/mame/ui/mainmenu.cpp +++ b/src/frontend/mame/ui/mainmenu.cpp @@ -120,7 +120,7 @@ void menu_main::populate(float &customtop, float &custombottom) item_append(_("Input (general)"), 0, (void *)INPUT_GROUPS); - item_append(_("Input (this machine)"), 0, (void *)INPUT_SPECIFIC); + item_append(_("Input (this Machine)"), 0, (void *)INPUT_SPECIFIC); if (ui().machine_info().has_analog()) item_append(_("Analog Controls"), 0, (void *)ANALOG); @@ -152,7 +152,7 @@ void menu_main::populate(float &customtop, float &custombottom) item_append(_("Tape Control"), 0, (void *)TAPE_CONTROL); if (pty_interface_enumerator(machine().root_device()).first() != nullptr) - item_append(_("Pseudo Terminals"), 0, (void *)PTY_INFO); + item_append(_("Pseudo terminals"), 0, (void *)PTY_INFO); if (ui().machine_info().has_bioses()) item_append(_("BIOS Selection"), 0, (void *)BIOS_SELECTION); @@ -197,7 +197,7 @@ void menu_main::populate(float &customtop, float &custombottom) item_append(menu_item_type::SEPARATOR); - item_append(string_format(_("About %1$s"), emulator_info::get_appname()), 0, (void *)ABOUT); + item_append(string_format(_("About %s"), emulator_info::get_appname()), 0, (void *)ABOUT); item_append(menu_item_type::SEPARATOR); diff --git a/src/frontend/mame/ui/menu.cpp b/src/frontend/mame/ui/menu.cpp index 1b8290f..4dc265f 100644 --- a/src/frontend/mame/ui/menu.cpp +++ b/src/frontend/mame/ui/menu.cpp @@ -480,8 +480,6 @@ void menu::draw(uint32_t flags) // add in width of right hand side if (!pitem.subtext().empty()) total_width += 2.0f * gutter_width + ui().get_string_width(pitem.subtext()); - else if (pitem.flags() & FLAG_UI_HEADING) - total_width += 4.0f * ud_arrow_width; // track the maximum if (total_width > visible_width) diff --git a/src/frontend/mame/ui/selmenu.cpp b/src/frontend/mame/ui/selmenu.cpp index 3c16f83..1b393ed 100644 --- a/src/frontend/mame/ui/selmenu.cpp +++ b/src/frontend/mame/ui/selmenu.cpp @@ -2768,7 +2768,7 @@ void menu_select_launch::infos_render(float origx1, float origy1, float origx2, } else { - m_info_buffer.clear(); + m_info_buffer = ""; mame_machine_manager::instance()->lua()->call_plugin("data", m_info_view - 1, m_info_buffer); } } @@ -3057,9 +3057,9 @@ void menu_select_launch::general_info(ui_system_info const *system, game_driver // if everything looks good, schedule the new driver if (audit_passed(summary)) - str << _("Media Audit Result\tOK\n"); + str << _("ROM Audit Result\tOK\n"); else - str << _("Media Audit Result\tBAD\n"); + str << _("ROM Audit Result\tBAD\n"); if (summary_samples == media_auditor::NONE_NEEDED) str << _("Samples Audit Result\tNone Needed\n"); @@ -3070,10 +3070,10 @@ void menu_select_launch::general_info(ui_system_info const *system, game_driver } else { - str << _("Media Audit\tDisabled\nSamples Audit\tDisabled\n"); + str << _("ROM Audit \tDisabled\nSamples Audit \tDisabled\n"); } - buffer = std::move(str).str(); + buffer = str.str(); } } // namespace ui diff --git a/src/frontend/mame/ui/sndmenu.cpp b/src/frontend/mame/ui/sndmenu.cpp index 1ee1e82..b184296 100644 --- a/src/frontend/mame/ui/sndmenu.cpp +++ b/src/frontend/mame/ui/sndmenu.cpp @@ -45,25 +45,29 @@ menu_sound_options::menu_sound_options(mame_ui_manager &mui, render_container &c } //------------------------------------------------- -// menu_dismissed +// dtor //------------------------------------------------- -void menu_sound_options::menu_dismissed() +menu_sound_options::~menu_sound_options() { emu_options &moptions = machine().options(); - if (strcmp(moptions.value(OSDOPTION_SOUND), m_sound ? OSDOPTVAL_AUTO : OSDOPTVAL_NONE)) + if (strcmp(moptions.value(OSDOPTION_SOUND), m_sound ? OSDOPTVAL_AUTO : OSDOPTVAL_NONE) != 0) + { moptions.set_value(OSDOPTION_SOUND, m_sound ? OSDOPTVAL_AUTO : OSDOPTVAL_NONE, OPTION_PRIORITY_CMDLINE); - + } if (moptions.bool_value(OPTION_COMPRESSOR) != m_compressor) + { moptions.set_value(OPTION_COMPRESSOR, m_compressor, OPTION_PRIORITY_CMDLINE); - + } if (moptions.int_value(OPTION_SAMPLERATE) != m_sound_rate[m_cur_rates]) + { moptions.set_value(OPTION_SAMPLERATE, m_sound_rate[m_cur_rates], OPTION_PRIORITY_CMDLINE); - + } if (moptions.bool_value(OPTION_SAMPLES) != m_samples) + { moptions.set_value(OPTION_SAMPLES, m_samples, OPTION_PRIORITY_CMDLINE); - + } } //------------------------------------------------- diff --git a/src/frontend/mame/ui/sndmenu.h b/src/frontend/mame/ui/sndmenu.h index 03f6820..35ac739 100644 --- a/src/frontend/mame/ui/sndmenu.h +++ b/src/frontend/mame/ui/sndmenu.h @@ -25,10 +25,10 @@ class menu_sound_options : public menu { public: menu_sound_options(mame_ui_manager &mui, render_container &container); + virtual ~menu_sound_options() override; protected: virtual void custom_render(void *selectedref, float top, float bottom, float x, float y, float x2, float y2) override; - virtual void menu_dismissed() override; private: enum diff --git a/src/frontend/mame/ui/submenu.cpp b/src/frontend/mame/ui/submenu.cpp index 496201c..c36f837 100644 --- a/src/frontend/mame/ui/submenu.cpp +++ b/src/frontend/mame/ui/submenu.cpp @@ -36,12 +36,12 @@ std::vector submenu::misc_options() { option_type::UI, N_("Enlarge images in the right panel"), OPTION_ENLARGE_SNAPS }, { option_type::EMU, N_("Cheats"), OPTION_CHEAT }, { option_type::EMU, N_("Show mouse pointer"), OPTION_UI_MOUSE }, - { option_type::EMU, N_("Confirm quit from emulation"), OPTION_CONFIRM_QUIT }, - { option_type::EMU, N_("Skip system information screen"), OPTION_SKIP_GAMEINFO }, + { option_type::EMU, N_("Confirm quit from machines"), OPTION_CONFIRM_QUIT }, + { option_type::EMU, N_("Skip information screen at startup"), OPTION_SKIP_GAMEINFO }, { option_type::UI, N_("Force 4:3 aspect for snapshot display"), OPTION_FORCED4X3 }, { option_type::UI, N_("Use image as background"), OPTION_USE_BACKGROUND }, { option_type::UI, N_("Skip BIOS selection menu"), OPTION_SKIP_BIOS_MENU }, - { option_type::UI, N_("Skip software part selection menu"), OPTION_SKIP_PARTS_MENU }, + { option_type::UI, N_("Skip software parts selection menu"), OPTION_SKIP_PARTS_MENU }, { option_type::UI, N_("Info auto audit"), OPTION_INFO_AUTO_AUDIT }, { option_type::UI, N_("Hide romless machine from available list"),OPTION_HIDE_ROMLESS } }; } @@ -88,11 +88,11 @@ std::vector submenu::advanced_options() { option_type::EMU, N_("Multi-mouse"), OPTION_MULTIMOUSE }, { option_type::EMU, N_("Steadykey"), OPTION_STEADYKEY }, { option_type::EMU, N_("UI active"), OPTION_UI_ACTIVE }, - { option_type::EMU, N_("Off-screen reload"), OPTION_OFFSCREEN_RELOAD }, + { option_type::EMU, N_("Offscreen reload"), OPTION_OFFSCREEN_RELOAD }, { option_type::EMU, N_("Joystick deadzone"), OPTION_JOYSTICK_DEADZONE }, { option_type::EMU, N_("Joystick saturation"), OPTION_JOYSTICK_SATURATION }, { option_type::EMU, N_("Natural keyboard"), OPTION_NATURAL_KEYBOARD }, - { option_type::EMU, N_("Allow contradictory joystick inputs"), OPTION_JOYSTICK_CONTRADICTORY }, + { option_type::EMU, N_("Simultaneous contradictory"), OPTION_JOYSTICK_CONTRADICTORY }, { option_type::EMU, N_("Coin impulse"), OPTION_COIN_IMPULSE } }; } @@ -103,7 +103,7 @@ std::vector submenu::control_options() { option_type::EMU, N_("Lightgun Device Assignment"), OPTION_LIGHTGUN_DEVICE }, { option_type::EMU, N_("Trackball Device Assignment"), OPTION_TRACKBALL_DEVICE }, { option_type::EMU, N_("Pedal Device Assignment"), OPTION_PEDAL_DEVICE }, - { option_type::EMU, N_("AD Stick Device Assignment"), OPTION_ADSTICK_DEVICE }, + { option_type::EMU, N_("Adstick Device Assignment"), OPTION_ADSTICK_DEVICE }, { option_type::EMU, N_("Paddle Device Assignment"), OPTION_PADDLE_DEVICE }, { option_type::EMU, N_("Dial Device Assignment"), OPTION_DIAL_DEVICE }, { option_type::EMU, N_("Positional Device Assignment"), OPTION_POSITIONAL_DEVICE }, diff --git a/src/frontend/mame/ui/textbox.cpp b/src/frontend/mame/ui/textbox.cpp index cd529aa..71de239 100644 --- a/src/frontend/mame/ui/textbox.cpp +++ b/src/frontend/mame/ui/textbox.cpp @@ -29,7 +29,6 @@ menu_textbox::menu_textbox(mame_ui_manager &mui, render_container &container) , m_layout_width(-1.0f) , m_desired_width(-1.0f) , m_desired_lines(-1) - , m_window_lines(0) , m_top_line(0) { } diff --git a/src/frontend/mame/ui/ui.cpp b/src/frontend/mame/ui/ui.cpp index f5978f9..6633544 100644 --- a/src/frontend/mame/ui/ui.cpp +++ b/src/frontend/mame/ui/ui.cpp @@ -351,7 +351,7 @@ void mame_ui_manager::initialize(running_machine &machine) const char *const service_mode_dipname = ioport_configurer::string_from_token(DEF_STR(Service_Mode)); for (auto &port : machine.ioport().ports()) for (ioport_field &field : port.second->fields()) - if ((field.type() == IPT_DIPSWITCH) && (field.name() == service_mode_dipname)) // FIXME: probably breaks with localisation, also issues with multiple devices + if (field.type() == IPT_DIPSWITCH && strcmp(field.name(), service_mode_dipname) == 0) field.set_defseq(machine.ioport().type_seq(IPT_SERVICE)); } diff --git a/src/lib/formats/cbm_tap.cpp b/src/lib/formats/cbm_tap.cpp index eeb1872..9ae77bf 100644 --- a/src/lib/formats/cbm_tap.cpp +++ b/src/lib/formats/cbm_tap.cpp @@ -136,7 +136,7 @@ static int len; /* This in fact gives the number of samples for half of the pulse */ static inline int tap_data_to_samplecount(int data, int frequency) { -// return (int) (0.5 * (0.5 + (((double)CBM_WAV_FREQUENCY / frequency) * (double)data))); // MAME TZX formula +// return (int) (0.5 * (0.5 + (((double)CBM_WAV_FREQUENCY / frequency) * (double)data))); // MESS TZX formula return (int) (0.5 * (((double)CBM_WAV_FREQUENCY / frequency) * (double)((data) + 0.5))); // tap2wav formula } diff --git a/src/lib/formats/dfi_dsk.cpp b/src/lib/formats/dfi_dsk.cpp index f2012b6..79077ab 100644 --- a/src/lib/formats/dfi_dsk.cpp +++ b/src/lib/formats/dfi_dsk.cpp @@ -73,7 +73,7 @@ bool dfi_format::load(util::random_read &io, uint32_t form_factor, const std::ve char sign[4]; io.read_at(0, sign, 4, actual); if(memcmp(sign, "DFER", 4) == 0) { - osd_printf_error("dfi_dsk: Old type Discferret image detected; the MAME Discferret decoder will not handle this properly, bailing out!\n"); + osd_printf_error("dfi_dsk: Old type Discferret image detected; the mess Discferret decoder will not handle this properly, bailing out!\n"); return false; } diff --git a/src/lib/formats/flopimg.h b/src/lib/formats/flopimg.h index 6055714..d9f4582 100644 --- a/src/lib/formats/flopimg.h +++ b/src/lib/formats/flopimg.h @@ -48,22 +48,22 @@ public: /*! @brief Load an image. The load function opens an image file and converts it to the - internal MAME floppy representation. + internal MESS floppy representation. @param io source buffer containing the image data. @param form_factor Physical form factor of disk, from the enum in floppy_image @param variants the variants from floppy_image the drive can handle - @param image output buffer for data in MAME internal format. + @param image output buffer for data in MESS internal format. @return true on success, false otherwise. */ virtual bool load(util::random_read &io, uint32_t form_factor, const std::vector &variants, floppy_image *image) = 0; /*! @brief Save an image. - The save function writes back an image from the MAME internal + The save function writes back an image from the MESS internal floppy representation to the appropriate format on disk. @param io output buffer for the data in the on-disk format. @param variants the variants from floppy_image the drive can handle - @param image source buffer containing data in MAME internal format. + @param image source buffer containing data in MESS internal format. @return true on success, false otherwise. */ virtual bool save(util::random_read_write &io, const std::vector &variants, floppy_image *image); diff --git a/src/lib/formats/sorc_dsk.cpp b/src/lib/formats/sorc_dsk.cpp index 4788b4f..2e7b30d 100644 --- a/src/lib/formats/sorc_dsk.cpp +++ b/src/lib/formats/sorc_dsk.cpp @@ -31,20 +31,22 @@ static int sorc_get_tracks_per_disk(floppy_image_legacy *floppy) static uint64_t sorc_translate_offset(floppy_image_legacy *floppy, int track, int head, int sector) { - return 270 * ((16 * uint64_t(track)) + sector); + return 270*(16*track+sector); } static floperr_t get_offset(floppy_image_legacy *floppy, int head, int track, int sector, bool sector_is_index, uint64_t *offset) { + uint64_t offs; /* translate the sector to a raw sector */ if (!sector_is_index) + { sector -= 1; - + } /* check to see if we are out of range */ if ((head != 0) || (track < 0) || (track >= 77) || (sector < 0) || (sector >= 16)) return FLOPPY_ERROR_SEEKERROR; - uint64_t offs = sorc_translate_offset(floppy, track, head, sector); + offs = sorc_translate_offset(floppy, track, head, sector); if (offset) *offset = offs; return FLOPPY_ERROR_SUCCESS; diff --git a/src/lib/formats/tzx_cas.cpp b/src/lib/formats/tzx_cas.cpp index 7772fd7..171eeda 100644 --- a/src/lib/formats/tzx_cas.cpp +++ b/src/lib/formats/tzx_cas.cpp @@ -570,7 +570,7 @@ static int tzx_cas_do_work( int16_t **buffer ) if (pause_time == 0) { /* pause = 0 is used to let an emulator automagically stop the tape - in MAME we do not do that, so we insert a 5 second pause. */ + in MESS we do not do that, so we insert a 5 second pause. */ pause_time = 5000; } size += tzx_cas_handle_block(buffer, cur_block, pause_time, 0, 0, 0, 0, 0, 0, 0, 0); diff --git a/src/lib/formats/wavfile.cpp b/src/lib/formats/wavfile.cpp index e410c7f..95b6d8b 100644 --- a/src/lib/formats/wavfile.cpp +++ b/src/lib/formats/wavfile.cpp @@ -252,36 +252,41 @@ const cassette_image::Format cassette_image::wavfile_format = This code has already identified some rounding errors *********************************************************************/ -[[maybe_unused]] void wavfile_testload(const char *fname) +#ifdef UNUSED_FUNCTION +void wavfile_testload(const char *fname) { - FILE *f = fopen(fname, "rb"); + cassette_image *cassette; + FILE *f; + long offset; + int freq, samples, i; + int32_t cassamp; + int16_t wavsamp; + + f = fopen(fname, "rb"); if (!f) return; - cassette_image::ptr cassette; - if (cassette_image::open(util::stdio_read_write_noclose(f, 0x00), &cassette_image::wavfile_format, cassette_image::FLAG_READONLY, cassette) != cassette_image::error::SUCCESS) + if (cassette_open(f, &stdio_ioprocs, &wavfile_format, cassette_image::FLAG_READONLY, &cassette)) { fclose(f); return; } - constexpr long offset = 44; - constexpr int freq = 44100; - constexpr int samples = 5667062; + offset = 44; + freq = 44100; + samples = 5667062; - for (int i = 0; i < samples; i++) + for (i = 0; i < samples; i++) { - int32_t cassamp; - cassette->get_sample(0, i / double(freq), 0.0, &cassamp); + cassette_get_sample(cassette, 0, i / (double) freq, 0.0, &cassamp); - int16_t wavsamp; fseek(f, offset + i * 2, SEEK_SET); fread(&wavsamp, 1, 2, f); - - assert(cassamp == (uint32_t(wavsamp) << 16)); + assert(cassamp == (((uint32_t) wavsamp) << 16)); } - cassette.reset(); + cassette_close(cassette); fclose(f); } +#endif diff --git a/src/osd/modules/opengl/gl_shader_mgr.cpp b/src/osd/modules/opengl/gl_shader_mgr.cpp index ea09a7f..a4f01bc 100644 --- a/src/osd/modules/opengl/gl_shader_mgr.cpp +++ b/src/osd/modules/opengl/gl_shader_mgr.cpp @@ -161,33 +161,35 @@ glsl_shader_info *glsl_shader_init(osd_gl_context *gl_ctx) #endif } if (err) return nullptr; - return new glsl_shader_info{ 0 }; + return (glsl_shader_info *) malloc(sizeof(glsl_shader_info *)); } int glsl_shader_free(glsl_shader_info *shinfo) { + int i,j; + pfn_glUseProgramObjectARB(0); // back to fixed function pipeline glFinish(); - for (int i=0; i(SDLMAME_SOUND_LOG); @@ -349,8 +350,8 @@ int sound_sdl::init(const osd_options &options) } osd_printf_verbose("Audio: Start initialization\n"); - char const *const audio_driver = SDL_GetCurrentAudioDriver(); - osd_printf_verbose("Audio: Driver is %s\n", audio_driver ? audio_driver : "not initialized"); + strncpy(audio_driver, SDL_GetCurrentAudioDriver(), sizeof(audio_driver)); + osd_printf_verbose("Audio: Driver is %s\n", audio_driver); sdl_xfer_samples = SDL_XFER_SAMPLES; stream_in_initialized = 0; diff --git a/src/tools/aueffectutil-Info.plist b/src/tools/aueffectutil-Info.plist new file mode 100644 index 0000000..e509415 --- /dev/null +++ b/src/tools/aueffectutil-Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDisplayName + AUEffectUtil + CFBundleDocumentTypes + + + CFBundleTypeExtensions + + aueffect + + CFBundleTypeName + AUEffect + CFBundleTypeOSTypes + + CFBundleTypeRole + Editor + NSDocumentClass + AUEffectDocument + + + CFBundleTypeExtensions + + aupreset + + CFBundleTypeName + AudioUnit Preset + CFBundleTypeOSTypes + + CFBundleTypeRole + Editor + NSDocumentClass + AUEffectDocument + + + CFBundleIdentifier + org.mamedev.aueffectutil + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + AUEffectUtil + + diff --git a/src/tools/aueffectutil.mm b/src/tools/aueffectutil.mm new file mode 100644 index 0000000..661800a --- /dev/null +++ b/src/tools/aueffectutil.mm @@ -0,0 +1,1076 @@ +// license:BSD-3-Clause +// copyright-holders:Vas Crabb +#import +#import +#import +#import +#import +#import +#import +#import +#import + +#include +#include + +#include + + +#ifdef MAC_OS_X_VERSION_MAX_ALLOWED + +#if MAC_OS_X_VERSION_MAX_ALLOWED < 1060 + +typedef ComponentDescription AudioComponentDescription; + +@protocol NSApplicationDelegate +@end + +@protocol NSWindowDelegate +@end + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED < 1060 + +#endif // MAC_OS_X_VERSION_MAX_ALLOWED + + +struct EffectInfo +{ + Component component; + OSType type; + OSType subtype; + OSType manufacturer; +}; + + +static NSString *const AUEffectUtilErrorDomain = @"AUEffectUtilErrorDomain"; + +static NSString *const AUEffectDocumentType = @"AUEffect"; +static NSString *const AUPresetDocumentType = @"AudioUnit Preset"; + +static NSString *const ComponentTypeKey = @"ComponentType"; +static NSString *const ComponentSubTypeKey = @"ComponentSubType"; +static NSString *const ComponentManufacturerKey = @"ComponentManufacturer"; +static NSString *const ClassInfoKey = @"ClassInfo"; +static NSString *const ForceGenericViewKey = @"ForceGenericView"; +static NSString *const WindowFrameKey = @"WindowFrame"; + + +static void UpdateChangeCountCallback( + void *userData, + void *object, + AudioUnitEvent const *inEvent, + UInt64 inEventHostTime, + AudioUnitParameterValue inParameterValue) +{ + [(NSDocument *)userData updateChangeCount:NSChangeDone]; +} + + +@interface AUEffectDocument : NSDocument +{ + IBOutlet NSWindow *window; + IBOutlet NSButton *genericViewButton; + IBOutlet NSPopUpButton *presetButton; + NSView *view; + NSSize headerSize; + CFArrayRef presets; + AUParameterListenerRef listener; + BOOL forceGenericView; + NSString *restoreFrame; + + AudioComponentDescription description; + AUGraph graph; + AUNode outputNode, sourceNode, effectNode; + AudioUnit outputUnit, sourceUnit, effectUnit; +} + +- (void)dealloc; + +- (void)makeWindowControllers; +- (BOOL)readFromData:(NSData *)data ofType:(NSString *)type error:(NSError **)error; +- (NSData *)dataOfType:(NSString *)type error:(NSError **)error; + +- (IBAction)toggleGenericView:(id)sender; +- (IBAction)loadPreset:(id)sender; + +- (void)viewFrameDidChange:(NSNotification *)notification; + +@end + +@implementation AUEffectDocument + +- (void)loadEffectUI { + if ((0 == effectNode) || !window) + return; + + BOOL customViewValid = NO; + OSStatus status; + UInt32 uiDescSize; + AudioUnitCocoaViewInfo *viewInfo; + status = AudioUnitGetPropertyInfo( + effectUnit, + kAudioUnitProperty_CocoaUI, + kAudioUnitScope_Global, + 0, + &uiDescSize, + NULL); + UInt32 const uiClassCount = 1 + ((uiDescSize - sizeof(*viewInfo)) / sizeof(viewInfo->mCocoaAUViewClass[0])); + if ((noErr == status) && (0 < uiClassCount)) + { + viewInfo = (AudioUnitCocoaViewInfo *)malloc(uiDescSize); + status = AudioUnitGetProperty(effectUnit, + kAudioUnitProperty_CocoaUI, + kAudioUnitScope_Global, + 0, + viewInfo, + &uiDescSize); + if (noErr == status) + { + NSBundle *const bundle = [NSBundle bundleWithPath:[(NSURL *)viewInfo->mCocoaAUViewBundleLocation path]]; + Class const viewClass = [bundle classNamed:(NSString *)viewInfo->mCocoaAUViewClass[0]]; + if (viewClass + && [viewClass conformsToProtocol:@protocol(AUCocoaUIBase)] + && [viewClass instancesRespondToSelector:@selector(uiViewForAudioUnit:withSize:)]) + { + customViewValid = YES; + if (!forceGenericView) + { + id const factory = [[viewClass alloc] init]; + view = [factory uiViewForAudioUnit:effectUnit + withSize:[[window contentView] bounds].size]; + [factory release]; + } + } + CFRelease(viewInfo->mCocoaAUViewBundleLocation); + for (UInt32 i = 0; i < uiClassCount; i++) + CFRelease(viewInfo->mCocoaAUViewClass[i]); + } + free(viewInfo); + } + if (!view) + { + view = [[[AUGenericView alloc] initWithAudioUnit:effectUnit] autorelease]; + [(AUGenericView *)view setShowsExpertParameters:YES]; + } + + [view setAutoresizingMask:NSViewNotSizable]; + [view setFrameOrigin:NSMakePoint(0, 0)]; + NSRect const oldFrame = [window frame]; + NSRect const desired = [window frameRectForContentRect:[view frame]]; + NSRect const newFrame = NSMakeRect(oldFrame.origin.x, + oldFrame.origin.y + oldFrame.size.height - headerSize.height - desired.size.height, + desired.size.width, + headerSize.height + desired.size.height); + [window setFrame:newFrame display:YES animate:NO]; + [[window contentView] addSubview:view]; + [view setPostsFrameChangedNotifications:YES]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(viewFrameDidChange:) + name:NSViewFrameDidChangeNotification + object:view]; + + [genericViewButton setEnabled:customViewValid]; + if (!customViewValid) + { + forceGenericView = YES; + [genericViewButton setState:NSOnState]; + } + + CFIndex const presetCount = (NULL != presets) ? CFArrayGetCount(presets) : 0; + [presetButton setEnabled:(0 < presetCount)]; + while (1 < [presetButton numberOfItems]) + [presetButton removeItemAtIndex:1]; + for (CFIndex i = 0; i < presetCount; i++) + { + AUPreset const *preset = (AUPreset const*)CFArrayGetValueAtIndex(presets, i); + NSMenuItem const *item = [[presetButton menu] addItemWithTitle:(NSString *)preset->presetName + action:@selector(loadPreset:) + keyEquivalent:@""]; + [item setTarget:self]; + [item setTag:i]; + } +} + +- (id)init { + if (!(self = [super init])) + return nil; + + window = nil; + genericViewButton = nil; + presetButton = nil; + view = nil; + presets = NULL; + listener = NULL; + forceGenericView = NO; + restoreFrame = nil; + + description.componentType = description.componentSubType = description.componentManufacturer = 0; + description.componentFlags = description.componentFlagsMask = 0; + graph = NULL; + outputNode = sourceNode = effectNode = 0; + + AudioComponentDescription const outputDesc = { kAudioUnitType_Output, + kAudioUnitSubType_DefaultOutput, + kAudioUnitManufacturer_Apple, + 0, + 0, }; + AudioComponentDescription const sourceDesc = { kAudioUnitType_Generator, + kAudioUnitSubType_AudioFilePlayer, + kAudioUnitManufacturer_Apple, + 0, + 0, }; + if ((noErr != NewAUGraph(&graph)) + || (noErr != AUGraphAddNode(graph, &outputDesc, &outputNode)) + || (noErr != AUGraphAddNode(graph, &sourceDesc, &sourceNode)) + || (noErr != AUGraphOpen(graph)) + || (noErr != AUGraphNodeInfo(graph, outputNode, NULL, &outputUnit)) + || (noErr != AUGraphNodeInfo(graph, sourceNode, NULL, &sourceUnit)) + || (noErr != AUGraphInitialize(graph))) + { + [self release]; + return nil; + } + + return self; +} + +- (void)dealloc { + if (presets) + CFRelease(presets); + + if (listener) + AUListenerDispose(listener); + + if (restoreFrame) + [restoreFrame release]; + + if (graph) + { + AUGraphClose(graph); + DisposeAUGraph(graph); + } + + [super dealloc]; +} + +- (void)makeWindowControllers { + genericViewButton = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 18)]; + [genericViewButton setAutoresizingMask:NSViewNotSizable]; + [[genericViewButton cell] setControlSize:NSSmallControlSize]; + [genericViewButton setButtonType:NSSwitchButton]; + [genericViewButton setBordered:NO]; + [genericViewButton setAllowsMixedState:NO]; + [genericViewButton setState:(forceGenericView ? NSOnState : NSOffState)]; + [genericViewButton setTitle:@"Use generic editor view"]; + [genericViewButton setAction:@selector(toggleGenericView:)]; + [genericViewButton setTarget:self]; + [genericViewButton sizeToFit]; + + presetButton = [[NSPopUpButton alloc] initWithFrame:NSMakeRect(0, 0, 100, 22) pullsDown:YES]; + [presetButton setAutoresizingMask:NSViewNotSizable]; + [[presetButton cell] setControlSize:NSSmallControlSize]; + [[presetButton cell] setFont:[NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]]; + [presetButton setTitle:@"Load preset"]; + [[[presetButton menu] addItemWithTitle:@"Load preset" action:NULL keyEquivalent:@""] setHidden:YES]; + [presetButton sizeToFit]; + + CGFloat const controlWidth = MAX(NSWidth([genericViewButton frame]), NSWidth([presetButton frame])); + NSRect const presetFrame = NSMakeRect( + 17, + 8, + controlWidth, + NSHeight([presetButton frame])); + NSRect const genericViewFrame = NSMakeRect( + 17, + NSMaxY(presetFrame) + 9, + controlWidth, + NSHeight([genericViewButton frame])); + [genericViewButton setFrame:genericViewFrame]; + [presetButton setFrame:presetFrame]; + + headerSize = NSMakeSize((2 * 17) + controlWidth, 18 + NSMaxY(genericViewFrame)); + NSView *const container = [[NSView alloc] initWithFrame:NSMakeRect(0, 0, headerSize.width, headerSize.height)]; + [container setAutoresizingMask:(NSViewMinXMargin | NSViewMaxXMargin | NSViewMinYMargin)]; + [container addSubview:genericViewButton]; + [genericViewButton release]; + [container addSubview:presetButton]; + [presetButton release]; + + window = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, headerSize.width, headerSize.height) + styleMask:(NSTitledWindowMask | + NSClosableWindowMask | + NSMiniaturizableWindowMask) + backing:NSBackingStoreBuffered + defer:YES]; + [window setReleasedWhenClosed:NO]; + [window setDelegate:self]; + [window setTitle:@"Effect"]; + [[window contentView] addSubview:container]; + [container release]; + [self setWindow:window]; + + NSWindowController *const controller = [[NSWindowController alloc] initWithWindow:window]; + [self addWindowController:controller]; + [controller release]; + [window release]; + + [self loadEffectUI]; + if (restoreFrame) + { + [window setFrameFromString:restoreFrame]; + } + else + { + NSRect const available = [[NSScreen mainScreen] visibleFrame]; + NSRect frame = [window frame]; + frame.origin.x = (NSWidth(available) - NSWidth(frame)) / 4; + frame.origin.y = (NSHeight(available) - NSHeight(frame)) * 3 / 4; + [window setFrame:frame display:YES animate:NO]; + } +} + +- (BOOL)readFromData:(NSData *)data ofType:(NSString *)type error:(NSError **)error { + OSStatus status; + UInt32 propertySize; + + BOOL const hasWrapper = [type isEqualToString:AUEffectDocumentType]; + if (!hasWrapper && ![type isEqualToString:AUPresetDocumentType]) + { + if (error) + { + NSString *const message = [NSString stringWithFormat:@"Unsupported document type %@", type]; + NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, + nil]; + *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; + } + return NO; + } + + NSString *errDesc = nil; + id const desc = [NSPropertyListSerialization propertyListFromData:data + mutabilityOption:0 + format:NULL + errorDescription:&errDesc]; + if (!desc || ![desc isKindOfClass:[NSDictionary class]] || errDesc) + { + if (error) + { + NSString *const message = [NSString stringWithFormat:@"Error in file format (%@)", errDesc]; + NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, + nil]; + *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; + } + if (errDesc) + [errDesc release]; + return NO; + } + + id const typeValue = [desc objectForKey:(hasWrapper ? ComponentTypeKey : (NSString *)CFSTR(kAUPresetTypeKey))]; + id const subtypeValue = [desc objectForKey:(hasWrapper ? ComponentSubTypeKey : (NSString *)CFSTR(kAUPresetSubtypeKey))]; + id const manufacturerValue = [desc objectForKey:(hasWrapper ? ComponentManufacturerKey : (NSString *)CFSTR(kAUPresetManufacturerKey))]; + if (!typeValue || ![typeValue isKindOfClass:[NSNumber class]] + || !subtypeValue || ![subtypeValue isKindOfClass:[NSNumber class]] + || !manufacturerValue || ![manufacturerValue isKindOfClass:[NSNumber class]] + || ([typeValue unsignedLongValue] != kAudioUnitType_Effect)) + { + if (error) + { + NSString *const message = @"Error in effect description file format"; + NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, + nil]; + *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; + } + return NO; + } + + if (presets) + { + CFRelease(presets); + presets = NULL; + } + if (listener) + { + AUListenerDispose(listener); + listener = NULL; + } + if (view) + { + [[NSNotificationCenter defaultCenter] removeObserver:self + name:NSViewFrameDidChangeNotification + object:nil]; + [view removeFromSuperview]; + view = nil; + } + if (0 != effectNode) + { + view = nil; + AUGraphRemoveNode(graph, effectNode); + effectNode = 0; + } + + description.componentType = [typeValue longValue]; + description.componentSubType = [subtypeValue longValue]; + description.componentManufacturer = [manufacturerValue longValue]; + status = noErr; + status = AUGraphClearConnections(graph); + if (noErr == status) + status = AUGraphAddNode(graph, &description, &effectNode); + if (noErr == status) + status = AUGraphNodeInfo(graph, effectNode, NULL, &effectUnit); + if (noErr == status) + status = AUGraphConnectNodeInput(graph, sourceNode, 0, effectNode, 0); + if (noErr == status) + status = AUGraphConnectNodeInput(graph, effectNode, 0, outputNode, 0); + if (noErr == status) + status = AUGraphUpdate(graph, NULL); + if (noErr != status) + { + if (error) + { + NSString * const message = @"Error encountered while configuring AudioUnit graph"; + NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; + NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, + underlying, NSUnderlyingErrorKey, + nil]; + *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; + } + return NO; + } + + CFPropertyListRef const classInfo = (CFPropertyListRef)(hasWrapper ? [desc objectForKey:ClassInfoKey] : desc); + if (classInfo) + { + AudioUnitParameter change = { effectUnit, kAUParameterListener_AnyParameter, 0, 0 }; + status = AudioUnitSetProperty( + effectUnit, + kAudioUnitProperty_ClassInfo, + kAudioUnitScope_Global, + 0, + &classInfo, + sizeof(classInfo)); + if (noErr == status) + status = AUParameterListenerNotify(NULL, NULL, &change); + if (noErr != status) + { + if (error) + { + NSString * const message = @"Error configuring effect"; + NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; + NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, + underlying, NSUnderlyingErrorKey, + nil]; + *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; + } + return NO; + } + } + + propertySize = 0; + status = AudioUnitGetPropertyInfo( + effectUnit, + kAudioUnitProperty_ParameterList, + kAudioUnitScope_Global, + 0, + &propertySize, + NULL); + if (noErr != status) + { + if (error) + { + NSString * const message = @"Error getting effect parameters"; + NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; + NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, + underlying, NSUnderlyingErrorKey, + nil]; + *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; + } + return NO; + } + UInt32 const paramCount = propertySize / sizeof(AudioUnitParameterID); + if (0U < paramCount) + { + status = AUEventListenerCreate( + UpdateChangeCountCallback, + self, + CFRunLoopGetCurrent(), + kCFRunLoopDefaultMode, + 0.05, + 0.05, + &listener); + if (noErr != status) + { + if (error) + { + NSString * const message = @"Error creating AudioUnit event listener"; + NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; + NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, + underlying, NSUnderlyingErrorKey, + nil]; + *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; + } + return NO; + } + AudioUnitParameterID *const params = (AudioUnitParameterID *)malloc(propertySize); + AudioUnitGetProperty( + effectUnit, + kAudioUnitProperty_ParameterList, + kAudioUnitScope_Global, + 0, + params, + &propertySize); + if (noErr != status) + { + free(params); + if (error) + { + NSString * const message = @"Error getting effect parameters"; + NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; + NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, + underlying, NSUnderlyingErrorKey, + nil]; + *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; + } + return NO; + } + for (UInt32 i = 0; (i < paramCount) && (noErr == status); i++) + { + AudioUnitEvent event; + event.mEventType = kAudioUnitEvent_ParameterValueChange; + event.mArgument.mParameter.mAudioUnit = effectUnit; + event.mArgument.mParameter.mParameterID = params[i]; + event.mArgument.mParameter.mScope = kAudioUnitScope_Global; + event.mArgument.mParameter.mElement = 0; + status = AUEventListenerAddEventType(listener, self, &event); + } + free(params); + if (noErr != status) + { + free(params); + if (error) + { + NSString * const message = @"Error getting effect parameters"; + NSError *const underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; + NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, + underlying, NSUnderlyingErrorKey, + nil]; + *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; + } + return NO; + } + } + + propertySize = sizeof(presets); + status = AudioUnitGetProperty( + effectUnit, + kAudioUnitProperty_FactoryPresets, + kAudioUnitScope_Global, + 0, + &presets, + &propertySize); + if ((noErr != status) && presets) + { + CFRelease(presets); + presets = NULL; + } + + if (hasWrapper) + { + if ([desc objectForKey:ForceGenericViewKey] + && [[desc objectForKey:ForceGenericViewKey] respondsToSelector:@selector(boolValue)]) + { + forceGenericView = [[desc objectForKey:ForceGenericViewKey] boolValue]; + [genericViewButton setState:(forceGenericView ? NSOnState : NSOffState)]; + } + if ([desc objectForKey:WindowFrameKey] + && [[desc objectForKey:WindowFrameKey] isKindOfClass:[NSString class]]) + { + if (restoreFrame) + [restoreFrame release]; + restoreFrame = [[NSString alloc] initWithString:[desc objectForKey:WindowFrameKey]]; + } + } + + [self loadEffectUI]; + + return YES; +} + +- (NSData *)dataOfType:(NSString *)type error:(NSError **)error { + CFPropertyListRef classInfo; + UInt32 infoSize = sizeof(classInfo); + OSStatus const status = AudioUnitGetProperty( + effectUnit, + kAudioUnitProperty_ClassInfo, + kAudioUnitScope_Global, + 0, + &classInfo, + &infoSize); + if (noErr != status) + { + if (NULL != error) + { + NSString const *message = @"Error getting effect settings"; + NSError const *underlying = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil]; + NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, + underlying, NSUnderlyingErrorKey, + nil]; + *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; + } + return nil; + } + NSDictionary *desc = nil; + if ([type isEqualToString:AUEffectDocumentType]) + { + NSNumber const *typeVal = [NSNumber numberWithUnsignedLong:description.componentType]; + NSNumber const *subtypeVal = [NSNumber numberWithUnsignedLong:description.componentSubType]; + NSNumber const *manufacturerVal = [NSNumber numberWithUnsignedLong:description.componentManufacturer]; + NSNumber const *forceGenericViewVal = [NSNumber numberWithBool:forceGenericView]; + NSString const *windowFrameVal = [window stringWithSavedFrame]; + desc = [NSDictionary dictionaryWithObjectsAndKeys:typeVal, ComponentTypeKey, + subtypeVal, ComponentSubTypeKey, + manufacturerVal, ComponentManufacturerKey, + classInfo, ClassInfoKey, + forceGenericViewVal, ForceGenericViewKey, + windowFrameVal, WindowFrameKey, + nil]; + } + else if ([type isEqualToString:AUPresetDocumentType]) + { + desc = [NSDictionary dictionaryWithDictionary:(NSDictionary *)classInfo]; + } + CFRelease(classInfo); + if (!desc) + { + NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:@"Unsupported document type", NSLocalizedDescriptionKey, + nil]; + *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; + return nil; + } + + NSString *errDesc = nil; + NSData *const data = [NSPropertyListSerialization dataFromPropertyList:desc + format:NSPropertyListXMLFormat_v1_0 + errorDescription:&errDesc]; + if (!data || errDesc) + { + if (error) + { + NSString *message; + if (errDesc) + message = [NSString stringWithFormat:@"Error serialising effect settings: %@", errDesc]; + else + message = @"Error serialising effect settings"; + NSDictionary *const info = [NSDictionary dictionaryWithObjectsAndKeys:message, NSLocalizedDescriptionKey, + nil]; + *error = [NSError errorWithDomain:AUEffectUtilErrorDomain code:0 userInfo:info]; + } + if (errDesc) + [errDesc release]; + return nil; + } + return data; +} + +- (IBAction)toggleGenericView:(id)sender { + forceGenericView = (NSOnState == [sender state]); + if (view) + { + [[NSNotificationCenter defaultCenter] removeObserver:self + name:NSViewFrameDidChangeNotification + object:nil]; + [view removeFromSuperview]; + view = nil; + } + if (0 != effectNode) + [self loadEffectUI]; +} + +- (IBAction)loadPreset:(id)sender { + OSStatus status; + + CFIndex const idx = [sender tag]; + CFIndex const total = (NULL == presets) ? 0 : CFArrayGetCount(presets); + if ((0 > idx) || (total <= idx)) + { + NSAlert const *alert = [[NSAlert alloc] init]; + [alert setMessageText:@"Invalid preset selected"]; + [alert setInformativeText:[NSString stringWithFormat:@"Tried to select preset %ld of %ld", + (long)idx + 1, + (long)total]]; + [alert beginSheetModalForWindow:window modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; + return; + } + + AUPreset const *preset = (AUPreset const *)CFArrayGetValueAtIndex(presets, idx); + status = AudioUnitSetProperty( + effectUnit, + kAudioUnitProperty_PresentPreset, + kAudioUnitScope_Global, + 0, + preset, + sizeof(AUPreset)); + if (noErr != status) + { + NSAlert const *alert = [[NSAlert alloc] init]; + [alert setMessageText:[NSString stringWithFormat:@"Error loading preset %@", preset->presetName]]; + [alert setInformativeText:[NSString stringWithFormat:@"Error %ld encountered while setting AudioUnit property", + (long)status]]; + [alert beginSheetModalForWindow:window modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; + return; + } + + AudioUnitParameter change = { effectUnit, kAUParameterListener_AnyParameter, 0, 0 }; + status = AUParameterListenerNotify(NULL, NULL, &change); + if (noErr != status) + { + NSAlert const *alert = [[NSAlert alloc] init]; + [alert setMessageText:[NSString stringWithFormat:@"Error notifying of parameter changes for preset %@", + preset->presetName]]; + [alert setInformativeText:[NSString stringWithFormat:@"Error %ld encountered while sending notification", + (long)status]]; + [alert beginSheetModalForWindow:window modalDelegate:nil didEndSelector:NULL contextInfo:NULL]; + return; + } +} + +- (void)viewFrameDidChange:(NSNotification *)notification { + NSRect const oldFrame = [window frame]; + NSRect const desired = [window frameRectForContentRect:[[notification object] frame]]; + NSRect const newFrame = NSMakeRect( + oldFrame.origin.x, + oldFrame.origin.y + oldFrame.size.height - headerSize.height- desired.size.height, + desired.size.width, + headerSize.height + desired.size.height); + [window setFrame:newFrame display:YES animate:NO]; +} + +@end + + +@interface AUEffectUtilAppDelegate : NSObject +{ + EffectInfo *effects; + + IBOutlet NSMenu *newEffectMenu; +} + +- (id)init; +- (void)dealloc; + +- (IBAction)newEffect:(id)sender; + +- (void)applicationWillFinishLaunching:(NSNotification *)notification; +- (void)applicationDidFinishLaunching:(NSNotification *)notification; +- (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender; + +@end + +@implementation AUEffectUtilAppDelegate + +- (void)appendApplicationMenu:(NSMenu *)parent { + NSMenuItem *item; + NSMenu *submenu; + NSString *const appName = [(NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()) objectForKey:@"CFBundleName"]; + + NSMenu *const menu = [[NSMenu alloc] initWithTitle:@"Application"]; + item = [parent addItemWithTitle:@"Application" action:NULL keyEquivalent:@""]; + [parent setSubmenu:menu forItem:item]; + [menu release]; + [menu setValue:@"NSAppleMenu" forKey:@"name"]; + + item = [menu addItemWithTitle:[NSString stringWithFormat:@"About %@", appName] action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; + [item setTarget:NSApp]; + + [menu addItem:[NSMenuItem separatorItem]]; + + item = [menu addItemWithTitle:@"Services" action:NULL keyEquivalent:@""]; + submenu = [[NSMenu alloc] initWithTitle:@"Services"]; + [menu setSubmenu:submenu forItem:item]; + [submenu release]; + [NSApp setServicesMenu:submenu]; + + [menu addItem:[NSMenuItem separatorItem]]; + + item = [menu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", appName] action:@selector(hide:) keyEquivalent:@"h"]; + item = [menu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; + [item setKeyEquivalentModifierMask:NSCommandKeyMask | NSAlternateKeyMask]; + item = [menu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; + + [menu addItem:[NSMenuItem separatorItem]]; + + item = [menu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", appName] action:@selector(terminate:) keyEquivalent:@"q"]; + [item setTarget:NSApp]; +} + +- (void)appendFileMenu:(NSMenu *)parent { + NSMenuItem *item; + + NSMenu *const menu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"File"]; + item = [parent addItemWithTitle:@"File" action:NULL keyEquivalent:@""]; + [parent setSubmenu:menu forItem:item]; + [menu release]; + + item = [menu addItemWithTitle:@"New" action:NULL keyEquivalent:@""]; + newEffectMenu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"New"]; + [menu setSubmenu:newEffectMenu forItem:item]; + [newEffectMenu release]; + item = [menu addItemWithTitle:[NSString stringWithFormat:@"Open%C", (unichar)0x2026] action:@selector(openDocument:) keyEquivalent:@"o"]; + + [menu addItem:[NSMenuItem separatorItem]]; + + item = [menu addItemWithTitle:@"Close" action:@selector(performClose:) keyEquivalent:@"w"]; + item = [menu addItemWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:@"s"]; + item = [menu addItemWithTitle:[NSString stringWithFormat:@"Save As%C", (unichar)0x2026] action:@selector(saveDocumentAs:) keyEquivalent:@"S"]; + item = [menu addItemWithTitle:@"Save All" action:@selector(saveAllDocuments:) keyEquivalent:@""]; + item = [menu addItemWithTitle:@"Revert to Saved" action:@selector(revertDocumentToSaved:) keyEquivalent:@"u"]; +} + +- (void)appendEditMenu:(NSMenu *)parent { + NSMenuItem *item; + + NSMenu *const menu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"Edit"]; + item = [parent addItemWithTitle:@"Edit" action:NULL keyEquivalent:@""]; + [parent setSubmenu:menu forItem:item]; + [menu release]; + + item = [menu addItemWithTitle:@"Undo" action:@selector(undo:) keyEquivalent:@"z"]; + item = [menu addItemWithTitle:@"Redo" action:@selector(redo:) keyEquivalent:@"Z"]; + + [menu addItem:[NSMenuItem separatorItem]]; + + item = [menu addItemWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"x"]; + item = [menu addItemWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"c"]; + item = [menu addItemWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"v"]; + item = [menu addItemWithTitle:@"Delete" action:@selector(delete:) keyEquivalent:@""]; + item = [menu addItemWithTitle:@"Select All" action:@selector(selectAll:) keyEquivalent:@"a"]; +} + +- (void)appendWindowMenu:(NSMenu *)parent { + NSMenuItem *item; + + NSMenu *const menu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"Window"]; + item = [parent addItemWithTitle:@"Window" action:NULL keyEquivalent:@""]; + [parent setSubmenu:menu forItem:item]; + [menu release]; + [NSApp setWindowsMenu:menu]; + + item = [menu addItemWithTitle:@"Minimize" action:@selector(performMinimize:) keyEquivalent:@"m"]; + item = [menu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""]; + + [menu addItem:[NSMenuItem separatorItem]]; + + item = [menu addItemWithTitle:@"Bring All to Front" action:@selector(arrangeInFront:) keyEquivalent:@""]; +} + +- (void)appendHelpMenu:(NSMenu *)parent { + NSMenuItem *item; + NSString *const appName = [(NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle()) objectForKey:@"CFBundleName"]; + + NSMenu *const menu = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"Help"]; + item = [parent addItemWithTitle:@"Help" action:NULL keyEquivalent:@""]; + [parent setSubmenu:menu forItem:item]; + [menu release]; + [menu setValue:@"NSHelpMenu" forKey:@"name"]; + if ([NSApp respondsToSelector:@selector(setHelpMenu:)]) + [NSApp performSelector:@selector(setHelpMenu:) withObject:menu]; + + item = [menu addItemWithTitle:[NSString stringWithFormat:@"%@ Help", appName] action:@selector(showHelp:) keyEquivalent:@"?"]; +} + +- (id)init { + if (!(self = [super init])) + return nil; + effects = NULL; + return self; +} + +- (void)dealloc { + if (effects) + free(effects); + [super dealloc]; +} + +- (IBAction)newEffect:(id)sender { + int const index = [sender tag]; + if ((0 > index) || (0 == effects[index].component)) + { + NSAlert *const alert = [[NSAlert alloc] init]; + [alert setAlertStyle:NSWarningAlertStyle]; + [alert setMessageText:@"Invalid effect component"]; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + [alert release]; + return; + } + + NSNumber *const typeValue = [NSNumber numberWithUnsignedLong:effects[index].type]; + NSNumber *const subtypeValue = [NSNumber numberWithUnsignedLong:effects[index].subtype]; + NSNumber *const manufacturerValue = [NSNumber numberWithUnsignedLong:effects[index].manufacturer]; + NSDictionary *const desc = [NSDictionary dictionaryWithObjectsAndKeys:typeValue, ComponentTypeKey, + subtypeValue, ComponentSubTypeKey, + manufacturerValue, ComponentManufacturerKey, + nil]; + NSString *errDesc = nil; + NSData *const data = [NSPropertyListSerialization dataFromPropertyList:desc + format:NSPropertyListXMLFormat_v1_0 + errorDescription:&errDesc]; + if (!data || errDesc) + { + NSAlert *const alert = [[NSAlert alloc] init]; + [alert setAlertStyle:NSWarningAlertStyle]; + [alert setMessageText:@"Error serialising properties for new effect"]; + if (errDesc) + [alert setInformativeText:[errDesc autorelease]]; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + [alert release]; + return; + } + + NSError *err = nil; + AUEffectDocument *const document = [[AUEffectDocument alloc] init]; + if (!document || ![document readFromData:data ofType:AUEffectDocumentType error:&err]) + { + [document release]; + if (err) + { + [[NSAlert alertWithError:err] runModal]; + } + else + { + NSAlert *const alert = [[NSAlert alloc] init]; + [alert setAlertStyle:NSWarningAlertStyle]; + [alert setMessageText:@"Error creating new effect document"]; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + [alert release]; + } + return; + } + + [document makeWindowControllers]; + [document showWindows]; + [[NSDocumentController sharedDocumentController] addDocument:document]; + [document release]; +} + +- (void)applicationWillFinishLaunching:(NSNotification *)notification { + NSMenu *const menubar = [[NSMenu allocWithZone:[NSMenu zone]] initWithTitle:@"MainMenu"]; + [NSApp setMainMenu:menubar]; + [menubar release]; + [self appendApplicationMenu:menubar]; + [self appendFileMenu:menubar]; + [self appendEditMenu:menubar]; + [self appendWindowMenu:menubar]; + [self appendHelpMenu:menubar]; + + ProcessSerialNumber const serial = { 0, kCurrentProcess }; + OSStatus const status = TransformProcessType(&serial, kProcessTransformToForegroundApplication); + if (noErr != status) + { + NSLog(@"Error transforming to foreground application (%ld)", (long)status); + [NSApp terminate:self]; + } + else + { + [NSApp activateIgnoringOtherApps:YES]; + } +} + +- (void)applicationDidFinishLaunching:(NSNotification *)notification { + ComponentDescription effectFilter = { kAudioUnitType_Effect, 0, 0, 0, 0 }; + long const count = CountComponents(&effectFilter); + if (0 == count) + { + NSAlert *const alert = [[NSAlert alloc] init]; + [alert setAlertStyle:NSWarningAlertStyle]; + [alert setMessageText:@"No AudioUnit effects found"]; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + [alert release]; + } + + std::vector > failed; + effects = (EffectInfo *)malloc(count * sizeof(*effects)); + Component effect = FindNextComponent(0, &effectFilter); + for (long i = 0; (i < count) && (effect != 0); i++, effect = FindNextComponent(effect, &effectFilter)) + { + ComponentDescription effectDesc; + Handle const nameHandle = NewHandle(4); + OSStatus const err = GetComponentInfo(effect, &effectDesc, nameHandle, NULL, NULL); + if (noErr == err) + { + effects[i].component = effect; + effects[i].type = effectDesc.componentType; + effects[i].subtype = effectDesc.componentSubType; + effects[i].manufacturer = effectDesc.componentManufacturer; + HLock(nameHandle); + CFStringRef const name = CFStringCreateWithPascalString( + NULL, + (unsigned char const *)*nameHandle, + kCFStringEncodingMacRoman); + HUnlock(nameHandle); + NSMenuItem *const item = [newEffectMenu addItemWithTitle:(NSString *)name + action:@selector(newEffect:) + keyEquivalent:@""]; + [item setTag:i]; + [item setTarget:self]; + CFRelease(name); + } + else + { + effects[i].component = 0; + failed.push_back(std::make_pair(effect, err)); + } + DisposeHandle(nameHandle); + } + + if (!failed.empty()) + { + NSString *const message = [NSString stringWithFormat:@"Failed to get info for %lu effect%s", + (unsigned long)failed.size(), + ((1U == failed.size()) ? "" : "s")]; + NSMutableString *const detail = [NSMutableString stringWithCapacity:(16 * failed.size())]; + std::vector >::const_iterator it = failed.begin(); + [detail appendFormat:@"%lu (%ld)", (unsigned long)it->first, (long)it->second]; + ++it; + while (failed.end() != it) + { + [detail appendFormat:@", %lu (%ld)", (unsigned long)it->first, (long)it->second]; + ++it; + } + NSAlert *const alert = [[NSAlert alloc] init]; + [alert setAlertStyle:NSWarningAlertStyle]; + [alert setMessageText:message]; + [alert setInformativeText:[NSString stringWithString:detail]]; + [alert addButtonWithTitle:@"OK"]; + [alert runModal]; + [alert release]; + } +} + +- (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender { + return NO; +} + +@end + + +int main(int argc, char *argv[]) +{ + NSAutoreleasePool *pool; + + // Initialise NSApplication + pool = [[NSAutoreleasePool alloc] init]; + [NSApplication sharedApplication]; + AUEffectUtilAppDelegate *const delegate = [[AUEffectUtilAppDelegate alloc] init]; + [[NSApplication sharedApplication] setDelegate:delegate]; + [pool release]; + + // Let's go! + pool = [[NSAutoreleasePool alloc] init]; + [NSApp run]; + [delegate release]; + [pool release]; + return 0; +} diff --git a/src/tools/castool.cpp b/src/tools/castool.cpp new file mode 100644 index 0000000..b5fa9cf --- /dev/null +++ b/src/tools/castool.cpp @@ -0,0 +1,227 @@ +// license:BSD-3-Clause +// copyright-holders:Miodrag Milanovic +/*************************************************************************** + + main.c + + Castool command line front end + + 27/03/2009 Initial version by Miodrag Milanovic + +***************************************************************************/ + +#include "formats/a26_cas.h" +#include "formats/ace_tap.h" +#include "formats/adam_cas.h" +#include "formats/apf_apt.h" +#include "formats/atom_tap.h" +#include "formats/cbm_tap.h" +#include "formats/cgen_cas.h" +#include "formats/coco_cas.h" +#include "formats/csw_cas.h" +#include "formats/fm7_cas.h" +#include "formats/fmsx_cas.h" +#include "formats/gtp_cas.h" +#include "formats/hect_tap.h" +#include "formats/kc_cas.h" +#include "formats/kim1_cas.h" +#include "formats/lviv_lvt.h" +#include "formats/mz_cas.h" +#include "formats/orao_cas.h" +#include "formats/oric_tap.h" +#include "formats/p2000t_cas.h" +#include "formats/p6001_cas.h" +#include "formats/phc25_cas.h" +#include "formats/pmd_cas.h" +#include "formats/primoptp.h" +#include "formats/rk_cas.h" +#include "formats/sc3000_bit.h" +#include "formats/sol_cas.h" +#include "formats/sorc_cas.h" +#include "formats/sord_cas.h" +#include "formats/spc1000_cas.h" +#include "formats/svi_cas.h" +#include "formats/thom_cas.h" +#include "formats/trs_cas.h" +#include "formats/tvc_cas.h" +#include "formats/tzx_cas.h" +#include "formats/uef_cas.h" +#include "formats/vg5k_cas.h" +#include "formats/vt_cas.h" +#include "formats/x07_cas.h" +#include "formats/x1_tap.h" +#include "formats/zx81_p.h" + +#include "corestr.h" +#include "ioprocs.h" +#include "osdcomm.h" + +#include +#include +#include +#include +#include +#include + + +struct SupportedCassetteFormats +{ + const char *name; + const cassette_image::Format * const *formats; + const char *desc; +}; + +const struct SupportedCassetteFormats formats[] = { + {"a26", a26_cassette_formats ,"Atari 2600 SuperCharger"}, + {"apf", apf_cassette_formats ,"APF Imagination Machine"}, + {"atom", atom_cassette_formats ,"Acorn Atom"}, + {"bbc", bbc_cassette_formats ,"Acorn BBC & Electron"}, + {"cbm", cbm_cassette_formats ,"Commodore 8-bit series"}, + {"cdt", cdt_cassette_formats ,"Amstrad CPC"}, + {"cgenie", cgenie_cassette_formats ,"EACA Colour Genie"}, + {"coco", coco_cassette_formats ,"Tandy Radio Shack Color Computer"}, + {"csw", csw_cassette_formats ,"Compressed Square Wave"}, + {"ddp", coleco_adam_cassette_formats ,"Coleco ADAM"}, + {"fm7", fm7_cassette_formats ,"Fujitsu FM-7"}, + {"fmsx", fmsx_cassette_formats ,"MSX"}, + {"gtp", gtp_cassette_formats ,"Elektronika inzenjering Galaksija"}, + {"hector", hector_cassette_formats ,"Micronique Hector & Interact Family Computer"}, + {"jupiter", ace_cassette_formats ,"Jupiter Cantab Jupiter Ace"}, + {"kc85", kc_cassette_formats ,"VEB Mikroelektronik KC 85"}, + {"kim1", kim1_cassette_formats ,"MOS KIM-1"}, + {"lviv", lviv_lvt_format ,"PK-01 Lviv"}, + {"mo5", mo5_cassette_formats ,"Thomson MO-series"}, + {"mz", mz700_cassette_formats ,"Sharp MZ-700"}, + {"orao", orao_cassette_formats ,"PEL Varazdin Orao"}, + {"oric", oric_cassette_formats ,"Tangerine Oric"}, + {"p2000t", p2000t_cassette_formats ,"Philips P2000T"}, + {"pc6001", pc6001_cassette_formats ,"NEC PC-6001"}, + {"phc25", phc25_cassette_formats ,"Sanyo PHC-25"}, + {"pmd85", pmd85_cassette_formats ,"Tesla PMD-85"}, + {"primo", primo_ptp_format ,"Microkey Primo"}, + {"rku", rku_cassette_formats ,"UT-88"}, + {"rk8", rk8_cassette_formats ,"Mikro-80"}, + {"rks", rks_cassette_formats ,"Specialist"}, + {"rko", rko_cassette_formats ,"Orion"}, + {"rkr", rkr_cassette_formats ,"Radio-86RK"}, + {"rka", rka_cassette_formats ,"Zavod BRA Apogee BK-01"}, + {"rkm", rkm_cassette_formats ,"Mikrosha"}, + {"rkp", rkp_cassette_formats ,"SAM SKB VM Partner-01.01"}, + {"sc3000", sc3000_cassette_formats ,"Sega SC-3000"}, + {"sol20", sol20_cassette_formats ,"PTC SOL-20"}, + {"sorcerer", sorcerer_cassette_formats ,"Exidy Sorcerer"}, + {"sordm5", sordm5_cassette_formats ,"Sord M5"}, + {"spc1000", spc1000_cassette_formats ,"Samsung SPC-1000"}, + {"svi", svi_cassette_formats ,"Spectravideo SVI-318 & SVI-328"}, + {"to7", to7_cassette_formats ,"Thomson TO-series"}, + {"trs80l2", trs80l2_cassette_formats ,"TRS-80 Level 2"}, + {"tvc64", tvc64_cassette_formats ,"Videoton TVC 64"}, + {"tzx", tzx_cassette_formats ,"Sinclair ZX Spectrum"}, + {"vg5k", vg5k_cassette_formats ,"Philips VG 5000"}, + {"vtech1", vtech1_cassette_formats ,"Video Technology Laser 110-310"}, + {"vtech2", vtech2_cassette_formats ,"Video Technology Laser 350-700"}, + {"x07", x07_cassette_formats ,"Canon X-07"}, + {"x1", x1_cassette_formats ,"Sharp X1"}, + {"zx80_o", zx80_o_format ,"Sinclair ZX80"}, + {"zx81_p", zx81_p_format ,"Sinclair ZX81"}, + + + + {nullptr,nullptr,nullptr} +}; + + +static std::string get_extension(const char *name) +{ + const char *s; + s = name; + if (s != nullptr) + s = strrchr(s, '.'); + return s ? std::string(s+1) : ""; +} + +static void display_usage(const char *argv0) +{ + fprintf(stderr, "Usage: \n"); + fprintf(stderr, " %s convert \n", argv0); +} + +static void display_formats(void) +{ + int i,j; + fprintf(stderr, "Supported formats:\n\n"); + for (i = 0; formats[i].name; i++) { + fprintf(stderr, "%10s - %s\n",formats[i].name,formats[i].desc); + for (j = 1; formats[i].formats[j]; j++) { + fprintf(stderr, "%15s %s\n","",formats[i].formats[j]->extensions); + } + } +} + +int CLIB_DECL main(int argc, char *argv[]) +{ + int i; + int found =0; + const cassette_image::Format * const *selected_formats = nullptr; + cassette_image::ptr cassette; + + if (argc > 1) + { + if (!core_stricmp("convert", argv[1])) + { + // convert command + if (argc!=5) { + fprintf(stderr, "Wrong parameter number.\n\n"); + display_usage(argv[0]); + return -1; + } else { + for (i = 0; formats[i].name; i++) { + if (core_stricmp(formats[i].name,argv[2])==0) { + selected_formats = formats[i].formats; + found = 1; + } + } + if (found==0) { + fprintf(stderr, "Wrong format name.\n\n"); + display_usage(argv[0]); + fprintf(stderr, "\n"); + display_formats(); + return -1; + } + + FILE *f = fopen(argv[3], "rb"); + if (!f) { + fprintf(stderr, "File %s not found.\n",argv[3]); + return -1; + } + + auto io = util::stdio_read_write(f, 0x00); + f = nullptr; + if (!io) { + fprintf(stderr, "Out of memory.\n"); + return -1; + } + + if (cassette_image::open_choices(std::move(io), get_extension(argv[3]), selected_formats, cassette_image::FLAG_READONLY, cassette) != cassette_image::error::SUCCESS) { + fprintf(stderr, "Invalid format of input file.\n"); + return -1; + } + + cassette->dump(argv[4]); + cassette.reset(); + goto theend; + } + } + } + + /* Usage */ + fprintf(stderr, "castool - Generic cassette manipulation tool for use with MAME\n\n"); + display_usage(argv[0]); + fprintf(stderr, "\n"); + display_formats(); + fprintf(stderr, "\nExample usage:\n"); + fprintf(stderr, " %s convert tzx game.tzx game.wav\n\n", argv[0]); + +theend : + return 0; +} diff --git a/src/tools/chdman.cpp b/src/tools/chdman.cpp index b292272..4301c9c 100644 --- a/src/tools/chdman.cpp +++ b/src/tools/chdman.cpp @@ -2955,7 +2955,7 @@ static void do_dump_metadata(parameters_map ¶ms) //------------------------------------------------- -// do_list_templates - list hard drive templates +// do_dump_metadata - dump metadata from a CHD //------------------------------------------------- static void do_list_templates(parameters_map ¶ms) diff --git a/src/tools/discrepancy-fixer.py b/src/tools/discrepancy-fixer.py new file mode 100755 index 0000000..d36d286 --- /dev/null +++ b/src/tools/discrepancy-fixer.py @@ -0,0 +1,82 @@ +#!/usr/bin/python +## +## license:BSD-3-Clause +## copyright-holders:Zoe Blade + +# Fix discrepancies in arcade ROM dump names, by Zoe Blade +# For Python 2 and 3 + +import sys +import xml.etree.ElementTree + + +def fixPair(parentMachine, childMachine): + changes = { } + for childRom in childMachine.iter('rom'): + for parentRom in parentMachine.iter('rom'): + if parentRom.get('sha1') == childRom.get('sha1'): + # ROM pair found + if parentRom.get('name') != childRom.get('name'): + # The names don't match + changes[childRom.get('name')] = parentRom.get('name') + + if changes: + sourceFilename = childMachine.get('sourcefile') + + try: + input = open(sourceFilename, 'r') + source = input.read() + input.close() + except Exception as e: + sys.stderr.write('%s: error reading %s: %s\n' % (sys.argv[0], sourceFilename, e)) + return False + + for oldRomFilename in changes: + newRomFilename = '"%s"' % (changes[oldRomFilename]) + oldRomFilename = '"%s"' % (oldRomFilename) + + paddedLen = max(len(oldRomFilename), len(newRomFilename)) + oldRomFilenamePadded = oldRomFilename.ljust(paddedLen, ' ') + newRomFilenamePadded = newRomFilename.ljust(paddedLen, ' ') + + source = source.replace(oldRomFilenamePadded, newRomFilenamePadded) # Try to preserve fancy spacing where possible + source = source.replace(oldRomFilename, newRomFilename) # Fallback on just replacing the filename + + sys.stdout.write('%s: %s -> %s\n' % (sourceFilename, oldRomFilename, newRomFilename)) + + output = open(sourceFilename, 'w') + output.write(source) + output.close() + + return True + + +if __name__ == '__main__': + if len(sys.argv) > 2: + sys.stderr.write('Usage:\n%s [arcade.xml]\n' % sys.argv[0]) + sys.exit(1) + + if len(sys.argv) > 1: + filename = sys.argv[1] + else: + filename = 'arcade.xml' + + sys.stderr.write('Loading XML file...') + sys.stderr.flush() + try: + root = xml.etree.ElementTree.parse(filename).getroot() + except Exception as e: + sys.stderr.write('\n%s: error parsing %s: %s\n' % (sys.argv[0], filename, e)) + sys.exit(2) + sys.stderr.write('done.\n') + + errors = 0 + for childMachine in root.iter('machine'): + if childMachine.get('cloneof'): + for parentMachine in root.iter('machine'): + if parentMachine.get('name') == childMachine.get('cloneof'): + # Machine pair found + if not fixPair(parentMachine, childMachine): + errors += 1 + + sys.exit(0 if errors == 0 else 3) diff --git a/src/tools/discrepancy-spotter.py b/src/tools/discrepancy-spotter.py new file mode 100755 index 0000000..27ba7c0 --- /dev/null +++ b/src/tools/discrepancy-spotter.py @@ -0,0 +1,51 @@ +#!/usr/bin/python +## +## license:BSD-3-Clause +## copyright-holders:Zoe Blade + +# Find discrepancies in arcade ROM dump names, by Zoe Blade +# For Python 2 and 3 + +import sys +import xml.etree.ElementTree + + +def checkPair(parentMachine, childMachine): + for childRom in childMachine.iter('rom'): + for parentRom in parentMachine.iter('rom'): + if parentRom.get('sha1') == childRom.get('sha1'): + # ROM pair found + if parentRom.get('name') != childRom.get('name'): + # The names don't match + sys.stdout.write('%s %s: %s -> %s\n' % (childMachine.get('sourcefile'), childMachine.get('name'), childRom.get('name'), parentRom.get('name'))) + else: + break + + +if __name__ == '__main__': + if len(sys.argv) > 2: + sys.stderr.write('Usage:\n%s [arcade.xml]\n' % sys.argv[0]) + sys.exit(1) + + if len(sys.argv) > 1: + filename = sys.argv[1] + else: + filename = 'arcade.xml' + + sys.stderr.write('Loading XML file...') + sys.stderr.flush() + try: + root = xml.etree.ElementTree.parse(filename).getroot() + except Exception as e: + sys.stderr.write('\n%s: error parsing %s: %s\n' % (sys.argv[0], filename, e)) + sys.exit(2) + sys.stderr.write('done.\n') + + for childMachine in root.iter('machine'): + if childMachine.get('cloneof'): + for parentMachine in root.iter('machine'): + if parentMachine.get('name') == childMachine.get('cloneof'): + # Machine pair found + checkPair(parentMachine, childMachine) + + sys.exit(0) diff --git a/src/tools/floptool.cpp b/src/tools/floptool.cpp new file mode 100644 index 0000000..f021e08 --- /dev/null +++ b/src/tools/floptool.cpp @@ -0,0 +1,711 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert +/*************************************************************************** + + (Floppy) image command-line manager + +***************************************************************************/ + +#include "image_handler.h" + +#include "corestr.h" +#include "ioprocs.h" + +#include +#include +#include +#include +#include +#include +#include + + +static formats_table formats; + +static void display_usage() +{ + fprintf(stderr, "Usage: \n"); + fprintf(stderr, " floptool.exe identify [ ...] -- Identify an image format\n"); + fprintf(stderr, " floptool.exe flopconvert [input_format|auto] output_format -- Convert a floppy image\n"); + fprintf(stderr, " floptool.exe flopcreate output_format filesystem -- Create a preformatted floppy image\n"); + fprintf(stderr, " floptool.exe flopdir input_format filesystem -- List the contents of a floppy image\n"); + fprintf(stderr, " floptool.exe flopread input_format filesystem -- Extract a file from a floppy image\n"); + fprintf(stderr, " floptool.exe flopwrite input_format filesystem -- Write a file into a floppy image\n"); +} + +static void display_formats() +{ + int sk = 0; + for(const auto &e : formats.floppy_format_info_by_key) { + int sz = e.first.size(); + if(sz > sk) + sk = sz; + } + + for(const auto &e : formats.filesystem_format_by_key) { + int sz = e.first.size(); + if(sz > sk) + sk = sz; + } + + for(const auto &e : formats.floppy_create_info_by_key) { + int sz = e.first.size(); + if(sz > sk) + sk = sz; + } + + fprintf(stderr, "Supported floppy formats:\n\n"); + for(const auto &e : formats.floppy_format_info_by_category) + if(!e.second.empty()) { + fprintf(stderr, "%s:\n", e.first.c_str()); + for(auto *fif : e.second) + fprintf(stderr, " %-*s r%c - %s [%s]\n", sk, fif->m_format->name(), fif->m_format->supports_save() ? 'w' : '-', fif->m_format->description(), fif->m_format->extensions()); + } + + fprintf(stderr, "\n\n"); + fprintf(stderr, "Supported filesystems (with floppy formatting names):\n\n"); + for(const auto &e : formats.filesystem_format_by_category) + if(!e.second.empty()) { + fprintf(stderr, "%s:\n", e.first.c_str()); + for(const auto &f : e.second) { + fprintf(stderr, " %-*s %c%c%c %c%c%c - %s\n", + sk, + f->m_manager->name(), + f->m_floppy || f->m_floppy_raw ? 'F' : '-', + f->m_hd ? 'H' : '-', + f->m_cd ? 'C' : '-', + f->m_manager->can_format() || f->m_floppy_raw ? 'f' : '-', + f->m_manager->can_read() ? 'r' : '-', + f->m_manager->can_write() ? 'w' : '-', + f->m_manager->description()); + for(auto &f2 : f->m_floppy_create) + fprintf(stderr, " %-*s - %s\n", + sk, + f2->m_name, + f2->m_description); + } + } +} + +static void display_full_usage() +{ + /* Usage */ + fprintf(stderr, "floptool - Generic floppy image manipulation tool for use with MAME\n\n"); + display_usage(); + fprintf(stderr, "\n"); + display_formats(); + +} + +static int identify(int argc, char *argv[]) +{ + // Need to detect CHDs too + + if(argc<3) { + fprintf(stderr, "Missing name of file to identify.\n\n"); + display_usage(); + return 1; + } + + int sz = 0; + for(int i=2; i sz) + sz = len; + } + + for(int i=2; im_format->name()); + if(len > sz2) + sz2 = len; + } + + bool first = true; + for(const auto &e : scores) { + printf("%-*s %c %3d - %-*s %s\n", sz, first ? argv[i] : "", first ? ':' : ' ', e.first, sz2, e.second->m_format->name(), e.second->m_format->description()); + first = false; + } + } + return 0; +} + +static const floppy_format_info *find_floppy_source_format(const char *name, image_handler &ih) +{ + const floppy_format_info *source_format; + if(!core_stricmp(name, "auto")) { + auto scores = ih.identify(formats); + if(scores.empty()) { + fprintf(stderr, "Error: Could not identify the format of file %s\n", ih.get_on_disk_path().c_str()); + return nullptr; + } + if(scores.size() >= 2 && scores[0].first == scores[1].first) { + fprintf(stderr, "Ambiguous source format. Possible formats:\n"); + int sz = 0; + for(const auto &e : scores) { + int len = strlen(e.second->m_format->name()); + if(len > sz) + sz = len; + } + + for(const auto &e : scores) + printf(" %3d - %-*s %s\n", e.first, sz, e.second->m_format->name(), e.second->m_format->description()); + return nullptr; + } + source_format = scores[0].second; + + } else { + source_format = formats.find_floppy_format_info_by_key(name); + if(!source_format) { + fprintf(stderr, "Error: Format '%s' unknown\n", name); + return nullptr; + + } + } + return source_format; +} + +static int flopconvert(int argc, char *argv[]) +{ + if(argc!=6) { + fprintf(stderr, "Incorrect number of arguments.\n\n"); + display_usage(); + return 1; + } + + image_handler ih; + ih.set_on_disk_path(argv[4]); + + const floppy_format_info *source_format = find_floppy_source_format(argv[2], ih); + if(!source_format) + return 1; + + const floppy_format_info *dest_format = formats.find_floppy_format_info_by_key(argv[3]); + if(!dest_format) { + fprintf(stderr, "Error: Format '%s' unknown\n", argv[3]); + return 1; + } + if(!dest_format->m_format->supports_save()) { + fprintf(stderr, "Error: Aaving to format '%s' unsupported\n", argv[3]); + return 1; + } + + if(ih.floppy_load(source_format)) { + fprintf(stderr, "Error: Loading as format '%s' failed\n", source_format->m_format->name()); + return 1; + } + + ih.set_on_disk_path(argv[5]); + + if(ih.floppy_save(dest_format)) { + fprintf(stderr, "Error: Saving as format '%s' failed\n", dest_format->m_format->name()); + return 1; + } + + return 0; +} + +static int flopcreate(int argc, char *argv[]) +{ + if(argc!=5) { + fprintf(stderr, "Incorrect number of arguments.\n\n"); + display_usage(); + return 1; + } + + auto dest_format = formats.find_floppy_format_info_by_key(argv[2]); + if(!dest_format) { + fprintf(stderr, "Error: Floppy format '%s' unknown\n", argv[3]); + return 1; + } + if(!dest_format->m_format->supports_save()) { + fprintf(stderr, "Error: Saving to format '%s' unsupported\n", argv[3]); + return 1; + } + + auto create_fs = formats.find_floppy_create_info_by_key(argv[3]); + if(!create_fs) { + fprintf(stderr, "Error: Floppy creation format '%s' unknown\n", argv[3]); + return 1; + } + if(!create_fs->m_manager->can_format()) { + fprintf(stderr, "Error: Floppy creation format '%s' does not support creating new images\n", argv[3]); + return 1; + } + + fs_meta_data meta; + image_handler ih; + ih.set_on_disk_path(argv[4]); + + ih.floppy_create(create_fs, meta); + return ih.floppy_save(dest_format); +} + +static void dir_scan(u32 depth, filesystem_t::dir_t dir, std::vector> &entries, const std::unordered_map &nmap, size_t nc, const std::vector &dmetad, const std::vector &fmetad) +{ + std::string head; + for(u32 i = 0; i != depth; i++) + head += " "; + auto contents = dir.contents(); + for(const auto &c : contents) { + size_t id = entries.size(); + entries.resize(id+1); + entries[id].resize(nc); + switch(c.m_type) { + case fs_dir_entry_type::dir: { + auto subdir = dir.dir_get(c.m_key); + auto meta = subdir.metadata(); + for(const auto &m : dmetad) { + if(!meta.has(m.m_name)) + continue; + size_t slot = nmap.find(m.m_name)->second; + std::string val = fs_meta::to_string(m.m_type, meta.get(m.m_name)); + if(slot == 0) + val = head + "dir " + val; + entries[id][slot] = val; + } + dir_scan(depth+1, subdir, entries, nmap, nc, dmetad, fmetad); + break; + } + case fs_dir_entry_type::file: + case fs_dir_entry_type::system_file: { + auto file = dir.file_get(c.m_key); + auto meta = file.metadata(); + for(const auto &m : fmetad) { + if(!meta.has(m.m_name)) + continue; + size_t slot = nmap.find(m.m_name)->second; + std::string val = fs_meta::to_string(m.m_type, meta.get(m.m_name)); + if(slot == 0) + val = head + (c.m_type == fs_dir_entry_type::system_file ? "sys " : "file ") + val; + entries[id][slot] = val; + } + break; + } + } + } +} + +static int generic_dir(image_handler &ih) +{ + auto [fsm, fs] = ih.get_fs(); + auto vmetad = fsm->volume_meta_description(); + auto fmetad = fsm->file_meta_description(); + auto dmetad = fsm->directory_meta_description(); + + auto vmeta = fs->metadata(); + if(!vmeta.empty()) { + std::string vinf = "Volume:"; + for(const auto &e : vmetad) + vinf += util::string_format(" %s=%s", fs_meta_data::entry_name(e.m_name), fs_meta::to_string(e.m_type, vmeta.get(e.m_name))); + printf("%s\n\n", vinf.c_str()); + } + + std::vector names; + names.push_back(fs_meta_name::name); + for(const auto &e : fmetad) + if(e.m_name != fs_meta_name::name) + names.push_back(e.m_name); + for(const auto &e : dmetad) + if(std::find(names.begin(), names.end(), e.m_name) == names.end()) + names.push_back(e.m_name); + + std::unordered_map nmap; + for(size_t i = 0; i != names.size(); i++) + nmap[names[i]] = i; + + auto root = fs->root(); + std::vector> entries; + + entries.resize(1); + for(fs_meta_name n : names) + entries[0].push_back(fs_meta_data::entry_name(n)); + + dir_scan(0, root, entries, nmap, names.size(), dmetad, fmetad); + + std::vector sizes(names.size()); + + for(const auto &e : entries) + for(unsigned int i=0; i != names.size(); i++) + sizes[i] = std::max(sizes[i], e[i].size()); + + for(const auto &e : entries) { + std::string l; + for(unsigned int i=0; i != names.size(); i++) { + if(i) + l += ' '; + l += util::string_format("%-*s", sizes[i], e[i]); + } + printf("%s\n", l.c_str()); + } + + return 0; +} + +static int flopdir(int argc, char *argv[]) +{ + if(argc!=5) { + fprintf(stderr, "Incorrect number of arguments.\n\n"); + display_usage(); + return 1; + } + + image_handler ih; + ih.set_on_disk_path(argv[4]); + + const floppy_format_info *source_format = find_floppy_source_format(argv[2], ih); + if(!source_format) + return 1; + + auto fs = formats.find_filesystem_format_by_key(argv[3]); + if(!fs) { + fprintf(stderr, "Error: Filesystem '%s' unknown\n", argv[3]); + return 1; + } + + if(!fs->m_manager || !fs->m_manager->can_read()) { + fprintf(stderr, "Error: Filesystem '%s' does not implement reading\n", argv[3]); + return 1; + } + + if(ih.floppy_load(source_format)) { + fprintf(stderr, "Error: Loading as format '%s' failed\n", source_format->m_format->name()); + return 1; + } + + if(ih.floppy_mount_fs(fs)) { + fprintf(stderr, "Error: Parsing as filesystem '%s' failed\n", fs->m_manager->name()); + return 1; + } + + return generic_dir(ih); +} + +static int hddir(int argc, char *argv[]) +{ + if(argc!=4) { + fprintf(stderr, "Incorrect number of arguments.\n\n"); + display_usage(); + return 1; + } + + image_handler ih; + ih.set_on_disk_path(argv[3]); + + auto fs = formats.find_filesystem_format_by_key(argv[2]); + if(!fs) { + fprintf(stderr, "Error: Filesystem '%s' unknown\n", argv[2]); + return 1; + } + + if(!fs->m_manager || !fs->m_manager->can_read()) { + fprintf(stderr, "Error: Filesystem '%s' does not implement reading\n", argv[2]); + return 1; + } + + if(ih.hd_mount_fs(fs)) { + fprintf(stderr, "Error: Parsing as filesystem '%s' failed\n", fs->m_manager->name()); + return 1; + } + + return generic_dir(ih); +} + + +static int generic_read(image_handler &ih, const char *srcpath, const char *dstpath) +{ + auto [fsm, fs] = ih.get_fs(); + + std::vector path = ih.path_split(srcpath); + + auto dir = fs->root(); + std::string apath; + for(unsigned int i = 0; i < path.size() - 1; i++) { + auto c = dir.contents(); + unsigned int j; + for(j = 0; j != c.size(); j++) + if(c[j].m_name == path[i]) + break; + if(j == c.size()) { + fprintf(stderr, "Error: directory %s%c%s not found\n", apath.c_str(), fsm->directory_separator(), path[i].c_str()); + return 1; + } + if(c[j].m_type != fs_dir_entry_type::dir) { + fprintf(stderr, "Error: %s%c%s is not a directory\n", apath.c_str(), fsm->directory_separator(), path[i].c_str()); + return 1; + } + dir = dir.dir_get(c[j].m_key); + apath += fsm->directory_separator() + path[i]; + } + + auto c = dir.contents(); + unsigned int j; + for(j = 0; j != c.size(); j++) + if(c[j].m_name == path.back()) + break; + if(j == c.size()) { + fprintf(stderr, "Error: file %s%c%s not found\n", apath.c_str(), fsm->directory_separator(), path.back().c_str()); + return 1; + } + auto file = dir.file_get(c[j].m_key); + auto meta = file.metadata(); + + if(!meta.has(fs_meta_name::length)) { + fprintf(stderr, "Error: %s%c%s is not a readable file\n", apath.c_str(), fsm->directory_separator(), path.back().c_str()); + return 1; + } + + image_handler::fsave(dstpath, file.read_all()); + + bool has_rsrc = fsm->has_rsrc() && meta.has(fs_meta_name::rsrc_length); + + if(has_rsrc) + image_handler::fsave_rsrc(image_handler::path_make_rsrc(dstpath), file.rsrc_read_all()); + + return 0; +} + + +static int flopread(int argc, char *argv[]) +{ + if(argc!=7) { + fprintf(stderr, "Incorrect number of arguments.\n\n"); + display_usage(); + return 1; + } + + image_handler ih; + ih.set_on_disk_path(argv[4]); + + const floppy_format_info *source_format = find_floppy_source_format(argv[2], ih); + if(!source_format) + return 1; + + auto fs = formats.find_filesystem_format_by_key(argv[3]); + if(!fs) { + fprintf(stderr, "Error: Filesystem '%s' unknown\n", argv[3]); + return 1; + } + + if(!fs->m_manager || !fs->m_manager->can_read()) { + fprintf(stderr, "Error: Filesystem '%s' does not implement reading\n", argv[3]); + return 1; + } + + if(ih.floppy_load(source_format)) { + fprintf(stderr, "Error: Loading as format '%s' failed\n", source_format->m_format->name()); + return 1; + } + + if(ih.floppy_mount_fs(fs)) { + fprintf(stderr, "Error: Parsing as filesystem '%s' failed\n", fs->m_manager->name()); + return 1; + } + + return generic_read(ih, argv[5], argv[6]); +} + +static int hdread(int argc, char *argv[]) +{ + if(argc!=6) { + fprintf(stderr, "Incorrect number of arguments.\n\n"); + display_usage(); + return 1; + } + + image_handler ih; + ih.set_on_disk_path(argv[3]); + + auto fs = formats.find_filesystem_format_by_key(argv[2]); + if(!fs) { + fprintf(stderr, "Error: Filesystem '%s' unknown\n", argv[2]); + return 1; + } + + if(!fs->m_manager || !fs->m_manager->can_read()) { + fprintf(stderr, "Error: Filesystem '%s' does not implement reading\n", argv[2]); + return 1; + } + + if(ih.hd_mount_fs(fs)) { + fprintf(stderr, "Error: Parsing as filesystem '%s' failed\n", fs->m_manager->name()); + return 1; + } + + return generic_read(ih, argv[4], argv[5]); +} + + + +static int generic_write(image_handler &ih, const char *srcpath, const char *dstpath) +{ + auto [fsm, fs] = ih.get_fs(); + + std::vector path = ih.path_split(dstpath); + + auto dir = fs->root(); + std::string apath; + for(unsigned int i = 0; i < path.size() - 1; i++) { + auto c = dir.contents(); + unsigned int j; + for(j = 0; j != c.size(); j++) + if(c[j].m_name == path[i]) + break; + if(j == c.size()) { + fprintf(stderr, "Error: directory %s%c%s not found\n", apath.c_str(), fsm->directory_separator(), path[i].c_str()); + return 1; + } + if(c[j].m_type != fs_dir_entry_type::dir) { + fprintf(stderr, "Error: %s%c%s is not a directory\n", apath.c_str(), fsm->directory_separator(), path[i].c_str()); + return 1; + } + dir = dir.dir_get(c[j].m_key); + apath += fsm->directory_separator() + path[i]; + } + + + fs_meta_data meta; + meta.set(fs_meta_name::name, path.back()); + + auto file = dir.file_create(meta); + auto filedata = image_handler::fload(srcpath); + file.replace(filedata); + + bool has_rsrc = fsm->has_rsrc(); + + if(has_rsrc) { + std::string rpath = image_handler::path_make_rsrc(dstpath); + + if(image_handler::fexists(rpath)) { + filedata = image_handler::fload_rsrc(rpath); + if(!filedata.empty()) + file.rsrc_replace(filedata); + } + } + + return 0; +} + + +static int flopwrite(int argc, char *argv[]) +{ + if(argc!=7) { + fprintf(stderr, "Incorrect number of arguments.\n\n"); + display_usage(); + return 1; + } + + image_handler ih; + ih.set_on_disk_path(argv[4]); + + const floppy_format_info *source_format = find_floppy_source_format(argv[2], ih); + if(!source_format) + return 1; + + auto fs = formats.find_filesystem_format_by_key(argv[3]); + if(!fs) { + fprintf(stderr, "Error: Filesystem '%s' unknown\n", argv[3]); + return 1; + } + + if(!fs->m_manager || !fs->m_manager->can_read()) { + fprintf(stderr, "Error: Filesystem '%s' does not implement reading\n", argv[3]); + return 1; + } + + if(ih.floppy_load(source_format)) { + fprintf(stderr, "Error: Loading as format '%s' failed\n", source_format->m_format->name()); + return 1; + } + + if(ih.floppy_mount_fs(fs)) { + fprintf(stderr, "Error: Parsing as filesystem '%s' failed\n", fs->m_manager->name()); + return 1; + } + + int err = generic_write(ih, argv[5], argv[6]); + if(err) + return err; + + ih.fs_to_floppy(); + if(ih.floppy_save(source_format)) + return 1; + + return 0; +} + +static int hdwrite(int argc, char *argv[]) +{ + if(argc!=6) { + fprintf(stderr, "Incorrect number of arguments.\n\n"); + display_usage(); + return 1; + } + + + image_handler ih; + ih.set_on_disk_path(argv[3]); + + auto fs = formats.find_filesystem_format_by_key(argv[2]); + if(!fs) { + fprintf(stderr, "Error: Filesystem '%s' unknown\n", argv[2]); + return 1; + } + + if(!fs->m_manager || !fs->m_manager->can_read()) { + fprintf(stderr, "Error: Filesystem '%s' does not implement reading\n", argv[2]); + return 1; + } + + if(ih.hd_mount_fs(fs)) { + fprintf(stderr, "Error: Parsing as filesystem '%s' failed\n", fs->m_manager->name()); + return 1; + } + + return generic_write(ih, argv[4], argv[5]); +} + +int CLIB_DECL main(int argc, char *argv[]) +{ + formats.init(); + + if(argc == 1) { + display_full_usage(); + return 0; + } + + try { + if(!core_stricmp("identify", argv[1])) + return identify(argc, argv); + else if(!core_stricmp("flopconvert", argv[1])) + return flopconvert(argc, argv); + else if(!core_stricmp("flopcreate", argv[1])) + return flopcreate(argc, argv); + else if(!core_stricmp("flopdir", argv[1])) + return flopdir(argc, argv); + else if(!core_stricmp("flopread", argv[1])) + return flopread(argc, argv); + else if(!core_stricmp("flopwrite", argv[1])) + return flopwrite(argc, argv); + else if(!core_stricmp("hddir", argv[1])) + return hddir(argc, argv); + else if(!core_stricmp("hdread", argv[1])) + return hdread(argc, argv); + else if(!core_stricmp("hdwrite", argv[1])) + return hdwrite(argc, argv); + else { + fprintf(stderr, "Unknown command '%s'\n\n", argv[1]); + display_usage(); + return 1; + } + } catch(const emu_fatalerror &err) { + fprintf(stderr, "Error: %s", err.what()); + return 1; + } +} diff --git a/src/tools/image_handler.cpp b/src/tools/image_handler.cpp new file mode 100644 index 0000000..a520383 --- /dev/null +++ b/src/tools/image_handler.cpp @@ -0,0 +1,409 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +// Image generic handler class and helpers + +#include "image_handler.h" + +#include "formats/all.h" +#include "formats/fsblk_vec.h" +#include "formats/fs_unformatted.h" + +#include "ioprocs.h" +#include "ioprocsfill.h" +#include "ioprocsvec.h" + + +// Fatalerror implementation + +emu_fatalerror::emu_fatalerror(util::format_argument_pack const &args) + : emu_fatalerror(0, args) +{ +} + +emu_fatalerror::emu_fatalerror(int _exitcode, util::format_argument_pack const &args) + : m_text(util::string_format(args)) + , m_code(_exitcode) +{ +} + + +// Format enumeration + +namespace { + struct enumerator : public mame_formats_enumerator { + formats_table *m_table; + std::string m_category; + + enumerator(formats_table *table) : mame_formats_enumerator(), m_table(table), m_category("None") {} + + virtual ~enumerator() = default; + virtual void add(const cassette_image::Format *const *formats) {} + + virtual void category(const char *name) { + m_category = name; + } + + virtual void add(floppy_format_type format) { + m_table->floppy_format_infos.emplace_back(std::make_unique(format(), m_category)); + } + + virtual void add(const filesystem_manager_t &fs) { + m_table->filesystem_formats.emplace_back(std::make_unique(&fs, m_category)); + } + }; + + struct fs_enum : public filesystem_manager_t::floppy_enumerator { + filesystem_format *m_format; + + fs_enum(filesystem_format *format) : m_format(format) {} + + virtual void add(floppy_format_type type, u32 image_size, const char *name, const char *description) override { + m_format->m_floppy = true; + m_format->m_floppy_create.emplace_back(std::make_unique(m_format->m_manager, type, image_size, name, description)); + } + + virtual void add_raw(const char *name, u32 key, const char *description) override { + m_format->m_floppy_raw = true; + m_format->m_floppy_create.emplace_back(std::make_unique(name, key, description)); + } + }; +} + +void formats_table::init() +{ + std::vector variants; + + enumerator en(this); + mame_formats_full_list(en); + + for(auto &f : filesystem_formats) { + fs_enum fen(f.get()); + f->m_manager->enumerate_f(fen, floppy_image::FF_UNKNOWN, variants); + } + + for(auto &f : floppy_format_infos) { + auto *ff = f.get(); + std::string key = ff->m_format->name(); + auto i = floppy_format_info_by_key.find(key); + if(i != floppy_format_info_by_key.end()) { + fprintf(stderr, "Collision on floppy format name %s between \"%s\" and \"%s\".\n", + ff->m_format->name(), + ff->m_format->description(), + i->second->m_format->description()); + exit(1); + } + + floppy_format_info_by_key[key] = ff; + floppy_format_info_by_category[ff->m_category].push_back(ff); + } + + for(auto &f : filesystem_formats) { + auto *ff = f.get(); + std::string key = ff->m_manager->name(); + auto i = filesystem_format_by_key.find(key); + if(i != filesystem_format_by_key.end()) { + fprintf(stderr, "Collision on filesystem name %s between \"%s\" and \"%s\".\n", + ff->m_manager->name(), + ff->m_manager->description(), + i->second->m_manager->description()); + exit(1); + } + + filesystem_format_by_key[key] = ff; + filesystem_format_by_category[ff->m_category].push_back(ff); + + for(auto &f2 : ff->m_floppy_create) { + auto *ff2 = f2.get(); + key = ff2->m_name; + auto i = floppy_create_info_by_key.find(key); + if(i != floppy_create_info_by_key.end()) { + fprintf(stderr, "Collision on floppy create name %s between \"%s\" and \"%s\".\n", + ff2->m_name, + ff2->m_description, + i->second->m_description); + exit(1); + } + + floppy_create_info_by_key[key] = ff2; + } + } +} + +const floppy_format_info *formats_table::find_floppy_format_info_by_key(const std::string &key) const +{ + auto i = floppy_format_info_by_key.find(key); + return i == floppy_format_info_by_key.end() ? nullptr : i->second; +} + +const filesystem_format *formats_table::find_filesystem_format_by_key(const std::string &key) const +{ + auto i = filesystem_format_by_key.find(key); + return i == filesystem_format_by_key.end() ? nullptr : i->second; +} + +const floppy_create_info *formats_table::find_floppy_create_info_by_key(const std::string &key) const +{ + auto i = floppy_create_info_by_key.find(key); + return i == floppy_create_info_by_key.end() ? nullptr : i->second; +} + + +// Image handling + +std::vector image_handler::fload(std::string path) +{ + char msg[4096]; + sprintf(msg, "Error opening %s for reading", path.c_str()); + auto fi = fopen(path.c_str(), "rb"); + if(!fi) { + perror(msg); + exit(1); + } + fseek(fi, 0, SEEK_END); + long size = ftell(fi); + std::vector filedata(size); + fseek(fi, 0, SEEK_SET); + fread(filedata.data(), filedata.size(), 1, fi); + fclose(fi); + + return filedata; +} + +std::vector image_handler::fload_rsrc(std::string path) +{ + auto filedata = fload(path); + const u8 *head = filedata.data(); + + if(filesystem_t::r32b(head+0x00) == 0x00051607 && + filesystem_t::r32b(head+0x04) == 0x00020000) { + u16 nent = filesystem_t::r16b(head+0x18); + for(u16 i=0; i != nent; i++) { + const u8 *e = head + 12*i; + if(filesystem_t::r32b(e+0) == 2) { + u32 start = filesystem_t::r32b(e+4); + u32 len = filesystem_t::r32b(e+8); + filedata.erase(filedata.begin(), filedata.begin() + start); + filedata.erase(filedata.begin() + len, filedata.end()); + return filedata; + } + } + } + filedata.clear(); + return filedata; +} + +void image_handler::fsave(std::string path, const std::vector &data) +{ + char msg[4096]; + sprintf(msg, "Error opening %s for writing", path.c_str()); + auto fo = fopen(path.c_str(), "wb"); + if(!fo) { + perror(msg); + exit(1); + } + + fwrite(data.data(), data.size(), 1, fo); + fclose(fo); +} + +void image_handler::fsave_rsrc(std::string path, const std::vector &data) +{ + u8 head[0x2a]; + + filesystem_t::w32b(head+0x00, 0x00051607); // Magic + filesystem_t::w32b(head+0x04, 0x00020000); // Version + filesystem_t::fill(head+0x08, 0, 16); // Filler + filesystem_t::w16b(head+0x18, 1); // Number of entries + filesystem_t::w32b(head+0x1a, 2); // Resource fork + filesystem_t::w32b(head+0x22, 0x2a); // Offset in the file + filesystem_t::w32b(head+0x26, data.size()); // Length + + char msg[4096]; + sprintf(msg, "Error opening %s for writing", path.c_str()); + auto fo = fopen(path.c_str(), "wb"); + if(!fo) { + perror(msg); + exit(1); + } + + fwrite(head, sizeof(head), 1, fo); + fwrite(data.data(), data.size(), 1, fo); + fclose(fo); +} + +image_handler::image_handler() : m_floppy_image(84, 2, floppy_image::FF_UNKNOWN) +{ +} + +void image_handler::set_on_disk_path(std::string path) +{ + m_on_disk_path = path; +} + +std::vector> image_handler::identify(const formats_table &formats) +{ + std::vector> res; + std::vector variants; + + FILE *f = fopen(m_on_disk_path.c_str(), "rb"); + if (!f) { + std::string msg = util::string_format("Error opening %s for reading", m_on_disk_path); + perror(msg.c_str()); + return res; + } + + auto io = util::stdio_read(f, 0xff); + + for(const auto &e : formats.floppy_format_info_by_key) { + u8 score = e.second->m_format->identify(*io, floppy_image::FF_UNKNOWN, variants); + if(score) + res.emplace_back(std::make_pair(score, e.second)); + } + + return res; +} + +bool image_handler::floppy_load(const floppy_format_info *format) +{ + std::vector variants; + FILE *f = fopen(m_on_disk_path.c_str(), "rb"); + if (!f) { + std::string msg = util::string_format("Error opening %s for reading", m_on_disk_path); + perror(msg.c_str()); + return true; + } + + auto io = util::stdio_read(f, 0xff); + + return !format->m_format->load(*io, floppy_image::FF_UNKNOWN, variants, &m_floppy_image); +} + +bool image_handler::floppy_save(const floppy_format_info *format) +{ + std::vector variants; + std::string msg = util::string_format("Error opening %s for writing", m_on_disk_path); + FILE *f = fopen(m_on_disk_path.c_str(), "wb"); + if (!f) { + perror(msg.c_str()); + return true; + } + + auto io = util::stdio_read_write(f, 0xff); + + return !format->m_format->save(*io, variants, &m_floppy_image); +} + +void image_handler::floppy_create(const floppy_create_info *format, fs_meta_data meta) +{ + if(format->m_type) { + std::vector variants; + std::vector img(format->m_image_size); + fsblk_vec_t blockdev(img); + auto fs = format->m_manager->mount(blockdev); + fs->format(meta); + + auto source_format = format->m_type(); + auto io = util::ram_read(img.data(), img.size(), 0xff); + source_format->load(*io, floppy_image::FF_UNKNOWN, variants, &m_floppy_image); + delete source_format; + } else { + fs_unformatted::format(format->m_key, &m_floppy_image); + } +} + +bool image_handler::floppy_mount_fs(const filesystem_format *format) +{ + m_floppy_fs_converter = nullptr; + for(const auto &ci : format->m_floppy_create) { + if(ci->m_type != m_floppy_fs_converter) { + std::vector variants; + m_floppy_fs_converter = ci->m_type; + m_sector_image.clear(); + auto load_format = m_floppy_fs_converter(); + util::random_read_write_fill_wrapper, 0xff> io(m_sector_image); + load_format->save(io, variants, &m_floppy_image); + delete load_format; + } + + if(ci->m_image_size == m_sector_image.size()) + goto success; + } + m_floppy_fs_converter = nullptr; + m_sector_image.clear(); + return true; + + success: + m_fsblk.reset(new fsblk_vec_t(m_sector_image)); + m_fsm = format->m_manager; + m_fs = m_fsm->mount(*m_fsblk); + return false; +} + +bool image_handler::hd_mount_fs(const filesystem_format *format) +{ + // Should use the chd mechanisms, one thing at a time... + + m_sector_image = fload(m_on_disk_path); + m_fsblk.reset(new fsblk_vec_t(m_sector_image)); + m_fsm = format->m_manager; + m_fs = m_fsm->mount(*m_fsblk); + return false; +} + +void image_handler::fs_to_floppy() +{ + std::vector variants; + auto io = util::ram_read(m_sector_image.data(), m_sector_image.size(), 0xff); + auto format = m_floppy_fs_converter(); + format->load(*io, floppy_image::FF_UNKNOWN, variants, &m_floppy_image); + delete format; +} + +std::vector image_handler::path_split(std::string path) const +{ + std::string opath = path; + std::vector rpath; + if(m_fsm->has_subdirectories()) { + std::string element; + char sep = m_fsm->directory_separator(); + for(char c : opath) { + if(c == sep) { + if(!element.empty()) { + rpath.push_back(element); + element.clear(); + } + } else + element += c; + } + if(!element.empty()) + rpath.push_back(element); + + } else + rpath.push_back(opath); + + return rpath; +} + +bool image_handler::fexists(std::string path) +{ + auto f = fopen(path.c_str(), "rb"); + if(f != nullptr) { + fclose(f); + return true; + } + return false; +} + + +std::string image_handler::path_make_rsrc(std::string path) +{ + auto p = path.end(); + while(p != path.begin() && p[-1] != '/') + p--; + std::string rpath(path.begin(), p); + rpath += "._"; + rpath += std::string(p, path.end()); + return rpath; +} + diff --git a/src/tools/image_handler.h b/src/tools/image_handler.h new file mode 100644 index 0000000..d9a861b --- /dev/null +++ b/src/tools/image_handler.h @@ -0,0 +1,119 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +// Image generic handler class and helpers + +#ifndef MAME_TOOLS_IMAGE_HANDLER_H +#define MAME_TOOLS_IMAGE_HANDLER_H + +#pragma once + +#include "../emu/emucore.h" + +#include "formats/fsmgr.h" + +#include +#include +#include +#include +#include + + +using u8 = uint8_t; +using u16 = uint16_t; +using u32 = uint32_t; + +struct floppy_format_info { + floppy_image_format_t *m_format; + std::string m_category; + + floppy_format_info(floppy_image_format_t *format, std::string category) : m_format(format), m_category(category) {} +}; + +struct floppy_create_info { + const filesystem_manager_t *m_manager; + + floppy_format_type m_type; + u32 m_image_size; + u32 m_key; + const char *m_name; + const char *m_description; + + floppy_create_info(const filesystem_manager_t *manager, floppy_format_type type, u32 image_size, const char *name, const char *description) : + m_manager(manager), m_type(type), m_image_size(image_size), m_key(0), m_name(name), m_description(description) + { } + + floppy_create_info(const char *name, u32 key, const char *description) : + m_manager(nullptr), m_type(nullptr), m_image_size(0), m_key(key), m_name(name), m_description(description) + { } +}; + +struct filesystem_format { + const filesystem_manager_t *m_manager; + std::vector> m_floppy_create; + std::string m_category; + bool m_floppy, m_floppy_raw, m_hd, m_cd; + + filesystem_format(const filesystem_manager_t *manager, std::string category) : m_manager(manager), m_category(category), m_floppy(false), m_floppy_raw(false), m_hd(false), m_cd(false) {} +}; + +struct formats_table { + std::vector> floppy_format_infos; + std::vector> filesystem_formats; + + std::map floppy_format_info_by_key; + std::map filesystem_format_by_key; + std::map floppy_create_info_by_key; + + std::map> floppy_format_info_by_category; + std::map> filesystem_format_by_category; + + void init(); + + const floppy_format_info *find_floppy_format_info_by_key(const std::string &key) const; + const filesystem_format *find_filesystem_format_by_key(const std::string &key) const; + const floppy_create_info *find_floppy_create_info_by_key(const std::string &key) const; +}; + +class image_handler { +public: + image_handler(); + + void set_on_disk_path(std::string path); + const std::string &get_on_disk_path() const { return m_on_disk_path; } + + std::vector> identify(const formats_table &formats); + + bool floppy_load(const floppy_format_info *format); + bool floppy_save(const floppy_format_info *format); + + void floppy_create(const floppy_create_info *format, fs_meta_data meta); + bool floppy_mount_fs(const filesystem_format *format); + bool hd_mount_fs(const filesystem_format *format); + void fs_to_floppy(); + + std::pair get_fs() const { return std::make_pair(m_fsm, m_fs.get()); } + + std::vector path_split(std::string path) const; + + static std::vector fload(std::string path); + static std::vector fload_rsrc(std::string path); + static void fsave(std::string path, const std::vector &data); + static void fsave_rsrc(std::string path, const std::vector &data); + static bool fexists(std::string path); + static std::string path_make_rsrc(std::string path); + +private: + std::string m_on_disk_path; + + floppy_image m_floppy_image; + + floppy_format_type m_floppy_fs_converter; + std::vector m_sector_image; + std::unique_ptr m_fsblk; + const filesystem_manager_t *m_fsm; + std::unique_ptr m_fs; + +}; + +#endif // MAME_TOOLS_IMAGE_HANDLER_H diff --git a/src/tools/imgtool/charconv.cpp b/src/tools/imgtool/charconv.cpp new file mode 100644 index 0000000..3ff6de6 --- /dev/null +++ b/src/tools/imgtool/charconv.cpp @@ -0,0 +1,107 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + charconv.cpp + + Imgtool character set conversion routines. + +***************************************************************************/ + +#include "charconv.h" + +#include "corestr.h" + +#include + +imgtool::simple_charconverter imgtool::charconverter_iso_8859_1(nullptr, nullptr); + + +//------------------------------------------------- +// simple_charconverter::simple_charconverter +//------------------------------------------------- + +imgtool::simple_charconverter::simple_charconverter(const char32_t lowpage[0x80], const char32_t highpage[0x80], unicode_normalization_form norm) + : m_norm(norm), m_lowpage(lowpage), m_highpage(highpage) +{ + // build the reverse lookup table + for (int i = 0; i < 256; i++) + { + const char32_t *page = i >= 128 ? m_highpage : m_lowpage; + char32_t unicode_char = page ? page[i % 128] : i; + m_reverse_lookup.emplace_back(unicode_char, (char)i); + } + + // and sort it + std::sort(m_reverse_lookup.begin(), m_reverse_lookup.end(), [](const std::pair &a, const std::pair &b) + { + return b.first > a.first; + }); +} + + +//------------------------------------------------- +// from_utf8 +//------------------------------------------------- + +void imgtool::simple_charconverter::from_utf8(std::ostream &dest, std::string_view src) const +{ + // normalize the incoming unicode + std::string const normalized_src = normalize_unicode(src, m_norm); + + auto nsrc = std::string_view(normalized_src); + while (!nsrc.empty()) + { + // get the next character + char32_t ch; + int rc = uchar_from_utf8(&ch, nsrc); + if (rc < 0) + { + ch = 0xFFFD; + rc = 1; + } + nsrc.remove_prefix(rc); + + // do the reverse lookup + auto lookup = std::lower_bound(m_reverse_lookup.begin(), m_reverse_lookup.end(), ch, [](const std::pair &a, const char32_t &b) + { + return a.first < b; + }); + if (lookup == m_reverse_lookup.end()) + throw charconverter_exception(); + + // and output the results + dest << lookup->second; + } +} + + +//------------------------------------------------- +// to_utf8 +//------------------------------------------------- + +void imgtool::simple_charconverter::to_utf8(std::ostream &dest, std::string_view src) const +{ + for (uint8_t c : src) + { + // which page is this in? + const char32_t *page = ((c & 0x80) == 0) ? m_lowpage : m_highpage; + + // is this page present? + if ((c & 0x80) == 0) + { + // no - pass it on + dest << c; + } + else + { + // yes - we need to do a lookup + size_t base = ((c & 0x80) == 0) ? 0x00 : 0x80; + char32_t ch = page[((unsigned char)(c)) - base]; + if (ch == 0) + throw charconverter_exception(); + + dest << utf8_from_uchar(ch); + } + } +} diff --git a/src/tools/imgtool/charconv.h b/src/tools/imgtool/charconv.h new file mode 100644 index 0000000..7df16f8 --- /dev/null +++ b/src/tools/imgtool/charconv.h @@ -0,0 +1,82 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + charconv.h + + Imgtool character set conversion routines. + +***************************************************************************/ + + +#ifndef IMGTOOL_CHARCONV_H +#define IMGTOOL_CHARCONV_H + +#include "unicode.h" + +#include +#include +#include + +namespace imgtool +{ + // ======================> charconverter + + // abstract base class for character conversions + class charconverter + { + public: + virtual void from_utf8(std::ostream &dest, std::string_view src) const = 0; + virtual void to_utf8(std::ostream &dest, std::string_view src) const = 0; + + std::string from_utf8(const std::string &src) const + { + // inlining so that the return value can potentially be removed by return value optimization + std::ostringstream stream; + from_utf8(stream, src); + return stream.str(); + } + + std::string to_utf8(const std::string &src) const + { + // inlining so that the return value can potentially be removed by return value optimization + std::ostringstream stream; + to_utf8(stream, src); + return stream.str(); + } + + }; + + // ======================> simple_charconverter + + // a simple implementation of charconverter that simply defines a code page for 0x80-0xFF + class simple_charconverter : public charconverter + { + public: + simple_charconverter(const char32_t highpage[0x80], unicode_normalization_form norm = unicode_normalization_form::C) + : simple_charconverter(nullptr, highpage, norm) + { + } + + simple_charconverter(const char32_t lowpage[0x80], const char32_t highpage[0x80], unicode_normalization_form norm = unicode_normalization_form::C); + + virtual void from_utf8(std::ostream &dest, std::string_view src) const override; + virtual void to_utf8(std::ostream &dest, std::string_view src) const override; + + private: + std::vector > m_reverse_lookup; + unicode_normalization_form m_norm; + const char32_t *m_lowpage; + const char32_t *m_highpage; + }; + + // exception that can be thrown from charconverter::from_utf8() if a character is illegal (charconverter::to_utf8() should never throw this) + class charconverter_exception + { + }; + + extern simple_charconverter charconverter_iso_8859_1; +}; + + +#endif // IMGTOOL_CHARCONV_H diff --git a/src/tools/imgtool/filtbas.cpp b/src/tools/imgtool/filtbas.cpp new file mode 100644 index 0000000..acd11a7 --- /dev/null +++ b/src/tools/imgtool/filtbas.cpp @@ -0,0 +1,3123 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/**************************************************************************** + + filtbas.c + + Filter for Microsoft-style tokenized BASIC files + + BASIC files typically follow the following format: + + int8 $ff + int16 + ... + int16 + int16 + int8[] + int8 $00 End of line delimiter + +*****************************************************************************/ + +#include +#include +#include + +#include "imgtool.h" +#include "filter.h" + +#include "formats/imageutl.h" + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define EOLN (CRLF == 1 ? "\r" : (CRLF == 2 ? "\n" : (CRLF == 3 ? "\r\n" : NULL))) + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +struct basictoken_tableent +{ + uint8_t shift; + uint8_t base; + const char *const *tokens; + int num_tokens; +}; + + + +struct basictokens +{ + uint16_t baseaddress; + uint8_t size_pos; + unsigned int skip_bytes : 15; + unsigned char bytes[20]; + unsigned int be : 1; + const basictoken_tableent *entries; + int num_entries; +}; + + + +/*************************************************************************** + IMPLEMENTATION +***************************************************************************/ + +/*------------------------------------------------- + basic_readfile - reads a file and decodes + BASIC tokens into ASCII text +-------------------------------------------------*/ + +static imgtoolerr_t basic_readfile(const basictokens *tokens, + imgtool::partition &partition, const char *filename, + const char *fork, imgtool::stream &destf) +{ + imgtoolerr_t err; + imgtool::stream::ptr mem_stream; + uint8_t line_header[4]; + uint16_t line_number; //, address; + uint8_t b, shift; + int i; + int in_string = false; + const basictoken_tableent *token_table; + const char *token; + + /* open a memory stream */ + mem_stream = imgtool::stream::open_mem(nullptr, 0); + if (!mem_stream) + return IMGTOOLERR_OUTOFMEMORY; + + /* read actual file */ + err = partition.read_file(filename, fork, *mem_stream, nullptr); + if (err) + return err; + + /* skip first few bytes */ + mem_stream->seek(tokens->skip_bytes, SEEK_SET); + + /* keep reading line headers */ + while(mem_stream->read(line_header, sizeof(line_header)) == sizeof(line_header)) + { + /* pluck the address and line number out */ + if (tokens->be) + { + //address = (uint16_t) + pick_integer_be(line_header, 0, 2); + line_number = (uint16_t) pick_integer_be(line_header, 2, 2); + } + else + { + //address = (uint16_t) + pick_integer_le(line_header, 0, 2); + line_number = (uint16_t) pick_integer_le(line_header, 2, 2); + } + + /* write the line number */ + destf.printf("%u ", (unsigned) line_number); + shift = 0x00; + + in_string = false; // in case the last line didn't terminate a string + + while((mem_stream->read(&b, 1) > 0) && (b != 0x00)) + { + if (b == 0x22) + in_string = in_string ? false : true; + + if ((b & 0x80) && (!in_string)) + { + token = nullptr; + + for (i = 0; i < tokens->num_entries; i++) + { + token_table = &tokens->entries[i]; + if (token_table->shift == shift) + { + if ((b - 0x80) < token_table->num_tokens) + { + token = token_table->tokens[b - 0x80]; + if (token) + shift = 0x00; + } + } + + if (token_table->shift == b) + { + shift = token_table->shift; + break; + } + } + + if (shift == 0x00) + destf.puts(token ? token : "!"); + } + else + { + destf.putc((char) b); + } + } + + destf.puts(EOLN); + } + + return err; +} + + + +/*------------------------------------------------- + basic_writefile - translates ASCII text to + BASIC tokens and writes it to a file +-------------------------------------------------*/ + +static imgtoolerr_t basic_writefile(const basictokens *tokens, + imgtool::partition &partition, const char *filename, + const char *fork, imgtool::stream &sourcef, util::option_resolution *opts) +{ + imgtool::stream::ptr mem_stream; + char buf[1024]; + int eof = false; + uint32_t len; + char c; + int i, j, pos, in_quotes; + uint16_t line_number; + uint8_t line_header[4]; + uint8_t file_size[2]; + const basictoken_tableent *token_table; + const char *token; + uint8_t token_shift, token_value; + uint16_t address; + + /* open a memory stream */ + mem_stream = imgtool::stream::open_mem(nullptr, 0); + if (!mem_stream) + return IMGTOOLERR_OUTOFMEMORY; + + /* write header */ + mem_stream->write(tokens->bytes, tokens->skip_bytes); + /* loop until the file is complete */ + while(!eof) + { + /* read a line */ + pos = 0; + while((len = sourcef.read(&c, 1)) > 0) + { + /* break if at end of line */ + if ((c == '\r') || (c == '\n')) + break; + + if (pos <= std::size(buf) - 1) + { + buf[pos++] = c; + } + } + eof = (len == 0); + buf[pos] = '\0'; + + /* ignore lines that don't start with digits */ + if (isdigit(buf[0])) + { + /* start at beginning of line */ + pos = 0; + + /* read line number */ + line_number = 0; + while(isdigit(buf[pos])) + { + line_number *= 10; + line_number += (buf[pos++] - '0'); + } + + /* determine address */ + if (tokens->baseaddress != 0) + { + address = tokens->baseaddress + (uint16_t)mem_stream->size() + 4; + } + else + { + address = 0x0000; + } + + /* set up line header */ + memset(&line_header, 0, sizeof(line_header)); + if (tokens->be) + { + place_integer_be(line_header, 0, 2, address); + place_integer_be(line_header, 2, 2, line_number); + } + else + { + place_integer_le(line_header, 0, 2, address); + place_integer_le(line_header, 2, 2, line_number); + } + + /* emit line header */ + mem_stream->write(line_header, sizeof(line_header)); + + /* skip spaces */ + while(isspace(buf[pos])) + pos++; + + /* when we start out, we are not within quotation marks */ + in_quotes = false; + + /* read until end of line */ + while(buf[pos] != '\0') + { + token = nullptr; + token_shift = 0; + token_value = 0; + + if (buf[pos] == '\"') + { + /* flip quotation status */ + in_quotes = !in_quotes; + } + else if (!in_quotes) + { + for (i = 0; (token == nullptr) && (i < tokens->num_entries); i++) + { + bool found = false; + token_table = &tokens->entries[i]; + for (j = 0; (token == nullptr) && (j < token_table->num_tokens); j++) + { + if (!strncmp(&buf[pos], token_table->tokens[j], strlen(token_table->tokens[j]))) + { + token = token_table->tokens[j]; + token_shift = token_table->shift; + token_value = token_table->base + j; + pos += strlen(token); + found = true; + break; + } + } + if (found) + break; + } + } + + /* did we find a token? */ + if (token != nullptr) + { + /* emit the token */ + if (token_shift != 0) + mem_stream->write(&token_shift, 1); + mem_stream->write(&token_value, 1); + } + else + { + /* no token; emit the byte */ + mem_stream->write(&buf[pos++], 1); + } + } + + /* emit line terminator */ + mem_stream->fill(0x00, 1); + } + } + + /* emit program terminator */ + mem_stream->fill(0x00, 2); + + /* reset stream */ + mem_stream->seek(tokens->size_pos, SEEK_SET); + + /* this is somewhat gross */ + if (tokens->skip_bytes >= 3) + { + if (tokens->be) + { + place_integer_be(file_size, 0, 2, mem_stream->size()); + } + else + { + place_integer_le(file_size, 0, 2, mem_stream->size()); + } + mem_stream->write(file_size, 2); + mem_stream->seek(0, SEEK_SET); + } + + /* write actual file */ + return partition.write_file(filename, fork, *mem_stream, opts, nullptr); +} + + + +/*************************************************************************** + TOKEN DEFINITIONS +***************************************************************************/ + +static const char *const cocobas_statements[] = +{ + "FOR", /* 0x80 */ + "GO", /* 0x81 */ + "REM", /* 0x82 */ + "'", /* 0x83 */ + "ELSE", /* 0x84 */ + "IF", /* 0x85 */ + "DATA", /* 0x86 */ + "PRINT", /* 0x87 */ + "ON", /* 0x88 */ + "INPUT", /* 0x89 */ + "END", /* 0x8a */ + "NEXT", /* 0x8b */ + "DIM", /* 0x8c */ + "READ", /* 0x8d */ + "RUN", /* 0x8e */ + "RESTORE", /* 0x8f */ + "RETURN", /* 0x90 */ + "STOP", /* 0x91 */ + "POKE", /* 0x92 */ + "CONT", /* 0x93 */ + "LIST", /* 0x94 */ + "CLEAR", /* 0x95 */ + "NEW", /* 0x96 */ + "CLOAD", /* 0x97 */ + "CSAVE", /* 0x98 */ + "OPEN", /* 0x99 */ + "CLOSE", /* 0x9a */ + "LLIST", /* 0x9b */ + "SET", /* 0x9c */ + "RESET", /* 0x9d */ + "CLS", /* 0x9e */ + "MOTOR", /* 0x9f */ + "SOUND", /* 0xa0 */ + "AUDIO", /* 0xa1 */ + "EXEC", /* 0xa2 */ + "SKIPF", /* 0xa3 */ + "TAB(", /* 0xa4 */ + "TO", /* 0xa5 */ + "SUB", /* 0xa6 */ + "THEN", /* 0xa7 */ + "NOT", /* 0xa8 */ + "STEP", /* 0xa9 */ + "OFF", /* 0xaa */ + "+", /* 0xab */ + "-", /* 0xac */ + "*", /* 0xad */ + "/", /* 0xae */ + "^", /* 0xaf */ + "AND", /* 0xb0 */ + "OR", /* 0xb1 */ + ">", /* 0xb2 */ + "=", /* 0xb3 */ + "<", /* 0xb4 */ + "DEL", /* 0xb5 */ + "EDIT", /* 0xb6 */ + "TRON", /* 0xb7 */ + "TROFF", /* 0xb8 */ + "DEF", /* 0xb9 */ + "LET", /* 0xba */ + "LINE", /* 0xbb */ + "PCLS", /* 0xbc */ + "PSET", /* 0xbd */ + "PRESET", /* 0xbe */ + "SCREEN", /* 0xbf */ + "PCLEAR", /* 0xc0 */ + "COLOR", /* 0xc1 */ + "CIRCLE", /* 0xc2 */ + "PAINT", /* 0xc3 */ + "GET", /* 0xc4 */ + "PUT", /* 0xc5 */ + "DRAW", /* 0xc6 */ + "PCOPY", /* 0xc7 */ + "PMODE", /* 0xc8 */ + "PLAY", /* 0xc9 */ + "DLOAD", /* 0xca */ + "RENUM", /* 0xcb */ + "FN", /* 0xcc */ + "USING", /* 0xcd */ + "DIR", /* 0xce */ + "DRIVE", /* 0xcf */ + "FIELD", /* 0xd0 */ + "FILES", /* 0xd1 */ + "KILL", /* 0xd2 */ + "LOAD", /* 0xd3 */ + "LSET", /* 0xd4 */ + "MERGE", /* 0xd5 */ + "RENAME", /* 0xd6 */ + "RSET", /* 0xd7 */ + "SAVE", /* 0xd8 */ + "WRITE", /* 0xd9 */ + "VERIFY", /* 0xda */ + "UNLOAD", /* 0xdb */ + "DSKINI", /* 0xdc */ + "BACKUP", /* 0xdd */ + "COPY", /* 0xde */ + "DSKI$", /* 0xdf */ + "DSKO$", /* 0xe0 */ + "DOS", /* 0xe1 */ + "WIDTH", /* 0xe2 */ + "PALETTE", /* 0xe3 */ + "HSCREEN", /* 0xe4 */ + "LPOKE", /* 0xe5 */ + "HCLS", /* 0xe6 */ + "HCOLOR", /* 0xe7 */ + "HPAINT", /* 0xe8 */ + "HCIRCLE", /* 0xe9 */ + "HLINE", /* 0xea */ + "HGET", /* 0xeb */ + "HPUT", /* 0xec */ + "HBUFF", /* 0xed */ + "HPRINT", /* 0xee */ + "ERR", /* 0xef */ + "BRK", /* 0xf0 */ + "LOCATE", /* 0xf1 */ + "HSTAT", /* 0xf2 */ + "HSET", /* 0xf3 */ + "HRESET", /* 0xf4 */ + "HDRAW", /* 0xf5 */ + "CMP", /* 0xf6 */ + "RGB", /* 0xf7 */ + "ATTR" /* 0xf8 */ +}; + +static const char *const cocobas_functions[] = +{ + "SGN", /* 0xff80 */ + "INT", /* 0xff81 */ + "ABS", /* 0xff82 */ + "USR", /* 0xff83 */ + "RND", /* 0xff84 */ + "SIN", /* 0xff85 */ + "PEEK", /* 0xff86 */ + "LEN", /* 0xff87 */ + "STR$", /* 0xff88 */ + "VAL", /* 0xff89 */ + "ASC", /* 0xff8a */ + "CHR$", /* 0xff8b */ + "EOF", /* 0xff8c */ + "JOYSTK", /* 0xff8d */ + "LEFT$", /* 0xff8e */ + "RIGHT$", /* 0xff8f */ + "MID$", /* 0xff90 */ + "POINT", /* 0xff91 */ + "INKEY$", /* 0xff92 */ + "MEM", /* 0xff93 */ + "ATN", /* 0xff94 */ + "COS", /* 0xff95 */ + "TAN", /* 0xff96 */ + "EXP", /* 0xff97 */ + "FIX", /* 0xff98 */ + "LOG", /* 0xff99 */ + "POS", /* 0xff9a */ + "SQR", /* 0xff9b */ + "HEX$", /* 0xff9c */ + "VARPTR", /* 0xff9d */ + "INSTR", /* 0xff9e */ + "TIMER", /* 0xff9f */ + "PPOINT", /* 0xffa0 */ + "STRING$", /* 0xffa1 */ + "CVN", /* 0xffa2 */ + "FREE", /* 0xffa3 */ + "LOC", /* 0xffa4 */ + "LOF", /* 0xffa5 */ + "MKN$", /* 0xffa6 */ + "AS", /* 0xffa7 */ + "LPEEK", /* 0xffa8 */ + "BUTTON", /* 0xffa9 */ + "HPOINT", /* 0xffaa */ + "ERNO", /* 0xffab */ + "ERLIN" /* 0xffac */ +}; + +static const char *const dragonbas_statements[] = +{ + "FOR", /* 0x80 */ + "GO", /* 0x81 */ + "REM", /* 0x82 */ + "'", /* 0x83 */ + "ELSE", /* 0x84 */ + "IF", /* 0x85 */ + "DATA", /* 0x86 */ + "PRINT", /* 0x87 */ + "ON", /* 0x88 */ + "INPUT", /* 0x89 */ + "END", /* 0x8a */ + "NEXT", /* 0x8b */ + "DIM", /* 0x8c */ + "READ", /* 0x8d */ + "LET", /* 0x8e */ + "RUN", /* 0x8f */ + "RESTORE", /* 0x90 */ + "RETURN", /* 0x91 */ + "STOP", /* 0x92 */ + "POKE", /* 0x93 */ + "CONT", /* 0x94 */ + "LIST", /* 0x95 */ + "CLEAR", /* 0x96 */ + "NEW", /* 0x97 */ + "DEF", /* 0x98 */ + "CLOAD", /* 0x99 */ + "CSAVE", /* 0x9a */ + "OPEN", /* 0x9b */ + "CLOSE", /* 0x9c */ + "LLIST", /* 0x9d */ + "SET", /* 0x9e */ + "RESET", /* 0x9f */ + "CLS", /* 0xa0 */ + "MOTOR", /* 0xa1 */ + "SOUND", /* 0xa2 */ + "AUDIO", /* 0xa3 */ + "EXEC", /* 0xa4 */ + "SKIPF", /* 0xa5 */ + "DEL", /* 0xa6 */ + "EDIT", /* 0xa7 */ + "TRON", /* 0xa8 */ + "TROFF", /* 0xa9 */ + "LINE", /* 0xaa */ + "PCLS", /* 0xab */ + "PSET", /* 0xac */ + "PRESET", /* 0xad */ + "SCREEN", /* 0xae */ + "PCLEAR", /* 0xaf */ + "COLOR", /* 0xb0 */ + "CIRCLE", /* 0xb1 */ + "PAINT", /* 0xb2 */ + "GET", /* 0xb3 */ + "PUT", /* 0xb4 */ + "DRAW", /* 0xb5 */ + "PCOPY", /* 0xb6 */ + "PMODE", /* 0xb7 */ + "PLAY", /* 0xb8 */ + "DLOAD", /* 0xb9 */ + "RENUM", /* 0xba */ + "TAB(", /* 0xbb */ + "TO", /* 0xbc */ + "SUB", /* 0xbd */ + "FN", /* 0xbe */ + "THEN", /* 0xbf */ + "NOT", /* 0xc0 */ + "STEP", /* 0xc1 */ + "OFF", /* 0xc2 */ + "+", /* 0xc3 */ + "-", /* 0xc4 */ + "*", /* 0xc5 */ + "/", /* 0xc6 */ + "^", /* 0xc7 */ + "AND", /* 0xc8 */ + "OR", /* 0xc9 */ + ">", /* 0xca */ + "=", /* 0xcb */ + "<", /* 0xcc */ + "USING", /* 0xcd */ + "AUTO", /* 0xce */ + "BACKUP", /* 0xcf */ + "BEEP", /* 0xd0 */ + "BOOT", /* 0xd1 */ + "CHAIN", /* 0xd2 */ + "COPY", /* 0xd3 */ + "CREATE", /* 0xd4 */ + "DIR", /* 0xd5 */ + "DRIVE", /* 0xd6 */ + "DSKINIT", /* 0xd7 */ + "FREAD", /* 0xd8 */ + "FWRITE", /* 0xd9 */ + "ERROR", /* 0xda */ + "KILL", /* 0xdb */ + "LOAD", /* 0xdc */ + "MERGE", /* 0xdd */ + "PROTECT", /* 0xde */ + "WAIT", /* 0xdf */ + "RENAME", /* 0xe0 */ + "SAVE", /* 0xe1 */ + "SREAD", /* 0xe2 */ + "SWRITE", /* 0xe3 */ + "VERIFY", /* 0xe4 */ + "FROM", /* 0xe5 */ + "FLREAD", /* 0xe6 */ + "SWAP" /* 0xe7 */ +}; + +static const char *const dragonbas_functions[] = +{ + "SGN", /* 0xff80 */ + "INT", /* 0xff81 */ + "ABS", /* 0xff82 */ + "POS", /* 0xff83 */ + "RND", /* 0xff84 */ + "SQR", /* 0xff85 */ + "LOG", /* 0xff86 */ + "EXP", /* 0xff87 */ + "SIN", /* 0xff88 */ + "COS", /* 0xff89 */ + "TAN", /* 0xff8a */ + "ATN", /* 0xff8b */ + "PEEK", /* 0xff8c */ + "LEN", /* 0xff8d */ + "STR$", /* 0xff8e */ + "VAL", /* 0xff8f */ + "ASC", /* 0xff90 */ + "CHR$", /* 0xff91 */ + "EOF", /* 0xff92 */ + "JOYSTK", /* 0xff93 */ + "FIX", /* 0xff94 */ + "HEX$", /* 0xff95 */ + "LEFT$", /* 0xff96 */ + "RIGHT$", /* 0xff97 */ + "MID$", /* 0xff98 */ + "POINT", /* 0xff99 */ + "INKEY$", /* 0xff9a */ + "MEM", /* 0xff9b */ + "VARPTR", /* 0xff9c */ + "INSTR", /* 0xff9d */ + "TIMER", /* 0xff9e */ + "PPOINT", /* 0xff9f */ + "STRING$", /* 0xffa0 */ + "USR", /* 0xffa1 */ + "LOF", /* 0xffa2 */ + "FREE", /* 0xffa3 */ + "ERL", /* 0xffa4 */ + "ERR", /* 0xffa5 */ + "HIMEM", /* 0xffa6 */ + "LOC", /* 0xffa7 */ + "FRE$" /* 0xffa8 */ +}; + +static const char *const vzbas[] = +{ + "END", /* 0x80 */ + "FOR", /* 0x81 */ + "RESET", /* 0x82 */ + "SET", /* 0x83 */ + "CLS", /* 0x84 */ + nullptr, /* 0x85 */ + "RANDOM", /* 0x86 */ + "NEXT", /* 0x87 */ + "DATA", /* 0x88 */ + "INPUT", /* 0x89 */ + "DIM", /* 0x8a */ + "READ", /* 0x8b */ + "LET", /* 0x8c */ + "GOTO", /* 0x8d */ + "RUN", /* 0x8e */ + "IF", /* 0x8f */ + "RESTORE", /* 0x90 */ + "GOSUB", /* 0x91 */ + "RETURN", /* 0x92 */ + "'", /* 0x93 */ + "STOP", /* 0x94 */ + "ELSE", /* 0x95 */ + "COPY", /* 0x96 */ + "COLOR", /* 0x97 */ + "VERIFY", /* 0x98 */ + "DEFINT", /* 0x99 */ + "DEFSNG", /* 0x9a */ + "DEFDBL", /* 0x9b */ + "CRUN", /* 0x9c */ + "MODE", /* 0x9d */ + "ERROR", /* 0x9e */ + "RESUME", /* 0x9f */ + "OUT", /* 0xa0 */ + "IN", /* 0xa1 */ + nullptr, /* 0xa2 */ + nullptr, /* 0xa3 */ + nullptr, /* 0xa4 */ + nullptr, /* 0xa5 */ + nullptr, /* 0xa6 */ + nullptr, /* 0xa7 */ + nullptr, /* 0xa8 */ + nullptr, /* 0xa9 */ + nullptr, /* 0xaa */ + nullptr, /* 0xab */ + nullptr, /* 0xac */ + nullptr, /* 0xad */ + "(RESET)", /* 0xae */ + "LPRINT", /* 0xaf */ + nullptr, /* 0xb0 */ + "POKE", /* 0xb1 */ + "PRINT", /* 0xb2 */ + "CONT", /* 0xb3 */ + "LIST", /* 0xb4 */ + "LLIST", /* 0xb5 */ + "DELETE", /* 0xb6 */ + "AUTO", /* 0xb7 */ + "CLEAR", /* 0xb8 */ + "CLOAD", /* 0xb9 */ + "CSAVE", /* 0xba */ + "NEW", /* 0xbb */ + "TAB(", /* 0xbc */ + "TO", /* 0xbd */ + nullptr, /* 0xbe */ + "USING", /* 0xbf */ + "VARPTR", /* 0xc0 */ + "USR", /* 0xc1 */ + "ERL", /* 0xc2 */ + "ERR", /* 0xc3 */ + "STRING$", /* 0xc4 */ + nullptr, /* 0xc5 */ + "POINT", /* 0xc6 */ + nullptr, /* 0xc7 */ + "MEM", /* 0xc8 */ + "INKEY$", /* 0xc9 */ + "THEN", /* 0xca */ + "NOT", /* 0xcb */ + "STEP", /* 0xcc */ + "+", /* 0xcd */ + "-", /* 0xce */ + "*", /* 0xcf */ + "/", /* 0xd0 */ + "^", /* 0xd1 */ + "AND", /* 0xd2 */ + "OR", /* 0xd3 */ + ">", /* 0xd4 */ + "=", /* 0xd5 */ + "<", /* 0xd6 */ + "SGN", /* 0xd7 */ + "INT", /* 0xd8 */ + "ABS", /* 0xd9 */ + "FRE", /* 0xda */ + "INP", /* 0xdb */ + "POS", /* 0xdc */ + "SQR", /* 0xdd */ + "AND", /* 0xde */ + "LOG", /* 0xdf */ + "EXP", /* 0xe0 */ + "COS", /* 0xe1 */ + "SIN", /* 0xe2 */ + "TAN", /* 0xe3 */ + "ATN", /* 0xe4 */ + "PEEK", /* 0xe5 */ + nullptr, /* 0xe6 */ + nullptr, /* 0xe7 */ + nullptr, /* 0xe8 */ + nullptr, /* 0xe9 */ + nullptr, /* 0xea */ + nullptr, /* 0xeb */ + nullptr, /* 0xec */ + nullptr, /* 0xed */ + nullptr, /* 0xee */ + "CINT", /* 0xef */ + "CSNG", /* 0xf0 */ + "CDBL", /* 0xf1 */ + "FIX", /* 0xf2 */ + "LEN", /* 0xf3 */ + "STR$", /* 0xf4 */ + "VAL", /* 0xf5 */ + "ASC", /* 0xf6 */ + "CHR$", /* 0xf7 */ + "LEFT$", /* 0xf8 */ + "RIGHT$", /* 0xf9 */ + "MID$", /* 0xfa */ + nullptr, /* 0xfb */ + nullptr, /* 0xfc */ + nullptr, /* 0xfd */ + nullptr, /* 0xfe */ + nullptr /* 0xff */ +}; + +static const char *const bml3bas_statements[] = +{ + "END", /* 0x80 */ + "FOR", /* 0x81 */ + "NEXT", /* 0x82 */ + "DATA", /* 0x83 */ + "DIM", /* 0x84 */ + "READ", /* 0x85 */ + "LET", /* 0x86 */ + "GO", /* 0x87 */ + "RUN", /* 0x88 */ + "IF", /* 0x89 */ + "RESTORE", /* 0x8a */ + "RETURN", /* 0x8b */ + "REM", /* 0x8c */ + "'", /* 0x8d */ + "STOP", /* 0x8e */ + "ELSE", /* 0x8f */ + "TRON", /* 0x90 */ + "TROFF", /* 0x91 */ + "SWAP", /* 0x92 */ + "DEFSTR", /* 0x93 */ + "DEFINT", /* 0x94 */ + "DEFSNG", /* 0x95 */ + "DEFDBL", /* 0x96 */ + "ON", /* 0x97 */ + "WAIT", /* 0x98 */ + "RENUM", /* 0x99 */ + "EDIT", /* 0x9a */ + "ERROR", /* 0x9b */ + "RESUME", /* 0x9c */ + "AUTO", /* 0x9d */ + "DELETE", /* 0x9e */ + "TERM", /* 0x9f */ + "WIDTH", /* 0xa0 */ + "UNLIST", /* 0xa1 */ + "MON", /* 0xa2 */ + "LOCATE", /* 0xa3 */ + "CLS", /* 0xa4 */ + "CONSOLE", /* 0xa5 */ + "PSET", /* 0xa6 */ + "PRESET", /* 0xa7 */ + "MOTOR", /* 0xa8 */ + "SKIPF", /* 0xa9 */ + "SAVE", /* 0xaa */ + "LOAD", /* 0xab */ + "MERGE", /* 0xac */ + "EXEC", /* 0xad */ + "OPEN", /* 0xae */ + "CLOSE", /* 0xaf */ + "FILES", /* 0xb0 */ + "COM", /* 0xb1 */ + "KEY", /* 0xb2 */ + "PAINT", /* 0xb3 */ + "BEEP", /* 0xb4 */ + "COLOR", /* 0xb5 */ + "LINE", /* 0xb6 */ + "DEF", /* 0xb7 */ + "POKE", /* 0xb8 */ + "PRINT", /* 0xb9 */ + "CONT", /* 0xba */ + "LIST", /* 0xbb */ + "CLEAR", /* 0xbc */ + "RANDOMIZE",/* 0xbd */ + "WHILE", /* 0xbe */ + "WEND", /* 0xbf */ + "NEW", /* 0xc0 */ + "TAB(", /* 0xc1 */ + "TO", /* 0xc2 */ + "SUB", /* 0xc3 */ + "FN", /* 0xc4 */ + "SPC(", /* 0xc5 */ + "USING", /* 0xc6 */ + "USR", /* 0xc7 */ + "ERL", /* 0xc8 */ + "ERR", /* 0xc9 */ + "OFF", /* 0xca */ + "THEN", /* 0xcb */ + "NOT", /* 0xcc */ + "STEP", /* 0xcd */ + "+", /* 0xce */ + "-", /* 0xcf */ + "*", /* 0xd0 */ + "/", /* 0xd1 */ + "^", /* 0xd2 */ + "AND", /* 0xd3 */ + "OR", /* 0xd4 */ + "XOR", /* 0xd5 */ + "EQV", /* 0xd6 */ + "IMP", /* 0xd7 */ + "MOD", /* 0xd8 */ + "\\", /* 0xd9 */ + ">", /* 0xda */ + "=", /* 0xdb */ + "<" /* 0xdc */ +}; + +static const char *const bml3bas_functions[] = +{ + "SGN", /* 0xff80 */ + "INT", /* 0xff81 */ + "ABS", /* 0xff82 */ + "FRE", /* 0xff83 */ + "POS", /* 0xff84 */ + "SQR", /* 0xff85 */ + "LOG", /* 0xff86 */ + "EXP", /* 0xff87 */ + "COS", /* 0xff88 */ + "SIN", /* 0xff89 */ + "TAN", /* 0xff8a */ + "ATN", /* 0xff8b */ + "PEEK", /* 0xff8c */ + "LEN", /* 0xff8d */ + "STR$", /* 0xff8e */ + "VAL", /* 0xff8f */ + "ASC", /* 0xff90 */ + "CHR$", /* 0xff91 */ + "CINT", /* 0xff92 */ + "CSNG", /* 0xff93 */ + "CDBL", /* 0xff94 */ + "FIX", /* 0xff95 */ + "SPACE$", /* 0xff96 */ + "HEX$", /* 0xff97 */ + "OCT$", /* 0xff98 */ + "LOF", /* 0xff99 */ + "EOF", /* 0xff9a */ + "PEN", /* 0xff9b */ + "LEFT$", /* 0xff9c */ + "RIGHT$", /* 0xff9d */ + "MID$", /* 0xff9e */ + "INSTR", /* 0xff9f */ + "SCREEN", /* 0xffa0 */ + "VARPTR", /* 0xffa1 */ + "STRING$", /* 0xffa2 */ + "RND", /* 0xffa3 */ + "INKEY$", /* 0xffa4 */ + "INPUT", /* 0xffa5 */ + "CSRLIN", /* 0xffa6 */ + "POINT", /* 0xffa7 */ + "TIME", /* 0xffa8 */ + "DATE" /* 0xffa9 */ +}; + + +#ifdef BASIC_ +/* ----------------------------------------------------------------------- * + * CBM machines * + * ----------------------------------------------------------------------- */ +static const char *const basic_10[] = /* "BASIC 1.0" - supported by pet */ +{ + "END", /* 0x80 */ + "FOR", /* 0x81 */ + "NEXT", /* 0x82 */ + "DATA", /* 0x83 */ + "INPUT#", /* 0x84 */ + "INPUT", /* 0x85 */ + "DIM", /* 0x86 */ + "READ", /* 0x87 */ + "LET", /* 0x88 */ + "GOTO", /* 0x89 */ + "RUN", /* 0x8a */ + "IF", /* 0x8b */ + "RESTORE", /* 0x8c */ + "GOSUB", /* 0x8d */ + "RETURN", /* 0x8e */ + "REM", /* 0x8f */ + "STOP", /* 0x90 */ + "ON", /* 0x91 */ + "WAIT", /* 0x92 */ + "LOAD", /* 0x93 */ + "SAVE", /* 0x94 */ + "VERIFY", /* 0x95 */ + "DEF", /* 0x96 */ + "POKE", /* 0x97 */ + "PRINT#", /* 0x98 */ + "PRINT", /* 0x99 */ + "CONT", /* 0x9a */ + "LIST", /* 0x9b */ + "CLR", /* 0x9c */ + "CMD", /* 0x9d */ + "SYS", /* 0x9e */ + "OPEN", /* 0x9f */ + "CLOSE", /* 0xa0 */ + "GET", /* 0xa1 */ + "NEW", /* 0xa2 */ + "TAB(", /* 0xa3 */ + "TO", /* 0xa4 */ + "FN", /* 0xa5 */ + "SPC(", /* 0xa6 */ + "THEN", /* 0xa7 */ + "NOT", /* 0xa8 */ + "STEP", /* 0xa9 */ + "+", /* 0xaa */ + "-", /* 0xab */ + "*", /* 0xac */ + "/", /* 0xad */ + "^", /* 0xae */ + "AND", /* 0xaf */ + "OR", /* 0xb0 */ + ">", /* 0xb1 */ + "=", /* 0xb2 */ + "<", /* 0xb3 */ + "SGN", /* 0xb4 */ + "INT", /* 0xb5 */ + "ABS", /* 0xb6 */ + "USR", /* 0xb7 */ + "FRE", /* 0xb8 */ + "POS", /* 0xb9 */ + "SQR", /* 0xba */ + "RND", /* 0xbb */ + "LOG", /* 0xbc */ + "EXP", /* 0xbd */ + "COS", /* 0xbe */ + "SIN", /* 0xbf */ + "TAN", /* 0xc0 */ + "ATN", /* 0xc1 */ + "PEEK", /* 0xc2 */ + "LEN", /* 0xc3 */ + "STR$", /* 0xc4 */ + "VAL", /* 0xc5 */ + "ASC", /* 0xc6 */ + "CHR$", /* 0xc7 */ + "LEFT$", /* 0xc8 */ + "RIGHT$", /* 0xc9 */ + "MID$", /* 0xca */ + NULL, /* 0xcb */ + NULL, /* 0xcc */ + NULL, /* 0xcd */ + NULL, /* 0xce */ + NULL, /* 0xcf */ + NULL, /* 0xd0 */ + NULL, /* 0xd1 */ + NULL, /* 0xd2 */ + NULL, /* 0xd3 */ + NULL, /* 0xd4 */ + NULL, /* 0xd5 */ + NULL, /* 0xd6 */ + NULL, /* 0xd7 */ + NULL, /* 0xd8 */ + NULL, /* 0xd9 */ + NULL, /* 0xda */ + NULL, /* 0xdb */ + NULL, /* 0xdc */ + NULL, /* 0xdd */ + NULL, /* 0xde */ + NULL, /* 0xdf */ + NULL, /* 0xe0 */ + NULL, /* 0xe1 */ + NULL, /* 0xe2 */ + NULL, /* 0xe3 */ + NULL, /* 0xe4 */ + NULL, /* 0xe5 */ + NULL, /* 0xe6 */ + NULL, /* 0xe7 */ + NULL, /* 0xe8 */ + NULL, /* 0xe9 */ + NULL, /* 0xea */ + NULL, /* 0xeb */ + NULL, /* 0xec */ + NULL, /* 0xed */ + NULL, /* 0xee */ + NULL, /* 0xef */ + NULL, /* 0xf0 */ + NULL, /* 0xf1 */ + NULL, /* 0xf2 */ + NULL, /* 0xf3 */ + NULL, /* 0xf4 */ + NULL, /* 0xf5 */ + NULL, /* 0xf6 */ + NULL, /* 0xf7 */ + NULL, /* 0xf8 */ + NULL, /* 0xf9 */ + NULL, /* 0xfa */ + NULL, /* 0xfb */ + NULL, /* 0xfc */ + NULL, /* 0xfd */ + NULL, /* 0xfe */ + "{PI}" /* 0xff - A single character shaped as greek lowercase 'PI' */ +}; + +static const char *const basic_20[] = /* "BASIC 2.0" - supported by vic20 & clones, c64 & clones, cbm30xx series */ +{ + "END", /* 0x80 */ + "FOR", /* 0x81 */ + "NEXT", /* 0x82 */ + "DATA", /* 0x83 */ + "INPUT#", /* 0x84 */ + "INPUT", /* 0x85 */ + "DIM", /* 0x86 */ + "READ", /* 0x87 */ + "LET", /* 0x88 */ + "GOTO", /* 0x89 */ + "RUN", /* 0x8a */ + "IF", /* 0x8b */ + "RESTORE", /* 0x8c */ + "GOSUB", /* 0x8d */ + "RETURN", /* 0x8e */ + "REM", /* 0x8f */ + "STOP", /* 0x90 */ + "ON", /* 0x91 */ + "WAIT", /* 0x92 */ + "LOAD", /* 0x93 */ + "SAVE", /* 0x94 */ + "VERIFY", /* 0x95 */ + "DEF", /* 0x96 */ + "POKE", /* 0x97 */ + "PRINT#", /* 0x98 */ + "PRINT", /* 0x99 */ + "CONT", /* 0x9a */ + "LIST", /* 0x9b */ + "CLR", /* 0x9c */ + "CMD", /* 0x9d */ + "SYS", /* 0x9e */ + "OPEN", /* 0x9f */ + "CLOSE", /* 0xa0 */ + "GET", /* 0xa1 */ + "NEW", /* 0xa2 */ + "TAB(", /* 0xa3 */ + "TO", /* 0xa4 */ + "FN", /* 0xa5 */ + "SPC(", /* 0xa6 */ + "THEN", /* 0xa7 */ + "NOT", /* 0xa8 */ + "STEP", /* 0xa9 */ + "+", /* 0xaa */ + "-", /* 0xab */ + "*", /* 0xac */ + "/", /* 0xad */ + "^", /* 0xae */ + "AND", /* 0xaf */ + "OR", /* 0xb0 */ + ">", /* 0xb1 */ + "=", /* 0xb2 */ + "<", /* 0xb3 */ + "SGN", /* 0xb4 */ + "INT", /* 0xb5 */ + "ABS", /* 0xb6 */ + "USR", /* 0xb7 */ + "FRE", /* 0xb8 */ + "POS", /* 0xb9 */ + "SQR", /* 0xba */ + "RND", /* 0xbb */ + "LOG", /* 0xbc */ + "EXP", /* 0xbd */ + "COS", /* 0xbe */ + "SIN", /* 0xbf */ + "TAN", /* 0xc0 */ + "ATN", /* 0xc1 */ + "PEEK", /* 0xc2 */ + "LEN", /* 0xc3 */ + "STR$", /* 0xc4 */ + "VAL", /* 0xc5 */ + "ASC", /* 0xc6 */ + "CHR$", /* 0xc7 */ + "LEFT$", /* 0xc8 */ + "RIGHT$", /* 0xc9 */ + "MID$", /* 0xca */ + "GO", /* 0xcb */ + NULL, /* 0xcc */ + NULL, /* 0xcd */ + NULL, /* 0xce */ + NULL, /* 0xcf */ + NULL, /* 0xd0 */ + NULL, /* 0xd1 */ + NULL, /* 0xd2 */ + NULL, /* 0xd3 */ + NULL, /* 0xd4 */ + NULL, /* 0xd5 */ + NULL, /* 0xd6 */ + NULL, /* 0xd7 */ + NULL, /* 0xd8 */ + NULL, /* 0xd9 */ + NULL, /* 0xda */ + NULL, /* 0xdb */ + NULL, /* 0xdc */ + NULL, /* 0xdd */ + NULL, /* 0xde */ + NULL, /* 0xdf */ + NULL, /* 0xe0 */ + NULL, /* 0xe1 */ + NULL, /* 0xe2 */ + NULL, /* 0xe3 */ + NULL, /* 0xe4 */ + NULL, /* 0xe5 */ + NULL, /* 0xe6 */ + NULL, /* 0xe7 */ + NULL, /* 0xe8 */ + NULL, /* 0xe9 */ + NULL, /* 0xea */ + NULL, /* 0xeb */ + NULL, /* 0xec */ + NULL, /* 0xed */ + NULL, /* 0xee */ + NULL, /* 0xef */ + NULL, /* 0xf0 */ + NULL, /* 0xf1 */ + NULL, /* 0xf2 */ + NULL, /* 0xf3 */ + NULL, /* 0xf4 */ + NULL, /* 0xf5 */ + NULL, /* 0xf6 */ + NULL, /* 0xf7 */ + NULL, /* 0xf8 */ + NULL, /* 0xf9 */ + NULL, /* 0xfa */ + NULL, /* 0xfb */ + NULL, /* 0xfc */ + NULL, /* 0xfd */ + NULL, /* 0xfe */ + "{PI}" /* 0xff - A single character shaped as greek lowercase 'PI' */ +}; + +static const char *const basic_20_super_expander_vic[] = /* "BASIC 2.0 with Super Expander" - supported by vic20 & clones */ +{ + "END", /* 0x80 */ + "FOR", /* 0x81 */ + "NEXT", /* 0x82 */ + "DATA", /* 0x83 */ + "INPUT#", /* 0x84 */ + "INPUT", /* 0x85 */ + "DIM", /* 0x86 */ + "READ", /* 0x87 */ + "LET", /* 0x88 */ + "GOTO", /* 0x89 */ + "RUN", /* 0x8a */ + "IF", /* 0x8b */ + "RESTORE", /* 0x8c */ + "GOSUB", /* 0x8d */ + "RETURN", /* 0x8e */ + "REM", /* 0x8f */ + "STOP", /* 0x90 */ + "ON", /* 0x91 */ + "WAIT", /* 0x92 */ + "LOAD", /* 0x93 */ + "SAVE", /* 0x94 */ + "VERIFY", /* 0x95 */ + "DEF", /* 0x96 */ + "POKE", /* 0x97 */ + "PRINT#", /* 0x98 */ + "PRINT", /* 0x99 */ + "CONT", /* 0x9a */ + "LIST", /* 0x9b */ + "CLR", /* 0x9c */ + "CMD", /* 0x9d */ + "SYS", /* 0x9e */ + "OPEN", /* 0x9f */ + "CLOSE", /* 0xa0 */ + "GET", /* 0xa1 */ + "NEW", /* 0xa2 */ + "TAB(", /* 0xa3 */ + "TO", /* 0xa4 */ + "FN", /* 0xa5 */ + "SPC(", /* 0xa6 */ + "THEN", /* 0xa7 */ + "NOT", /* 0xa8 */ + "STEP", /* 0xa9 */ + "+", /* 0xaa */ + "-", /* 0xab */ + "*", /* 0xac */ + "/", /* 0xad */ + "^", /* 0xae */ + "AND", /* 0xaf */ + "OR", /* 0xb0 */ + ">", /* 0xb1 */ + "=", /* 0xb2 */ + "<", /* 0xb3 */ + "SGN", /* 0xb4 */ + "INT", /* 0xb5 */ + "ABS", /* 0xb6 */ + "USR", /* 0xb7 */ + "FRE", /* 0xb8 */ + "POS", /* 0xb9 */ + "SQR", /* 0xba */ + "RND", /* 0xbb */ + "LOG", /* 0xbc */ + "EXP", /* 0xbd */ + "COS", /* 0xbe */ + "SIN", /* 0xbf */ + "TAN", /* 0xc0 */ + "ATN", /* 0xc1 */ + "PEEK", /* 0xc2 */ + "LEN", /* 0xc3 */ + "STR$", /* 0xc4 */ + "VAL", /* 0xc5 */ + "ASC", /* 0xc6 */ + "CHR$", /* 0xc7 */ + "LEFT$", /* 0xc8 */ + "RIGHT$", /* 0xc9 */ + "MID$", /* 0xca */ + "GO", /* 0xcb */ + "KEY", /* 0xcc */ + "GRAPHIC", /* 0xcd */ + "SCNCLR", /* 0xce */ + "CIRCLE", /* 0xcf */ + "DRAW", /* 0xd0 */ + "REGION", /* 0xd1 */ + "COLOR", /* 0xd2 */ + "POINT", /* 0xd3 */ + "SOUND", /* 0xd4 */ + "CHAR", /* 0xd5 */ + "PAINT", /* 0xd6 */ + "RPOT", /* 0xd7 */ + "RPEN", /* 0xd8 */ + "RSND", /* 0xd9 */ + "RCOLR", /* 0xda */ + "RGR", /* 0xdb */ + "RJOY", /* 0xdc */ + "RDOT", /* 0xdd */ + NULL, /* 0xde */ + NULL, /* 0xdf */ + NULL, /* 0xe0 */ + NULL, /* 0xe1 */ + NULL, /* 0xe2 */ + NULL, /* 0xe3 */ + NULL, /* 0xe4 */ + NULL, /* 0xe5 */ + NULL, /* 0xe6 */ + NULL, /* 0xe7 */ + NULL, /* 0xe8 */ + NULL, /* 0xe9 */ + NULL, /* 0xea */ + NULL, /* 0xeb */ + NULL, /* 0xec */ + NULL, /* 0xed */ + NULL, /* 0xee */ + NULL, /* 0xef */ + NULL, /* 0xf0 */ + NULL, /* 0xf1 */ + NULL, /* 0xf2 */ + NULL, /* 0xf3 */ + NULL, /* 0xf4 */ + NULL, /* 0xf5 */ + NULL, /* 0xf6 */ + NULL, /* 0xf7 */ + NULL, /* 0xf8 */ + NULL, /* 0xf9 */ + NULL, /* 0xfa */ + NULL, /* 0xfb */ + NULL, /* 0xfc */ + NULL, /* 0xfd */ + NULL, /* 0xfe */ + "{PI}" /* 0xff - A single character shaped as greek lowercase 'PI' */ +}; + +static const char *const basic_20_turtle_basic_10[] = /* "BASIC 2.0 with Turtle BASIC 1.0" - supported by vic20 & clones */ +{ + "END", /* 0x80 */ + "FOR", /* 0x81 */ + "NEXT", /* 0x82 */ + "DATA", /* 0x83 */ + "INPUT#", /* 0x84 */ + "INPUT", /* 0x85 */ + "DIM", /* 0x86 */ + "READ", /* 0x87 */ + "LET", /* 0x88 */ + "GOTO", /* 0x89 */ + "RUN", /* 0x8a */ + "IF", /* 0x8b */ + "RESTORE", /* 0x8c */ + "GOSUB", /* 0x8d */ + "RETURN", /* 0x8e */ + "REM", /* 0x8f */ + "STOP", /* 0x90 */ + "ON", /* 0x91 */ + "WAIT", /* 0x92 */ + "LOAD", /* 0x93 */ + "SAVE", /* 0x94 */ + "VERIFY", /* 0x95 */ + "DEF", /* 0x96 */ + "POKE", /* 0x97 */ + "PRINT#", /* 0x98 */ + "PRINT", /* 0x99 */ + "CONT", /* 0x9a */ + "LIST", /* 0x9b */ + "CLR", /* 0x9c */ + "CMD", /* 0x9d */ + "SYS", /* 0x9e */ + "OPEN", /* 0x9f */ + "CLOSE", /* 0xa0 */ + "GET", /* 0xa1 */ + "NEW", /* 0xa2 */ + "TAB(", /* 0xa3 */ + "TO", /* 0xa4 */ + "FN", /* 0xa5 */ + "SPC(", /* 0xa6 */ + "THEN", /* 0xa7 */ + "NOT", /* 0xa8 */ + "STEP", /* 0xa9 */ + "+", /* 0xaa */ + "-", /* 0xab */ + "*", /* 0xac */ + "/", /* 0xad */ + "^", /* 0xae */ + "AND", /* 0xaf */ + "OR", /* 0xb0 */ + ">", /* 0xb1 */ + "=", /* 0xb2 */ + "<", /* 0xb3 */ + "SGN", /* 0xb4 */ + "INT", /* 0xb5 */ + "ABS", /* 0xb6 */ + "USR", /* 0xb7 */ + "FRE", /* 0xb8 */ + "POS", /* 0xb9 */ + "SQR", /* 0xba */ + "RND", /* 0xbb */ + "LOG", /* 0xbc */ + "EXP", /* 0xbd */ + "COS", /* 0xbe */ + "SIN", /* 0xbf */ + "TAN", /* 0xc0 */ + "ATN", /* 0xc1 */ + "PEEK", /* 0xc2 */ + "LEN", /* 0xc3 */ + "STR$", /* 0xc4 */ + "VAL", /* 0xc5 */ + "ASC", /* 0xc6 */ + "CHR$", /* 0xc7 */ + "LEFT$", /* 0xc8 */ + "RIGHT$", /* 0xc9 */ + "MID$", /* 0xca */ + "GO", /* 0xcb */ + "GRAPHIC", /* 0xcc */ + "OLD", /* 0xcd */ + "TURN", /* 0xce */ + "PEN", /* 0xcf */ + "DRAW", /* 0xd0 */ + "MOVE", /* 0xd1 */ + "POINT", /* 0xd2 */ + "KILL", /* 0xd3 */ + "WRITE", /* 0xd4 */ + "REPEAT", /* 0xd5 */ + "SCREEN", /* 0xd6 */ + "DOKE", /* 0xd7 */ + "RELOC", /* 0xd8 */ + "FILL", /* 0xd9 */ + "RTIME", /* 0xda */ + "BASE", /* 0xdb */ + "PAUSE", /* 0xdc */ + "POP", /* 0xdd */ + "COLOR", /* 0xde */ + "MERGE", /* 0xdf */ + "CHAR", /* 0xe0 */ + "TAKE", /* 0xe1 */ + "SOUND", /* 0xe2 */ + "VOL", /* 0xe3 */ + "PUT", /* 0xe4 */ + "PLACE", /* 0xe5 */ + "CLS", /* 0xe6 */ + "ACCEPT", /* 0xe7 */ + "RESET", /* 0xe8 */ + "GRAB", /* 0xe9 */ + "RDOT", /* 0xea */ + "PLR$", /* 0xeb */ + "DEEK", /* 0xec */ + "JOY", /* 0xed */ + NULL, /* 0xee */ + NULL, /* 0xef */ + NULL, /* 0xf0 */ + NULL, /* 0xf1 */ + NULL, /* 0xf2 */ + NULL, /* 0xf3 */ + NULL, /* 0xf4 */ + NULL, /* 0xf5 */ + NULL, /* 0xf6 */ + NULL, /* 0xf7 */ + NULL, /* 0xf8 */ + NULL, /* 0xf9 */ + NULL, /* 0xfa */ + NULL, /* 0xfb */ + NULL, /* 0xfc */ + NULL, /* 0xfd */ + NULL, /* 0xfe */ + "{PI}" /* 0xff - A single character shaped as greek lowercase 'PI' */ +}; + +static const char *const basic_20_speech_basic_27[] = /* "BASIC 2.0 with Speech BASIC 2.7" - supported by c64 & clones */ +{ + "END", /* 0x80 */ + "FOR", /* 0x81 */ + "NEXT", /* 0x82 */ + "DATA", /* 0x83 */ + "INPUT#", /* 0x84 */ + "INPUT", /* 0x85 */ + "DIM", /* 0x86 */ + "READ", /* 0x87 */ + "LET", /* 0x88 */ + "GOTO", /* 0x89 */ + "RUN", /* 0x8a */ + "IF", /* 0x8b */ + "RESTORE", /* 0x8c */ + "GOSUB", /* 0x8d */ + "RETURN", /* 0x8e */ + "REM", /* 0x8f */ + "STOP", /* 0x90 */ + "ON", /* 0x91 */ + "WAIT", /* 0x92 */ + "LOAD", /* 0x93 */ + "SAVE", /* 0x94 */ + "VERIFY", /* 0x95 */ + "DEF", /* 0x96 */ + "POKE", /* 0x97 */ + "PRINT#", /* 0x98 */ + "PRINT", /* 0x99 */ + "CONT", /* 0x9a */ + "LIST", /* 0x9b */ + "CLR", /* 0x9c */ + "CMD", /* 0x9d */ + "SYS", /* 0x9e */ + "OPEN", /* 0x9f */ + "CLOSE", /* 0xa0 */ + "GET", /* 0xa1 */ + "NEW", /* 0xa2 */ + "TAB(", /* 0xa3 */ + "TO", /* 0xa4 */ + "FN", /* 0xa5 */ + "SPC(", /* 0xa6 */ + "THEN", /* 0xa7 */ + "NOT", /* 0xa8 */ + "STEP", /* 0xa9 */ + "+", /* 0xaa */ + "-", /* 0xab */ + "*", /* 0xac */ + "/", /* 0xad */ + "^", /* 0xae */ + "AND", /* 0xaf */ + "OR", /* 0xb0 */ + ">", /* 0xb1 */ + "=", /* 0xb2 */ + "<", /* 0xb3 */ + "SGN", /* 0xb4 */ + "INT", /* 0xb5 */ + "ABS", /* 0xb6 */ + "USR", /* 0xb7 */ + "FRE", /* 0xb8 */ + "POS", /* 0xb9 */ + "SQR", /* 0xba */ + "RND", /* 0xbb */ + "LOG", /* 0xbc */ + "EXP", /* 0xbd */ + "COS", /* 0xbe */ + "SIN", /* 0xbf */ + "TAN", /* 0xc0 */ + "ATN", /* 0xc1 */ + "PEEK", /* 0xc2 */ + "LEN", /* 0xc3 */ + "STR$", /* 0xc4 */ + "VAL", /* 0xc5 */ + "ASC", /* 0xc6 */ + "CHR$", /* 0xc7 */ + "LEFT$", /* 0xc8 */ + "RIGHT$", /* 0xc9 */ + "MID$", /* 0xca */ + "GO", /* 0xcb */ + "RESET", /* 0xcc */ + "BASIC", /* 0xcd */ + "HELP", /* 0xce */ + "KEY", /* 0xcf */ + "HIMEM", /* 0xd0 */ + "DISK", /* 0xd1 */ + "DIR", /* 0xd2 */ + "BLOAD", /* 0xd3 */ + "BSAVE", /* 0xd4 */ + "MAP", /* 0xd5 */ + "MEM", /* 0xd6 */ + "PAUSE", /* 0xd7 */ + "BLOCK", /* 0xd8 */ + "HEAR", /* 0xd9 */ + "RECORD", /* 0xda */ + "PLAY", /* 0xdb */ + "VOLDEF", /* 0xdc */ + "COLDEF", /* 0xdd */ + "HEX", /* 0xde */ + "DEZ", /* 0xdf */ + "SCREEN", /* 0xe0 */ + "EXEC", /* 0xe1 */ + "MON", /* 0xe2 */ + "{LEFT ARROW}", /* 0xe3 - A single character shaped as a left pointing arrow */ + "FROM", /* 0xe4 */ + "SPEED", /* 0xe5 */ + "OFF", /* 0xe6 */ + NULL, /* 0xe7 */ + NULL, /* 0xe8 */ + NULL, /* 0xe9 */ + NULL, /* 0xea */ + NULL, /* 0xeb */ + NULL, /* 0xec */ + NULL, /* 0xed */ + NULL, /* 0xee */ + NULL, /* 0xef */ + NULL, /* 0xf0 */ + NULL, /* 0xf1 */ + NULL, /* 0xf2 */ + NULL, /* 0xf3 */ + NULL, /* 0xf4 */ + NULL, /* 0xf5 */ + NULL, /* 0xf6 */ + NULL, /* 0xf7 */ + NULL, /* 0xf8 */ + NULL, /* 0xf9 */ + NULL, /* 0xfa */ + NULL, /* 0xfb */ + NULL, /* 0xfc */ + NULL, /* 0xfd */ + NULL, /* 0xfe */ + "{PI}" /* 0xff - A single character shaped as greek lowercase 'PI' */ +}; + +static const char *const basic_20_at_basic[] = /* "BASIC 2.0 with @BASIC" - supported by c64 & clones */ +{ + "END", /* 0x80 */ + "FOR", /* 0x81 */ + "NEXT", /* 0x82 */ + "DATA", /* 0x83 */ + "INPUT#", /* 0x84 */ + "INPUT", /* 0x85 */ + "DIM", /* 0x86 */ + "READ", /* 0x87 */ + "LET", /* 0x88 */ + "GOTO", /* 0x89 */ + "RUN", /* 0x8a */ + "IF", /* 0x8b */ + "RESTORE", /* 0x8c */ + "GOSUB", /* 0x8d */ + "RETURN", /* 0x8e */ + "REM", /* 0x8f */ + "STOP", /* 0x90 */ + "ON", /* 0x91 */ + "WAIT", /* 0x92 */ + "LOAD", /* 0x93 */ + "SAVE", /* 0x94 */ + "VERIFY", /* 0x95 */ + "DEF", /* 0x96 */ + "POKE", /* 0x97 */ + "PRINT#", /* 0x98 */ + "PRINT", /* 0x99 */ + "CONT", /* 0x9a */ + "LIST", /* 0x9b */ + "CLR", /* 0x9c */ + "CMD", /* 0x9d */ + "SYS", /* 0x9e */ + "OPEN", /* 0x9f */ + "CLOSE", /* 0xa0 */ + "GET", /* 0xa1 */ + "NEW", /* 0xa2 */ + "TAB(", /* 0xa3 */ + "TO", /* 0xa4 */ + "FN", /* 0xa5 */ + "SPC(", /* 0xa6 */ + "THEN", /* 0xa7 */ + "NOT", /* 0xa8 */ + "STEP", /* 0xa9 */ + "+", /* 0xaa */ + "-", /* 0xab */ + "*", /* 0xac */ + "/", /* 0xad */ + "^", /* 0xae */ + "AND", /* 0xaf */ + "OR", /* 0xb0 */ + ">", /* 0xb1 */ + "=", /* 0xb2 */ + "<", /* 0xb3 */ + "SGN", /* 0xb4 */ + "INT", /* 0xb5 */ + "ABS", /* 0xb6 */ + "USR", /* 0xb7 */ + "FRE", /* 0xb8 */ + "POS", /* 0xb9 */ + "SQR", /* 0xba */ + "RND", /* 0xbb */ + "LOG", /* 0xbc */ + "EXP", /* 0xbd */ + "COS", /* 0xbe */ + "SIN", /* 0xbf */ + "TAN", /* 0xc0 */ + "ATN", /* 0xc1 */ + "PEEK", /* 0xc2 */ + "LEN", /* 0xc3 */ + "STR$", /* 0xc4 */ + "VAL", /* 0xc5 */ + "ASC", /* 0xc6 */ + "CHR$", /* 0xc7 */ + "LEFT$", /* 0xc8 */ + "RIGHT$", /* 0xc9 */ + "MID$", /* 0xca */ + "GO", /* 0xcb */ + "TRACE", /* 0xcc */ + "DELETE", /* 0xcd */ + "AUTO", /* 0xce */ + "OLD", /* 0xcf */ + "DUMP", /* 0xd0 */ + "FIND", /* 0xd1 */ + "RENUMBER", /* 0xd2 */ + "DLOAD", /* 0xd3 */ + "DSAVE", /* 0xd4 */ + "DVERIFY", /* 0xd5 */ + "DIRECTORY" /* 0xd6 */ + "CATALOG", /* 0xd7 */ + "SCRATCH", /* 0xd8 */ + "COLLECT", /* 0xd9 */ + "RENAME", /* 0xda */ + "COPY", /* 0xdb */ + "BACKUP", /* 0xdc */ + "DISK", /* 0xdd */ + "HEADER", /* 0xde */ + "APPEND", /* 0xdf */ + "MERGE", /* 0xe0 */ + "MLOAD", /* 0xe1 */ + "MVERIFY", /* 0xe2 */ + "MSAVE", /* 0xe3 */ + "KEY", /* 0xe4 */ + "BASIC", /* 0xe5 */ + "RESET", /* 0xe6 */ + "EXIT", /* 0xe7 */ + "ENTER", /* 0xe8 */ + "DOKE", /* 0xe9 */ + "SET", /* 0xea */ + "HELP", /* 0xeb */ + "SCREEN", /* 0xec */ + "LOMEM", /* 0xed */ + "HIMEM", /* 0xee */ + "COLOUR", /* 0xef */ + "TYPE", /* 0xf0 */ + "TIME", /* 0xf1 */ + "DEEK", /* 0xf2 */ + "HEX$", /* 0xf3 */ + "BIN$", /* 0xf4 */ + "OFF", /* 0xf5 */ + "ALARM", /* 0xf6 */ + NULL, /* 0xf7 */ + NULL, /* 0xf8 */ + NULL, /* 0xf9 */ + NULL, /* 0xfa */ + NULL, /* 0xfb */ + NULL, /* 0xfc */ + NULL, /* 0xfd */ + NULL, /* 0xfe */ + "{PI}" /* 0xff - A single character shaped as greek lowercase 'PI' */ +}; + +static const char *const basic_20_simon_s_basic[] = /* "BASIC 2.0 with Simon's BASIC" - supported by c64 & clones */ +{ + "END", /* 0x80 */ + "FOR", /* 0x81 */ + "NEXT", /* 0x82 */ + "DATA", /* 0x83 */ + "INPUT#", /* 0x84 */ + "INPUT", /* 0x85 */ + "DIM", /* 0x86 */ + "READ", /* 0x87 */ + "LET", /* 0x88 */ + "GOTO", /* 0x89 */ + "RUN", /* 0x8a */ + "IF", /* 0x8b */ + "RESTORE", /* 0x8c */ + "GOSUB", /* 0x8d */ + "RETURN", /* 0x8e */ + "REM", /* 0x8f */ + "STOP", /* 0x90 */ + "ON", /* 0x91 */ + "WAIT", /* 0x92 */ + "LOAD", /* 0x93 */ + "SAVE", /* 0x94 */ + "VERIFY", /* 0x95 */ + "DEF", /* 0x96 */ + "POKE", /* 0x97 */ + "PRINT#", /* 0x98 */ + "PRINT", /* 0x99 */ + "CONT", /* 0x9a */ + "LIST", /* 0x9b */ + "CLR", /* 0x9c */ + "CMD", /* 0x9d */ + "SYS", /* 0x9e */ + "OPEN", /* 0x9f */ + "CLOSE", /* 0xa0 */ + "GET", /* 0xa1 */ + "NEW", /* 0xa2 */ + "TAB(", /* 0xa3 */ + "TO", /* 0xa4 */ + "FN", /* 0xa5 */ + "SPC(", /* 0xa6 */ + "THEN", /* 0xa7 */ + "NOT", /* 0xa8 */ + "STEP", /* 0xa9 */ + "+", /* 0xaa */ + "-", /* 0xab */ + "*", /* 0xac */ + "/", /* 0xad */ + "^", /* 0xae */ + "AND", /* 0xaf */ + "OR", /* 0xb0 */ + ">", /* 0xb1 */ + "=", /* 0xb2 */ + "<", /* 0xb3 */ + "SGN", /* 0xb4 */ + "INT", /* 0xb5 */ + "ABS", /* 0xb6 */ + "USR", /* 0xb7 */ + "FRE", /* 0xb8 */ + "POS", /* 0xb9 */ + "SQR", /* 0xba */ + "RND", /* 0xbb */ + "LOG", /* 0xbc */ + "EXP", /* 0xbd */ + "COS", /* 0xbe */ + "SIN", /* 0xbf */ + "TAN", /* 0xc0 */ + "ATN", /* 0xc1 */ + "PEEK", /* 0xc2 */ + "LEN", /* 0xc3 */ + "STR$", /* 0xc4 */ + "VAL", /* 0xc5 */ + "ASC", /* 0xc6 */ + "CHR$", /* 0xc7 */ + "LEFT$", /* 0xc8 */ + "RIGHT$", /* 0xc9 */ + "MID$", /* 0xca */ + "GO", /* 0xcb */ + NULL, /* 0xcc */ + NULL, /* 0xcd */ + NULL, /* 0xce */ + NULL, /* 0xcf */ + NULL, /* 0xd0 */ + NULL, /* 0xd1 */ + NULL, /* 0xd2 */ + NULL, /* 0xd3 */ + NULL, /* 0xd4 */ + NULL, /* 0xd5 */ + NULL, /* 0xd6 */ + NULL, /* 0xd7 */ + NULL, /* 0xd8 */ + NULL, /* 0xd9 */ + NULL, /* 0xda */ + NULL, /* 0xdb */ + NULL, /* 0xdc */ + NULL, /* 0xdd */ + NULL, /* 0xde */ + NULL, /* 0xdf */ + NULL, /* 0xe0 */ + NULL, /* 0xe1 */ + NULL, /* 0xe2 */ + NULL, /* 0xe3 */ + NULL, /* 0xe4 */ + NULL, /* 0xe5 */ + NULL, /* 0xe6 */ + NULL, /* 0xe7 */ + NULL, /* 0xe8 */ + NULL, /* 0xe9 */ + NULL, /* 0xea */ + NULL, /* 0xeb */ + NULL, /* 0xec */ + NULL, /* 0xed */ + NULL, /* 0xee */ + NULL, /* 0xef */ + NULL, /* 0xf0 */ + NULL, /* 0xf1 */ + NULL, /* 0xf2 */ + NULL, /* 0xf3 */ + NULL, /* 0xf4 */ + NULL, /* 0xf5 */ + NULL, /* 0xf6 */ + NULL, /* 0xf7 */ + NULL, /* 0xf8 */ + NULL, /* 0xf9 */ + NULL, /* 0xfa */ + NULL, /* 0xfb */ + NULL, /* 0xfc */ + NULL, /* 0xfd */ + NULL, /* 0xfe */ + "{PI}", /* 0xff - A single character shaped as greek lowercase 'PI' */ + NULL, /* 0x6400 */ + "HIRES", /* 0x6401 */ + "PLOT", /* 0x6402 */ + "LINE", /* 0x6403 */ + "BLOCK", /* 0x6404 */ + "FCHR", /* 0x6405 */ + "FCOL", /* 0x6406 */ + "FILL", /* 0x6407 */ + "REC", /* 0x6408 */ + "ROT", /* 0x6409 */ + "DRAW", /* 0x640a */ + "CHAR", /* 0x640b */ + "HI COL", /* 0x640c */ + "INV", /* 0x640d */ + "FRAC", /* 0x640e */ + "MOVE", /* 0x640f */ + "PLACE", /* 0x6410 */ + "UPB", /* 0x6411 */ + "UPW", /* 0x6412 */ + "LEFTW", /* 0x6413 */ + "LEFTB", /* 0x6414 */ + "DOWNB", /* 0x6415 */ + "DOWNW", /* 0x6416 */ + "RIGHTB", /* 0x6417 */ + "RIGHTW", /* 0x6418 */ + "MULTI", /* 0x6419 */ + "COLOUR", /* 0x641a */ + "MMOB", /* 0x641b */ + "BFLASH", /* 0x641c */ + "MOB SET", /* 0x641d */ + "MUSIC", /* 0x641e */ + "FLASH", /* 0x641f */ + "REPEAT", /* 0x6420 */ + "PLAY", /* 0x6421 */ + ">>", /* 0x6422 */ + "CENTRE", /* 0x6423 */ + "ENVELOPE", /* 0x6424 */ + "CGOTO", /* 0x6425 */ + "WAVE", /* 0x6426 */ + "FETCH", /* 0x6427 */ + "AT(", /* 0x6428 */ + "UNTIL", /* 0x6429 */ + ">>", /* 0x642a */ + ">>", /* 0x642b */ + "USE", /* 0x642c */ + ">>", /* 0x642d */ + "GLOBAL", /* 0x642e */ + ">>", /* 0x642f */ + "RESET", /* 0x6430 */ + "PROC", /* 0x6431 */ + "CALL", /* 0x6432 */ + "EXEC", /* 0x6433 */ + "END PROC", /* 0x6434 */ + "EXIT", /* 0x6435 */ + "END LOOP", /* 0x6436 */ + "ON KEY", /* 0x6437 */ + "DISABLE", /* 0x6438 */ + "RESUME", /* 0x6439 */ + "LOOP", /* 0x643a */ + "DELAY", /* 0x643b */ + ">>", /* 0x643c */ + ">>", /* 0x643d */ + ">>", /* 0x643e */ + ">>", /* 0x643f */ + "SECURE", /* 0x6440 */ + "DISAPA", /* 0x6441 */ + "CIRCLE", /* 0x6442 */ + "ON ERROR", /* 0x6443 */ + "NO ERROR", /* 0x6444 */ + "LOCAL", /* 0x6445 */ + "RCOMP", /* 0x6446 */ + "ELSE", /* 0x6447 */ + "RETRACE", /* 0x6448 */ + "TRACE", /* 0x6449 */ + "DIR", /* 0x644a */ + "PAGE", /* 0x644b */ + "DUMP", /* 0x644c */ + "FIND", /* 0x644d */ + "OPTION", /* 0x644e */ + "AUTO", /* 0x644f */ + "OLD", /* 0x6450 */ + "JOY", /* 0x6451 */ + "MOD", /* 0x6452 */ + "DIV", /* 0x6453 */ + ">>", /* 0x6454 */ + "DUP", /* 0x6455 */ + "INKEY", /* 0x6456 */ + "INST", /* 0x6457 */ + "TEST", /* 0x6458 */ + "LIN", /* 0x6459 */ + "EXOR", /* 0x645a */ + "INSERT", /* 0x645b */ + "POT", /* 0x645c */ + "PENX", /* 0x645d */ + ">>", /* 0x645e */ + "PENY", /* 0x645f */ + "SOUND", /* 0x6460 */ + "GRAPHICS", /* 0x6461 */ + "DESIGN", /* 0x6462 */ + "RLOCMOB", /* 0x6463 */ + "CMOB", /* 0x6464 */ + "BCKGNDS", /* 0x6465 */ + "PAUSE", /* 0x6466 */ + "NRM", /* 0x6467 */ + "MOB OFF", /* 0x6468 */ + "OFF", /* 0x6469 */ + "ANGL", /* 0x646a */ + "ARC", /* 0x646b */ + "COLD", /* 0x646c */ + "SCRSV", /* 0x646d */ + "SCRLD", /* 0x646e */ + "TEXT", /* 0x646f */ + "CSET", /* 0x6470 */ + "VOL", /* 0x6471 */ + "DISK", /* 0x6472 */ + "HRDCPY", /* 0x6473 */ + "KEY", /* 0x6474 */ + "PAINT", /* 0x6475 */ + "LOW COL", /* 0x6476 */ + "COPY", /* 0x6477 */ + "MERGE", /* 0x6478 */ + "RENUMBER", /* 0x6479 */ + "MEM", /* 0x647a */ + "DETECT", /* 0x647b */ + "CHECK", /* 0x647c */ + "DISPLAY", /* 0x647d */ + "ERR", /* 0x647e */ + "OUT" /* 0x647f */ +}; + +static const char *const basic_20_exp_40[] = /* "BASIC 2.0 with BASIC 4.0 Expansion" - supported by c64 & clones */ +{ + "END", /* 0x80 */ + "FOR", /* 0x81 */ + "NEXT", /* 0x82 */ + "DATA", /* 0x83 */ + "INPUT#", /* 0x84 */ + "INPUT", /* 0x85 */ + "DIM", /* 0x86 */ + "READ", /* 0x87 */ + "LET", /* 0x88 */ + "GOTO", /* 0x89 */ + "RUN", /* 0x8a */ + "IF", /* 0x8b */ + "RESTORE", /* 0x8c */ + "GOSUB", /* 0x8d */ + "RETURN", /* 0x8e */ + "REM", /* 0x8f */ + "STOP", /* 0x90 */ + "ON", /* 0x91 */ + "WAIT", /* 0x92 */ + "LOAD", /* 0x93 */ + "SAVE", /* 0x94 */ + "VERIFY", /* 0x95 */ + "DEF", /* 0x96 */ + "POKE", /* 0x97 */ + "PRINT#", /* 0x98 */ + "PRINT", /* 0x99 */ + "CONT", /* 0x9a */ + "LIST", /* 0x9b */ + "CLR", /* 0x9c */ + "CMD", /* 0x9d */ + "SYS", /* 0x9e */ + "OPEN", /* 0x9f */ + "CLOSE", /* 0xa0 */ + "GET", /* 0xa1 */ + "NEW", /* 0xa2 */ + "TAB(", /* 0xa3 */ + "TO", /* 0xa4 */ + "FN", /* 0xa5 */ + "SPC(", /* 0xa6 */ + "THEN", /* 0xa7 */ + "NOT", /* 0xa8 */ + "STEP", /* 0xa9 */ + "+", /* 0xaa */ + "-", /* 0xab */ + "*", /* 0xac */ + "/", /* 0xad */ + "^", /* 0xae */ + "AND", /* 0xaf */ + "OR", /* 0xb0 */ + ">", /* 0xb1 */ + "=", /* 0xb2 */ + "<", /* 0xb3 */ + "SGN", /* 0xb4 */ + "INT", /* 0xb5 */ + "ABS", /* 0xb6 */ + "USR", /* 0xb7 */ + "FRE", /* 0xb8 */ + "POS", /* 0xb9 */ + "SQR", /* 0xba */ + "RND", /* 0xbb */ + "LOG", /* 0xbc */ + "EXP", /* 0xbd */ + "COS", /* 0xbe */ + "SIN", /* 0xbf */ + "TAN", /* 0xc0 */ + "ATN", /* 0xc1 */ + "PEEK", /* 0xc2 */ + "LEN", /* 0xc3 */ + "STR$", /* 0xc4 */ + "VAL", /* 0xc5 */ + "ASC", /* 0xc6 */ + "CHR$", /* 0xc7 */ + "LEFT$", /* 0xc8 */ + "RIGHT$", /* 0xc9 */ + "MID$", /* 0xca */ + "GO", /* 0xcb */ + "CONCAT", /* 0xcc */ + "DOPEN", /* 0xcd */ + "DCLOSE", /* 0xce */ + "RECORD", /* 0xcf */ + "HEADER", /* 0xd0 */ + "COLLECT", /* 0xd1 */ + "BACKUP", /* 0xd2 */ + "COPY", /* 0xd3 */ + "APPEND", /* 0xd4 */ + "DSAVE", /* 0xd5 */ + "DLOAD", /* 0xd6 */ + "CATALOG", /* 0xd7 */ + "RENAME", /* 0xd8 */ + "SCRATCH", /* 0xd9 */ + "DIRECTORY", /* 0xda */ + "COLOR", /* 0xdb */ + "COLD", /* 0xdc */ + "KEY", /* 0xdd */ + "DVERIFY", /* 0xde */ + "DELETE" /* 0xdf */ + "AUTO", /* 0xe0 */ + "MERGE", /* 0xe1 */ + "OLD", /* 0xe2 */ + "MONITOR", /* 0xe3 */ + NULL, /* 0xe4 */ + NULL, /* 0xe5 */ + NULL, /* 0xe6 */ + NULL, /* 0xe7 */ + NULL, /* 0xe8 */ + NULL, /* 0xe9 */ + NULL, /* 0xea */ + NULL, /* 0xeb */ + NULL, /* 0xec */ + NULL, /* 0xed */ + NULL, /* 0xee */ + NULL, /* 0xef */ + NULL, /* 0xf0 */ + NULL, /* 0xf1 */ + NULL, /* 0xf2 */ + NULL, /* 0xf3 */ + NULL, /* 0xf4 */ + NULL, /* 0xf5 */ + NULL, /* 0xf6 */ + NULL, /* 0xf7 */ + NULL, /* 0xf8 */ + NULL, /* 0xf9 */ + NULL, /* 0xfa */ + NULL, /* 0xfb */ + NULL, /* 0xfc */ + NULL, /* 0xfd */ + NULL, /* 0xfe */ + "{PI}" /* 0xff - A single character shaped as greek lowercase 'PI' */ +}; + +static const char *const basic_35[] = /* "BASIC 3.5" - supported by c16 & clones, except c364 */ +{ + "END", /* 0x80 */ + "FOR", /* 0x81 */ + "NEXT", /* 0x82 */ + "DATA", /* 0x83 */ + "INPUT#", /* 0x84 */ + "INPUT", /* 0x85 */ + "DIM", /* 0x86 */ + "READ", /* 0x87 */ + "LET", /* 0x88 */ + "GOTO", /* 0x89 */ + "RUN", /* 0x8a */ + "IF", /* 0x8b */ + "RESTORE", /* 0x8c */ + "GOSUB", /* 0x8d */ + "RETURN", /* 0x8e */ + "REM", /* 0x8f */ + "STOP", /* 0x90 */ + "ON", /* 0x91 */ + "WAIT", /* 0x92 */ + "LOAD", /* 0x93 */ + "SAVE", /* 0x94 */ + "VERIFY", /* 0x95 */ + "DEF", /* 0x96 */ + "POKE", /* 0x97 */ + "PRINT#", /* 0x98 */ + "PRINT", /* 0x99 */ + "CONT", /* 0x9a */ + "LIST", /* 0x9b */ + "CLR", /* 0x9c */ + "CMD", /* 0x9d */ + "SYS", /* 0x9e */ + "OPEN", /* 0x9f */ + "CLOSE", /* 0xa0 */ + "GET", /* 0xa1 */ + "NEW", /* 0xa2 */ + "TAB(", /* 0xa3 */ + "TO", /* 0xa4 */ + "FN", /* 0xa5 */ + "SPC(", /* 0xa6 */ + "THEN", /* 0xa7 */ + "NOT", /* 0xa8 */ + "STEP", /* 0xa9 */ + "+", /* 0xaa */ + "-", /* 0xab */ + "*", /* 0xac */ + "/", /* 0xad */ + "^", /* 0xae */ + "AND", /* 0xaf */ + "OR", /* 0xb0 */ + ">", /* 0xb1 */ + "=", /* 0xb2 */ + "<", /* 0xb3 */ + "SGN", /* 0xb4 */ + "INT", /* 0xb5 */ + "ABS", /* 0xb6 */ + "USR", /* 0xb7 */ + "FRE", /* 0xb8 */ + "POS", /* 0xb9 */ + "SQR", /* 0xba */ + "RND", /* 0xbb */ + "LOG", /* 0xbc */ + "EXP", /* 0xbd */ + "COS", /* 0xbe */ + "SIN", /* 0xbf */ + "TAN", /* 0xc0 */ + "ATN", /* 0xc1 */ + "PEEK", /* 0xc2 */ + "LEN", /* 0xc3 */ + "STR$", /* 0xc4 */ + "VAL", /* 0xc5 */ + "ASC", /* 0xc6 */ + "CHR$", /* 0xc7 */ + "LEFT$", /* 0xc8 */ + "RIGHT$", /* 0xc9 */ + "MID$", /* 0xca */ + "GO", /* 0xcb */ + "RGR", /* 0xcc */ + "RCLR" /* 0xcd */ + "RLUM" /* 0xce */ + "JOY", /* 0xcf */ + "RDOT" /* 0xd0 */ + "DEC", /* 0xd1 */ + "HEX$", /* 0xd2 */ + "ERR$", /* 0xd3 */ + "INSTR", /* 0xd4 */ + "ELSE", /* 0xd5 */ + "RESUME", /* 0xd6 */ + "TRAP", /* 0xd7 */ + "TRON", /* 0xd8 */ + "TROFF", /* 0xd9 */ + "SOUND", /* 0xda */ + "VOL", /* 0xdb */ + "AUTO", /* 0xdc */ + "PUDEF", /* 0xdd */ + "GRAPHIC", /* 0xde */ + "PAINT", /* 0xdf */ + "CHAR", /* 0xe0 */ + "BOX", /* 0xe1 */ + "CIRCLE", /* 0xe2 */ + "GSHAPE", /* 0xe3 */ + "SSHAPE", /* 0xe4 */ + "DRAW", /* 0xe5 */ + "LOCATE", /* 0xe6 */ + "COLOR", /* 0xe7 */ + "SCNCLR", /* 0xe8 */ + "SCALE", /* 0xe9 */ + "HELP", /* 0xea */ + "DO", /* 0xeb */ + "LOOP", /* 0xec */ + "EXIT", /* 0xed */ + "DIRECTORY", /* 0xee */ + "DSAVE", /* 0xef */ + "DLOAD", /* 0xf0 */ + "HEADER", /* 0xf1 */ + "SCRATCH", /* 0xf2 */ + "COLLECT", /* 0xf3 */ + "COPY", /* 0xf4 */ + "RENAME", /* 0xf5 */ + "BACKUP", /* 0xf6 */ + "DELETE", /* 0xf7 */ + "RENUMBER", /* 0xf8 */ + "KEY", /* 0xf9 */ + "MONITOR", /* 0xfa */ + "USING", /* 0xfb */ + "UNTIL", /* 0xfc */ + "WHILE", /* 0xfd */ + NULL, /* 0xfe */ + "{PI}" /* 0xff - A single character shaped as greek lowercase 'PI' */ +}; + +static const char *const basic_35_magic_voice[] = /* "BASIC 3.5 with Magic Voice Speech Synthesizer" - supported by c364 */ +{ + "END", /* 0x80 */ + "FOR", /* 0x81 */ + "NEXT", /* 0x82 */ + "DATA", /* 0x83 */ + "INPUT#", /* 0x84 */ + "INPUT", /* 0x85 */ + "DIM", /* 0x86 */ + "READ", /* 0x87 */ + "LET", /* 0x88 */ + "GOTO", /* 0x89 */ + "RUN", /* 0x8a */ + "IF", /* 0x8b */ + "RESTORE", /* 0x8c */ + "GOSUB", /* 0x8d */ + "RETURN", /* 0x8e */ + "REM", /* 0x8f */ + "STOP", /* 0x90 */ + "ON", /* 0x91 */ + "WAIT", /* 0x92 */ + "LOAD", /* 0x93 */ + "SAVE", /* 0x94 */ + "VERIFY", /* 0x95 */ + "DEF", /* 0x96 */ + "POKE", /* 0x97 */ + "PRINT#", /* 0x98 */ + "PRINT", /* 0x99 */ + "CONT", /* 0x9a */ + "LIST", /* 0x9b */ + "CLR", /* 0x9c */ + "CMD", /* 0x9d */ + "SYS", /* 0x9e */ + "OPEN", /* 0x9f */ + "CLOSE", /* 0xa0 */ + "GET", /* 0xa1 */ + "NEW", /* 0xa2 */ + "TAB(", /* 0xa3 */ + "TO", /* 0xa4 */ + "FN", /* 0xa5 */ + "SPC(", /* 0xa6 */ + "THEN", /* 0xa7 */ + "NOT", /* 0xa8 */ + "STEP", /* 0xa9 */ + "+", /* 0xaa */ + "-", /* 0xab */ + "*", /* 0xac */ + "/", /* 0xad */ + "^", /* 0xae */ + "AND", /* 0xaf */ + "OR", /* 0xb0 */ + ">", /* 0xb1 */ + "=", /* 0xb2 */ + "<", /* 0xb3 */ + "SGN", /* 0xb4 */ + "INT", /* 0xb5 */ + "ABS", /* 0xb6 */ + "USR", /* 0xb7 */ + "FRE", /* 0xb8 */ + "POS", /* 0xb9 */ + "SQR", /* 0xba */ + "RND", /* 0xbb */ + "LOG", /* 0xbc */ + "EXP", /* 0xbd */ + "COS", /* 0xbe */ + "SIN", /* 0xbf */ + "TAN", /* 0xc0 */ + "ATN", /* 0xc1 */ + "PEEK", /* 0xc2 */ + "LEN", /* 0xc3 */ + "STR$", /* 0xc4 */ + "VAL", /* 0xc5 */ + "ASC", /* 0xc6 */ + "CHR$", /* 0xc7 */ + "LEFT$", /* 0xc8 */ + "RIGHT$", /* 0xc9 */ + "MID$", /* 0xca */ + "GO", /* 0xcb */ + "RGR", /* 0xcc */ + "RCLR" /* 0xcd */ + "RLUM" /* 0xce */ + "JOY", /* 0xcf */ + "RDOT" /* 0xd0 */ + "DEC", /* 0xd1 */ + "HEX$", /* 0xd2 */ + "ERR$", /* 0xd3 */ + "INSTR", /* 0xd4 */ + "ELSE", /* 0xd5 */ + "RESUME", /* 0xd6 */ + "TRAP", /* 0xd7 */ + "TRON", /* 0xd8 */ + "TROFF", /* 0xd9 */ + "SOUND", /* 0xda */ + "VOL", /* 0xdb */ + "AUTO", /* 0xdc */ + "PUDEF", /* 0xdd */ + "GRAPHIC", /* 0xde */ + "PAINT", /* 0xdf */ + "CHAR", /* 0xe0 */ + "BOX", /* 0xe1 */ + "CIRCLE", /* 0xe2 */ + "GSHAPE", /* 0xe3 */ + "SSHAPE", /* 0xe4 */ + "DRAW", /* 0xe5 */ + "LOCATE", /* 0xe6 */ + "COLOR", /* 0xe7 */ + "SCNCLR", /* 0xe8 */ + "SCALE", /* 0xe9 */ + "HELP", /* 0xea */ + "DO", /* 0xeb */ + "LOOP", /* 0xec */ + "EXIT", /* 0xed */ + "DIRECTORY", /* 0xee */ + "DSAVE", /* 0xef */ + "DLOAD", /* 0xf0 */ + "HEADER", /* 0xf1 */ + "SCRATCH", /* 0xf2 */ + "COLLECT", /* 0xf3 */ + "COPY", /* 0xf4 */ + "RENAME", /* 0xf5 */ + "BACKUP", /* 0xf6 */ + "DELETE", /* 0xf7 */ + "RENUMBER", /* 0xf8 */ + "KEY", /* 0xf9 */ + "MONITOR", /* 0xfa */ + "USING", /* 0xfb */ + "UNTIL", /* 0xfc */ + "WHILE", /* 0xfd */ + NULL, /* 0xfe - Prefix for additional tokens */ + "{PI}", /* 0xff - A single character shaped as greek lowercase 'PI' */ + NULL, /* 0xfe00 */ + "RATE", /* 0xfe01 */ + "VOC", /* 0xfe02 */ + NULL, /* 0xfe03 */ + "RDY", /* 0xfe04 */ + NULL, /* 0xfe05 */ + NULL, /* 0xfe06 */ + NULL, /* 0xfe07 */ + NULL, /* 0xfe08 */ + NULL, /* 0xfe09 */ + "SAY" /* 0xfe0a */ +}; + +static const char *const basic_40[] = /* "BASIC 4.0" - supported by cbm40xx & cbm80xx series, p500, cbm600 & clones */ +{ + "END", /* 0x80 */ + "FOR", /* 0x81 */ + "NEXT", /* 0x82 */ + "DATA", /* 0x83 */ + "INPUT#", /* 0x84 */ + "INPUT", /* 0x85 */ + "DIM", /* 0x86 */ + "READ", /* 0x87 */ + "LET", /* 0x88 */ + "GOTO", /* 0x89 */ + "RUN", /* 0x8a */ + "IF", /* 0x8b */ + "RESTORE", /* 0x8c */ + "GOSUB", /* 0x8d */ + "RETURN", /* 0x8e */ + "REM", /* 0x8f */ + "STOP", /* 0x90 */ + "ON", /* 0x91 */ + "WAIT", /* 0x92 */ + "LOAD", /* 0x93 */ + "SAVE", /* 0x94 */ + "VERIFY", /* 0x95 */ + "DEF", /* 0x96 */ + "POKE", /* 0x97 */ + "PRINT#", /* 0x98 */ + "PRINT", /* 0x99 */ + "CONT", /* 0x9a */ + "LIST", /* 0x9b */ + "CLR", /* 0x9c */ + "CMD", /* 0x9d */ + "SYS", /* 0x9e */ + "OPEN", /* 0x9f */ + "CLOSE", /* 0xa0 */ + "GET", /* 0xa1 */ + "NEW", /* 0xa2 */ + "TAB(", /* 0xa3 */ + "TO", /* 0xa4 */ + "FN", /* 0xa5 */ + "SPC(", /* 0xa6 */ + "THEN", /* 0xa7 */ + "NOT", /* 0xa8 */ + "STEP", /* 0xa9 */ + "+", /* 0xaa */ + "-", /* 0xab */ + "*", /* 0xac */ + "/", /* 0xad */ + "^", /* 0xae */ + "AND", /* 0xaf */ + "OR", /* 0xb0 */ + ">", /* 0xb1 */ + "=", /* 0xb2 */ + "<", /* 0xb3 */ + "SGN", /* 0xb4 */ + "INT", /* 0xb5 */ + "ABS", /* 0xb6 */ + "USR", /* 0xb7 */ + "FRE", /* 0xb8 */ + "POS", /* 0xb9 */ + "SQR", /* 0xba */ + "RND", /* 0xbb */ + "LOG", /* 0xbc */ + "EXP", /* 0xbd */ + "COS", /* 0xbe */ + "SIN", /* 0xbf */ + "TAN", /* 0xc0 */ + "ATN", /* 0xc1 */ + "PEEK", /* 0xc2 */ + "LEN", /* 0xc3 */ + "STR$", /* 0xc4 */ + "VAL", /* 0xc5 */ + "ASC", /* 0xc6 */ + "CHR$", /* 0xc7 */ + "LEFT$", /* 0xc8 */ + "RIGHT$", /* 0xc9 */ + "MID$", /* 0xca */ + "GO", /* 0xcb */ + "CONCAT", /* 0xcc */ + "DOPEN", /* 0xcd */ + "DCLOSE", /* 0xce */ + "RECORD", /* 0xcf */ + "HEADER", /* 0xd0 */ + "COLLECT", /* 0xd1 */ + "BACKUP", /* 0xd2 */ + "COPY", /* 0xd3 */ + "APPEND", /* 0xd4 */ + "DSAVE", /* 0xd5 */ + "DLOAD", /* 0xd6 */ + "CATALOG", /* 0xd7 */ + "RENAME", /* 0xd8 */ + "SCRATCH", /* 0xd9 */ + "DIRECTORY", /* 0xda */ + NULL, /* 0xdb */ + NULL, /* 0xdc */ + NULL, /* 0xdd */ + NULL, /* 0xde */ + NULL, /* 0xdf */ + NULL, /* 0xe0 */ + NULL, /* 0xe1 */ + NULL, /* 0xe2 */ + NULL, /* 0xe3 */ + NULL, /* 0xe4 */ + NULL, /* 0xe5 */ + NULL, /* 0xe6 */ + NULL, /* 0xe7 */ + NULL, /* 0xe8 */ + NULL, /* 0xe9 */ + NULL, /* 0xea */ + NULL, /* 0xeb */ + NULL, /* 0xec */ + NULL, /* 0xed */ + NULL, /* 0xee */ + NULL, /* 0xef */ + NULL, /* 0xf0 */ + NULL, /* 0xf1 */ + NULL, /* 0xf2 */ + NULL, /* 0xf3 */ + NULL, /* 0xf4 */ + NULL, /* 0xf5 */ + NULL, /* 0xf6 */ + NULL, /* 0xf7 */ + NULL, /* 0xf8 */ + NULL, /* 0xf9 */ + NULL, /* 0xfa */ + NULL, /* 0xfb */ + NULL, /* 0xfc */ + NULL, /* 0xfd */ + NULL, /* 0xfe */ + "{PI}" /* 0xff - A single character shaped as greek lowercase 'PI' */ +}; + +static const char *const basic_70[] = /* "BASIC 7.0" - supported by c128 & clones */ +{ + "END", /* 0x80 */ + "FOR", /* 0x81 */ + "NEXT", /* 0x82 */ + "DATA", /* 0x83 */ + "INPUT#", /* 0x84 */ + "INPUT", /* 0x85 */ + "DIM", /* 0x86 */ + "READ", /* 0x87 */ + "LET", /* 0x88 */ + "GOTO", /* 0x89 */ + "RUN", /* 0x8a */ + "IF", /* 0x8b */ + "RESTORE", /* 0x8c */ + "GOSUB", /* 0x8d */ + "RETURN", /* 0x8e */ + "REM", /* 0x8f */ + "STOP", /* 0x90 */ + "ON", /* 0x91 */ + "WAIT", /* 0x92 */ + "LOAD", /* 0x93 */ + "SAVE", /* 0x94 */ + "VERIFY", /* 0x95 */ + "DEF", /* 0x96 */ + "POKE", /* 0x97 */ + "PRINT#", /* 0x98 */ + "PRINT", /* 0x99 */ + "CONT", /* 0x9a */ + "LIST", /* 0x9b */ + "CLR", /* 0x9c */ + "CMD", /* 0x9d */ + "SYS", /* 0x9e */ + "OPEN", /* 0x9f */ + "CLOSE", /* 0xa0 */ + "GET", /* 0xa1 */ + "NEW", /* 0xa2 */ + "TAB(", /* 0xa3 */ + "TO", /* 0xa4 */ + "FN", /* 0xa5 */ + "SPC(", /* 0xa6 */ + "THEN", /* 0xa7 */ + "NOT", /* 0xa8 */ + "STEP", /* 0xa9 */ + "+", /* 0xaa */ + "-", /* 0xab */ + "*", /* 0xac */ + "/", /* 0xad */ + "^", /* 0xae */ + "AND", /* 0xaf */ + "OR", /* 0xb0 */ + ">", /* 0xb1 */ + "=", /* 0xb2 */ + "<", /* 0xb3 */ + "SGN", /* 0xb4 */ + "INT", /* 0xb5 */ + "ABS", /* 0xb6 */ + "USR", /* 0xb7 */ + "FRE", /* 0xb8 */ + "POS", /* 0xb9 */ + "SQR", /* 0xba */ + "RND", /* 0xbb */ + "LOG", /* 0xbc */ + "EXP", /* 0xbd */ + "COS", /* 0xbe */ + "SIN", /* 0xbf */ + "TAN", /* 0xc0 */ + "ATN", /* 0xc1 */ + "PEEK", /* 0xc2 */ + "LEN", /* 0xc3 */ + "STR$", /* 0xc4 */ + "VAL", /* 0xc5 */ + "ASC", /* 0xc6 */ + "CHR$", /* 0xc7 */ + "LEFT$", /* 0xc8 */ + "RIGHT$", /* 0xc9 */ + "MID$", /* 0xca */ + "GO", /* 0xcb */ + "RGR", /* 0xcc */ + "RCLR", /* 0xcd */ + NULL, /* 0xce - Prefix for additional tokens */ + "JOY", /* 0xcf */ + "RDOT" /* 0xd0 */ + "DEC", /* 0xd1 */ + "HEX$", /* 0xd2 */ + "ERR$", /* 0xd3 */ + "INSTR", /* 0xd4 */ + "ELSE", /* 0xd5 */ + "RESUME", /* 0xd6 */ + "TRAP", /* 0xd7 */ + "TRON", /* 0xd8 */ + "TROFF", /* 0xd9 */ + "SOUND", /* 0xda */ + "VOL", /* 0xdb */ + "AUTO", /* 0xdc */ + "PUDEF", /* 0xdd */ + "GRAPHIC", /* 0xde */ + "PAINT", /* 0xdf */ + "CHAR", /* 0xe0 */ + "BOX", /* 0xe1 */ + "CIRCLE", /* 0xe2 */ + "GSHAPE", /* 0xe3 */ + "SSHAPE", /* 0xe4 */ + "DRAW", /* 0xe5 */ + "LOCATE", /* 0xe6 */ + "COLOR", /* 0xe7 */ + "SCNCLR", /* 0xe8 */ + "SCALE", /* 0xe9 */ + "HELP", /* 0xea */ + "DO", /* 0xeb */ + "LOOP", /* 0xec */ + "EXIT", /* 0xed */ + "DIRECTORY", /* 0xee */ + "DSAVE", /* 0xef */ + "DLOAD", /* 0xf0 */ + "HEADER", /* 0xf1 */ + "SCRATCH", /* 0xf2 */ + "COLLECT", /* 0xf3 */ + "COPY", /* 0xf4 */ + "RENAME", /* 0xf5 */ + "BACKUP", /* 0xf6 */ + "DELETE", /* 0xf7 */ + "RENUMBER", /* 0xf8 */ + "KEY", /* 0xf9 */ + "MONITOR", /* 0xfa */ + "USING", /* 0xfb */ + "UNTIL", /* 0xfc */ + "WHILE", /* 0xfd */ + NULL, /* 0xfe - Prefix for additional tokens */ + "{PI}", /* 0xff - A single character shaped as greek lowercase 'PI' */ + NULL, /* 0xce00 */ + NULL, /* 0xce01 */ + "POT", /* 0xce02 */ + "BUMP", /* 0xce03 */ + "PEN", /* 0xce04 */ + "RSPPOS", /* 0xce05 */ + "RSPRITE", /* 0xce06 */ + "RSPCOLOR", /* 0xce07 */ + "XOR", /* 0xce08 */ + "RWINDOW", /* 0xce09 */ + "POINTER", /* 0xce0a */ + NULL, /* 0xfe00 */ + NULL, /* 0xfe01 */ + "BANK", /* 0xfe02 */ + "FILTER", /* 0xfe03 */ + "PLAY", /* 0xfe04 */ + "TEMPO", /* 0xfe05 */ + "MOVSPR", /* 0xfe06 */ + "SPRITE", /* 0xfe07 */ + "SPRCOLOR", /* 0xfe08 */ + "RREG", /* 0xfe09 */ + "ENVELOPE", /* 0xfe0a */ + "SLEEP", /* 0xfe0b */ + "CATALOG", /* 0xfe0c */ + "DOPEN", /* 0xfe0d */ + "APPEND", /* 0xfe0e */ + "DCLOSE", /* 0xfe0f */ + "BSAVE", /* 0xfe10 */ + "BLOAD", /* 0xfe11 */ + "RECORD", /* 0xfe12 */ + "CONCAT", /* 0xfe13 */ + "DVERIFY", /* 0xfe14 */ + "DCLEAR", /* 0xfe15 */ + "SPRSAV", /* 0xfe16 */ + "COLLISION", /* 0xfe17 */ + "BEGIN", /* 0xfe18 */ + "BEND", /* 0xfe19 */ + "WINDOW", /* 0xfe1a */ + "BOOT", /* 0xfe1b */ + "WIDTH", /* 0xfe1c */ + "SPRDEF", /* 0xfe1d */ + "QUIT", /* 0xfe1e */ + "STASH", /* 0xfe1f */ + NULL, /* 0xfe20 */ + "FETCH", /* 0xfe21 */ + NULL, /* 0xfe22 */ + "SWAP", /* 0xfe23 */ + "OFF", /* 0xfe24 */ + "FAST", /* 0xfe25 */ + "SLOW" /* 0xfe26 */ +}; + +static const char *const basic_100[] = /* "BASIC 10.0" - supported by c65 & clones */ +{ + "END", /* 0x80 */ + "FOR", /* 0x81 */ + "NEXT", /* 0x82 */ + "DATA", /* 0x83 */ + "INPUT#", /* 0x84 */ + "INPUT", /* 0x85 */ + "DIM", /* 0x86 */ + "READ", /* 0x87 */ + "LET", /* 0x88 */ + "GOTO", /* 0x89 */ + "RUN", /* 0x8a */ + "IF", /* 0x8b */ + "RESTORE", /* 0x8c */ + "GOSUB", /* 0x8d */ + "RETURN", /* 0x8e */ + "REM", /* 0x8f */ + "STOP", /* 0x90 */ + "ON", /* 0x91 */ + "WAIT", /* 0x92 */ + "LOAD", /* 0x93 */ + "SAVE", /* 0x94 */ + "VERIFY", /* 0x95 */ + "DEF", /* 0x96 */ + "POKE", /* 0x97 */ + "PRINT#", /* 0x98 */ + "PRINT", /* 0x99 */ + "CONT", /* 0x9a */ + "LIST", /* 0x9b */ + "CLR", /* 0x9c */ + "CMD", /* 0x9d */ + "SYS", /* 0x9e */ + "OPEN", /* 0x9f */ + "CLOSE", /* 0xa0 */ + "GET", /* 0xa1 */ + "NEW", /* 0xa2 */ + "TAB(", /* 0xa3 */ + "TO", /* 0xa4 */ + "FN", /* 0xa5 */ + "SPC(", /* 0xa6 */ + "THEN", /* 0xa7 */ + "NOT", /* 0xa8 */ + "STEP", /* 0xa9 */ + "+", /* 0xaa */ + "-", /* 0xab */ + "*", /* 0xac */ + "/", /* 0xad */ + "^", /* 0xae */ + "AND", /* 0xaf */ + "OR", /* 0xb0 */ + ">", /* 0xb1 */ + "=", /* 0xb2 */ + "<", /* 0xb3 */ + "SGN", /* 0xb4 */ + "INT", /* 0xb5 */ + "ABS", /* 0xb6 */ + "USR", /* 0xb7 */ + "FRE", /* 0xb8 */ + "POS", /* 0xb9 */ + "SQR", /* 0xba */ + "RND", /* 0xbb */ + "LOG", /* 0xbc */ + "EXP", /* 0xbd */ + "COS", /* 0xbe */ + "SIN", /* 0xbf */ + "TAN", /* 0xc0 */ + "ATN", /* 0xc1 */ + "PEEK", /* 0xc2 */ + "LEN", /* 0xc3 */ + "STR$", /* 0xc4 */ + "VAL", /* 0xc5 */ + "ASC", /* 0xc6 */ + "CHR$", /* 0xc7 */ + "LEFT$", /* 0xc8 */ + "RIGHT$", /* 0xc9 */ + "MID$", /* 0xca */ + "GO", /* 0xcb */ + "RGR", /* 0xcc */ + "RCLR", /* 0xcd */ + NULL, /* 0xce - Prefix for additional tokens */ + "JOY", /* 0xcf */ + "RDOT" /* 0xd0 */ + "DEC", /* 0xd1 */ + "HEX$", /* 0xd2 */ + "ERR$", /* 0xd3 */ + "INSTR", /* 0xd4 */ + "ELSE", /* 0xd5 */ + "RESUME", /* 0xd6 */ + "TRAP", /* 0xd7 */ + "TRON", /* 0xd8 */ + "TROFF", /* 0xd9 */ + "SOUND", /* 0xda */ + "VOL", /* 0xdb */ + "AUTO", /* 0xdc */ + "PUDEF", /* 0xdd */ + "GRAPHIC", /* 0xde */ + "PAINT", /* 0xdf */ + "CHAR", /* 0xe0 */ + "BOX", /* 0xe1 */ + "CIRCLE", /* 0xe2 */ + "PASTE", /* 0xe3 */ + "CUT", /* 0xe4 */ + "LINE", /* 0xe5 */ + "LOCATE", /* 0xe6 */ + "COLOR", /* 0xe7 */ + "SCNCLR", /* 0xe8 */ + "SCALE", /* 0xe9 */ + "HELP", /* 0xea */ + "DO", /* 0xeb */ + "LOOP", /* 0xec */ + "EXIT", /* 0xed */ + "DIR", /* 0xee */ + "DSAVE", /* 0xef */ + "DLOAD", /* 0xf0 */ + "HEADER", /* 0xf1 */ + "SCRATCH", /* 0xf2 */ + "COLLECT", /* 0xf3 */ + "COPY", /* 0xf4 */ + "RENAME", /* 0xf5 */ + "BACKUP", /* 0xf6 */ + "DELETE", /* 0xf7 */ + "RENUMBER", /* 0xf8 */ + "KEY", /* 0xf9 */ + "MONITOR", /* 0xfa */ + "USING", /* 0xfb */ + "UNTIL", /* 0xfc */ + "WHILE", /* 0xfd */ + NULL, /* 0xfe - Prefix for additional tokens */ + "{PI}", /* 0xff - A single character shaped as greek lowercase 'PI' */ + NULL, /* 0xce00 */ + NULL, /* 0xce01 */ + "POT", /* 0xce02 */ + "BUMP", /* 0xce03 */ + "PEN", /* 0xce04 */ + "RSPPOS", /* 0xce05 */ + "RSPRITE", /* 0xce06 */ + "RSPCOLOR", /* 0xce07 */ + "XOR", /* 0xce08 */ + "RWINDOW", /* 0xce09 */ + "POINTER", /* 0xce0a */ + NULL, /* 0xfe00 */ + NULL, /* 0xfe01 */ + "BANK", /* 0xfe02 */ + "FILTER", /* 0xfe03 */ + "PLAY", /* 0xfe04 */ + "TEMPO", /* 0xfe05 */ + "MOVSPR", /* 0xfe06 */ + "SPRITE", /* 0xfe07 */ + "SPRCOLOR", /* 0xfe08 */ + "RREG", /* 0xfe09 */ + "ENVELOPE", /* 0xfe0a */ + "SLEEP", /* 0xfe0b */ + "CATALOG", /* 0xfe0c */ + "DOPEN", /* 0xfe0d */ + "APPEND", /* 0xfe0e */ + "DCLOSE", /* 0xfe0f */ + "BSAVE", /* 0xfe10 */ + "BLOAD", /* 0xfe11 */ + "RECORD", /* 0xfe12 */ + "CONCAT", /* 0xfe13 */ + "DVERIFY", /* 0xfe14 */ + "DCLEAR", /* 0xfe15 */ + "SPRSAV", /* 0xfe16 */ + "COLLISION", /* 0xfe17 */ + "BEGIN", /* 0xfe18 */ + "BEND", /* 0xfe19 */ + "WINDOW", /* 0xfe1a */ + "BOOT", /* 0xfe1b */ + "WIDTH", /* 0xfe1c */ + "SPRDEF", /* 0xfe1d */ + "QUIT", /* 0xfe1e */ + "DMA", /* 0xfe1f */ + NULL, /* 0xfe20 */ + "DMA", /* 0xfe21 */ + NULL, /* 0xfe22 */ + "DMA", /* 0xfe23 */ + "OFF", /* 0xfe24 */ + "FAST", /* 0xfe25 */ + "SLOW", /* 0xfe26 */ + "TYPE", /* 0xfe27 */ + "BVERIFY", /* 0xfe28 */ + "ECTORY", /* 0xfe29 */ + "ERASE", /* 0xfe2a */ + "FIND", /* 0xfe2b */ + "CHANGE", /* 0xfe2c */ + "SET", /* 0xfe2d */ + "SCREEN", /* 0xfe2e */ + "POLYGON", /* 0xfe2f */ + "ELLIPSE", /* 0xfe30 */ + "VIEWPORT", /* 0xfe31 */ + "GCOPY", /* 0xfe32 */ + "PEN", /* 0xfe33 */ + "PALETTE", /* 0xfe34 */ + "DMODE", /* 0xfe35 */ + "DPAT", /* 0xfe36 */ + "PIC", /* 0xfe37 */ + "GENLOCK", /* 0xfe38 */ + "FOREGROUND", /* 0xfe39 */ + NULL, /* 0xfe3a */ + "BACKGROUND", /* 0xfe3b */ + "BORDER", /* 0xfe3c */ + "HIGHLIGHT" /* 0xfe3d */ +}; + +#endif + + + +/*************************************************************************** + COCO BASIC +***************************************************************************/ + +static const basictoken_tableent cocobas_tokenents[] = +{ + { 0x00, 0x80, cocobas_statements, std::size(cocobas_statements) }, + { 0xff, 0x80, cocobas_functions, std::size(cocobas_functions) } +}; + +static const basictokens cocobas_tokens = +{ + 0x2600, + 1, + 3, + {0xFF, 0x00, 0x00}, + true, + cocobas_tokenents, + std::size(cocobas_tokenents) +}; + +static imgtoolerr_t cocobas_readfile(imgtool::partition &partition, const char *filename, + const char *fork, imgtool::stream &destf) +{ + return basic_readfile(&cocobas_tokens, partition, filename, fork, destf); +} + +static imgtoolerr_t cocobas_writefile(imgtool::partition &partition, const char *filename, + const char *fork, imgtool::stream &sourcef, util::option_resolution *opts) +{ + return basic_writefile(&cocobas_tokens, partition, filename, fork, sourcef, opts); +} + +void filter_cocobas_getinfo(uint32_t state, union filterinfo *info) +{ + switch(state) + { + case FILTINFO_STR_NAME: info->s = "cocobas"; break; + case FILTINFO_STR_HUMANNAME: info->s = "CoCo Tokenized Basic Files"; break; + case FILTINFO_PTR_READFILE: info->read_file = cocobas_readfile; break; + case FILTINFO_PTR_WRITEFILE: info->write_file = cocobas_writefile; break; + } +} + + + +/*************************************************************************** + DRAGON BASIC +***************************************************************************/ + +static const basictoken_tableent dragonbas_tokenents[] = +{ + { 0x00, 0x80, dragonbas_statements, std::size(dragonbas_statements) }, + { 0xff, 0x80, dragonbas_functions, std::size(dragonbas_functions) } +}; + +static const basictokens dragonbas_tokens = +{ + 0x2415, + 4, + 9, + {0x55, 0x01, 0x24, 0x01, 0x00, 0x2A, 0x8B, 0x8D, 0xAA}, + true, + dragonbas_tokenents, + std::size(dragonbas_tokenents) +}; + +static imgtoolerr_t dragonbas_readfile(imgtool::partition &partition, const char *filename, + const char *fork, imgtool::stream &destf) +{ + return basic_readfile(&dragonbas_tokens, partition, filename, fork, destf); +} + +static imgtoolerr_t dragonbas_writefile(imgtool::partition &partition, const char *filename, + const char *fork, imgtool::stream &sourcef, util::option_resolution *opts) +{ + return basic_writefile(&dragonbas_tokens, partition, filename, fork, sourcef, opts); +} + +void filter_dragonbas_getinfo(uint32_t state, union filterinfo *info) +{ + switch(state) + { + case FILTINFO_STR_NAME: info->s = "dragonbas"; break; + case FILTINFO_STR_HUMANNAME: info->s = "Dragon Tokenized Basic Files"; break; + case FILTINFO_PTR_READFILE: info->read_file = dragonbas_readfile; break; + case FILTINFO_PTR_WRITEFILE: info->write_file = dragonbas_writefile; break; + } +} + + + +/*************************************************************************** + VZBASIC +***************************************************************************/ + +static const basictoken_tableent vzbas_tokenents[] = +{ + { 0x00, 0x80, vzbas, std::size(vzbas) } +}; + + + +static const basictokens vzbas_tokens = +{ + 0x7ae9, + 0, + 0, + {0x00}, + false, + vzbas_tokenents, + std::size(vzbas_tokenents) +}; + +static imgtoolerr_t vzbas_readfile(imgtool::partition &partition, const char *filename, + const char *fork, imgtool::stream &destf) +{ + return basic_readfile(&vzbas_tokens, partition, filename, fork, destf); +} + +static imgtoolerr_t vzbas_writefile(imgtool::partition &partition, const char *filename, + const char *fork, imgtool::stream &sourcef, util::option_resolution *opts) +{ + return basic_writefile(&vzbas_tokens, partition, filename, fork, sourcef, opts); +} + +void filter_vzbas_getinfo(uint32_t state, union filterinfo *info) +{ + switch(state) + { + case FILTINFO_STR_NAME: info->s = "vzbas"; break; + case FILTINFO_STR_HUMANNAME: info->s = "Laser/VZ Tokenized Basic Files"; break; + case FILTINFO_PTR_READFILE: info->read_file = vzbas_readfile; break; + case FILTINFO_PTR_WRITEFILE: info->write_file = vzbas_writefile; break; + } +} + + + +/*************************************************************************** + BML3 BASIC +***************************************************************************/ + +static const basictoken_tableent bml3bas_tokenents[] = +{ + { 0x00, 0x80, bml3bas_statements, std::size(bml3bas_statements) }, + { 0xff, 0x80, bml3bas_functions, std::size(bml3bas_functions) } +}; + +static const basictokens bml3bas_tokens = +{ + 0x2600, + 1, + 3, + {0xFF, 0x00, 0x00}, + true, + bml3bas_tokenents, + std::size(bml3bas_tokenents) +}; + +static imgtoolerr_t bml3bas_readfile(imgtool::partition &partition, const char *filename, + const char *fork, imgtool::stream &destf) +{ + return basic_readfile(&bml3bas_tokens, partition, filename, fork, destf); +} + +static imgtoolerr_t bml3bas_writefile(imgtool::partition &partition, const char *filename, + const char *fork, imgtool::stream &sourcef, util::option_resolution *opts) +{ + return basic_writefile(&bml3bas_tokens, partition, filename, fork, sourcef, opts); +} + +void filter_bml3bas_getinfo(uint32_t state, union filterinfo *info) +{ + switch(state) + { + case FILTINFO_STR_NAME: info->s = "bml3bas"; break; + case FILTINFO_STR_HUMANNAME: info->s = "Basic Master Level 3 Tokenized Basic Files"; break; + case FILTINFO_PTR_READFILE: info->read_file = bml3bas_readfile; break; + case FILTINFO_PTR_WRITEFILE: info->write_file = bml3bas_writefile; break; + } +} diff --git a/src/tools/imgtool/filteoln.cpp b/src/tools/imgtool/filteoln.cpp new file mode 100644 index 0000000..6d80e9b --- /dev/null +++ b/src/tools/imgtool/filteoln.cpp @@ -0,0 +1,108 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/**************************************************************************** + + filteoln.c + + Native end-of-line filter + +*****************************************************************************/ + +#include "imgtool.h" +#include "filter.h" + +#include + +#define EOLN (CRLF == 1 ? "\r" : (CRLF == 2 ? "\n" : (CRLF == 3 ? "\r\n" : NULL))) + + + +static imgtoolerr_t convert_stream_eolns(imgtool::stream &source, imgtool::stream &dest, const char *eoln) +{ + size_t len, i, pos; + char buffer[2000]; + int hit_cr = false; + + while((len = source.read(buffer, sizeof(buffer))) > 0) + { + pos = 0; + + for (i = 0; i < len; i++) + { + switch(buffer[i]) + { + case '\r': + case '\n': + if (!hit_cr || (buffer[i] != '\n')) + { + if (i > pos) + dest.write(buffer + pos, i - pos); + dest.write(eoln, strlen(eoln)); + } + pos = i + 1; + break; + } + hit_cr = (buffer[i] == '\r'); + } + + if (i > pos) + dest.write(buffer + pos, i - pos); + } + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t ascii_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf) +{ + imgtoolerr_t err; + imgtool::stream::ptr mem_stream; + + mem_stream = imgtool::stream::open_mem(nullptr, 0); + if (!mem_stream) + return IMGTOOLERR_OUTOFMEMORY; + + err = partition.read_file(filename, fork, *mem_stream, nullptr); + if (err) + return err; + + mem_stream->seek(SEEK_SET, 0); + return convert_stream_eolns(*mem_stream, destf, EOLN); +} + + + +static imgtoolerr_t ascii_writefile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts) +{ + imgtoolerr_t err; + imgtool::stream::ptr mem_stream; + const char *eoln; + + /* create a stream */ + mem_stream = imgtool::stream::open_mem(nullptr, 0); + if (!mem_stream) + return IMGTOOLERR_OUTOFMEMORY; + + eoln = partition.get_info_string(IMGTOOLINFO_STR_EOLN); + + err = convert_stream_eolns(sourcef, *mem_stream, eoln); + if (err) + return err; + mem_stream->seek(SEEK_SET, 0); + + return partition.write_file(filename, fork, *mem_stream, opts, nullptr); +} + + + +void filter_eoln_getinfo(uint32_t state, union filterinfo *info) +{ + switch(state) + { + case FILTINFO_STR_NAME: info->s = "ascii"; break; + case FILTINFO_STR_HUMANNAME: info->s = "Ascii"; break; + case FILTINFO_PTR_READFILE: info->read_file = ascii_readfile; break; + case FILTINFO_PTR_WRITEFILE: info->write_file = ascii_writefile; break; + } +} diff --git a/src/tools/imgtool/filter.cpp b/src/tools/imgtool/filter.cpp new file mode 100644 index 0000000..647ab15 --- /dev/null +++ b/src/tools/imgtool/filter.cpp @@ -0,0 +1,83 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + filter.c + + Imgtool filters + +***************************************************************************/ + +#include "filter.h" + +#include + +/* ----------------------------------------------------------------------- */ + +int64_t filter_get_info_int(filter_getinfoproc get_info, uint32_t state) +{ + union filterinfo info; + info.i = 0; + get_info(state, &info); + return info.i; +} + +void *filter_get_info_ptr(filter_getinfoproc get_info, uint32_t state) +{ + union filterinfo info; + info.p = nullptr; + get_info(state, &info); + return info.p; +} + +void *filter_get_info_fct(filter_getinfoproc get_info, uint32_t state) +{ + union filterinfo info; + info.f = nullptr; + get_info(state, &info); + return info.f; +} + +const char *filter_get_info_string(filter_getinfoproc get_info, uint32_t state) +{ + union filterinfo info; + info.s = nullptr; + get_info(state, &info); + return info.s; +} + +/* ----------------------------------------------------------------------- */ + +const filter_getinfoproc filters[] = +{ + filter_eoln_getinfo, + filter_cocobas_getinfo, + filter_dragonbas_getinfo, + filter_macbinary_getinfo, + filter_vzsnapshot_getinfo, + filter_vzbas_getinfo, + filter_thombas5_getinfo, + filter_thombas7_getinfo, + filter_thombas128_getinfo, + filter_thomcrypt_getinfo, + filter_bml3bas_getinfo, + filter_hp9845data_getinfo, + nullptr +}; + + + +filter_getinfoproc filter_lookup(const char *name) +{ + int i; + const char *filter_name; + + for (i = 0; filters[i]; i++) + { + filter_name = filter_get_info_string(filters[i], FILTINFO_STR_NAME); + if (!strcmp(name, filter_name)) + return filters[i]; + } + + return nullptr; +} diff --git a/src/tools/imgtool/filter.h b/src/tools/imgtool/filter.h new file mode 100644 index 0000000..75c1319 --- /dev/null +++ b/src/tools/imgtool/filter.h @@ -0,0 +1,63 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + filter.h + + Imgtool filters + +***************************************************************************/ + +#ifndef FILTER_H +#define FILTER_H + +#include "library.h" + +struct imgtool_filter; + +enum +{ + /* --- the following bits of info are returned as 64-bit signed integers --- */ + FILTINFO_INT_FIRST = 0x00000, + FILTINFO_INT_STATESIZE, + + /* --- the following bits of info are returned as pointers to data or functions --- */ + FILTINFO_PTR_FIRST = 0x10000, + FILTINFO_PTR_READFILE, + FILTINFO_PTR_WRITEFILE, + FILTINFO_PTR_CHECKSTREAM, + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + FILTINFO_STR_FIRST = 0x20000, + FILTINFO_STR_NAME, + FILTINFO_STR_HUMANNAME, + FILTINFO_STR_EXTENSION +}; + +extern const filter_getinfoproc filters[]; + +filter_getinfoproc filter_lookup(const char *name); + +/* ----------------------------------------------------------------------- */ + +int64_t filter_get_info_int(filter_getinfoproc get_info, uint32_t state); +void *filter_get_info_ptr(filter_getinfoproc get_info, uint32_t state); +void *filter_get_info_fct(filter_getinfoproc get_info, uint32_t state); +const char *filter_get_info_string(filter_getinfoproc get_info, uint32_t state); + +/* ----------------------------------------------------------------------- */ + +extern void filter_eoln_getinfo(uint32_t state, union filterinfo *info); +extern void filter_cocobas_getinfo(uint32_t state, union filterinfo *info); +extern void filter_dragonbas_getinfo(uint32_t state, union filterinfo *info); +extern void filter_macbinary_getinfo(uint32_t state, union filterinfo *info); +extern void filter_vzsnapshot_getinfo(uint32_t state, union filterinfo *info); +extern void filter_vzbas_getinfo(uint32_t state, union filterinfo *info); +extern void filter_thombas5_getinfo(uint32_t state, union filterinfo *info); +extern void filter_thombas7_getinfo(uint32_t state, union filterinfo *info); +extern void filter_thombas128_getinfo(uint32_t state, union filterinfo *info); +extern void filter_thomcrypt_getinfo(uint32_t state, union filterinfo *info); +extern void filter_bml3bas_getinfo(uint32_t state, union filterinfo *info); +extern void filter_hp9845data_getinfo(uint32_t state, union filterinfo *info); + +#endif /* FILTER_H */ diff --git a/src/tools/imgtool/formats/coco_dsk.cpp b/src/tools/imgtool/formats/coco_dsk.cpp new file mode 100644 index 0000000..33363b2 --- /dev/null +++ b/src/tools/imgtool/formats/coco_dsk.cpp @@ -0,0 +1,1187 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/********************************************************************* + + formats/coco_dsk.c + + Tandy Color Computer / Dragon disk images + +*********************************************************************/ + +#include +#include +#include + +#include "formats/coco_dsk.h" +#include "formats/basicdsk.h" +#include "formats/imageutl.h" +#include "opresolv.h" +#include + +/* ----------------------------------------------------------------------- + * JVC (Jeff Vavasour CoCo) format + * + * Used by Jeff Vavasour's CoCo Emulators + * + * Documentation taken from Tim Linder's web site: + * http://home.netcom.com/~tlindner/JVC.html + * + * A. Header length + * The header length is determined by the file length modulo 256: + * headerSize = fileLength % 256; + * This means that the header is variable length and the minimum size + * is zero bytes, and the maximum size of 255 bytes. + * + * B. Header + * Here is a description of the header bytes: + * Byte Offset Description Default + * ----------- ----------------- ------- + * 0 Sectors per track 18 + * 1 Side count 1 + * 2 Sector size code 1 + * 3 First sector ID 1 + * 4 Sector attribute flag 0 + * + * If the sector attribute flag is zero then the track count is determined + * by the formula: + * + * (fileLength - headerSize) / (sectorsPerTrack * (128 << + * sectorSizeCode)) / sideCount + * + * If the sector attribute flag is non zero then the track count is + * determined by the more complex formula: + * + * (fileLength - headerSize) / (sectorsPerTrack * ((128 << + * sectorSizeCode) + 1) ) / sideCount + * + * If the length of the header is to short to contain the geometry desired, + * then the default values are assumed. If the header length is zero the all + * of the geometry is assumed. When creating disk images it is desirable to + * make the header length as short as possible. The header should only be + * used to deviate from the default values. + * + * The sector data begins immediately after the header. If the header length + * is zero then the sector data is at the beginning file. + * + * C. Sectors per track + * This is the number of sectors per track (ones based). A value of 18 + * means there are 18 sectors per track + * + * D. Side Count + * This is the number of sides in the disk image. Values of 1 or 2 are + * acceptable. If there are two sides then the tracks are interleaved. + * The first track in the image file is track zero side 1, the second + * track in the image file is track zero side 2. + * + * E. Sector size + * The is the same value that is stored in the wd179x ID field to + * determine sector size: + * + * 0x00 128 bytes + * 0x01 256 bytes + * 0x02 512 bytes + * 0x03 1024 bytes + * + * Other values are undefined. Every sector in the disk image must be the + * same size. + * + * F. First sector ID + * This determines the first sector ID for each track. Each successive + * sector adds one to the previous ID. If the first sector ID is 1, then + * the second sector has an ID of 2, and the third has an ID of 3. + * + * G. Sector Attribute Flag + * If this byte is non zero, then each sector contains an additional + * byte prepended to the sector data. If the attribute flag is zero then + * there are no extra bytes in front of the sector data. + * + * H. Sector attribute byte + * This byte is put at the beginning of every sector if the header flag + * is turned on. The information this byte contains is the same as the + * status register (of the wd179x) would contain when a 'Read Sector' + * command was issued. The bit fields are defined as: + * + * Bit position: + * --------------- + * 7 6 5 4 3 2 1 0 + * | | | | | | | | + * | | | | | | | +--- Not used. Set to zero. + * | | | | | | +----- Not used. Set to zero. + * | | | | | +------- Not used. Set to zero. + * | | | | +--------- Set on CRC error. + * | | | +----------- Set if sector not found. + * | | +------------- Record type: 1 - Deleted Data Mark, 0 - Data Mark. + * | +--------------- Not Used. Set to zero. + * +----------------- Not Used. Set to zero. + * + * ----------------------------------------------------------------------- */ + +static int coco_jvc_decode_header(floppy_image_legacy *floppy, uint64_t size, + struct basicdsk_geometry *geometry) +{ + uint8_t header[256]; + uint8_t sector_attribute_flag; + uint16_t physical_bytes_per_sector; + uint32_t header_size, file_size; + struct basicdsk_geometry dummy_geometry; + + if (geometry) + memset(geometry, 0, sizeof(*geometry)); + else + geometry = &dummy_geometry; + + if (size > 0xFFFFFFFF) + return -1; + file_size = (uint32_t) size; + + /* read the header */ + header_size = (uint32_t) file_size % 0x100; + floppy_image_read(floppy, header, 0, header_size); + geometry->offset = header_size; + + /* byte offset 0 - sectors per track */ + geometry->sectors = (header_size > 0) ? header[0] : 18; + if (geometry->sectors <= 0) + return -1; + + /* byte offset 1 - side count */ + geometry->heads = (header_size > 1) ? header[1] : 1; + if (geometry->heads <= 0) + return -1; + + /* byte offset 2 - sector size code */ + geometry->sector_length = 128 << ((header_size > 2) ? header[2] : 1); + if (geometry->sector_length <= 0) + return -1; + + /* byte offset 3 - first sector ID */ + geometry->first_sector_id = (header_size > 3) ? header[3] : 1; + + /* byte offset 4 - sector attribute flag */ + sector_attribute_flag = (header_size > 4) ? header[4] : 0; + if (sector_attribute_flag != 0) + return -1; /* we do not support sector attribute flags */ + + physical_bytes_per_sector = geometry->sector_length; + if (sector_attribute_flag) + physical_bytes_per_sector++; + + geometry->tracks = (file_size - header_size) / geometry->sectors / geometry->heads / physical_bytes_per_sector; + + /* do we have an oddball size? reject this file if not */ + if ((file_size - header_size) % physical_bytes_per_sector) + return -1; + + /* minimum of 35 tracks; support degenerate JVC files */ + if (geometry->tracks < 35) + geometry->tracks = 35; + + return 0; +} + + + +static FLOPPY_IDENTIFY(coco_jvc_identify) +{ + uint64_t size; + size = floppy_image_size(floppy); + *vote = coco_jvc_decode_header(floppy, size, NULL) ? 0 : 100; + return FLOPPY_ERROR_SUCCESS; +} + + + +static FLOPPY_CONSTRUCT(coco_jvc_construct) +{ + struct basicdsk_geometry geometry; + uint8_t header[5]; + size_t header_size; + + if (params) + { + /* create */ + memset(&geometry, 0, sizeof(geometry)); + geometry.heads = params->lookup_int(PARAM_HEADS); + geometry.tracks = params->lookup_int(PARAM_TRACKS); + geometry.sectors = params->lookup_int(PARAM_SECTORS); + geometry.first_sector_id = params->lookup_int(PARAM_FIRST_SECTOR_ID); + geometry.sector_length = params->lookup_int(PARAM_SECTOR_LENGTH); + + header[0] = (uint8_t) geometry.sectors; + header[1] = (uint8_t) geometry.heads; + header[2] = (uint8_t) compute_log2(geometry.sector_length) - 7; + header[3] = (uint8_t) geometry.first_sector_id; + header[4] = 0; + + /* now that we have the header computed, figure out the header size */ + header_size = 0; + if (header[0] != 18) + header_size = 1; + if (header[1] != 1) + header_size = 2; + if (header[2] != 1) + header_size = 3; + if (header[3] != 1) + header_size = 4; + if (header[4] != 0) + header_size = 5; + + geometry.offset = header_size; + + floppy_image_write(floppy, header, 0, header_size); + } + else + { + /* load */ + if (coco_jvc_decode_header(floppy, floppy_image_size(floppy), &geometry)) + return FLOPPY_ERROR_INVALIDIMAGE; + } + return basicdsk_construct(floppy, &geometry); +} + + +/* ----------------------------------------------------------------------- + * OS-9 file format + * + * This file format is largely a hack because there are a large amount of + * disk images that do not have geometry image separate from the disk image + * itself. So we support OS-9 images with are simply basic disks whose + * geometry is determined by the disk image. + * + * OS-9 images identified by an LSN; which are simply blocks of 256 bytes + * + * LSN0 + * Byte size use + * $00 3 sectors on disk + * $03 1 track size in sectors + * $04 2 bytes in allocation bit map; typically 1bit/sector so for + * 35 tracks of 18 sectors each that's $4E (single sided disk) + * 40 tracks per side, 18 sectors each = $B4 + * $06 2 sectors per bit in allocation map; normally 1 + * $08 3 LSN of root directory; normally 2 but depends on size of + * $04 value + * $0B 2 owner's user number; normally 0 + * $0D 1 disk attributes; normally $FF + * $0E 2 pseudo random number for identification + * $10 1 disk format; typical is 3 + * %00000001 0=single side 1=double side + * %00000010 0=single density (non Coco) 1=double density + * %00000100 0=48tracks/inch 1=96tracks/inch + * $11 2 sectors per track; normal is $12 skip several not needed + * for Format + * $1A 5 date of creation Y:M:D:H:M + * $1F 32 ASCII name of disk, last letter has $80 added to it, + * the full 32 bytes do not need to be used. + * + * Allocation bit map, fill with zeros and set bits from low to high as + * sectors are used. So, for a fresh disk sectors LSN0,LSN1,LSN2, and LSN3 + * will be in use so the first byte will be $FF $C0 and all others + * in the map are $00 + * + * Root directory LSN2 + * Byte size use + * $00 1 attributes will be $BF + * $01 2 owners ID will be $0000 + * $03 5 date last modified will be creation date Y:M:D:H:M + * $08 1 link count; set to $02 + * $09 4 file size in bytes, set to $40 + * $0D 3 date created Y:M:D + * $10 3 block LSN set to current sector number+1 ie $03 in this case + * $13 2 size in sectors of directory block, set to $07 + * All other bytes in sector set to $00 + * LSN3 first sector of directory with names + * Fill sector with all $00 and then set listed bytes + * $00 2 value $2EAE which is .. with last byte+$80 + * $1F 1 value $02 LSN for start of this directory as there is + * none higher in tree + * $20 1 value $AE which is . with $80 added + * $3F 1 value $02 LSN for start of this directory + * ----------------------------------------------------------------------- */ + +static floperr_t coco_os9_readheader(floppy_image_legacy *floppy, struct basicdsk_geometry *geometry) +{ + uint8_t header[0x20]; + int total_sectors; + + floppy_image_read(floppy, header, 0, sizeof(header)); + + total_sectors = (header[0x00] << 16) | (header[0x01] << 8) | header[0x02]; + + memset(geometry, 0, sizeof(*geometry)); + geometry->first_sector_id = 1; + geometry->sector_length = 256; + geometry->sectors = (header[0x11] << 8) + header[0x12]; + geometry->heads = (header[0x10] & 0x01) ? 2 : 1; + + if (!geometry->sectors) + return FLOPPY_ERROR_INVALIDIMAGE; + + geometry->tracks = total_sectors / geometry->sectors / geometry->heads; + + if (total_sectors != geometry->tracks * geometry->sectors * geometry->heads) + return FLOPPY_ERROR_INVALIDIMAGE; + + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t coco_os9_post_format(floppy_image_legacy *floppy, util::option_resolution *params) +{ + uint8_t header[0x0400]; + floperr_t err; + time_t t; + struct tm *ltime; + int heads, tracks, sectors, total_sectors; + + heads = params->lookup_int(PARAM_HEADS); + tracks = params->lookup_int(PARAM_TRACKS); + sectors = params->lookup_int(PARAM_SECTORS); + total_sectors = heads * tracks * sectors; + + /* write the initial header */ + time(&t); + ltime = localtime(&t); + + memset(&header, 0, sizeof(header)); + header[0x0000] = (uint8_t) (total_sectors >> 16); + header[0x0001] = (uint8_t) (total_sectors >> 8); + header[0x0002] = (uint8_t) (total_sectors >> 0); + header[0x0003] = (uint8_t) sectors; + header[0x0004] = (uint8_t) (((total_sectors + 7) / 8) >> 8); + header[0x0005] = (uint8_t) (((total_sectors + 7) / 8) >> 0); + header[0x0006] = 0x00; + header[0x0007] = 0x01; + header[0x0008] = 0x00; + header[0x0009] = 0x00; + header[0x000a] = 0x02; + header[0x000b] = 0x00; + header[0x000c] = 0x00; + header[0x000d] = 0xff; + header[0x000e] = floppy_random_byte(floppy); + header[0x000f] = floppy_random_byte(floppy); + header[0x0010] = (heads == 2) ? 3 : 2; + header[0x0011] = (uint8_t) (sectors >> 8); + header[0x0012] = (uint8_t) (sectors >> 0); + header[0x001A] = (uint8_t) ltime->tm_year; + header[0x001B] = (uint8_t) ltime->tm_mon + 1; + header[0x001C] = (uint8_t) ltime->tm_mday; + header[0x001D] = (uint8_t) ltime->tm_hour; + header[0x001E] = (uint8_t) ltime->tm_min; + header[0x001F] = 0xA0; + header[0x0100] = 0xFF; + header[0x0101] = 0xC0; + header[0x0200] = 0xBF; + header[0x0201] = 0x00; + header[0x0202] = 0x00; + header[0x0203] = (uint8_t) ltime->tm_year; + header[0x0204] = (uint8_t) ltime->tm_mon + 1; + header[0x0205] = (uint8_t) ltime->tm_mday; + header[0x0206] = (uint8_t) ltime->tm_hour; + header[0x0207] = (uint8_t) ltime->tm_min; + header[0x0208] = 0x02; + header[0x0209] = 0x00; + header[0x020A] = 0x00; + header[0x020B] = 0x00; + header[0x020C] = 0x40; + header[0x020D] = (uint8_t) (ltime->tm_year % 100); + header[0x020E] = (uint8_t) ltime->tm_mon; + header[0x020F] = (uint8_t) ltime->tm_mday; + header[0x0210] = 0x00; + header[0x0211] = 0x00; + header[0x0212] = 0x03; + header[0x0213] = 0x00; + header[0x0214] = 0x07; + header[0x0300] = 0x2E; + header[0x0301] = 0xAE; + header[0x031F] = 0x02; + header[0x0320] = 0xAE; + header[0x033F] = 0x02; + + if (total_sectors % 8) + header[0x0100 + (total_sectors / 8)] = 0xFF >> (total_sectors % 8); + + err = floppy_write_sector(floppy, 0, 0, 1, 0, &header[0x0000], 256, 0); + if (err) + return err; + + err = floppy_write_sector(floppy, 0, 0, 2, 0, &header[0x0100], 256, 0); + if (err) + return err; + + err = floppy_write_sector(floppy, 0, 0, 3, 0, &header[0x0200], 256, 0); + if (err) + return err; + + err = floppy_write_sector(floppy, 0, 0, 4, 0, &header[0x0300], 256, 0); + if (err) + return err; + + return FLOPPY_ERROR_SUCCESS; +} + + + +static FLOPPY_IDENTIFY(coco_os9_identify) +{ + struct basicdsk_geometry geometry; + *vote = coco_os9_readheader(floppy, &geometry) ? 0 : 100; + return FLOPPY_ERROR_SUCCESS; +} + + + +static FLOPPY_CONSTRUCT(coco_os9_construct) +{ + floperr_t err; + struct basicdsk_geometry geometry; + + if (params) + { + /* create */ + memset(&geometry, 0, sizeof(geometry)); + geometry.heads = params->lookup_int(PARAM_HEADS); + geometry.tracks = params->lookup_int(PARAM_TRACKS); + geometry.sectors = params->lookup_int(PARAM_SECTORS); + geometry.first_sector_id = params->lookup_int(PARAM_FIRST_SECTOR_ID); + geometry.sector_length = params->lookup_int(PARAM_SECTOR_LENGTH); + } + else + { + /* open */ + err = coco_os9_readheader(floppy, &geometry); + if (err) + return err; + } + + /* actually construct the image */ + err = basicdsk_construct(floppy, &geometry); + floppy_callbacks(floppy)->post_format = coco_os9_post_format; + return err; +} + + + +/* ----------------------------------------------------------------------- + * VDK file format + * + * Used by Paul Burgin's PC-Dragon emulator + * + * Offset Bytes Field Description + * ------ ----- ------------ ----------- + * 0 1 magic1 Signature byte 1 ('d') + * 1 1 magic2 Signature byte 2 ('k') + * 2 2 header_len Total header length (little endian) + * 4 1 ver_actual Version of the VDK format (0x10) + * 5 1 ver_compat Backwards compatibility version (0x10) + * 6 1 source_id Identify of the file source + * 7 1 source_ver Version of the file source + * 8 1 tracks Number of tracks + * 9 1 sides Number of sides (1-2) + * 10 1 flags Various flags + * bit 0: Write protect + * bit 1: A Lock + * bit 2: F Lock + * bit 3: Disk set + * 11 1 compression Compression flags (bits 0-2) and name length + * ----------------------------------------------------------------------- */ + + + +static int coco_vdk_decode_header(floppy_image_legacy *floppy, struct basicdsk_geometry *geometry) +{ + uint8_t header[12]; + uint8_t heads, tracks, sectors; + uint16_t sector_length, offset; + uint64_t size; + + size = floppy_image_size(floppy); + + floppy_image_read(floppy, header, 0, sizeof(header)); + + if (header[0] != 'd') + return -1; + if (header[1] != 'k') + return -1; + if (header[5] != 0x10) + return -1; + if (header[11] & 0x07) + return -1; + + heads = header[9]; + tracks = header[8]; + sectors = 18; + sector_length = 0x100; + + offset = header[3] * 0x100 + header[2]; + + if (size != ((uint32_t) heads * tracks * sectors * sector_length + offset)) + return -1; + + if (geometry) + { + memset(geometry, 0, sizeof(*geometry)); + geometry->heads = heads; + geometry->tracks = tracks; + geometry->sectors = sectors; + geometry->first_sector_id = 1; + geometry->sector_length = sector_length; + geometry->offset = offset; + } + return 0; +} + + + +static FLOPPY_IDENTIFY(coco_vdk_identify) +{ + *vote = coco_vdk_decode_header(floppy, NULL) ? 0 : 100; + return FLOPPY_ERROR_SUCCESS; +} + + + +static FLOPPY_CONSTRUCT(coco_vdk_construct) +{ + struct basicdsk_geometry geometry; + uint8_t header[12]; + + if (params) + { + /* create */ + memset(&geometry, 0, sizeof(geometry)); + geometry.heads = params->lookup_int(PARAM_HEADS); + geometry.tracks = params->lookup_int(PARAM_TRACKS); + geometry.sectors = 18; + geometry.first_sector_id = 1; + geometry.sector_length = 256; + geometry.offset = sizeof(header); + + memset(&header, 0, sizeof(header)); + header[0] = 'd'; + header[1] = 'k'; + header[2] = (uint8_t) (sizeof(header) >> 0); + header[3] = (uint8_t) (sizeof(header) >> 8); + header[4] = 0x10; + header[5] = 0x10; + header[8] = (uint8_t) geometry.tracks; + header[9] = (uint8_t) geometry.heads; + + floppy_image_write(floppy, header, 0, sizeof(header)); + } + else + { + /* load */ + if (coco_vdk_decode_header(floppy, &geometry)) + return FLOPPY_ERROR_INVALIDIMAGE; + } + return basicdsk_construct(floppy, &geometry); +} + + + +/* ----------------------------------------------------------------------- + * DMK file format + * + * David M. Keil's disk image format is aptly called an 'on disk' image + * format. This means that whatever written to the disk is enocded into + * the image file. IDAMS, sector headers, traling CRCs, and intra sector + * spacing. * + * + * HEADER DESCRIPTION: + * + * Offset Bytes Field Description + * ------ ----- ------------ ----------- + * 0 1 write_prot 0xff = Writed Protected, 0x00 = R/W + * 1 1 tracks Number of tracks + * 2 2 track_length Bytes per track (little endian) + * 4 1 disk_options Miscellaneous flags + * bit 0-3: Unused + * bit 4: 1=single sided 0=dbl + * bit 5: Unused + * bit 6: Single density? + * bit 6: Ignore density flags? + * 5 7 reserved Reserved for future use + * 12 4 real_disk_code If this is 0x12345678 (little endian) + * then access a real disk drive + * (unsupported) + * + * Each track begins with a track TOC, consisting of 64 little endian 16-bit + * integers. Each integer has the following format: + * bit 0-13: Offset from beginning of track to 'FE' byte of IDAM + * Note these are always sorted from first to last. All empty + * entries are 0x00 + * bit 14: Undefined (reserved) + * bit 15: Sector double density (0=SD 1=DD) + * ----------------------------------------------------------------------- */ + +struct dmk_tag +{ + int heads; + int tracks; + uint32_t track_size; +}; + +#define DMK_HEADER_LEN 16 +#define DMK_TOC_LEN 64 +#define DMK_IDAM_LENGTH 7 +#define DMK_DATA_GAP 80 +#define DMK_LEAD_IN 32 +#define DMK_EXTRA_TRACK_LENGTH 156 + +#define dmk_idam_type(x) (x)[0] +#define dmk_idam_track(x) (x)[1] +#define dmk_idam_side(x) (x)[2] +#define dmk_idam_sector(x) (x)[3] +#define dmk_idam_sectorlength(x) (x)[4] +#define dmk_idam_crc(x) (((x)[5] << 8) + (x)[6]) +#define dmk_idam_set_crc(x, crc) (x)[5] = ((crc) >> 8); (x)[6] = ((crc) >> 0); + + +static struct dmk_tag *get_dmk_tag(floppy_image_legacy *floppy) +{ + return (dmk_tag *)floppy_tag(floppy); +} + + +static floperr_t coco_dmk_get_offset(floppy_image_legacy *floppy, int head, int track, uint64_t *offset) +{ + struct dmk_tag *tag = get_dmk_tag(floppy); + + if ((head < 0) || (head >= tag->heads) || (track < 0) || (track >= tag->tracks)) + return FLOPPY_ERROR_SEEKERROR; + + *offset = track; + *offset *= tag->heads; + *offset += head; + *offset *= tag->track_size; + *offset += DMK_HEADER_LEN; + return FLOPPY_ERROR_SUCCESS; +} + + + +static uint32_t coco_dmk_min_track_size(int sectors, int sector_length) +{ + int sector_physical_length; + sector_physical_length = 8 + 3 + DMK_IDAM_LENGTH + 22 + 12 + 3 + 1 + sector_length + 2 + 24; + return DMK_TOC_LEN * 2 + DMK_LEAD_IN + (sectors * sector_physical_length); +} + + + +static floperr_t coco_dmk_read_track(floppy_image_legacy *floppy, int head, int track, uint64_t offset, void *buffer, size_t buflen) +{ + floperr_t err; + uint64_t track_offset; + + err = coco_dmk_get_offset(floppy, head, track, &track_offset); + if (err) + return err; + + floppy_image_read(floppy, buffer, offset + track_offset, buflen); + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t coco_dmk_write_track(floppy_image_legacy *floppy, int head, int track, uint64_t offset, const void *buffer, size_t buflen) +{ + floperr_t err; + uint64_t track_offset; + + err = coco_dmk_get_offset(floppy, head, track, &track_offset); + if (err) + return err; + + floppy_image_write(floppy, buffer, offset + track_offset, buflen); + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t coco_dmk_get_track_data_offset(floppy_image_legacy *floppy, int head, int track, uint64_t *offset) +{ + *offset = DMK_TOC_LEN + 1; + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t coco_dmk_format_track(floppy_image_legacy *floppy, int head, int track, util::option_resolution *params) +{ + int sectors; + int sector_length; + int interleave; + int first_sector_id; + floperr_t err; + int physical_sector; + int logical_sector; + int track_position; + uint16_t idam_offset; + uint16_t crc; + uint8_t *track_data; + void *track_data_v; + uint32_t max_track_size; + + sectors = params->lookup_int(PARAM_SECTORS); + sector_length = params->lookup_int(PARAM_SECTOR_LENGTH); + interleave = params->lookup_int(PARAM_INTERLEAVE); + first_sector_id = params->lookup_int(PARAM_FIRST_SECTOR_ID); + + max_track_size = get_dmk_tag(floppy)->track_size; + + if (sectors > DMK_TOC_LEN) + return FLOPPY_ERROR_INTERNAL; + + if (max_track_size < coco_dmk_min_track_size(sectors, sector_length)) + return FLOPPY_ERROR_NOSPACE; + + err = floppy_load_track(floppy, head, track, true, &track_data_v, NULL); + if (err) + return err; + track_data = (uint8_t *) track_data_v; + + // set up sector map + std::vector sector_map(sectors, -1); + + physical_sector = 0; + for (logical_sector = 0; logical_sector < sectors; logical_sector++) + { + while(sector_map[physical_sector] >= 0) + { + physical_sector++; + physical_sector %= sectors; + } + + sector_map[physical_sector] = logical_sector + first_sector_id; + physical_sector += interleave + 1; + physical_sector %= sectors; + } + + // set up track table of contents + physical_sector = 0; + track_position = DMK_TOC_LEN * 2 + DMK_LEAD_IN; + while(physical_sector < DMK_TOC_LEN) + { + if (physical_sector >= sectors) + { + // no more sectors + idam_offset = 0; + } + else + { + // this is a sector + logical_sector = sector_map[physical_sector]; + + // write the sector + memset(&track_data[track_position], 0x00, 8); + track_position += 8; + + memset(&track_data[track_position], 0xA1, 3); + track_position += 3; + + idam_offset = track_position | 0x8000; + dmk_idam_type( &track_data[track_position]) = 0xFE; + dmk_idam_track( &track_data[track_position]) = track; + dmk_idam_side( &track_data[track_position]) = head; + dmk_idam_sector( &track_data[track_position]) = logical_sector; + dmk_idam_sectorlength( &track_data[track_position]) = compute_log2(sector_length / 128); + crc = ccitt_crc16(0xcdb4, &track_data[track_position], DMK_IDAM_LENGTH - 2); + dmk_idam_set_crc( &track_data[track_position], crc); + track_position += DMK_IDAM_LENGTH; + + memset(&track_data[track_position], 0x4E, 22); + track_position += 22; + + memset(&track_data[track_position], 0x00, 12); + track_position += 12; + + memset(&track_data[track_position], 0xA1, 3); + track_position += 3; + + // write sector body + track_data[track_position] = 0xFB; + memset(&track_data[track_position + 1], floppy_get_filler(floppy), sector_length); + crc = ccitt_crc16(0xcdb4, &track_data[track_position], sector_length + 1); + track_data[track_position + sector_length + 1] = (uint8_t) (crc >> 8); + track_data[track_position + sector_length + 2] = (uint8_t) (crc >> 0); + track_position += sector_length + 3; + + // write sector footer + memset(&track_data[track_position], 0x4E, 24); + track_position += 24; + } + + // write the TOC entry + track_data[physical_sector * 2 + 0] = (uint8_t) (idam_offset >> 0); + track_data[physical_sector * 2 + 1] = (uint8_t) (idam_offset >> 8); + + physical_sector++; + } + + // write track lead in + memset(&track_data[physical_sector * 2], 0x4e, DMK_LEAD_IN); + + // write track footer + assert(max_track_size >= (uint32_t)track_position); + memset(&track_data[track_position], 0x4e, max_track_size - track_position); + + return FLOPPY_ERROR_SUCCESS; +} + + + +static int coco_dmk_get_heads_per_disk(floppy_image_legacy *floppy) +{ + return get_dmk_tag(floppy)->heads; +} + + + +static int coco_dmk_get_tracks_per_disk(floppy_image_legacy *floppy) +{ + return get_dmk_tag(floppy)->tracks; +} + + + +static uint32_t coco_dmk_get_track_size(floppy_image_legacy *floppy, int head, int track) +{ + return get_dmk_tag(floppy)->track_size; +} + + + +static floperr_t coco_dmk_seek_sector_in_track(floppy_image_legacy *floppy, int head, int track, int sector, int sector_is_index, int dirtify, uint8_t **sector_data, uint32_t *sector_length) +{ + struct dmk_tag *tag = get_dmk_tag(floppy); + floperr_t err; + uint32_t idam_offset = 0; + uint16_t calculated_crc; + size_t i; + size_t offs; + int state; + uint8_t *track_data; + void *track_data_v; + size_t track_length; + size_t sec_len; + + err = floppy_load_track(floppy, head, track, dirtify, &track_data_v, &track_length); + if (err) + return err; + track_data = (uint8_t *) track_data_v; + + /* search for matching IDAM */ + for (i = 0; i < DMK_TOC_LEN; i++) + { + idam_offset = track_data[i * 2 + 1]; + idam_offset <<= 8; + idam_offset |= track_data[i * 2 + 0]; + idam_offset &= 0x3FFF; + + if (idam_offset == 0) + { + /* we've reached the end of the road */ + i = DMK_TOC_LEN; + break; + } + + if ((idam_offset + DMK_IDAM_LENGTH) >= tag->track_size) + continue; + + calculated_crc = ccitt_crc16(0xCDB4, &track_data[idam_offset], DMK_IDAM_LENGTH - 2); + + if (calculated_crc == dmk_idam_crc(&track_data[idam_offset])) + { + if (sector_is_index) + { + /* the sector is indexed; decrement the index and go */ + if (sector-- == 0) + break; + } + else + { + /* check IDAM integrity and check for matching sector */ + if (sector == dmk_idam_sector(&track_data[idam_offset]) +/* && (track == dmk_idam_track(&track_data[idam_offset])) */ +/* && (head == dmk_idam_side(&track_data[idam_offset])) */ + ) + break; + } + } + } + + if (i >= DMK_TOC_LEN) + return FLOPPY_ERROR_SEEKERROR; + + /* we found a matching sector ID */ + state = 0; + offs = idam_offset + DMK_IDAM_LENGTH; + + /* find pattern 0xA1A1FB; this represents the start of a data sector */ + for (i = 0; i < DMK_DATA_GAP; i++) + { + /* overflowing the track? */ + if ((i + offs) >= tag->track_size) + return FLOPPY_ERROR_SEEKERROR; + + if (track_data[offs + i] == 0xA1) + state++; + else if ((track_data[offs + i] == 0xFB) && state) + break; + else + state = 0; + } + if (i >= DMK_DATA_GAP) + return FLOPPY_ERROR_SEEKERROR; + + offs += i + 1; + sec_len = 128 << dmk_idam_sectorlength(&track_data[idam_offset]); + + if ((offs + sec_len) > track_length) + return FLOPPY_ERROR_INVALIDIMAGE; + + if (sector_data) + *sector_data = track_data + offs; + if (sector_length) + *sector_length = sec_len; + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t coco_dmk_get_sector_length(floppy_image_legacy *floppy, int head, int track, int sector, uint32_t *sector_length) +{ + return coco_dmk_seek_sector_in_track(floppy, head, track, sector, false, false, NULL, sector_length); +} + + + +static floperr_t coco_dmk_get_indexed_sector_info(floppy_image_legacy *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, uint32_t *sector_length, unsigned long *flags) +{ + floperr_t err; + uint32_t idam_offset; + const uint8_t *track_data; + void *track_data_v; + + if (sector_index*2 >= DMK_TOC_LEN) + return FLOPPY_ERROR_SEEKERROR; + + err = floppy_load_track(floppy, head, track, false, &track_data_v, NULL); + if (err) + return err; + track_data = (uint8_t *) track_data_v; + + idam_offset = track_data[sector_index * 2 + 1]; + idam_offset <<= 8; + idam_offset |= track_data[sector_index * 2 + 0]; + idam_offset &= 0x3FFF; + + if (idam_offset == 0) + return FLOPPY_ERROR_SEEKERROR; + + if (cylinder) + *cylinder = dmk_idam_track(&track_data[idam_offset]); + if (side) + *side = dmk_idam_side(&track_data[idam_offset]); + if (sector) + *sector = dmk_idam_sector(&track_data[idam_offset]); + if (sector_length) + *sector_length = 128 << dmk_idam_sectorlength(&track_data[idam_offset]); + if (flags) + /* TODO: read DAM or DDAM and determine flags */ + *flags = 0; + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t internal_coco_dmk_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, int sector_is_index, void *buffer, size_t buflen) +{ + floperr_t err; + uint32_t sector_length; + uint16_t crc_on_disk; + uint16_t calculated_crc; + uint8_t *sector_data; + + err = coco_dmk_seek_sector_in_track(floppy, head, track, sector, sector_is_index, false, §or_data, §or_length); + if (err) + return err; + + crc_on_disk = sector_data[sector_length + 0]; + crc_on_disk <<= 8; + crc_on_disk += sector_data[sector_length + 1]; + + calculated_crc = ccitt_crc16(0xE295, sector_data, sector_length); + if (calculated_crc != crc_on_disk) + return FLOPPY_ERROR_INVALIDIMAGE; + + memcpy(buffer, sector_data, std::min(size_t(sector_length), buflen)); + + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t internal_coco_dmk_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, int sector_is_index, const void *buffer, size_t buflen, int ddam) +{ + floperr_t err; + uint32_t sector_length; + uint8_t *sector_data; + uint16_t crc; + + err = coco_dmk_seek_sector_in_track(floppy, head, track, sector, sector_is_index, true, §or_data, §or_length); + if (err) + return err; + + if (buflen > sector_length) + return FLOPPY_ERROR_INTERNAL; + + memcpy(sector_data, buffer, buflen); + + crc = ccitt_crc16(0xE295, sector_data, sector_length); + sector_data[sector_length + 0] = crc >> 8; + sector_data[sector_length + 1] = crc >> 0; + return FLOPPY_ERROR_SUCCESS; +} + + + +static floperr_t coco_dmk_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + return internal_coco_dmk_read_sector(floppy, head, track, sector, false, buffer, buflen); +} + +static floperr_t coco_dmk_write_sector(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + return internal_coco_dmk_write_sector(floppy, head, track, sector, false, buffer, buflen, ddam); +} + +static floperr_t coco_dmk_read_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen) +{ + return internal_coco_dmk_read_sector(floppy, head, track, sector, true, buffer, buflen); +} + +static floperr_t coco_dmk_write_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector, const void *buffer, size_t buflen, int ddam) +{ + return internal_coco_dmk_write_sector(floppy, head, track, sector, true, buffer, buflen, ddam); +} + + + +static void coco_dmk_interpret_header(floppy_image_legacy *floppy, int *heads, int *tracks, int *track_size) +{ + uint8_t header[DMK_HEADER_LEN]; + + floppy_image_read(floppy, header, 0, DMK_HEADER_LEN); + + if (tracks) + *tracks = header[1]; + if (heads) + *heads = (header[4] & 0x10) ? 1 : 2; + if (track_size) + *track_size = ((int) header[3]) * 0x100 + header[2]; +} + + + +FLOPPY_CONSTRUCT(coco_dmk_construct) +{ + struct FloppyCallbacks *callbacks; + struct dmk_tag *tag; + uint8_t header[DMK_HEADER_LEN]; + int heads, tracks, track_size, sectors, sector_length; + + if (params) + { + heads = params->lookup_int(PARAM_HEADS); + tracks = params->lookup_int(PARAM_TRACKS); + sectors = params->lookup_int(PARAM_SECTORS); + sector_length = params->lookup_int(PARAM_SECTOR_LENGTH); + + track_size = coco_dmk_min_track_size(sectors, sector_length) + DMK_EXTRA_TRACK_LENGTH; + + memset(header, 0, sizeof(header)); + header[1] = tracks; + header[2] = track_size >> 0; + header[3] = track_size >> 8; + header[4] = (heads == 2) ? 0x00 : 0x10; + + floppy_image_write(floppy, header, 0, sizeof(header)); + } + else + { + coco_dmk_interpret_header(floppy, &heads, &tracks, &track_size); + } + + tag = (dmk_tag *)floppy_create_tag(floppy, sizeof(struct dmk_tag)); + if (!tag) + return FLOPPY_ERROR_OUTOFMEMORY; + tag->heads = heads; + tag->track_size = track_size; + tag->tracks = tracks; + + callbacks = floppy_callbacks(floppy); + callbacks->read_track = coco_dmk_read_track; + callbacks->write_track = coco_dmk_write_track; + callbacks->get_track_data_offset = coco_dmk_get_track_data_offset; + callbacks->format_track = coco_dmk_format_track; + callbacks->get_heads_per_disk = coco_dmk_get_heads_per_disk; + callbacks->get_tracks_per_disk = coco_dmk_get_tracks_per_disk; + callbacks->get_track_size = coco_dmk_get_track_size; + callbacks->get_sector_length = coco_dmk_get_sector_length; + callbacks->get_indexed_sector_info = coco_dmk_get_indexed_sector_info; + callbacks->read_sector = coco_dmk_read_sector; + callbacks->write_sector = coco_dmk_write_sector; + callbacks->read_indexed_sector = coco_dmk_read_indexed_sector; + callbacks->write_indexed_sector = coco_dmk_write_indexed_sector; + + return FLOPPY_ERROR_SUCCESS; +} + + + +FLOPPY_IDENTIFY(coco_dmk_identify) +{ + int heads, tracks, track_size; + uint64_t size, expected_size; + + size = floppy_image_size(floppy); + coco_dmk_interpret_header(floppy, &heads, &tracks, &track_size); + expected_size = DMK_HEADER_LEN + (heads * tracks * track_size); + *vote = (size == expected_size) ? 100 : 0; + return FLOPPY_ERROR_SUCCESS; +} + + + +/* ----------------------------------------------------------------------- */ + +LEGACY_FLOPPY_OPTIONS_START( coco ) + LEGACY_FLOPPY_OPTION( coco_jvc, "dsk", "CoCo JVC disk image", coco_jvc_identify, coco_jvc_construct, NULL, + HEADS([1]-2) + TRACKS([35]-255) + SECTORS(1-[18]-255) + SECTOR_LENGTH(128/[256]/512/1024) + FIRST_SECTOR_ID(0-[1])) + LEGACY_FLOPPY_OPTION( coco_os9, "os9", "CoCo OS-9 disk image", coco_os9_identify, coco_os9_construct, NULL, + HEADS([1]-2) + TRACKS([35]-255) + SECTORS(1-[18]-255) + SECTOR_LENGTH([256]) + FIRST_SECTOR_ID([1])) + LEGACY_FLOPPY_OPTION( coco_vdk, "vdk", "CoCo VDK disk image", coco_vdk_identify, coco_vdk_construct, NULL, + HEADS([1]-2) + TRACKS([35]-255) + SECTORS([18]) + SECTOR_LENGTH([256]) + FIRST_SECTOR_ID([1])) + LEGACY_FLOPPY_OPTION( coco_dmk, "dsk,dmk", "CoCo DMK disk image", coco_dmk_identify, coco_dmk_construct, NULL, + HEADS([1]-2) + TRACKS([35]-255) + SECTORS(1-[18]) + SECTOR_LENGTH(128/[256]/512/1024/2048/4096/8192) + INTERLEAVE(0-[6]-17) + FIRST_SECTOR_ID(0-[1])) +LEGACY_FLOPPY_OPTIONS_END diff --git a/src/tools/imgtool/formats/coco_dsk.h b/src/tools/imgtool/formats/coco_dsk.h new file mode 100644 index 0000000..bf9bccc --- /dev/null +++ b/src/tools/imgtool/formats/coco_dsk.h @@ -0,0 +1,24 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/********************************************************************* + + formats/coco_dsk.h + + Tandy Color Computer / Dragon disk images + +*********************************************************************/ + +#ifndef COCO_DSK_H +#define COCO_DSK_H + +#include "formats/flopimg_legacy.h" + + +/**************************************************************************/ + +LEGACY_FLOPPY_OPTIONS_EXTERN(coco); + +FLOPPY_IDENTIFY(coco_dmk_identify); +FLOPPY_CONSTRUCT(coco_dmk_construct); + +#endif /* COCO_DSK_H */ diff --git a/src/tools/imgtool/formats/pc_dsk_legacy.cpp b/src/tools/imgtool/formats/pc_dsk_legacy.cpp new file mode 100644 index 0000000..771f0de --- /dev/null +++ b/src/tools/imgtool/formats/pc_dsk_legacy.cpp @@ -0,0 +1,140 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/********************************************************************* + + formats/pc_dsk_legacy.cpp + + PC disk images (legacy support for imgtool) + +*********************************************************************/ + +#include "formats/pc_dsk_legacy.h" +#include "formats/basicdsk.h" + +#include "opresolv.h" + +struct pc_disk_sizes +{ + uint32_t image_size; + int sectors; + int heads; +}; + + + +static const struct pc_disk_sizes disk_sizes[] = +{ + { 8*1*40*512, 8, 1}, /* 5 1/4 inch double density single sided */ + { 8*2*40*512, 8, 2}, /* 5 1/4 inch double density */ + { 9*1*40*512, 9, 1}, /* 5 1/4 inch double density single sided */ + { 9*2*40*512, 9, 2}, /* 5 1/4 inch double density */ + {10*2*40*512, 10, 2}, /* 5 1/4 inch double density single sided */ + { 9*2*80*512, 9, 2}, /* 80 tracks 5 1/4 inch drives rare in PCs */ + { 9*2*80*512, 9, 2}, /* 3 1/2 inch double density */ + {15*2*80*512, 15, 2}, /* 5 1/4 inch high density (or japanese 3 1/2 inch high density) */ + {18*2*80*512, 18, 2}, /* 3 1/2 inch high density */ + {21*2*80*512, 21, 2}, /* 3 1/2 inch high density DMF */ + {36*2*80*512, 36, 2} /* 3 1/2 inch enhanced density */ +}; + + + +static floperr_t pc_dsk_compute_geometry(floppy_image_legacy *floppy, struct basicdsk_geometry *geometry) +{ + int i; + uint64_t size; + + memset(geometry, 0, sizeof(*geometry)); + size = floppy_image_size(floppy); + + for (i = 0; i < std::size(disk_sizes); i++) + { + if (disk_sizes[i].image_size == size) + { + geometry->sectors = disk_sizes[i].sectors; + geometry->heads = disk_sizes[i].heads; + geometry->sector_length = 512; + geometry->first_sector_id = 1; + geometry->tracks = (int) (size / disk_sizes[i].sectors / disk_sizes[i].heads / geometry->sector_length); + return FLOPPY_ERROR_SUCCESS; + } + } + + if (size >= 0x1a) + { + /* + * get info from boot sector. + * not correct on all disks + */ + uint8_t scl, spt, heads; + floppy_image_read(floppy, &scl, 0x0c, 1); + floppy_image_read(floppy, &spt, 0x18, 1); + floppy_image_read(floppy, &heads, 0x1A, 1); + + if (size == ((uint64_t) scl) * spt * heads * 0x200) + { + geometry->sectors = spt; + geometry->heads = heads; + geometry->sector_length = 512; + geometry->first_sector_id = 1; + geometry->tracks = scl; + return FLOPPY_ERROR_SUCCESS; + } + } + + return FLOPPY_ERROR_SUCCESS; +} + + + +static FLOPPY_IDENTIFY(pc_dsk_identify) +{ + floperr_t err; + struct basicdsk_geometry geometry; + + err = pc_dsk_compute_geometry(floppy, &geometry); + if (err) + return err; + + *vote = geometry.heads ? 100 : 0; + return FLOPPY_ERROR_SUCCESS; +} + + + +static FLOPPY_CONSTRUCT(pc_dsk_construct) +{ + floperr_t err; + struct basicdsk_geometry geometry; + + if (params) + { + /* create */ + memset(&geometry, 0, sizeof(geometry)); + geometry.heads = params->lookup_int(PARAM_HEADS); + geometry.tracks = params->lookup_int(PARAM_TRACKS); + geometry.sectors = params->lookup_int(PARAM_SECTORS); + geometry.first_sector_id = 1; + geometry.sector_length = 512; + } + else + { + /* open */ + err = pc_dsk_compute_geometry(floppy, &geometry); + if (err) + return err; + } + + return basicdsk_construct(floppy, &geometry); +} + + + +/* ----------------------------------------------------------------------- */ + +LEGACY_FLOPPY_OPTIONS_START( pc ) + LEGACY_FLOPPY_OPTION( pc_dsk, "dsk,ima,img,ufi,360", "PC floppy disk image", pc_dsk_identify, pc_dsk_construct, nullptr, + HEADS([1]-2) + TRACKS(40/[80]) + SECTORS(8/[9]/10/15/18/36)) +LEGACY_FLOPPY_OPTIONS_END diff --git a/src/tools/imgtool/formats/pc_dsk_legacy.h b/src/tools/imgtool/formats/pc_dsk_legacy.h new file mode 100644 index 0000000..14cb33f --- /dev/null +++ b/src/tools/imgtool/formats/pc_dsk_legacy.h @@ -0,0 +1,21 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/********************************************************************* + + formats/pc_dsk_legacy.h + + PC disk images (legacy support for imgtool) + +*********************************************************************/ + +#ifndef PC_DSK_LEGACY_H +#define PC_DSK_LEGACY_H + +#include "formats/flopimg_legacy.h" + + +/**************************************************************************/ + +LEGACY_FLOPPY_OPTIONS_EXTERN(pc); + +#endif /* PC_DSK_LEGACY_H */ diff --git a/src/tools/imgtool/formats/vt_dsk_legacy.cpp b/src/tools/imgtool/formats/vt_dsk_legacy.cpp new file mode 100644 index 0000000..6cf5785 --- /dev/null +++ b/src/tools/imgtool/formats/vt_dsk_legacy.cpp @@ -0,0 +1,58 @@ +// license:GPL-2.0+ +// copyright-holders:Juergen Buchmueller +/********************************************************************* + + formats/vt_dsk_legacy.cpp + + VTech Laser/VZ disk images (legacy support) + +*********************************************************************/ + +#include "formats/vt_dsk_legacy.h" +#include "formats/basicdsk.h" + +#include "opresolv.h" + +#include + +static FLOPPY_IDENTIFY(vz_identify) +{ + uint64_t size = floppy_image_size(floppy); + *vote = ((size == 98560) || (size == 99200) || (size == 99184)) ? 100 : 0; + return FLOPPY_ERROR_SUCCESS; +} + +static FLOPPY_CONSTRUCT(vz_construct) +{ + struct basicdsk_geometry geometry; + memset(&geometry, 0, sizeof(geometry)); + + if (params) + { + geometry.heads = params->lookup_int(PARAM_HEADS); + geometry.tracks = params->lookup_int(PARAM_TRACKS); + geometry.sectors = params->lookup_int(PARAM_SECTORS); + geometry.first_sector_id = params->lookup_int(PARAM_FIRST_SECTOR_ID); + geometry.sector_length = params->lookup_int(PARAM_SECTOR_LENGTH); + } + else + { + geometry.heads = 1; + geometry.tracks = 40; + geometry.sectors = 16; + geometry.first_sector_id = 0; + geometry.sector_length = floppy_image_size(floppy)/geometry.tracks/geometry.sectors; + } + + return basicdsk_construct(floppy, &geometry); +} + + +LEGACY_FLOPPY_OPTIONS_START(vz) + LEGACY_FLOPPY_OPTION(vtech1, "dsk", "Laser/VZ disk image", vz_identify, vz_construct, NULL, + HEADS([1]) + TRACKS([40]) + SECTORS([16]) + SECTOR_LENGTH([154]) + FIRST_SECTOR_ID([0])) +LEGACY_FLOPPY_OPTIONS_END diff --git a/src/tools/imgtool/formats/vt_dsk_legacy.h b/src/tools/imgtool/formats/vt_dsk_legacy.h new file mode 100644 index 0000000..fe8f0dd --- /dev/null +++ b/src/tools/imgtool/formats/vt_dsk_legacy.h @@ -0,0 +1,18 @@ +// license:GPL-2.0+ +// copyright-holders:Juergen Buchmueller +/********************************************************************* + + formats/vt_dsk_legacy.h + + VTech Laser/VZ disk images (legacy support) + +*********************************************************************/ + +#ifndef VT_DSK_LEGACY_H +#define VT_DSK_LEGACY_H + +#include "formats/flopimg_legacy.h" + +LEGACY_FLOPPY_OPTIONS_EXTERN(vz); + +#endif /* VT_DSK_LEGACY_H */ diff --git a/src/tools/imgtool/iflopimg.cpp b/src/tools/imgtool/iflopimg.cpp new file mode 100644 index 0000000..2d785c9 --- /dev/null +++ b/src/tools/imgtool/iflopimg.cpp @@ -0,0 +1,324 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/********************************************************************* + + iflopimg.c + + Bridge code for Imgtool into the standard floppy code + +*********************************************************************/ + +#include "iflopimg.h" + +#include "imgtool.h" +#include "library.h" + +#include "ioprocs.h" + +#include + + +imgtoolerr_t imgtool_floppy_error(floperr_t err) +{ + switch(err) + { + case FLOPPY_ERROR_SUCCESS: + return IMGTOOLERR_SUCCESS; + + case FLOPPY_ERROR_OUTOFMEMORY: + return IMGTOOLERR_OUTOFMEMORY; + + case FLOPPY_ERROR_INVALIDIMAGE: + return IMGTOOLERR_CORRUPTIMAGE; + + case FLOPPY_ERROR_SEEKERROR: + return IMGTOOLERR_SEEKERROR; + + case FLOPPY_ERROR_UNSUPPORTED: + return IMGTOOLERR_UNIMPLEMENTED; + + default: + return IMGTOOLERR_UNEXPECTED; + } +} + + + +/********************************************************************* + Imgtool handlers +*********************************************************************/ + +struct imgtool_floppy_image +{ + floppy_image_legacy *floppy; +}; + + + + +static imgtoolerr_t imgtool_floppy_open_internal(imgtool::image &image, imgtool::stream::ptr &&stream) +{ + floperr_t ferr; + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + struct imgtool_floppy_image *fimg; + const imgtool_class *imgclass; + const struct FloppyFormat *format; + imgtoolerr_t (*open)(imgtool::image &image, imgtool::stream *f); + + fimg = (struct imgtool_floppy_image *) image.extra_bytes(); + imgclass = &image.module().imgclass; + format = (const struct FloppyFormat *) imgclass->derived_param; + open = (imgtoolerr_t (*)(imgtool::image &, imgtool::stream *)) imgtool_get_info_ptr(imgclass, IMGTOOLINFO_PTR_FLOPPY_OPEN); + + // open up the floppy + ferr = floppy_open(imgtool::stream_read_write(std::move(stream), 0xff), "", format, FLOPPY_FLAGS_READWRITE, &fimg->floppy); + if (ferr) + { + err = imgtool_floppy_error(ferr); + goto done; + } + + if (open) + { + err = open(image, nullptr); + if (err) + goto done; + } + +done: + if (err && fimg->floppy) + { + floppy_close(fimg->floppy); + fimg->floppy = nullptr; + } + return err; +} + + + +static imgtoolerr_t imgtool_floppy_open(imgtool::image &image, imgtool::stream::ptr &&stream) +{ + return imgtool_floppy_open_internal(image, std::move(stream)); +} + + + +static imgtoolerr_t imgtool_floppy_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *opts) +{ + floperr_t ferr; + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + struct imgtool_floppy_image *fimg; + const imgtool_class *imgclass; + const struct FloppyFormat *format; + imgtoolerr_t (*create)(imgtool::image &, imgtool::stream *, util::option_resolution *); + imgtoolerr_t (*open)(imgtool::image &, imgtool::stream *f); + + fimg = (struct imgtool_floppy_image *) image.extra_bytes(); + imgclass = &image.module().imgclass; + format = (const struct FloppyFormat *) imgclass->derived_param; + create = (imgtoolerr_t (*)(imgtool::image &, imgtool::stream *, util::option_resolution *)) imgtool_get_info_ptr(imgclass, IMGTOOLINFO_PTR_FLOPPY_CREATE); + open = (imgtoolerr_t (*)(imgtool::image &, imgtool::stream *)) imgtool_get_info_ptr(imgclass, IMGTOOLINFO_PTR_FLOPPY_OPEN); + + // open up the floppy + ferr = floppy_create(imgtool::stream_read_write(std::move(stream), 0xff), format, opts, &fimg->floppy); + if (ferr) + { + err = imgtool_floppy_error(ferr); + goto done; + } + + // do we have to do extra stuff when creating the image? + if (create) + { + err = create(image, nullptr, opts); + if (err) + goto done; + } + + // do we have to do extra stuff when opening the image? + if (open) + { + err = open(image, nullptr); + if (err) + goto done; + } + +done: + if (err && fimg->floppy) + { + floppy_close(fimg->floppy); + fimg->floppy = nullptr; + } + return err; +} + + + +static void imgtool_floppy_close(imgtool::image &img) +{ + floppy_close(imgtool_floppy(img)); +} + + + +static imgtoolerr_t imgtool_floppy_read_sector(imgtool::image &image, uint32_t track, uint32_t head, uint32_t sector, std::vector &buffer) +{ + floperr_t ferr; + uint32_t sector_size; + + // get the sector length + ferr = floppy_get_sector_length(imgtool_floppy(image), head, track, sector, §or_size); + if (ferr) + return imgtool_floppy_error(ferr); + + // resize the buffer accordingly + try { buffer.resize(sector_size); } + catch (std::bad_alloc const &) { return IMGTOOLERR_OUTOFMEMORY; } + + // and read the sector + ferr = floppy_read_sector(imgtool_floppy(image), head, track, sector, 0, &buffer[0], sector_size); + if (ferr) + return imgtool_floppy_error(ferr); + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t imgtool_floppy_write_sector(imgtool::image &image, uint32_t track, uint32_t head, uint32_t sector, const void *buffer, size_t len, int ddam) +{ + floperr_t ferr; + + ferr = floppy_write_sector(imgtool_floppy(image), head, track, sector, 0, buffer, len, ddam); + if (ferr) + return imgtool_floppy_error(ferr); + + return IMGTOOLERR_SUCCESS; +} + + + +static void imgtool_floppy_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + const struct FloppyFormat *format; + imgtool_class derived_class; + + format = (const struct FloppyFormat *) imgclass->derived_param; + memset(&derived_class, 0, sizeof(derived_class)); + derived_class.get_info = imgclass->derived_get_info; + + switch(state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: + info->i = sizeof(struct imgtool_floppy_image) + + imgtool_get_info_int(&derived_class, IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES); + break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case IMGTOOLINFO_STR_NAME: + sprintf(info->s = imgtool_temp_str(), "%s_%s", format->name, + imgtool_get_info_string(&derived_class, IMGTOOLINFO_STR_NAME)); + break; + case IMGTOOLINFO_STR_DESCRIPTION: + sprintf(info->s = imgtool_temp_str(), "%s (%s)", format->description, + imgtool_get_info_string(&derived_class, IMGTOOLINFO_STR_DESCRIPTION)); + break; + case IMGTOOLINFO_STR_FILE_EXTENSIONS: strcpy(info->s = imgtool_temp_str(), format->extensions); break; + case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC: info->p = (void*)format->param_guidelines; break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case IMGTOOLINFO_PTR_OPEN: info->open = imgtool_floppy_open; break; + case IMGTOOLINFO_PTR_CREATE: info->create = imgtool_floppy_create; break; + case IMGTOOLINFO_PTR_CLOSE: info->close = imgtool_floppy_close; break; + case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE: info->createimage_optguide = format->param_guidelines ? &floppy_option_guide() : nullptr; break; + case IMGTOOLINFO_PTR_READ_SECTOR: info->read_sector = imgtool_floppy_read_sector; break; + case IMGTOOLINFO_PTR_WRITE_SECTOR: info->write_sector = imgtool_floppy_write_sector; break; + + default: imgclass->derived_get_info(imgclass, state, info); break; + } +} + + + +int imgtool_floppy_make_class(int index, imgtool_class *imgclass) +{ + const struct FloppyFormat *format; + + /* get the format */ + format = (const struct FloppyFormat *) + imgtool_get_info_ptr(imgclass, IMGTOOLINFO_PTR_FLOPPY_FORMAT); + assert(format); + if (!format[index].construct) + return false; + + imgclass->derived_get_info = imgclass->get_info; + imgclass->get_info = imgtool_floppy_get_info; + imgclass->derived_param = (void *) &format[index]; + return true; +} + + + +floppy_image_legacy *imgtool_floppy(imgtool::image &img) +{ + struct imgtool_floppy_image *fimg; + fimg = (struct imgtool_floppy_image *) img.extra_bytes(); + return fimg->floppy; +} + + + +static imgtoolerr_t imgtool_floppy_transfer_sector_tofrom_stream(imgtool::image &img, int head, int track, int sector, int offset, size_t length, imgtool::stream &f, int direction) +{ + floperr_t err; + floppy_image_legacy *floppy; + std::vector buffer; + + floppy = imgtool_floppy(img); + + buffer.resize(length); + + if (direction) + { + err = floppy_read_sector(floppy, head, track, sector, offset, &buffer[0], length); + if (err) + goto done; + f.write(&buffer[0], length); + } + else + { + f.read(&buffer[0], length); + err = floppy_write_sector(floppy, head, track, sector, offset, &buffer[0], length, 0); /* TODO: pass ddam argument from imgtool */ + if (err) + goto done; + } + + err = FLOPPY_ERROR_SUCCESS; + +done: + return imgtool_floppy_error(err); +} + + + +imgtoolerr_t imgtool_floppy_read_sector_to_stream(imgtool::image &img, int head, int track, int sector, int offset, size_t length, imgtool::stream &f) +{ + return imgtool_floppy_transfer_sector_tofrom_stream(img, head, track, sector, offset, length, f, 1); +} + + + +imgtoolerr_t imgtool_floppy_write_sector_from_stream(imgtool::image &img, int head, int track, int sector, int offset, size_t length, imgtool::stream &f) +{ + return imgtool_floppy_transfer_sector_tofrom_stream(img, head, track, sector, offset, length, f, 0); +} + + + +void *imgtool_floppy_extrabytes(imgtool::image &img) +{ + struct imgtool_floppy_image *fimg; + fimg = (struct imgtool_floppy_image *) img.extra_bytes(); + return fimg + 1; +} diff --git a/src/tools/imgtool/iflopimg.h b/src/tools/imgtool/iflopimg.h new file mode 100644 index 0000000..43bbb1e --- /dev/null +++ b/src/tools/imgtool/iflopimg.h @@ -0,0 +1,44 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/********************************************************************* + + iflopimg.h + + Bridge code for Imgtool into the standard floppy code + +*********************************************************************/ +#ifndef MAME_TOOLS_IMGTOOL_IFLOPIMG_H +#define MAME_TOOLS_IMGTOOL_IFLOPIMG_H + +#pragma once + +#include "library.h" + +#include "formats/flopimg_legacy.h" + + +/*************************************************************************** + + Prototypes + +***************************************************************************/ + +enum +{ + IMGTOOLINFO_PTR_FLOPPY_FORMAT = IMGTOOLINFO_PTR_CLASS_SPECIFIC, + IMGTOOLINFO_PTR_FLOPPY_OPEN, + IMGTOOLINFO_PTR_FLOPPY_CREATE +}; + +int imgtool_floppy_make_class(int index, imgtool_class *imgclass); + +floppy_image_legacy *imgtool_floppy(imgtool::image &img); +imgtoolerr_t imgtool_floppy_error(floperr_t err); + +imgtoolerr_t imgtool_floppy_read_sector_to_stream(imgtool::image &img, int head, int track, int sector, int offset, size_t length, imgtool::stream &f); +imgtoolerr_t imgtool_floppy_write_sector_from_stream(imgtool::image &img, int head, int track, int sector, int offset, size_t length, imgtool::stream &f); + + +void *imgtool_floppy_extrabytes(imgtool::image &img); + +#endif // MAME_TOOLS_IMGTOOL_IFLOPIMG_H diff --git a/src/tools/imgtool/imghd.cpp b/src/tools/imgtool/imghd.cpp new file mode 100644 index 0000000..09a694c --- /dev/null +++ b/src/tools/imgtool/imghd.cpp @@ -0,0 +1,250 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods, Raphael Nabet +/* + Code to interface the MESS image code with MAME's harddisk core. + + We do not support diff files as it will involve some changes in the MESS + image code. + + Raphael Nabet 2003 +*/ + +#include "imghd.h" +#include "library.h" +#include "stream.h" + +#include "harddisk.h" +#include "opresolv.h" + + +static imgtoolerr_t map_chd_error(std::error_condition chderr) +{ + if (!chderr) + return IMGTOOLERR_SUCCESS; + else if (std::errc::not_enough_memory == chderr) + return IMGTOOLERR_OUTOFMEMORY; + else if (chd_file::error::FILE_NOT_WRITEABLE == chderr) + return IMGTOOLERR_READONLY; + else + return IMGTOOLERR_UNEXPECTED; +} + + + +/* + imghd_create() + + Create a MAME HD image +*/ +imgtoolerr_t imghd_create(imgtool::stream &stream, uint32_t hunksize, uint32_t cylinders, uint32_t heads, uint32_t sectors, uint32_t seclen) +{ + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + chd_file chd; + std::error_condition rc; + chd_codec_type compression[4] = { CHD_CODEC_NONE }; + + /* sanity check args -- see parse_hunk_size() in src/lib/util/chd.cpp */ + if (hunksize > (1024 * 1024)) + { + err = IMGTOOLERR_PARAMCORRUPT; + return err; + } + if (hunksize <= 0) + hunksize = 4096; /* default value */ + + /* bail if we are read only */ + if (stream.is_read_only()) + { + err = IMGTOOLERR_READONLY; + return err; + } + + /* calculations */ + const uint64_t logicalbytes = (uint64_t)cylinders * heads * sectors * seclen; + + /* create the new hard drive */ + rc = chd.create(stream_read_write(stream, 0), logicalbytes, hunksize, seclen, compression); + if (rc) + { + err = map_chd_error(rc); + return err; + } + + /* write the metadata */ + const std::string metadata = util::string_format(HARD_DISK_METADATA_FORMAT, cylinders, heads, sectors, seclen); + rc = chd.write_metadata(HARD_DISK_METADATA_TAG, 0, metadata); + if (rc) + { + err = map_chd_error(rc); + return err; + } + + /* alloc and zero buffer */ + std::vector cache; + cache.resize(hunksize); + memset(&cache[0], 0, hunksize); + + /* zero out every hunk */ + const int totalhunks = (logicalbytes + hunksize - 1) / hunksize; + for (int hunknum = 0; hunknum < totalhunks; hunknum++) + { + rc = chd.write_units(hunknum, &cache[0]); + if (rc) + { + err = IMGTOOLERR_WRITEERROR; + return err; + } + } + + return err; +} + + + +/* + imghd_open() + + Open stream as a MAME HD image +*/ +imgtoolerr_t imghd_open(imgtool::stream &stream, struct mess_hard_disk_file *hard_disk) +{ + std::error_condition chderr; + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + + hard_disk->hard_disk = nullptr; + + chderr = hard_disk->chd.open(stream_read_write(stream, 0), !stream.is_read_only()); + if (chderr) + { + err = map_chd_error(chderr); + goto done; + } + + hard_disk->hard_disk = hard_disk_open(&hard_disk->chd); + if (!hard_disk->hard_disk) + { + err = IMGTOOLERR_UNEXPECTED; + goto done; + } + hard_disk->stream = &stream; + +done: + if (err) + imghd_close(hard_disk); + return err; +} + + + +/* + imghd_close() + + Close MAME HD image +*/ +void imghd_close(struct mess_hard_disk_file *disk) +{ + if (disk->hard_disk) + { + hard_disk_close(disk->hard_disk); + disk->hard_disk = nullptr; + } + if (disk->stream) + { + delete disk->stream; + disk->stream = nullptr; + } +} + + + +/* + imghd_read() + + Read sector(s) from MAME HD image +*/ +imgtoolerr_t imghd_read(struct mess_hard_disk_file *disk, uint32_t lbasector, void *buffer) +{ + uint32_t reply = hard_disk_read(disk->hard_disk, lbasector, buffer); + return reply ? IMGTOOLERR_SUCCESS : IMGTOOLERR_READERROR; +} + + + +/* + imghd_write() + + Write sector(s) from MAME HD image +*/ +imgtoolerr_t imghd_write(struct mess_hard_disk_file *disk, uint32_t lbasector, const void *buffer) +{ + uint32_t reply = hard_disk_write(disk->hard_disk, lbasector, buffer); + return reply ? IMGTOOLERR_SUCCESS : IMGTOOLERR_WRITEERROR; +} + + + +/* + imghd_get_header() + + Return pointer to the header of MAME HD image +*/ +const hard_disk_info *imghd_get_header(struct mess_hard_disk_file *disk) +{ + const hard_disk_info *reply; + reply = hard_disk_get_info(disk->hard_disk); + return reply; +} + + +static imgtoolerr_t mess_hd_image_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *createoptions); + +enum +{ + mess_hd_createopts_blocksize = 'B', + mess_hd_createopts_cylinders = 'C', + mess_hd_createopts_heads = 'D', + mess_hd_createopts_sectors = 'E', + mess_hd_createopts_seclen = 'F' +}; + +OPTION_GUIDE_START( mess_hd_create_optionguide ) + OPTION_INT(mess_hd_createopts_blocksize, "blocksize", "Sectors Per Block" ) + OPTION_INT(mess_hd_createopts_cylinders, "cylinders", "Cylinders" ) + OPTION_INT(mess_hd_createopts_heads, "heads", "Heads" ) + OPTION_INT(mess_hd_createopts_sectors, "sectors", "Total Sectors" ) + OPTION_INT(mess_hd_createopts_seclen, "seclen", "Sector Bytes" ) +OPTION_GUIDE_END + +#define mess_hd_create_optionspecs "B1-[4]-2048;C1-[32]-65536;D1-[8]-64;E1-[128]-4096;F128/256/[512]/1024/2048/4096/8192/16384/32768/65536" + + +void hd_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "mess_hd"); break; + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "MESS hard disk image"); break; + case IMGTOOLINFO_STR_FILE_EXTENSIONS: strcpy(info->s = imgtool_temp_str(), "hd"); break; + + case IMGTOOLINFO_PTR_CREATE: info->create = mess_hd_image_create; break; + + case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE: info->createimage_optguide = &mess_hd_create_optionguide; break; + case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC: strcpy(info->s = imgtool_temp_str(), mess_hd_create_optionspecs); break; + } +} + + + +static imgtoolerr_t mess_hd_image_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *createoptions) +{ + uint32_t blocksize, cylinders, heads, sectors, seclen; + + /* read options */ + blocksize = createoptions->lookup_int(mess_hd_createopts_blocksize); + cylinders = createoptions->lookup_int(mess_hd_createopts_cylinders); + heads = createoptions->lookup_int(mess_hd_createopts_heads); + sectors = createoptions->lookup_int(mess_hd_createopts_sectors); + seclen = createoptions->lookup_int(mess_hd_createopts_seclen); + + return imghd_create(*stream.get(), blocksize * seclen, cylinders, heads, sectors, seclen); +} diff --git a/src/tools/imgtool/imghd.h b/src/tools/imgtool/imghd.h new file mode 100644 index 0000000..0f96603 --- /dev/null +++ b/src/tools/imgtool/imghd.h @@ -0,0 +1,50 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/**************************************************************************** + + imghd.h + + Bridge between Imgtool and CHD hard disk images + +****************************************************************************/ + +#ifndef IMGHD_H +#define IMGHD_H + +#include "imgterrs.h" + +#include "harddisk.h" + + +namespace imgtool +{ + class stream; +} + +struct mess_hard_disk_file +{ + imgtool::stream *stream; + hard_disk_file *hard_disk; + chd_file chd; +}; + + +/* create a new hard disk */ +imgtoolerr_t imghd_create(imgtool::stream &stream, uint32_t blocksize, uint32_t cylinders, uint32_t heads, uint32_t sectors, uint32_t seclen); + +/* opens a hard disk given an Imgtool stream */ +imgtoolerr_t imghd_open(imgtool::stream &stream, mess_hard_disk_file *hard_disk); + +/* close a hard disk */ +void imghd_close(struct mess_hard_disk_file *disk); + +/* reads data from a hard disk */ +imgtoolerr_t imghd_read(struct mess_hard_disk_file *disk, uint32_t lbasector, void *buffer); + +/* writes data to a hard disk */ +imgtoolerr_t imghd_write(struct mess_hard_disk_file *disk, uint32_t lbasector, const void *buffer); + +/* gets the header from a hard disk */ +const hard_disk_info *imghd_get_header(struct mess_hard_disk_file *disk); + +#endif /* IMGHD_H */ diff --git a/src/tools/imgtool/imgterrs.cpp b/src/tools/imgtool/imgterrs.cpp new file mode 100644 index 0000000..9018ed7 --- /dev/null +++ b/src/tools/imgtool/imgterrs.cpp @@ -0,0 +1,56 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + imgterrs.c + + Imgtool errors + +***************************************************************************/ + +#include "imgterrs.h" + +#include +#include + +static const char *const msgs[] = +{ + "Out of memory", + "Unexpected error", + "Argument too long", + "Read error", + "Write error", + "Image is read only", + "Corrupt image", + "Corrupt file", + "Corrupt directory", + "File not found", + "Unrecognized format", + "Not implemented", + "Parameter too small", + "Parameter too large", + "Missing parameter not found", + "Inappropriate parameter", + "Invalid parameter", + "Bad file name", + "Out of space on image", + "Input past end of file", + "Cannot specify path", + "Invalid path", + "Path not found", + "Directory not empty", + "Seek error", + "File system does not support forks", + "Fork not found", + "Invalid partition" +}; + + + +const char *imgtool_error(imgtoolerr_t err) +{ + err = (imgtoolerr_t)(ERRORCODE(err) - 1); + assert(err >= 0); + assert(err < std::size(msgs)); + return msgs[err]; +} diff --git a/src/tools/imgtool/imgterrs.h b/src/tools/imgtool/imgterrs.h new file mode 100644 index 0000000..1cdafb6 --- /dev/null +++ b/src/tools/imgtool/imgterrs.h @@ -0,0 +1,70 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + imgterrs.h + + Imgtool errors + +***************************************************************************/ + +#ifndef IMGTERRS_H +#define IMGTERRS_H + +/* Error codes */ +enum imgtoolerr_t +{ + IMGTOOLERR_SUCCESS, + IMGTOOLERR_OUTOFMEMORY, + IMGTOOLERR_UNEXPECTED, + IMGTOOLERR_BUFFERTOOSMALL, + IMGTOOLERR_READERROR, + IMGTOOLERR_WRITEERROR, + IMGTOOLERR_READONLY, + IMGTOOLERR_CORRUPTIMAGE, + IMGTOOLERR_CORRUPTFILE, + IMGTOOLERR_CORRUPTDIR, + IMGTOOLERR_FILENOTFOUND, + IMGTOOLERR_MODULENOTFOUND, + IMGTOOLERR_UNIMPLEMENTED, + IMGTOOLERR_PARAMTOOSMALL, + IMGTOOLERR_PARAMTOOLARGE, + IMGTOOLERR_PARAMNEEDED, + IMGTOOLERR_PARAMNOTNEEDED, + IMGTOOLERR_PARAMCORRUPT, + IMGTOOLERR_BADFILENAME, + IMGTOOLERR_NOSPACE, + IMGTOOLERR_INPUTPASTEND, + IMGTOOLERR_CANNOTUSEPATH, + IMGTOOLERR_INVALIDPATH, + IMGTOOLERR_PATHNOTFOUND, + IMGTOOLERR_DIRNOTEMPTY, + IMGTOOLERR_SEEKERROR, + IMGTOOLERR_NOFORKS, + IMGTOOLERR_FORKNOTFOUND, + IMGTOOLERR_INVALIDPARTITION +}; + + + +/* These error codes are actually modifiers that make it easier to distinguish + * the cause of an error + * + * Note - drivers should not use these modifiers + */ +#define IMGTOOLERR_SRC_MODULE 0x1000 +#define IMGTOOLERR_SRC_FUNCTIONALITY 0x2000 +#define IMGTOOLERR_SRC_IMAGEFILE 0x3000 +#define IMGTOOLERR_SRC_FILEONIMAGE 0x4000 +#define IMGTOOLERR_SRC_NATIVEFILE 0x5000 + +#define ERRORCODE(err) ((err) & 0x0fff) +#define ERRORSOURCE(err) ((err) & 0xf000) +#define ERRORPARAM(err) (((err) & 0xf0000) / 0x10000) + +#define PARAM_TO_ERROR(errcode, param) ((errcode) | ((param) * 0x10000)) + + +const char *imgtool_error(imgtoolerr_t err); + +#endif /* IMGTERRS_H */ diff --git a/src/tools/imgtool/imgtool.cpp b/src/tools/imgtool/imgtool.cpp new file mode 100644 index 0000000..c407b55 --- /dev/null +++ b/src/tools/imgtool/imgtool.cpp @@ -0,0 +1,2375 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + imgtool.cpp + + Core code for Imgtool + +***************************************************************************/ + +#include "imgtool.h" +#include "charconv.h" +#include "filter.h" +#include "library.h" +#include "modules.h" + +#include "formats/imageutl.h" + +#include "corefile.h" +#include "corestr.h" +#include "opresolv.h" + +#include +#include +#include +#include + + + +/*************************************************************************** + GLOBALS +***************************************************************************/ + +static std::unique_ptr global_imgtool_library; + +static int global_omit_untested; +static void (*global_warn)(const char *message); + + +void CLIB_DECL ATTR_PRINTF(1,2) logerror(const char *format, ...) +{ + va_list arg; + va_start(arg, format); + vprintf(format, arg); + va_end(arg); +} + +/*************************************************************************** + + Imgtool initialization and basics + +***************************************************************************/ + +//------------------------------------------------- +// rtrim +//------------------------------------------------- + +void rtrim(char *buf) +{ + size_t buflen; + char *s; + + buflen = strlen(buf); + if (buflen) + { + for (s = &buf[buflen-1]; s >= buf && (*s >= '\0') && isspace(*s); s--) + *s = '\0'; + } +} + + +//------------------------------------------------- +// strncpyz +//------------------------------------------------- + +char *strncpyz(char *dest, const char *source, size_t len) +{ + char *s; + if (len) { + s = strncpy(dest, source, len - 1); + dest[len-1] = '\0'; + } + else { + s = dest; + } + return s; +} + + +//------------------------------------------------- +// extract_padded_string +//------------------------------------------------- + +static std::string extract_padded_string(const char *source, size_t len, char pad) +{ + while ((len > 0) && (source[len - 1] == pad)) + len--; + + return std::string(source, len); +} + + +//------------------------------------------------- +// extract_padded_filename - this is a common +// enough scenario that it is justified to have +// this in common code +//------------------------------------------------- + +std::string extract_padded_filename(const char *source, size_t filename_length, size_t extension_length, char pad) +{ + std::string filename = extract_padded_string(source, filename_length, pad); + std::string extension = extract_padded_string(source + filename_length, extension_length, pad); + return extension.empty() ? filename : filename + "." + extension; +} + + +//------------------------------------------------- +// markerrorsource - marks where an error source +//------------------------------------------------- + +static imgtoolerr_t markerrorsource(imgtoolerr_t err) +{ + switch(err) + { + case IMGTOOLERR_OUTOFMEMORY: + case IMGTOOLERR_UNEXPECTED: + case IMGTOOLERR_BUFFERTOOSMALL: + /* Do nothing */ + break; + + case IMGTOOLERR_FILENOTFOUND: + case IMGTOOLERR_BADFILENAME: + err = imgtoolerr_t(err | IMGTOOLERR_SRC_FILEONIMAGE); + break; + + default: + err = imgtoolerr_t(err | IMGTOOLERR_SRC_IMAGEFILE); + break; + } + return err; +} + +//------------------------------------------------- +// internal_error - debug function for raising +// internal errors +//------------------------------------------------- + +static void internal_error(const imgtool_module *module, const char *message) +{ +#ifdef MAME_DEBUG + printf("%s: %s\n", module->name.c_str(), message); +#endif +} + + +//------------------------------------------------- +// imgtool_init - initializes the imgtool core +//------------------------------------------------- + +void imgtool_init(bool omit_untested, void (*warn)(const char *message)) +{ + imgtoolerr_t err; + err = imgtool_create_canonical_library(omit_untested, global_imgtool_library); + assert(err == IMGTOOLERR_SUCCESS); + if (err == IMGTOOLERR_SUCCESS) + { + global_imgtool_library->sort(imgtool::library::sort_type::DESCRIPTION); + } + global_omit_untested = omit_untested; + global_warn = warn; +} + + + +//------------------------------------------------- +// imgtool_exit - closes out the imgtool core +//------------------------------------------------- + +void imgtool_exit(void) +{ + if (global_imgtool_library) + global_imgtool_library.reset(); + + global_warn = nullptr; +} + + + +//------------------------------------------------- +// imgtool_find_module - looks up a module +//------------------------------------------------- + +const imgtool_module *imgtool_find_module(const std::string &modulename) +{ + return global_imgtool_library->findmodule(modulename); +} + + +//------------------------------------------------- +// imgtool_find_module - looks up a module +//------------------------------------------------- + +const imgtool::library::modulelist &imgtool_get_modules() +{ + return global_imgtool_library->modules(); +} + + +//------------------------------------------------- +// imgtool_get_module_features - retrieves a +// structure identifying this module's features +// associated with an image +//------------------------------------------------- + +imgtool_module_features imgtool_get_module_features(const imgtool_module *module) +{ + imgtool_module_features features; + memset(&features, 0, sizeof(features)); + + if (module->create) + features.supports_create = 1; + if (module->open) + features.supports_open = 1; + if (module->read_sector) + features.supports_readsector = 1; + if (module->write_sector) + features.supports_writesector = 1; + return features; +} + + + +//------------------------------------------------- +// imgtool_warn - issues a warning +//------------------------------------------------- + +void imgtool_warn(const char *format, ...) +{ + va_list va; + char buffer[2000]; + + if (global_warn) + { + va_start(va, format); + vsprintf(buffer, format, va); + va_end(va); + global_warn(buffer); + } +} + + + +//------------------------------------------------- +// evaluate_module - evaluates a single file to +// determine what module can best handle a file +//------------------------------------------------- + +static imgtoolerr_t evaluate_module(const char *fname, const imgtool_module *module, float &result) +{ + imgtoolerr_t err; + imgtool::image::ptr image; + imgtool::partition::ptr partition; + imgtool::directory::ptr imageenum; + imgtool_dirent ent; + float current_result; + + result = 0.0; + + err = imgtool::image::open(module, fname, OSD_FOPEN_READ, image); + if (err) + goto done; + + if (image) + { + current_result = module->open_is_strict ? 0.9 : 0.5; + + err = imgtool::partition::open(*image, 0, partition); + if (err) + goto done; + + err = imgtool::directory::open(*partition, "", imageenum); + if (err) + goto done; + + memset(&ent, 0, sizeof(ent)); + do + { + err = imageenum->get_next(ent); + if (err) + goto done; + + if (ent.corrupt) + current_result = (current_result * 99 + 1.00f) / 100; + else + current_result = (current_result + 1.00f) / 2; + } + while(!ent.eof); + + result = current_result; + } + +done: + if (ERRORCODE(err) == IMGTOOLERR_CORRUPTIMAGE) + err = IMGTOOLERR_SUCCESS; + return err; +} + + +//------------------------------------------------- +// identify_file - attempts to determine the module +// for any given image +//------------------------------------------------- + +imgtoolerr_t imgtool::image::identify_file(const char *fname, imgtool_module **modules, size_t count) +{ + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + imgtool::library &library = *global_imgtool_library.get(); + imgtool_module *insert_module; + imgtool_module *temp_module; + size_t i = 0; + const char *extension; + float val, temp_val; + std::unique_ptr values; + + if (count <= 0) + return IMGTOOLERR_UNEXPECTED; + + for (i = 0; i < count; i++) + modules[i] = nullptr; + if (count > 1) + count--; /* null terminate */ + + try { values = std::make_unique(count); } + catch (std::bad_alloc const &) { return IMGTOOLERR_OUTOFMEMORY; } + for (i = 0; i < count; i++) + values[i] = 0.0; + + /* figure out the file extension, if any */ + extension = strrchr(fname, '.'); + if (extension) + extension++; + + /* iterate through all modules */ + for (const auto &module : library.modules()) + { + if (!extension || image_find_extension(module->extensions.c_str(), extension)) + { + err = evaluate_module(fname, module.get(), val); + if (err) + return err; + + insert_module = module.get(); + for (i = 0; (val > 0.0f) && (i < count); i++) + { + if (val > values[i]) + { + temp_val = values[i]; + temp_module = modules[i]; + values[i] = val; + modules[i] = insert_module; + val = temp_val; + insert_module = temp_module; + } + } + } + } + + if (!modules[0]) + return imgtoolerr_t(IMGTOOLERR_MODULENOTFOUND | IMGTOOLERR_SRC_IMAGEFILE); + + return IMGTOOLERR_SUCCESS; +} + + + +//------------------------------------------------- +// get_geometry - gets the geometry +// of an image; note that this may disagree with +// particular sectors; this is a common copy +// protection scheme +//------------------------------------------------- + +imgtoolerr_t imgtool::image::get_geometry(uint32_t *tracks, uint32_t *heads, uint32_t *sectors) +{ + uint32_t dummy; + + /* some sanitization, to make the callbacks easier to implement */ + if (!tracks) + tracks = &dummy; + if (!heads) + heads = &dummy; + if (!sectors) + sectors = &dummy; + *tracks = 0; + *heads = 0; + *sectors = 0; + + /* implemented? */ + if (!module().get_geometry) + return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + + return module().get_geometry(*this, tracks, heads, sectors); +} + + + +//------------------------------------------------- +// read_sector - reads a sector on an image +//------------------------------------------------- + +imgtoolerr_t imgtool::image::read_sector(uint32_t track, uint32_t head, + uint32_t sector, std::vector &buffer) +{ + // implemented? + if (!module().read_sector) + return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + + return module().read_sector(*this, track, head, sector, buffer); +} + + + +//------------------------------------------------- +// write_sector - writes a sector on an image +//------------------------------------------------- + +imgtoolerr_t imgtool::image::write_sector(uint32_t track, uint32_t head, + uint32_t sector, const void *buffer, size_t len) +{ + // implemented? + if (!module().write_sector) + return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + + return module().write_sector(*this, track, head, sector, buffer, len); +} + + + +//------------------------------------------------- +// get_block_size - gets the size of a standard +// block on an image +//------------------------------------------------- + +imgtoolerr_t imgtool::image::get_block_size(uint32_t &length) +{ + // implemented? + if (module().block_size == 0) + return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + + length = module().block_size; + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// read_block - reads a standard block on an image +//------------------------------------------------- + +imgtoolerr_t imgtool::image::read_block(uint64_t block, void *buffer) +{ + // implemented? + if (!module().read_block) + return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + + return module().read_block(*this, buffer, block); +} + + +//------------------------------------------------- +// write_block - writes a standard block on an image +//------------------------------------------------- + +imgtoolerr_t imgtool::image::write_block(uint64_t block, const void *buffer) +{ + // implemented? + if (!module().write_block) + return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + + return module().write_block(*this, buffer, block); +} + + +//------------------------------------------------- +// clear_block - clears a standard block on an image +//------------------------------------------------- + +imgtoolerr_t imgtool::image::clear_block(uint64_t block, uint8_t data) +{ + imgtoolerr_t err; + uint8_t *block_data = nullptr; + uint32_t length; + + err = get_block_size(length); + if (err) + goto done; + + block_data = (uint8_t*)malloc(length); + if (!block_data) + { + err = (imgtoolerr_t)IMGTOOLERR_OUTOFMEMORY; + goto done; + } + memset(block_data, data, length); + + err = write_block(block, block_data); + if (err) + goto done; + +done: + if (block_data) + free(block_data); + return err; +} + + +//------------------------------------------------- +// list_partitions - lists the partitions on an image +//------------------------------------------------- + +imgtoolerr_t imgtool::image::list_partitions(std::vector &partitions) +{ + imgtoolerr_t err; + + // clear out partitions first + partitions.clear(); + + // implemented? + if (module().list_partitions) + { + // if so, call the module's callback + err = module().list_partitions(*this, partitions); + if (err) + return err; + } + else + { + // default implementation + partitions.emplace_back(module().imgclass, 0, ~0); + } + return IMGTOOLERR_SUCCESS; +} + + +/*************************************************************************** + + Imgtool partition management + +***************************************************************************/ + +//------------------------------------------------- +// imgtool::partition ctor +//------------------------------------------------- + +imgtool::partition::partition(imgtool::image &image, const imgtool_class &imgclass, int partition_index, uint64_t base_block, uint64_t block_count) + : m_image(image) + , m_base_block(base_block) + , m_block_count(block_count) + , m_imgclass(imgclass) +{ + // does this partition type have extra bytes? + size_t extra_bytes_size = imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_PARTITION_EXTRA_BYTES); + if (extra_bytes_size > 0) + { + m_extra_bytes = std::make_unique(extra_bytes_size); + memset(m_extra_bytes.get(), 0, sizeof(m_extra_bytes.get()[0]) * extra_bytes_size); + } + + m_directory_extra_bytes = imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES); + m_path_separator = (char)imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_PATH_SEPARATOR); + m_alternate_path_separator = (char)imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_ALTERNATE_PATH_SEPARATOR); + m_prefer_ucase = imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_PREFER_UCASE) ? 1 : 0; + m_supports_creation_time = imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_SUPPORTS_CREATION_TIME) ? 1 : 0; + m_supports_lastmodified_time = imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_SUPPORTS_LASTMODIFIED_TIME) ? 1 : 0; + m_supports_bootblock = imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_SUPPORTS_BOOTBLOCK) ? 1 : 0; + m_begin_enum = (imgtoolerr_t(*)(imgtool::directory &, const char *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_BEGIN_ENUM); + m_next_enum = (imgtoolerr_t(*)(imgtool::directory &, imgtool_dirent &)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_NEXT_ENUM); + m_close_enum = (void(*)(imgtool::directory &)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_CLOSE_ENUM); + m_free_space = (imgtoolerr_t(*)(imgtool::partition &, uint64_t *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_FREE_SPACE); + m_read_file = (imgtoolerr_t(*)(imgtool::partition &, const char *, const char *, imgtool::stream &)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_READ_FILE); + m_write_file = (imgtoolerr_t(*)(imgtool::partition &, const char *, const char *, imgtool::stream &, util::option_resolution *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_WRITE_FILE); + m_delete_file = (imgtoolerr_t(*)(imgtool::partition &, const char *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_DELETE_FILE); + m_list_forks = (imgtoolerr_t(*)(imgtool::partition &, const char *, std::vector &forks)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_LIST_FORKS); + m_create_dir = (imgtoolerr_t(*)(imgtool::partition &, const char *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_CREATE_DIR); + m_delete_dir = (imgtoolerr_t(*)(imgtool::partition &, const char *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_DELETE_DIR); + m_list_attrs = (imgtoolerr_t(*)(imgtool::partition &, const char *, uint32_t *, size_t)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_LIST_ATTRS); + m_get_attrs = (imgtoolerr_t(*)(imgtool::partition &, const char *, const uint32_t *, imgtool_attribute *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_GET_ATTRS); + m_set_attrs = (imgtoolerr_t(*)(imgtool::partition &, const char *, const uint32_t *, const imgtool_attribute *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_SET_ATTRS); + m_attr_name = (imgtoolerr_t(*)(uint32_t, const imgtool_attribute *, char *, size_t)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_ATTR_NAME); + m_get_iconinfo = (imgtoolerr_t(*)(imgtool::partition &, const char *, imgtool_iconinfo *)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_GET_ICON_INFO); + m_suggest_transfer = (imgtoolerr_t(*)(imgtool::partition &, const char *, imgtool_transfer_suggestion *, size_t)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_SUGGEST_TRANSFER); + m_get_chain = (imgtoolerr_t(*)(imgtool::partition &, const char *, imgtool_chainent *, size_t)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_GET_CHAIN); + m_writefile_optguide = (const util::option_guide *) imgtool_get_info_ptr(&imgclass, IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE); + + const char *writefile_optspec = (const char *)imgtool_get_info_ptr(&imgclass, IMGTOOLINFO_STR_WRITEFILE_OPTSPEC); + if (writefile_optspec) + m_writefile_optspec.assign(writefile_optspec); + + // mask out if writing is untested + if (global_omit_untested && imgtool_get_info_int(&imgclass, IMGTOOLINFO_INT_WRITING_UNTESTED)) + { + m_write_file = nullptr; + m_delete_file = nullptr; + m_create_dir = nullptr; + m_delete_dir = nullptr; + m_writefile_optguide = nullptr; + m_writefile_optspec = nullptr; + } +} + + +//------------------------------------------------- +// imgtool::partition dtor +//------------------------------------------------- + +imgtool::partition::~partition() +{ +} + + +//------------------------------------------------- +// open - opens a partition +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::open(imgtool::image &image, int partition_index, imgtool::partition::ptr &partition) +{ + imgtoolerr_t err = (imgtoolerr_t)IMGTOOLERR_SUCCESS; + imgtool::partition::ptr p; + std::vector partitions; + imgtoolerr_t (*open_partition)(imgtool::partition &partition, uint64_t first_block, uint64_t block_count); + + // list the partitions + err = image.list_partitions(partitions); + if (err) + return err; + + // is this an invalid index? + if ((partition_index < 0) || (partition_index >= partitions.size()) || (!partitions[partition_index].imgclass().get_info && !partitions[partition_index].imgclass().derived_get_info)) + return IMGTOOLERR_INVALIDPARTITION; + + // use this partition + const imgtool_class &imgclass(partitions[partition_index].imgclass()); + uint64_t base_block = partitions[partition_index].base_block(); + uint64_t block_count = partitions[partition_index].block_count(); + + // allocate the new partition object + try { p = std::make_unique(image, imgclass, partition_index, base_block, block_count); } + catch (std::bad_alloc const &) + { + err = (imgtoolerr_t)IMGTOOLERR_OUTOFMEMORY; + goto done; + } + + // call the partition open function, if present + open_partition = (imgtoolerr_t (*)(imgtool::partition &, uint64_t, uint64_t)) imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_OPEN_PARTITION); + if (open_partition) + { + /* we have an open partition function */ + err = (*open_partition)(*p, base_block, block_count); + if (err) + goto done; + } + +done: + if (!err) + partition = std::move(p); + else + partition.reset(); + return err; +} + + + +/*************************************************************************** + + Imgtool partition operations + +***************************************************************************/ + +//------------------------------------------------- +// get_attribute_name - retrieves the human readable +// name for an attribute +//------------------------------------------------- + +void imgtool::partition::get_attribute_name(uint32_t attribute, const imgtool_attribute *attr_value, + char *buffer, size_t buffer_len) +{ + imgtoolerr_t err = (imgtoolerr_t)IMGTOOLERR_UNIMPLEMENTED; + + buffer[0] = '\0'; + + if (attr_value) + { + if (m_attr_name) + err = m_attr_name(attribute, attr_value, buffer, buffer_len); + + if (err == (imgtoolerr_t)IMGTOOLERR_UNIMPLEMENTED) + { + switch(attribute & 0xF0000) + { + case IMGTOOLATTR_INT_FIRST: + snprintf(buffer, buffer_len, "%d", (int) attr_value->i); + break; + } + } + } + else + { + switch(attribute) + { + case IMGTOOLATTR_INT_MAC_TYPE: + snprintf(buffer, buffer_len, "File type"); + break; + case IMGTOOLATTR_INT_MAC_CREATOR: + snprintf(buffer, buffer_len, "File creator"); + break; + case IMGTOOLATTR_INT_MAC_FINDERFLAGS: + snprintf(buffer, buffer_len, "Finder flags"); + break; + case IMGTOOLATTR_INT_MAC_COORDX: + snprintf(buffer, buffer_len, "X coordinate"); + break; + case IMGTOOLATTR_INT_MAC_COORDY: + snprintf(buffer, buffer_len, "Y coordinate"); + break; + case IMGTOOLATTR_INT_MAC_FINDERFOLDER: + snprintf(buffer, buffer_len, "Finder folder"); + break; + case IMGTOOLATTR_INT_MAC_ICONID: + snprintf(buffer, buffer_len, "Icon ID"); + break; + case IMGTOOLATTR_INT_MAC_SCRIPTCODE: + snprintf(buffer, buffer_len, "Script code"); + break; + case IMGTOOLATTR_INT_MAC_EXTENDEDFLAGS: + snprintf(buffer, buffer_len, "Extended flags"); + break; + case IMGTOOLATTR_INT_MAC_COMMENTID: + snprintf(buffer, buffer_len, "Comment ID"); + break; + case IMGTOOLATTR_INT_MAC_PUTAWAYDIRECTORY: + snprintf(buffer, buffer_len, "Putaway directory"); + break; + case IMGTOOLATTR_TIME_CREATED: + snprintf(buffer, buffer_len, "Creation time"); + break; + case IMGTOOLATTR_TIME_LASTMODIFIED: + snprintf(buffer, buffer_len, "Last modified time"); + break; + } + } +} + + +//------------------------------------------------- +// test_imgtool_datetime - unit test for imgtool::datetime +//------------------------------------------------- + +static bool test_imgtool_datetime(int second, int minute, int hour, int day_of_month, int month, int year) +{ + bool error = false; + + util::arbitrary_datetime t; + t.second = second; + t.minute = minute; + t.hour = hour; + t.day_of_month = day_of_month; + t.month = month; + t.year = year; + + imgtool::datetime dt(imgtool::datetime::datetime_type::GMT, t); + std::tm t2 = dt.gmtime(); + + if (t2.tm_sec != second) + { + util::stream_format(std::wcerr, L"test_imgtool_datetime(): Expected t2.tm_sec to be %d, instead got %d\n", second, t2.tm_sec); + error = true; + } + if (t2.tm_min != minute) + { + util::stream_format(std::wcerr, L"test_imgtool_datetime(): Expected t2.tm_min to be %d, instead got %d\n", minute, t2.tm_min); + error = true; + } + if (t2.tm_hour != hour) + { + util::stream_format(std::wcerr, L"test_imgtool_datetime(): Expected t2.tm_hour to be %d, instead got %d\n", hour, t2.tm_hour); + error = true; + } + if (t2.tm_mday != day_of_month) + { + util::stream_format(std::wcerr, L"test_imgtool_datetime(): Expected t2.tm_mday to be %d, instead got %d\n", day_of_month, t2.tm_mday); + error = true; + } + if (t2.tm_mon != month - 1) + { + util::stream_format(std::wcerr, L"test_imgtool_datetime(): Expected t2.tm_mon to be %d, instead got %d\n", month - 1, t2.tm_mon); + error = true; + } + if (t2.tm_year != year - 1900) + { + util::stream_format(std::wcerr, L"test_imgtool_datetime(): Expected t2.tm_mon to be %d, instead got %d\n", year - 1900, t2.tm_year); + error = true; + } + return error; +} + + +//------------------------------------------------- +// test_imgtool_datetime - unit tests for imgtool::datetime +//------------------------------------------------- + +static bool test_imgtool_datetime() +{ + bool error = false; + + // various test cases for imgtool::datetime + if (test_imgtool_datetime(34, 23, 12, 18, 3, 1993)) // March 18th, 1993 12:23:34 + error = true; + if (test_imgtool_datetime(0, 20, 16, 25, 12, 1976)) // December 25th, 1976 16:20:00 + error = true; + + return error; +} + + +//------------------------------------------------- +// imgtool_validitychecks - checks the validity +// of the imgtool modules +//------------------------------------------------- + +bool imgtool_validitychecks(void) +{ + bool error = false; + imgtoolerr_t err = (imgtoolerr_t)IMGTOOLERR_SUCCESS; + imgtool_module_features features; + int created_library = false; + + // various test cases for imgtool::datetime + if (test_imgtool_datetime()) + error = true; + + if (!global_imgtool_library) + { + imgtool_init(false, nullptr); + created_library = true; + } + + for (const auto &module : global_imgtool_library->modules()) + { + features = imgtool_get_module_features(module.get()); + + if (module->name.empty()) + { + util::stream_format(std::wcerr, L"imgtool module has null 'name'\n"); + error = true; + } + if (module->description.empty()) + { + util::stream_format(std::wcerr, L"imgtool module %s has null 'description'\n", wstring_from_utf8(module->name)); + error = true; + } + if (module->extensions.empty()) + { + util::stream_format(std::wcerr, L"imgtool module %s has null 'extensions'\n", wstring_from_utf8(module->extensions)); + error = true; + } + +#if 0 + /* sanity checks on modules that do not support directories */ + if (!module->path_separator) + { + if (module->alternate_path_separator) + { + util::stream_format(std::wcerr, L"imgtool module %s specified alternate_path_separator but not path_separator\n", wstring_from_utf8(module->name)); + error = true; + } + if (module->initial_path_separator) + { + util::stream_format(std::wcerr, L"imgtool module %s specified initial_path_separator without directory support\n", wstring_from_utf8(module->name)); + error = true; + } + if (module->create_dir) + { + util::stream_format(std::wcerr, L"imgtool module %s implements create_dir without directory support\n", wstring_from_utf8(module->name)); + error = true; + } + if (module->delete_dir) + { + util::stream_format(std::wcerr, L"imgtool module %s implements delete_dir without directory support\n", wstring_from_utf8(module->name)); + error = true; + } + } +#endif + + /* sanity checks on creation options */ + if (module->createimage_optguide || !module->createimage_optspec.empty()) + { + if (!module->create) + { + util::stream_format(std::wcerr, L"imgtool module %s has creation options without supporting create\n", wstring_from_utf8(module->name)); + error = true; + } + if ((!module->createimage_optguide && !module->createimage_optspec.empty()) + || (module->createimage_optguide && module->createimage_optspec.empty())) + { + util::stream_format(std::wcerr, L"imgtool module %s does has partially incomplete creation options\n", wstring_from_utf8(module->name)); + error = true; + } + + if (module->createimage_optguide && !module->createimage_optspec.empty()) + { + auto resolution = std::make_unique(*module->createimage_optguide); + resolution->set_specification(module->createimage_optspec.c_str()); + } + } + } + + if (created_library) + imgtool_exit(); + if (err) + { + util::stream_format(std::wcerr, L"imgtool: %s\n", wstring_from_utf8(imgtool_error(err))); + error = true; + } + return error; +} + + + +/*------------------------------------------------- + imgtool_temp_str - provides a temporary string + buffer used for string passing +-------------------------------------------------*/ + +char *imgtool_temp_str(void) +{ + static int index; + static char temp_string_pool[32][256]; + return temp_string_pool[index++ % std::size(temp_string_pool)]; +} + + + +/*************************************************************************** + + Image handling functions + +***************************************************************************/ + +imgtoolerr_t imgtool::image::internal_open(const imgtool_module *module, const std::string &filename, + int read_or_write, util::option_resolution *createopts, imgtool::image::ptr &outimg) +{ + imgtoolerr_t err; + imgtool::stream::ptr stream; + imgtool::image::ptr image; + + outimg.reset(); + + // is the requested functionality implemented? + if ((read_or_write == OSD_FOPEN_RW_CREATE) ? !module->create : !module->open) + { + err = imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + goto done; + } + + // open the stream + stream = imgtool::stream::open(filename, read_or_write); + if (!stream) + { + err = imgtoolerr_t(IMGTOOLERR_FILENOTFOUND | IMGTOOLERR_SRC_IMAGEFILE); + goto done; + } + + // setup the image structure + try { image = std::make_unique(*module); } + catch (std::bad_alloc const &) + { + err = imgtoolerr_t(IMGTOOLERR_OUTOFMEMORY); + goto done; + } + + // actually call create or open + err = (read_or_write == OSD_FOPEN_RW_CREATE) + ? module->create(*image, std::move(stream), createopts) + : module->open(*image, std::move(stream)); + if (err) + { + err = markerrorsource(err); + goto done; + } + + // we've succeeded - set the output image, and record that + // we are "okay to close" + image->m_okay_to_close = true; + outimg = std::move(image); + +done: + return err; +} + + +//------------------------------------------------- +// open - open an image +//------------------------------------------------- + +imgtoolerr_t imgtool::image::open(const imgtool_module *module, const std::string &filename, int read_or_write, ptr &outimg) +{ + read_or_write = read_or_write ? OSD_FOPEN_RW : OSD_FOPEN_READ; + return internal_open(module, filename, read_or_write, nullptr, outimg); +} + + +//------------------------------------------------- +// imgtool::image::open_byname - open an image +//------------------------------------------------- + +imgtoolerr_t imgtool::image::open(const std::string &modulename, const std::string &filename, int read_or_write, ptr &outimg) +{ + const imgtool_module *module; + + module = imgtool_find_module(modulename); + if (!module) + return imgtoolerr_t(IMGTOOLERR_MODULENOTFOUND | IMGTOOLERR_SRC_MODULE); + + return open(module, filename, read_or_write, outimg); +} + + +//------------------------------------------------- +// imgtool::image::image +//------------------------------------------------- + +imgtool::image::image(const imgtool_module &module) + : m_module(module) + , m_okay_to_close(false) +{ + if (module.image_extra_bytes > 0) + { + m_extra_bytes = std::make_unique(module.image_extra_bytes); + std::fill_n(&m_extra_bytes[0], module.image_extra_bytes, 0); + } + +} + + +//------------------------------------------------- +// imgtool::image::~image +//------------------------------------------------- + +imgtool::image::~image() +{ + if (m_okay_to_close && module().close) + module().close(*this); +} + + +//------------------------------------------------- +// create - creates an image +//------------------------------------------------- + +imgtoolerr_t imgtool::image::create(const imgtool_module *module, const std::string &filename, + util::option_resolution *opts, ptr &image) +{ + std::unique_ptr alloc_resolution; + + /* allocate dummy options if necessary */ + if (!opts && module->createimage_optguide) + { + try { alloc_resolution.reset(new util::option_resolution(*module->createimage_optguide)); } + catch (...) { return (imgtoolerr_t)IMGTOOLERR_OUTOFMEMORY; } + + if (!module->createimage_optspec.empty()) + alloc_resolution->set_specification(module->createimage_optspec.c_str()); + + opts = alloc_resolution.get(); + } + + return internal_open(module, filename, OSD_FOPEN_RW_CREATE, opts, image); +} + + +//------------------------------------------------- +// create - creates an image +//------------------------------------------------- + +imgtoolerr_t imgtool::image::create(const std::string &modulename, const std::string &filename, util::option_resolution *opts, ptr &image) +{ + const imgtool_module *module; + + module = imgtool_find_module(modulename); + if (!module) + return imgtoolerr_t(IMGTOOLERR_MODULENOTFOUND | IMGTOOLERR_SRC_MODULE); + + return create(module, filename, opts, image); +} + + +//------------------------------------------------- +// create - creates an image +//------------------------------------------------- + +imgtoolerr_t imgtool::image::create(const imgtool_module *module, const std::string &filename, + util::option_resolution *opts) +{ + std::unique_ptr image; + return create(module, filename, opts, image); +} + + +//------------------------------------------------- +// create - creates an image +//------------------------------------------------- + +imgtoolerr_t imgtool::image::create(const std::string &modulename, const std::string &filename, util::option_resolution *opts) +{ + std::unique_ptr image; + return create(modulename, filename, opts, image); +} + + +//------------------------------------------------- +// info - returns format specific information about an image +//------------------------------------------------- + +std::string imgtool::image::info() +{ + std::string string; + if (module().info) + { + std::stringstream stream; + module().info(*this, stream); + string = stream.str(); + } + return string; +} + + + +#define PATH_MUSTBEDIR 0x00000001 +#define PATH_LEAVENULLALONE 0x00000002 +#define PATH_CANBEBOOTBLOCK 0x00000004 + +//------------------------------------------------- +// partition::canonicalize_path - normalizes a path string +// into a NUL delimited list +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::canonicalize_path(uint32_t flags, const char *path, std::string &result) +{ + imgtoolerr_t err = (imgtoolerr_t)IMGTOOLERR_SUCCESS; + + // is this path NULL? if so, is that ignored? + if (!path && (flags & PATH_LEAVENULLALONE)) + return IMGTOOLERR_SUCCESS; + + // is this the special filename for bootblocks? + if (path == FILENAME_BOOTBLOCK) + { + if (!(flags & PATH_CANBEBOOTBLOCK)) + err = (imgtoolerr_t)IMGTOOLERR_UNEXPECTED; + else if (!m_supports_bootblock) + err = (imgtoolerr_t)IMGTOOLERR_FILENOTFOUND; + return err; + } + + // normalize the path into the native character set + std::string converted_path; + imgtool::charconverter * const converter = reinterpret_cast(get_info_ptr(IMGTOOLINFO_PTR_CHARCONVERTER)); + if (converter) + { + converted_path = converter->from_utf8(path); + path = converted_path.c_str(); + } + + if (m_path_separator == '\0') + { + if (flags & PATH_MUSTBEDIR) + { + // do we specify a path when paths are not supported? */ + if (path && *path) + return imgtoolerr_t(IMGTOOLERR_CANNOTUSEPATH | IMGTOOLERR_SRC_FUNCTIONALITY); + + result = ""; /* normalize empty path */ + } + else + { + // simple passthrough + result = path; + } + } + else + { + const char *s = path ? path : ""; + + // allocate space for a new canonical path + std::ostringstream stream; + + // copy the path + bool in_path_separator = true; + size_t i = 0; + do + { + if ((s[i] != '\0') && (s[i] != m_path_separator) && (s[i] != m_alternate_path_separator)) + { + stream << s[i]; + in_path_separator = false; + } + else if (!in_path_separator) + { + stream << '\0'; + in_path_separator = true; + } + } + while(s[i++] != '\0'); + stream << '\0' << '\0'; + result = stream.str(); + } + + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// partition::canonicalize_fork +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::canonicalize_fork(const char **fork) +{ + // does this module support forks? + if (m_list_forks) + { + // this module supports forks; make sure that fork is non-NULL + if (!*fork) + *fork = ""; + } + else + { + // this module does not support forks; make sure that fork is NULL + if (*fork) + return IMGTOOLERR_NOFORKS; + } + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// partition::get_directory_entry - retrieves +// the nth directory entry within a partition +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::get_directory_entry(const char *path, int index, imgtool_dirent &ent) +{ + imgtoolerr_t err; + imgtool::directory::ptr imgenum; + + if (index < 0) + { + err = (imgtoolerr_t)IMGTOOLERR_PARAMTOOSMALL; + goto done; + } + + err = imgtool::directory::open(*this, path, imgenum); + if (err) + goto done; + + do + { + err = imgenum->get_next(ent); + if (err) + goto done; + + if (ent.eof) + { + err = (imgtoolerr_t)IMGTOOLERR_FILENOTFOUND; + goto done; + } + } + while(index--); + +done: + if (err) + memset(ent.filename, 0, sizeof(ent.filename)); + return err; +} + + +//------------------------------------------------- +// partition::get_file_size - returns free +// space on a partition, in bytes +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::get_file_size(const char *fname, uint64_t &filesize) +{ + imgtoolerr_t err; + imgtool::directory::ptr imgenum; + imgtool_dirent ent; + const char *path; + + path = nullptr; /* TODO: Need to parse off the path */ + + filesize = ~((uint64_t) 0); + memset(&ent, 0, sizeof(ent)); + + err = imgtool::directory::open(*this, path, imgenum); + if (err) + goto done; + + do + { + err = imgenum->get_next(ent); + if (err) + goto done; + + if (!core_stricmp(fname, ent.filename)) + { + filesize = ent.filesize; + goto done; + } + } + while(ent.filename[0]); + + err = (imgtoolerr_t)IMGTOOLERR_FILENOTFOUND; + +done: + return err; +} + + +//------------------------------------------------- +// partition::list_file_attributes - identifies +// all attributes on a file +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::list_file_attributes(const char *path, uint32_t *attrs, size_t len) +{ + imgtoolerr_t err; + + memset(attrs, 0, sizeof(*attrs) * len); + + if (!m_list_attrs) + return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + + // canonicalize path + std::string canonical_path; + err = canonicalize_path(PATH_LEAVENULLALONE, path, canonical_path); + if (err) + return err; + + err = m_list_attrs(*this, canonical_path.c_str(), attrs, len); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// partition::get_file_attributes - retrieves +// attributes on a file +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::get_file_attributes(const char *path, const uint32_t *attrs, imgtool_attribute *values) +{ + imgtoolerr_t err; + if (!m_get_attrs) + return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + + // canonicalize path + std::string canonical_path; + err = canonicalize_path(PATH_LEAVENULLALONE, path, canonical_path); + if (err) + return err; + + err = m_get_attrs(*this, canonical_path.c_str(), attrs, values); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// imgtool::partition::put_file_attributes - sets +// attributes on a file +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::put_file_attributes(const char *path, const uint32_t *attrs, const imgtool_attribute *values) +{ + imgtoolerr_t err; + if (!m_set_attrs) + return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + + // canonicalize path + std::string canonical_path; + err = canonicalize_path(PATH_LEAVENULLALONE, path, canonical_path); + if (err) + return err; + + err = m_set_attrs(*this, canonical_path.c_str(), attrs, values); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// partition::get_file_attribute - retrieves +// an attribute on a single file +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::get_file_attribute(const char *path, uint32_t attr, imgtool_attribute &value) +{ + uint32_t attrs[2]; + attrs[0] = attr; + attrs[1] = 0; + return get_file_attributes(path, attrs, &value); +} + + +//------------------------------------------------- +// partition::put_file_attribute - sets +// attributes on a single file +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::put_file_attribute(const char *path, uint32_t attr, const imgtool_attribute &value) +{ + uint32_t attrs[2]; + attrs[0] = attr; + attrs[1] = 0; + return put_file_attributes(path, attrs, &value); +} + + +//------------------------------------------------- +// partition::get_icon_info - retrieves the +// icon for a file stored on a partition +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::get_icon_info(const char *path, imgtool_iconinfo *iconinfo) +{ + imgtoolerr_t err; + if (!m_get_iconinfo) + return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + + // canonicalize path + std::string canonical_path; + err = canonicalize_path(0, path, canonical_path); + if (err) + return err; + + memset(iconinfo, 0, sizeof(*iconinfo)); + err = m_get_iconinfo(*this, canonical_path.c_str(), iconinfo); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// partition::suggest_file_filters - suggests a +// list of filters appropriate for a file on a +// partition +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::suggest_file_filters(const char *path, + imgtool::stream *stream, imgtool_transfer_suggestion *suggestions, size_t suggestions_length) +{ + imgtoolerr_t err; + int i, j; + imgtoolerr_t (*check_stream)(imgtool::stream &stream, imgtool_suggestion_viability_t *viability); + size_t position; + + // clear out buffer + memset(suggestions, 0, sizeof(*suggestions) * suggestions_length); + + if (!m_suggest_transfer) + return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + + // canonicalize path + std::string canonical_path; + err = canonicalize_path(PATH_LEAVENULLALONE, path, canonical_path); + if (err) + return err; + + // invoke the module's suggest call + err = m_suggest_transfer(*this, canonical_path.c_str(), suggestions, suggestions_length); + if (err) + return err; + + // loop on resulting suggestions, and do the following: + // 1. Call check_stream if present, and remove disqualified streams + // 2. Fill in missing descriptions + i = j = 0; + while(suggestions[i].viability) + { + if (stream && suggestions[i].filter) + { + check_stream = (imgtoolerr_t (*)(imgtool::stream &, imgtool_suggestion_viability_t *)) filter_get_info_fct(suggestions[i].filter, FILTINFO_PTR_CHECKSTREAM); + if (check_stream) + { + position = stream->tell(); + err = check_stream(*stream, &suggestions[i].viability); + stream->seek(position, SEEK_SET); + if (err) + return err; + } + } + + /* the check_stream proc can remove the option by clearing out the viability */ + if (suggestions[i].viability) + { + /* we may have to move this suggestion, if one was removed */ + if (i != j) + memcpy(&suggestions[j], &suggestions[i], sizeof(*suggestions)); + + /* if the description is missing, fill it in */ + if (!suggestions[j].description) + { + if (suggestions[j].filter) + suggestions[j].description = filter_get_info_string(suggestions[i].filter, FILTINFO_STR_HUMANNAME); + else + suggestions[j].description = "Raw"; + } + + j++; + } + i++; + } + suggestions[j].viability = (imgtool_suggestion_viability_t)0; + + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// partition::get_chain - retrieves the block +// chain for a file or directory on a partition +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::get_chain(const char *path, imgtool_chainent *chain, size_t chain_size) +{ + size_t i; + + assert(chain_size > 0); + + if (!m_get_chain) + return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + + // initialize the chain array, so the module's get_chain function can be lazy + for (i = 0; i < chain_size; i++) + { + chain[i].level = 0; + chain[i].block = ~0; + } + + return m_get_chain(*this, path, chain, chain_size - 1); +} + + +//------------------------------------------------- +// partition::get_chain_string - retrieves +// the block chain for a file or directory on a +// partition +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::get_chain_string(const char *path, char *buffer, size_t buffer_len) +{ + imgtoolerr_t err; + imgtool_chainent chain[512]; + uint64_t last_block; + uint8_t cur_level = 0; + int len, i; + int comma_needed = false; + + // determine the last block identifier + chain[0].block = ~0; + last_block = chain[0].block; + + err = get_chain(path, chain, std::size(chain)); + if (err) + return err; + + len = snprintf(buffer, buffer_len, "["); + buffer += len; + buffer_len -= len; + + for (i = 0; chain[i].block != last_block; i++) + { + while(cur_level < chain[i].level) + { + len = snprintf(buffer, buffer_len, " ["); + buffer += len; + buffer_len -= len; + cur_level++; + comma_needed = false; + } + while(cur_level > chain[i].level) + { + len = snprintf(buffer, buffer_len, "]"); + buffer += len; + buffer_len -= len; + cur_level--; + } + + if (comma_needed) + { + len = snprintf(buffer, buffer_len, ", "); + buffer += len; + buffer_len -= len; + } + + len = snprintf(buffer, buffer_len, "%u", (unsigned) chain[i].block); + buffer += len; + buffer_len -= len; + comma_needed = true; + } + + do + { + len = snprintf(buffer, buffer_len, "]"); + buffer += len; + buffer_len -= len; + } + while(cur_level-- > 0); + + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// partition::get_free_space - returns the +// amount of free space on a partition +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::get_free_space(uint64_t &sz) +{ + imgtoolerr_t err; + + if (!m_free_space) + return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + + err = m_free_space(*this, &sz); + if (err) + return (imgtoolerr_t)(err | IMGTOOLERR_SRC_IMAGEFILE); + + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// partition::read_file - starts reading +// from a file on a partition with a stream +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::read_file(const char *filename, const char *fork, imgtool::stream &destf, filter_getinfoproc filter) +{ + imgtoolerr_t err; + union filterinfo u; + + if (!m_read_file) + return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + + if (filter) + { + // use a filter + filter(FILTINFO_PTR_READFILE, &u); + if (!u.read_file) + return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + + err = u.read_file(*this, filename, fork, destf); + if (err) + return markerrorsource(err); + } + else + { + // canonicalize path + std::string canonical_path; + err = canonicalize_path(PATH_CANBEBOOTBLOCK, filename, canonical_path); + if (err) + return err; + + err = canonicalize_fork(&fork); + if (err) + return err; + + // invoke the actual module + err = m_read_file(*this, canonical_path.c_str(), fork, destf); + if (err) + return markerrorsource(err); + } + + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// partition::write_file - starts writing +// to a new file on an image with a stream +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::write_file(const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts, filter_getinfoproc filter) +{ + imgtoolerr_t err; + std::unique_ptr alloc_resolution; + uint64_t free_space; + uint64_t file_size; + union filterinfo u; + + if (!m_write_file) + return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + + if (filter) + { + // use a filter + filter(FILTINFO_PTR_WRITEFILE, &u); + if (!u.write_file) + return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + + err = u.write_file(*this, filename, fork, sourcef, opts); + if (err) + return markerrorsource(err); + } + else + { + // does this partition prefer upper case file names? + std::string ucase_str; + if (m_prefer_ucase) + { + std::ostringstream s; + size_t i = 0; + while (filename[i]) + { + char32_t ch; + int count = uchar_from_utf8(&ch, &filename[i], UTF8_CHAR_MAX); + if (count > 0) + { + char32_t upper_ch = uchar_toupper(ch); + s << utf8_from_uchar(upper_ch); + } + i += count > 0 ? count : 1; + } + ucase_str = s.str(); + filename = ucase_str.c_str(); + } + + // canonicalize path + std::string canonical_path; + err = canonicalize_path(PATH_CANBEBOOTBLOCK, filename, canonical_path); + if (err) + return err; + + err = canonicalize_fork(&fork); + if (err) + return err; + + // allocate dummy options if necessary + if (!opts && m_writefile_optguide) + { + try { alloc_resolution.reset(new util::option_resolution(*m_writefile_optguide)); } + catch (...) + { + return IMGTOOLERR_OUTOFMEMORY; + } + if (!m_writefile_optspec.empty()) + alloc_resolution->set_specification(m_writefile_optspec); + opts = alloc_resolution.get(); + } + + // if free_space is implemented; do a quick check to see if space is available + if (m_free_space) + { + err = m_free_space(*this, &free_space); + if (err) + return markerrorsource(err); + + file_size = sourcef.size(); + + if (file_size > free_space) + return markerrorsource(IMGTOOLERR_NOSPACE); + } + + // actually invoke the write file handler + err = m_write_file(*this, canonical_path.c_str(), fork, sourcef, opts); + if (err) + return markerrorsource(err); + } + + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// partition::get_file - read a file from +// an image, storing it into a native file +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::get_file(const char *filename, const char *fork, + const char *dest, filter_getinfoproc filter) +{ + imgtoolerr_t err; + imgtool::stream::ptr f; + char *alloc_dest = nullptr; + const char *filter_extension = nullptr; + + if (!dest) + { + // determine the filter extension, if appropriate + if (filter != nullptr) + filter_extension = filter_get_info_string(filter, FILTINFO_STR_EXTENSION); + + if (filter_extension != nullptr) + { + alloc_dest = (char*)malloc(strlen(filename) + 1 + strlen(filter_extension) + 1); + if (!alloc_dest) + return IMGTOOLERR_OUTOFMEMORY; + + sprintf(alloc_dest, "%s.%s", filename, filter_extension); + dest = alloc_dest; + } + else + { + if (filename == FILENAME_BOOTBLOCK) + dest = "boot.bin"; + else + dest = filename; + } + } + + f = imgtool::stream::open(dest, OSD_FOPEN_WRITE); + if (!f) + { + err = imgtoolerr_t(IMGTOOLERR_FILENOTFOUND | IMGTOOLERR_SRC_NATIVEFILE); + goto done; + } + + err = read_file(filename, fork, *f, filter); + +done: + if (alloc_dest != nullptr) + free(alloc_dest); + return err; +} + + +//------------------------------------------------- +// partition::put_file - read a native file +// and store it on a partition +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::put_file(const char *newfname, const char *fork, + const char *source, util::option_resolution *opts, filter_getinfoproc filter) +{ + imgtoolerr_t err; + imgtool::stream::ptr f; + std::string alloc_newfname; + std::string basename; + + if (!newfname) + { + basename = std::string(core_filename_extract_base(source)); + newfname = basename.c_str(); + } + + imgtool::charconverter *charconverter = (imgtool::charconverter *) get_info_ptr(IMGTOOLINFO_PTR_CHARCONVERTER); + if (charconverter) + { + // convert to native format + try + { + alloc_newfname = charconverter->from_utf8(newfname); + } + catch (charconverter_exception) + { + return imgtoolerr_t(IMGTOOLERR_BADFILENAME | IMGTOOLERR_SRC_NATIVEFILE); + } + newfname = alloc_newfname.c_str(); + } + + f = imgtool::stream::open(source, OSD_FOPEN_READ); + if (f) + err = write_file(newfname, fork, *f, opts, filter); + else + err = imgtoolerr_t(IMGTOOLERR_FILENOTFOUND | IMGTOOLERR_SRC_NATIVEFILE); + + return err; +} + + + +/*------------------------------------------------- + imgtool::partition::delete_file - delete a file + on a partition +-------------------------------------------------*/ + +imgtoolerr_t imgtool::partition::delete_file(const char *fname) +{ + imgtoolerr_t err; + if (!m_delete_file) + return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + + // canonicalize path + std::string canonical_path; + err = canonicalize_path(0, fname, canonical_path); + if (err) + return err; + + err = m_delete_file(*this, canonical_path.c_str()); + if (err) + return markerrorsource(err); + + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// partition::list_file_forks - lists all +// forks on an image +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::list_file_forks(const char *path, std::vector &forks) +{ + imgtoolerr_t err; + if (!m_list_forks) + return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + + // canonicalize path + std::string canonical_path; + err = canonicalize_path(0, path, canonical_path); + if (err) + return err; + + // call the callback + forks.clear(); + err = m_list_forks(*this, canonical_path.c_str(), forks); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// partition::create_directory - creates a +// directory on a partition +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::create_directory(const char *path) +{ + imgtoolerr_t err; + + // implemented? + if (!m_create_dir) + return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + + // canonicalize path + std::string canonical_path; + err = canonicalize_path(PATH_MUSTBEDIR, path, canonical_path); + if (err) + return err; + + err = m_create_dir(*this, path); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// partition::delete_directory - deletes a +// directory on a partition +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::delete_directory(const char *path) +{ + imgtoolerr_t err; + + // implemented? + if (!m_delete_dir) + return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + + // canonicalize path + std::string canonical_path; + err = canonicalize_path(PATH_MUSTBEDIR, path, canonical_path); + if (err) + return err; + + err = m_delete_dir(*this, path); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// partition::get_block_size - gets the +// size of a standard block on a partition +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::get_block_size(uint32_t &length) +{ + return m_image.get_block_size(length); +} + + +//------------------------------------------------- +// partition::is_block_in_range +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::map_block_to_image_block(uint64_t partition_block, uint64_t &image_block) const +{ + if (partition_block >= m_block_count) + return IMGTOOLERR_SEEKERROR; + + image_block = m_base_block + partition_block; + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// partition::read_block - reads a standard +// block on a partition +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::read_block(uint64_t block, void *buffer) +{ + uint64_t image_block; + imgtoolerr_t err = map_block_to_image_block(block, image_block); + if (err) + return err; + + return m_image.read_block(image_block, buffer); +} + + +//------------------------------------------------- +// partition::write_block - writes a +// standard block on a partition +//------------------------------------------------- + +imgtoolerr_t imgtool::partition::write_block(uint64_t block, const void *buffer) +{ + uint64_t image_block; + imgtoolerr_t err = map_block_to_image_block(block, image_block); + if (err) + return err; + + return m_image.write_block(image_block, buffer); +} + + +//------------------------------------------------- +// partition::get_features - retrieves a +// structure identifying this partition's features +// associated with an image +//------------------------------------------------- + +imgtool_partition_features imgtool::partition::get_features() const +{ + imgtool_partition_features features; + memset(&features, 0, sizeof(features)); + + if (m_read_file) + features.supports_reading = 1; + if (m_write_file) + features.supports_writing = 1; + if (m_delete_file) + features.supports_deletefile = 1; + if (m_path_separator) + features.supports_directories = 1; + if (m_free_space) + features.supports_freespace = 1; + if (m_create_dir) + features.supports_createdir = 1; + if (m_delete_dir) + features.supports_deletedir = 1; + if (m_supports_creation_time) + features.supports_creation_time = 1; + if (m_supports_lastmodified_time) + features.supports_lastmodified_time = 1; + if (m_get_iconinfo) + features.supports_geticoninfo = 1; + if (!features.supports_writing && !features.supports_createdir && !features.supports_deletefile && !features.supports_deletedir) + features.is_read_only = 1; + return features; +} + + +//------------------------------------------------- +// partition::get_info_ptr - retrieves a +// pointer associated with a partition's format +//------------------------------------------------- + +void *imgtool::partition::get_info_ptr(uint32_t state) +{ + return imgtool_get_info_ptr(&m_imgclass, state); +} + + +//------------------------------------------------- +// partition::get_info_string - retrieves a +// string associated with a partition's format +//------------------------------------------------- + +const char *imgtool::partition::get_info_string(uint32_t state) +{ + return imgtool_get_info_string(&m_imgclass, state); +} + + +//------------------------------------------------- +// partition::get_info_int - retrieves a +// pointer associated with a partition's format +//------------------------------------------------- + +uint64_t imgtool::partition::get_info_int(uint32_t state) +{ + return imgtool_get_info_int(&m_imgclass, state); +} + + +//------------------------------------------------- +// partition::extra_bytes - returns extra +// bytes on a partition +//------------------------------------------------- + +void *imgtool::partition::extra_bytes() +{ + if (!m_extra_bytes) + throw false; + return m_extra_bytes.get(); +} + + +/*************************************************************************** + + Path handling functions + +***************************************************************************/ + +//------------------------------------------------- +// partition::get_root_path - retrieves +// the path root of this partition +//------------------------------------------------- + +const char *imgtool::partition::get_root_path() +{ + int initial_path_separator; + char path_separator; + char *buf; + int pos = 0; + + initial_path_separator = get_info_int(IMGTOOLINFO_INT_INITIAL_PATH_SEPARATOR) ? 1 : 0; + path_separator = (char) get_info_int(IMGTOOLINFO_INT_PATH_SEPARATOR); + buf = imgtool_temp_str(); + + if (initial_path_separator) + buf[pos++] = path_separator; + buf[pos] = '\0'; + return buf; +} + + +//------------------------------------------------- +// partition::path_concatenate - retrieves +// a pointer associated with a partition's format +//------------------------------------------------- + +const char *imgtool::partition::path_concatenate(const char *path1, const char *path2) +{ + char path_separator; + size_t len; + char *buffer; + size_t buffer_len; + + path_separator = (char) get_info_int(IMGTOOLINFO_INT_PATH_SEPARATOR); + len = strlen(path1); + + /* prepare buffer */ + buffer = imgtool_temp_str(); + buffer_len = 256; + + if (!strcmp(path2, ".")) + { + /* keep the same directory */ + snprintf(buffer, buffer_len, "%s", path1); + } + else if (!strcmp(path2, "..")) + { + /* up one directory */ + while((len > 0) && (path1[len - 1] == path_separator)) + len--; + while((len > 0) && (path1[len - 1] != path_separator)) + len--; + while((len > 1) && (path1[len - 1] == path_separator)) + len--; + snprintf(buffer, buffer_len, "%s", path1); + buffer[len] = '\0'; + } + else + { + /* append a path */ + if ((*path1 != 0) && (path1[strlen(path1) - 1] != path_separator)) + snprintf(buffer, buffer_len, "%s%c%s", path1, path_separator, path2); + else + snprintf(buffer, buffer_len, "%s%s", path1, path2); + } + return buffer; +} + + +//------------------------------------------------- +// partition::get_base_name - retrieves +// a base name for a partition specific path +//------------------------------------------------- + +const char *imgtool::partition::get_base_name(const char *path) +{ + char path_separator; + const char *new_path = path; + int i; + + path_separator = (char) get_info_int(IMGTOOLINFO_INT_PATH_SEPARATOR); + + for (i = 0; path[i]; i++) + { + if (path[i] == path_separator) + new_path = &path[i + 1]; + } + return new_path; +} + + + +/*************************************************************************** + + Directory handling functions + +***************************************************************************/ + +//------------------------------------------------- +// directory ctor +//------------------------------------------------- + +imgtool::directory::directory(imgtool::partition &partition) + : m_partition(partition) + , m_okay_to_close(false) +{ + if (partition.m_directory_extra_bytes > 0) + { + m_extra_bytes = std::make_unique(partition.m_directory_extra_bytes); + memset(m_extra_bytes.get(), 0, sizeof(m_extra_bytes.get()[0] * partition.m_directory_extra_bytes)); + } +} + + +//------------------------------------------------- +// directory::open - begins +// enumerating files on a partition +//------------------------------------------------- + +imgtoolerr_t imgtool::directory::open(imgtool::partition &partition, const std::string &path, imgtool::directory::ptr &outenum) +{ + imgtoolerr_t err = imgtoolerr_t(IMGTOOLERR_SUCCESS); + imgtool::directory::ptr enumeration; + + outenum.reset(); + + if (!partition.m_next_enum) + return imgtoolerr_t(IMGTOOLERR_UNIMPLEMENTED | IMGTOOLERR_SRC_FUNCTIONALITY); + + std::string canonical_path; + err = partition.canonicalize_path(PATH_MUSTBEDIR, path.c_str(), canonical_path); + if (err) + return err; + + try { enumeration = std::make_unique(partition); } + catch (std::bad_alloc const &) + { + return imgtoolerr_t(IMGTOOLERR_OUTOFMEMORY); + } + + if (partition.m_begin_enum) + { + err = partition.m_begin_enum(*enumeration, canonical_path.c_str()); + if (err) + return markerrorsource(err); + } + + enumeration->m_okay_to_close = true; + outenum = std::move(enumeration); + + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// directory dtor +//------------------------------------------------- + +imgtool::directory::~directory() +{ + if (m_okay_to_close && m_partition.m_close_enum) + m_partition.m_close_enum(*this); +} + + +//------------------------------------------------- +// directory::get_next - continues +// enumerating files within a partition +//------------------------------------------------- + +imgtoolerr_t imgtool::directory::get_next(imgtool_dirent &ent) +{ + imgtoolerr_t err; + + // This makes it so that drivers don't have to take care of clearing + // the attributes if they don't apply + memset(&ent, 0, sizeof(ent)); + + err = m_partition.m_next_enum(*this, ent); + if (err) + return markerrorsource(err); + + imgtool::charconverter * const converter = reinterpret_cast(m_partition.get_info_ptr(IMGTOOLINFO_PTR_CHARCONVERTER)); + if (converter) + { + std::string new_fname; + try + { + new_fname = converter->to_utf8(ent.filename); + } + catch (charconverter_exception) + { + return imgtoolerr_t(IMGTOOLERR_BADFILENAME); + } + snprintf(ent.filename, std::size(ent.filename), "%s", new_fname.c_str()); + } + + // don't trust the module! + if (!m_partition.m_supports_creation_time && (ent.creation_time.type() != imgtool::datetime::datetime_type::NONE)) + { + internal_error(nullptr, "next_enum() specified creation_time, which is marked as unsupported by this module"); + return IMGTOOLERR_UNEXPECTED; + } + if (!m_partition.m_supports_lastmodified_time && (ent.lastmodified_time.type() != imgtool::datetime::datetime_type::NONE)) + { + internal_error(nullptr, "next_enum() specified lastmodified_time, which is marked as unsupported by this module"); + return IMGTOOLERR_UNEXPECTED; + } + if (!m_partition.m_path_separator && ent.directory) + { + internal_error(nullptr, "next_enum() returned a directory, which is marked as unsupported by this module"); + return IMGTOOLERR_UNEXPECTED; + } + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// unknown_partition_get_info - represents an +// unknown partition +//------------------------------------------------- + +void unknown_partition_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "unknown"); break; + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "Unknown partition type"); break; + } +} diff --git a/src/tools/imgtool/imgtool.h b/src/tools/imgtool/imgtool.h new file mode 100644 index 0000000..4a66d7f --- /dev/null +++ b/src/tools/imgtool/imgtool.h @@ -0,0 +1,265 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + imgtool.h + + Main headers for Imgtool core + +***************************************************************************/ + +#ifndef IMGTOOL_H +#define IMGTOOL_H + +#include "library.h" +#include "stream.h" + +#include "osdcomm.h" + +#include +#include +#include + +/* ----------------------------------------------------------------------- */ + +#define EOLN_CR "\x0d" +#define EOLN_LF "\x0a" +#define EOLN_CRLF "\x0d\x0a" + +#define FILENAME_BOOTBLOCK ((const char *) 1) + +enum +{ + OSD_FOPEN_READ, + OSD_FOPEN_WRITE, + OSD_FOPEN_RW, + OSD_FOPEN_RW_CREATE +}; + +/* --------------------------------------------------------------------------- + * Image calls + * + * These are the calls that front ends should use for manipulating images. You + * should never call the module functions directly because they may not be + * implemented (i.e. - the function pointers are NULL). The img_* functions are + * aware of these issues and will make the appropriate checks as well as + * marking up return codes with the source. In addition, some of the img_* + * calls are high level calls that simply image manipulation + * + * Calls that return 'int' that are not explicitly noted otherwise return + * imgtool error codes + * --------------------------------------------------------------------------- + */ + +struct imgtool_module_features +{ + unsigned int supports_create : 1; + unsigned int supports_open : 1; + unsigned int supports_readsector : 1; + unsigned int supports_writesector : 1; + unsigned int is_read_only : 1; +}; + +struct imgtool_partition_features +{ + unsigned int supports_reading : 1; + unsigned int supports_writing : 1; + unsigned int supports_deletefile : 1; + unsigned int supports_directories : 1; + unsigned int supports_freespace : 1; + unsigned int supports_createdir : 1; + unsigned int supports_deletedir : 1; + unsigned int supports_creation_time : 1; + unsigned int supports_lastmodified_time : 1; + unsigned int supports_forks : 1; + unsigned int supports_geticoninfo : 1; + unsigned int is_read_only : 1; +}; + +/* ----- initialization and basics ----- */ +void imgtool_init(bool omit_untested, void (*warning)(const char *message)); +void imgtool_exit(void); +const imgtool_module *imgtool_find_module(const std::string &modulename); +const imgtool::library::modulelist &imgtool_get_modules(); +imgtool_module_features imgtool_get_module_features(const imgtool_module *module); +void imgtool_warn(const char *format, ...) ATTR_PRINTF(1,2); + +// ----- image management ----- +namespace imgtool +{ + class image + { + public: + typedef std::unique_ptr ptr; + + image(const imgtool_module &module); + ~image(); + + static imgtoolerr_t identify_file(const char *filename, imgtool_module **modules, size_t count); + static imgtoolerr_t open(const imgtool_module *module, const std::string &filename, int read_or_write, ptr &outimg); + static imgtoolerr_t open(const std::string &modulename, const std::string &filename, int read_or_write, ptr &outimg); + static imgtoolerr_t create(const imgtool_module *module, const std::string &filename, util::option_resolution *opts, ptr &image); + static imgtoolerr_t create(const std::string &modulename, const std::string &filename, util::option_resolution *opts, ptr &image); + static imgtoolerr_t create(const imgtool_module *module, const std::string &filename, util::option_resolution *opts); + static imgtoolerr_t create(const std::string &modulename, const std::string &filename, util::option_resolution *opts); + + std::string info(); + imgtoolerr_t get_geometry(uint32_t *tracks, uint32_t *heads, uint32_t *sectors); + imgtoolerr_t read_sector(uint32_t track, uint32_t head, uint32_t sector, std::vector &buffer); + imgtoolerr_t write_sector(uint32_t track, uint32_t head, uint32_t sector, const void *buffer, size_t len); + imgtoolerr_t get_block_size(uint32_t &length); + imgtoolerr_t read_block(uint64_t block, void *buffer); + imgtoolerr_t write_block(uint64_t block, const void *buffer); + imgtoolerr_t clear_block(uint64_t block, uint8_t data); + imgtoolerr_t list_partitions(std::vector &partitions); + const imgtool_module &module() { return m_module; } + void *extra_bytes() { return m_extra_bytes.get(); } + + private: + const imgtool_module &m_module; + std::unique_ptr m_extra_bytes; + + // because of an idiosyncrasy of how imgtool::image::internal_open() works, we are only "okay to close" + // by invoking the module's close function once internal_open() succeeds. the long term solution is + // better C++ adoption (e.g. - std::unique_ptr<>, std:move() etc) + bool m_okay_to_close; + + static imgtoolerr_t internal_open(const imgtool_module *module, const std::string &filename, + int read_or_write, util::option_resolution *createopts, imgtool::image::ptr &outimg); + }; +} + +namespace imgtool +{ + // ----- partition management ----- + class partition + { + friend class directory; + public: + typedef std::unique_ptr ptr; + + // ctor/dtor + partition(imgtool::image &image, const imgtool_class &imgclass, int partition_index, uint64_t base_block, uint64_t block_count); + ~partition(); + + static imgtoolerr_t open(imgtool::image &image, int partition_index, ptr &partition); + imgtool::image &image() { return m_image; } + + // ----- partition operations ----- + imgtoolerr_t get_directory_entry(const char *path, int index, imgtool_dirent &ent); + imgtoolerr_t get_file_size(const char *filename, uint64_t &filesize); + imgtoolerr_t get_free_space(uint64_t &sz); + imgtoolerr_t read_file(const char *filename, const char *fork, imgtool::stream &destf, filter_getinfoproc filter); + imgtoolerr_t write_file(const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *resolution, filter_getinfoproc filter); + imgtoolerr_t get_file(const char *filename, const char *fork, const char *dest, filter_getinfoproc filter); + imgtoolerr_t put_file(const char *newfname, const char *fork, const char *source, util::option_resolution *opts, filter_getinfoproc filter); + imgtoolerr_t delete_file(const char *fname); + imgtoolerr_t list_file_forks(const char *path, std::vector &forks); + imgtoolerr_t create_directory(const char *path); + imgtoolerr_t delete_directory(const char *path); + imgtoolerr_t list_file_attributes(const char *path, uint32_t *attrs, size_t len); + imgtoolerr_t get_file_attributes(const char *path, const uint32_t *attrs, imgtool_attribute *values); + imgtoolerr_t put_file_attributes(const char *path, const uint32_t *attrs, const imgtool_attribute *values); + imgtoolerr_t get_file_attribute(const char *path, uint32_t attr, imgtool_attribute &value); + imgtoolerr_t put_file_attribute(const char *path, uint32_t attr, const imgtool_attribute &value); + void get_attribute_name(uint32_t attribute, const imgtool_attribute *attr_value, char *buffer, size_t buffer_len); + imgtoolerr_t get_icon_info(const char *path, imgtool_iconinfo *iconinfo); + imgtoolerr_t suggest_file_filters(const char *path, imgtool::stream *stream, imgtool_transfer_suggestion *suggestions, size_t suggestions_length); + imgtoolerr_t get_block_size(uint32_t &length); + imgtoolerr_t read_block(uint64_t block, void *buffer); + imgtoolerr_t write_block(uint64_t block, const void *buffer); + imgtoolerr_t get_chain(const char *path, imgtool_chainent *chain, size_t chain_size); + imgtoolerr_t get_chain_string(const char *path, char *buffer, size_t buffer_len); + imgtool_partition_features get_features() const; + void * get_info_ptr(uint32_t state); + const char * get_info_string(uint32_t state); + uint64_t get_info_int(uint32_t state); + void * extra_bytes(); + + // ----- path management ----- + const char * get_root_path(); + const char * path_concatenate(const char *path1, const char *path2); + const char * get_base_name(const char *path); + + private: + imgtool::image &m_image; + //int m_partition_index; + uint64_t m_base_block; + uint64_t m_block_count; + + imgtool_class m_imgclass; + size_t m_directory_extra_bytes; + + char m_path_separator; + char m_alternate_path_separator; + + unsigned int m_prefer_ucase : 1; + unsigned int m_supports_creation_time : 1; + unsigned int m_supports_lastmodified_time : 1; + unsigned int m_supports_bootblock : 1; /* this module supports loading/storing the boot block */ + + std::function m_begin_enum; + std::function m_next_enum; + std::function m_close_enum; + std::function m_free_space; + std::function m_read_file; + std::function m_write_file; + std::function m_delete_file; + std::function &forks)> m_list_forks; + std::function m_create_dir; + std::function m_delete_dir; + std::function m_list_attrs; + std::function m_get_attrs; + std::function m_set_attrs; + std::function m_attr_name; + std::function m_get_iconinfo; + std::function m_suggest_transfer; + std::function m_get_chain; + + const util::option_guide *m_writefile_optguide; + std::string m_writefile_optspec; + + std::unique_ptr m_extra_bytes; + + // methods + imgtoolerr_t canonicalize_path(uint32_t flags, const char *path, std::string &result); + imgtoolerr_t canonicalize_fork(const char **fork); + imgtoolerr_t map_block_to_image_block(uint64_t partition_block, uint64_t &image_block) const; + }; + + // ----- directory management ----- + class directory + { + public: + typedef std::unique_ptr ptr; + + // ctor/dtor + directory(imgtool::partition &partition); + ~directory(); + + // methods + static imgtoolerr_t open(imgtool::partition &partition, const std::string &path, ptr &outenum); + imgtoolerr_t get_next(imgtool_dirent &ent); + + // accessors + imgtool::partition &partition() { return m_partition; } + imgtool::image &image() { return partition().image(); } + const imgtool_module &module() { return partition().image().module(); } + void *extra_bytes() { assert(m_extra_bytes); return m_extra_bytes.get(); } + + private: + imgtool::partition &m_partition; + std::unique_ptr m_extra_bytes; + bool m_okay_to_close; // similar wart as what is on imgtool::image + }; +}; + +/* ----- special ----- */ +bool imgtool_validitychecks(void); +void unknown_partition_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info); + +char *strncpyz(char *dest, const char *source, size_t len); +void rtrim(char *buf); +std::string extract_padded_filename(const char *source, size_t filename_length, size_t extension_length, char pad = ' '); + +#endif /* IMGTOOL_H */ diff --git a/src/tools/imgtool/library.cpp b/src/tools/imgtool/library.cpp new file mode 100644 index 0000000..f53db6f --- /dev/null +++ b/src/tools/imgtool/library.cpp @@ -0,0 +1,314 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/**************************************************************************** + + library.cpp + + Code relevant to the Imgtool library; analogous to the MESS/MAME driver + list. + +****************************************************************************/ + +#include "library.h" + +#include "corestr.h" + +#include +#include + +namespace imgtool { + +datetime::imgtool_clock::duration datetime::s_gmt_offset = datetime::calculate_gmt_offset(); + +//------------------------------------------------- +// datetime ctor +//------------------------------------------------- + +datetime::datetime(datetime_type type, std::chrono::time_point tp) + : m_type(type) + , m_time_point(imgtool_clock::from_system_clock(tp)) +{ +} + + +//------------------------------------------------- +// datetime ctor +//------------------------------------------------- + +datetime::datetime(datetime_type type, time_t t) + : datetime(type, std::chrono::system_clock::from_time_t(t)) +{ +} + + +//------------------------------------------------- +// datetime ctor +//------------------------------------------------- + +datetime::datetime(datetime_type type, const util::arbitrary_datetime &dt, bool clamp) + : m_type(type) + , m_time_point(imgtool_clock::from_arbitrary_datetime(dt, clamp)) +{ +} + + +//------------------------------------------------- +// datetime::now +//------------------------------------------------- + +datetime datetime::now(datetime_type type) +{ + return imgtool::datetime( + type, + std::chrono::system_clock::now()); +} + + +//------------------------------------------------- +// datetime::localtime +//------------------------------------------------- + +std::tm datetime::localtime() const +{ + imgtool_clock::time_point tp; + + switch (type()) + { + case datetime_type::LOCAL: + tp = time_point(); + break; + case datetime_type::GMT: + tp = time_point() + s_gmt_offset; + break; + default: + tp = imgtool_clock::time_point(); + break; + } + return imgtool_clock::to_tm(tp); +} + + +//------------------------------------------------- +// datetime::gmtime +//------------------------------------------------- + +std::tm datetime::gmtime() const +{ + imgtool_clock::time_point tp; + + switch (type()) + { + case datetime_type::GMT: + tp = time_point(); + break; + case datetime_type::LOCAL: + tp = time_point() - s_gmt_offset; + break; + default: + tp = imgtool_clock::time_point(); + break; + } + return imgtool_clock::to_tm(tp); +} + + +//------------------------------------------------- +// datetime::calculate_gmt_offset +//------------------------------------------------- + +datetime::imgtool_clock::duration datetime::calculate_gmt_offset() +{ + time_t t = time(nullptr); + std::tm utc_tm = *std::gmtime(&t); + time_t utc = mktime(&utc_tm); + std::tm local_tm = *std::localtime(&t); + time_t local = mktime(&local_tm); + double d = difftime(local, utc) * imgtool_clock::period::den / imgtool_clock::period::num; + return imgtool_clock::duration((std::int64_t) d); +} + + +//------------------------------------------------- +// datetime::to_time_t +//------------------------------------------------- + +time_t datetime::to_time_t() const +{ + auto system_clock_tp = imgtool_clock::to_system_clock(time_point()); + return std::chrono::system_clock::to_time_t(system_clock_tp); +} + + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +library::library() +{ +} + + +//------------------------------------------------- +// dtor +//------------------------------------------------- + +library::~library() +{ +} + + +//------------------------------------------------- +// add_class +//------------------------------------------------- + +void library::add_class(const imgtool_class *imgclass) +{ + char const *temp; + + // allocate the module and place it in the chain + m_modules.emplace_back(std::make_unique()); + imgtool_module *module = m_modules.back().get(); + + module->imgclass = *imgclass; + module->name = imgtool_get_info_string(imgclass, IMGTOOLINFO_STR_NAME); + module->description = imgtool_get_info_string(imgclass, IMGTOOLINFO_STR_DESCRIPTION); + module->extensions = imgtool_get_info_string(imgclass, IMGTOOLINFO_STR_FILE_EXTENSIONS); + temp = imgtool_get_info_string(imgclass, IMGTOOLINFO_STR_EOLN); + module->eoln = (temp != nullptr) ? temp : ""; + module->initial_path_separator = imgtool_get_info_int(imgclass, IMGTOOLINFO_INT_INITIAL_PATH_SEPARATOR) ? true : false; + module->open_is_strict = imgtool_get_info_int(imgclass, IMGTOOLINFO_INT_OPEN_IS_STRICT) ? true : false; + module->tracks_are_called_cylinders = imgtool_get_info_int(imgclass, IMGTOOLINFO_INT_TRACKS_ARE_CALLED_CYLINDERS) ? true : false; + module->writing_untested = imgtool_get_info_int(imgclass, IMGTOOLINFO_INT_WRITING_UNTESTED) ? true : false; + module->creation_untested = imgtool_get_info_int(imgclass, IMGTOOLINFO_INT_CREATION_UNTESTED) ? true : false; + module->open = (imgtoolerr_t (*)(imgtool::image &, std::unique_ptr &&)) imgtool_get_info_fct(imgclass, IMGTOOLINFO_PTR_OPEN); + module->create = (imgtoolerr_t (*)(imgtool::image &, std::unique_ptr &&, util::option_resolution *)) imgtool_get_info_fct(imgclass, IMGTOOLINFO_PTR_CREATE); + module->close = (void (*)(imgtool::image &)) imgtool_get_info_fct(imgclass, IMGTOOLINFO_PTR_CLOSE); + module->info = (void (*)(imgtool::image &, std::ostream &)) imgtool_get_info_fct(imgclass, IMGTOOLINFO_PTR_INFO); + module->read_sector = (imgtoolerr_t (*)(imgtool::image &, uint32_t, uint32_t, uint32_t, std::vector &)) imgtool_get_info_fct(imgclass, IMGTOOLINFO_PTR_READ_SECTOR); + module->write_sector = (imgtoolerr_t (*)(imgtool::image &, uint32_t, uint32_t, uint32_t, const void *, size_t)) imgtool_get_info_fct(imgclass, IMGTOOLINFO_PTR_WRITE_SECTOR); + module->get_geometry = (imgtoolerr_t (*)(imgtool::image &, uint32_t *, uint32_t *, uint32_t *))imgtool_get_info_fct(imgclass, IMGTOOLINFO_PTR_GET_GEOMETRY); + module->read_block = (imgtoolerr_t (*)(imgtool::image &, void *, uint64_t)) imgtool_get_info_fct(imgclass, IMGTOOLINFO_PTR_READ_BLOCK); + module->write_block = (imgtoolerr_t (*)(imgtool::image &, const void *, uint64_t)) imgtool_get_info_fct(imgclass, IMGTOOLINFO_PTR_WRITE_BLOCK); + module->list_partitions = (imgtoolerr_t (*)(imgtool::image &, std::vector &)) imgtool_get_info_fct(imgclass, IMGTOOLINFO_PTR_LIST_PARTITIONS); + module->block_size = imgtool_get_info_int(imgclass, IMGTOOLINFO_INT_BLOCK_SIZE); + module->createimage_optguide = (const util::option_guide *) imgtool_get_info_ptr(imgclass, IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE); + temp = (char const *)imgtool_get_info_ptr(imgclass, IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC); + module->createimage_optspec = (temp != nullptr) ? temp : ""; + module->image_extra_bytes += imgtool_get_info_int(imgclass, IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES); +} + + +//------------------------------------------------- +// add +//------------------------------------------------- + +void library::add(imgtool_get_info get_info) +{ + int (*make_class)(int index, imgtool_class *imgclass); + imgtool_class imgclass; + int i, result; + + // try this class + memset(&imgclass, 0, sizeof(imgclass)); + imgclass.get_info = get_info; + + // do we have derived getinfo functions? + make_class = (int (*)(int index, imgtool_class *imgclass)) + imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_MAKE_CLASS); + + if (make_class) + { + i = 0; + do + { + // clear out the class + memset(&imgclass, 0, sizeof(imgclass)); + imgclass.get_info = get_info; + + // make the class + result = make_class(i++, &imgclass); + if (result) + add_class(&imgclass); + } + while(result); + } + else + { + add_class(&imgclass); + } +} + + +//------------------------------------------------- +// unlink +//------------------------------------------------- + +void library::unlink(const std::string &module_name) +{ + const modulelist::iterator iter = find(module_name); + if (iter != m_modules.end()) + m_modules.erase(iter); +} + + +//------------------------------------------------- +// module_compare +//------------------------------------------------- + +int library::module_compare(const imgtool_module *m1, const imgtool_module *m2, sort_type sort) +{ + int rc = 0; + switch(sort) + { + case sort_type::NAME: + rc = strcmp(m1->name.c_str(), m2->name.c_str()); + break; + case sort_type::DESCRIPTION: + rc = core_stricmp(m1->description.c_str(), m2->description.c_str()); + break; + } + return rc; +} + + +//------------------------------------------------- +// sort +//------------------------------------------------- + +void library::sort(sort_type sort) +{ + auto compare = [this, sort](const std::unique_ptr &a, const std::unique_ptr &b) + { + return module_compare(a.get(), b.get(), sort) < 0; + }; + m_modules.sort(compare); +} + + +//------------------------------------------------- +// find +//------------------------------------------------- + +library::modulelist::iterator library::find(const std::string &module_name) +{ + return std::find_if( + m_modules.begin(), + m_modules.end(), + [module_name](std::unique_ptr &module) { return !module_name.compare(module->name); }); +} + + +//------------------------------------------------- +// findmodule +//------------------------------------------------- + +const imgtool_module *library::findmodule(const std::string &module_name) +{ + modulelist::iterator iter = find(module_name); + return iter != m_modules.end() + ? iter->get() + : nullptr; +} + + +} // namespace imgtool diff --git a/src/tools/imgtool/library.h b/src/tools/imgtool/library.h new file mode 100644 index 0000000..312d3bb --- /dev/null +++ b/src/tools/imgtool/library.h @@ -0,0 +1,551 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/**************************************************************************** + + library.h + + Code relevant to the Imgtool library; analogous to the MAME driver list. + + Unlike MAME which has a static driver lists, Imgtool has a concept of a + library and this library is built at startup time. + dynamic for which modules are added to. This makes "dynamic" modules + much easier + +****************************************************************************/ +#ifndef MAME_TOOLS_IMGTOOL_LIBRARY_H +#define MAME_TOOLS_IMGTOOL_LIBRARY_H + +#pragma once + +#include "imgterrs.h" + +#include "timeconv.h" +#include "utilfwd.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace imgtool +{ + class image; + class partition; + class directory; + class charconverter; + class stream; +}; + +enum imgtool_suggestion_viability_t +{ + SUGGESTION_END, + SUGGESTION_POSSIBLE, + SUGGESTION_RECOMMENDED +}; + +union filterinfo +{ + int64_t i; /* generic integers */ + void * p; /* generic pointers */ + void * f; /* generic function pointers */ + const char *s; /* generic strings */ + + imgtoolerr_t (*read_file)(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf); + imgtoolerr_t (*write_file)(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts); + imgtoolerr_t (*check_stream)(imgtool::stream &stream, imgtool_suggestion_viability_t *viability); +}; + +typedef void (*filter_getinfoproc)(uint32_t state, union filterinfo *info); + +namespace imgtool +{ + class datetime + { + public: + typedef util::arbitrary_clock > imgtool_clock; + + enum datetime_type + { + NONE, + LOCAL, + GMT + }; + + datetime() + : m_type(datetime_type::NONE) + { + } + + + template + datetime(datetime_type type, std::chrono::time_point > tp) + : m_type(type) + , m_time_point(imgtool_clock::from_arbitrary_time_point(tp)) + { + } + + datetime(datetime_type type, std::chrono::time_point tp); + datetime(datetime_type type, time_t t); + datetime(datetime_type type, const util::arbitrary_datetime &dt, bool clamp = true); + datetime(const datetime &that) = default; + datetime(datetime &&that) = default; + + // accessors + datetime_type type() const { return m_type; } + bool empty() const { return type() == datetime_type::NONE; } + std::chrono::time_point time_point() const { return m_time_point; } + + // operators + datetime &operator =(const datetime &that) + { + m_type = that.m_type; + m_time_point = that.m_time_point; + return *this; + } + + // returns the current time + static datetime now(datetime_type type); + + // returns time structures + std::tm localtime() const; + std::tm gmtime() const; + time_t to_time_t() const; + + private: + static imgtool_clock::duration s_gmt_offset; + datetime_type m_type; + std::chrono::time_point m_time_point; + + static imgtool_clock::duration calculate_gmt_offset(); + }; +}; + +struct imgtool_dirent +{ + char filename[1024]; + char attr[64]; + uint64_t filesize; + + imgtool::datetime creation_time; + imgtool::datetime lastmodified_time; + imgtool::datetime lastaccess_time; + + char softlink[1024]; + char comment[256]; + + /* flags */ + unsigned int eof : 1; + unsigned int corrupt : 1; + unsigned int directory : 1; + unsigned int hardlink : 1; +}; + +struct imgtool_chainent +{ + uint8_t level; + uint64_t block; +}; + +namespace imgtool +{ + class fork_entry + { + public: + enum class type_t + { + DATA, + RESOURCE, + ALT + }; + + fork_entry(uint64_t size, type_t type = type_t::DATA) + : m_size(size) + , m_type(type) + , m_name(default_name(type)) + { + + } + + fork_entry(uint64_t size, std::string &&name) + : m_size(size) + , m_type(fork_entry::type_t::ALT) + , m_name(std::move(name)) + { + } + + fork_entry(const fork_entry &that) = default; + fork_entry(fork_entry &&that) = default; + + uint64_t size() const { return m_size; } + type_t type() const { return m_type; } + const std::string &name() const { return m_name; } + + private: + static std::string default_name(type_t type) + { + switch (type) + { + case type_t::DATA: + return std::string(""); + case type_t::RESOURCE: + return std::string("RESOURCE_FORK"); + default: + throw false; + } + } + + uint64_t m_size; + type_t m_type; + std::string m_name; + }; +} + +struct imgtool_transfer_suggestion +{ + imgtool_suggestion_viability_t viability; + filter_getinfoproc filter; + const char *fork; + const char *description; +}; + +enum +{ + /* --- the following bits of info are returned as 64-bit signed integers --- */ + IMGTOOLATTR_INT_FIRST = 0x00000, + IMGTOOLATTR_INT_MAC_TYPE, + IMGTOOLATTR_INT_MAC_CREATOR, + IMGTOOLATTR_INT_MAC_FINDERFLAGS, + IMGTOOLATTR_INT_MAC_COORDX, + IMGTOOLATTR_INT_MAC_COORDY, + IMGTOOLATTR_INT_MAC_FINDERFOLDER, + IMGTOOLATTR_INT_MAC_ICONID, + IMGTOOLATTR_INT_MAC_SCRIPTCODE, + IMGTOOLATTR_INT_MAC_EXTENDEDFLAGS, + IMGTOOLATTR_INT_MAC_COMMENTID, + IMGTOOLATTR_INT_MAC_PUTAWAYDIRECTORY, + + /* --- the following bits of info are returned as pointers to data or functions --- */ + IMGTOOLATTR_PTR_FIRST = 0x10000, + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + IMGTOOLATTR_STR_FIRST = 0x20000, + + /* --- the following bits of info are returned as time_t values --- */ + IMGTOOLATTR_TIME_FIRST = 0x30000, + IMGTOOLATTR_TIME_CREATED, + IMGTOOLATTR_TIME_LASTMODIFIED +}; + +union imgtool_attribute +{ + int64_t i; + time_t t; +}; + +struct imgtool_iconinfo +{ + unsigned icon16x16_specified : 1; + unsigned icon32x32_specified : 1; + uint32_t icon16x16[16][16]; + uint32_t icon32x32[32][32]; +}; + +enum +{ + /* --- the following bits of info are returned as 64-bit signed integers --- */ + IMGTOOLINFO_INT_FIRST = 0x00000, + IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES, + IMGTOOLINFO_INT_PARTITION_EXTRA_BYTES, + IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES, + IMGTOOLINFO_INT_PATH_SEPARATOR, + IMGTOOLINFO_INT_ALTERNATE_PATH_SEPARATOR, + IMGTOOLINFO_INT_PREFER_UCASE, + IMGTOOLINFO_INT_INITIAL_PATH_SEPARATOR, + IMGTOOLINFO_INT_OPEN_IS_STRICT, + IMGTOOLINFO_INT_SUPPORTS_CREATION_TIME, + IMGTOOLINFO_INT_SUPPORTS_LASTMODIFIED_TIME, + IMGTOOLINFO_INT_TRACKS_ARE_CALLED_CYLINDERS, + IMGTOOLINFO_INT_WRITING_UNTESTED, + IMGTOOLINFO_INT_CREATION_UNTESTED, + IMGTOOLINFO_INT_SUPPORTS_BOOTBLOCK, + IMGTOOLINFO_INT_BLOCK_SIZE, + + IMGTOOLINFO_INT_CLASS_SPECIFIC = 0x08000, + + /* --- the following bits of info are returned as pointers to data or functions --- */ + IMGTOOLINFO_PTR_FIRST = 0x10000, + + IMGTOOLINFO_PTR_OPEN, + IMGTOOLINFO_PTR_CREATE, + IMGTOOLINFO_PTR_CLOSE, + IMGTOOLINFO_PTR_OPEN_PARTITION, + IMGTOOLINFO_PTR_CREATE_PARTITION, + IMGTOOLINFO_PTR_INFO, + IMGTOOLINFO_PTR_BEGIN_ENUM, + IMGTOOLINFO_PTR_NEXT_ENUM, + IMGTOOLINFO_PTR_CLOSE_ENUM, + IMGTOOLINFO_PTR_FREE_SPACE, + IMGTOOLINFO_PTR_READ_FILE, + IMGTOOLINFO_PTR_WRITE_FILE, + IMGTOOLINFO_PTR_DELETE_FILE, + IMGTOOLINFO_PTR_LIST_FORKS, + IMGTOOLINFO_PTR_CREATE_DIR, + IMGTOOLINFO_PTR_DELETE_DIR, + IMGTOOLINFO_PTR_LIST_ATTRS, + IMGTOOLINFO_PTR_GET_ATTRS, + IMGTOOLINFO_PTR_SET_ATTRS, + IMGTOOLINFO_PTR_ATTR_NAME, + IMGTOOLINFO_PTR_GET_ICON_INFO, + IMGTOOLINFO_PTR_SUGGEST_TRANSFER, + IMGTOOLINFO_PTR_GET_CHAIN, + IMGTOOLINFO_PTR_GET_GEOMETRY, + IMGTOOLINFO_PTR_READ_SECTOR, + IMGTOOLINFO_PTR_WRITE_SECTOR, + IMGTOOLINFO_PTR_READ_BLOCK, + IMGTOOLINFO_PTR_WRITE_BLOCK, + IMGTOOLINFO_PTR_APPROVE_FILENAME_CHAR, + IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE, + IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE, + IMGTOOLINFO_PTR_MAKE_CLASS, + IMGTOOLINFO_PTR_LIST_PARTITIONS, + IMGTOOLINFO_PTR_CHARCONVERTER, + + IMGTOOLINFO_PTR_CLASS_SPECIFIC = 0x18000, + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + IMGTOOLINFO_STR_FIRST = 0x20000, + + IMGTOOLINFO_STR_NAME, + IMGTOOLINFO_STR_DESCRIPTION, + IMGTOOLINFO_STR_FILE, + IMGTOOLINFO_STR_FILE_EXTENSIONS, + IMGTOOLINFO_STR_EOLN, + IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC, + IMGTOOLINFO_STR_WRITEFILE_OPTSPEC, + + IMGTOOLINFO_STR_CLASS_SPECIFIC = 0x28000 +}; + + + +union imgtoolinfo; + +struct imgtool_class; +typedef void (*imgtool_get_info)(const imgtool_class *, uint32_t, union imgtoolinfo *); + +struct imgtool_class +{ + imgtool_get_info get_info; + imgtool_get_info derived_get_info; + void *derived_param; +}; + + + +namespace imgtool +{ + class partition_info + { + public: + partition_info(imgtool_get_info get_info, uint64_t base_block, uint64_t block_count) + : m_base_block(base_block) + , m_block_count(block_count) + { + memset(&m_imgclass, 0, sizeof(m_imgclass)); + m_imgclass.get_info = get_info; + } + + partition_info(imgtool_class imgclass, uint64_t base_block, uint64_t block_count) + : m_imgclass(imgclass) + , m_base_block(base_block) + , m_block_count(block_count) + { + } + + const imgtool_class &imgclass() const { return m_imgclass; } + uint64_t base_block() const { return m_base_block; } + uint64_t block_count() const { return m_block_count; } + + private: + imgtool_class m_imgclass; + uint64_t m_base_block; + uint64_t m_block_count; + }; +}; + + +union imgtoolinfo +{ + int64_t i; /* generic integers */ + void * p; /* generic pointers */ + void * f; /* generic function pointers */ + char * s; /* generic strings */ + + imgtoolerr_t (*open) (imgtool::image &image, std::unique_ptr &&stream); + void (*close) (imgtool::image &image); + imgtoolerr_t (*create) (imgtool::image &image, std::unique_ptr &&stream, util::option_resolution *opts); + imgtoolerr_t (*create_partition) (imgtool::image &image, uint64_t first_block, uint64_t block_count); + void (*info) (imgtool::image &image, std::ostream &stream); + imgtoolerr_t (*begin_enum) (imgtool::directory &enumeration, const char *path); + imgtoolerr_t (*next_enum) (imgtool::directory &enumeration, imgtool_dirent &ent); + void (*close_enum) (imgtool::directory &enumeration); + imgtoolerr_t (*open_partition) (imgtool::partition &partition, uint64_t first_block, uint64_t block_count); + imgtoolerr_t (*free_space) (imgtool::partition &partition, uint64_t *size); + imgtoolerr_t (*read_file) (imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf); + imgtoolerr_t (*write_file) (imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts); + imgtoolerr_t (*delete_file) (imgtool::partition &partition, const char *filename); + imgtoolerr_t (*list_forks) (imgtool::partition &partition, const char *path, std::vector &forks); + imgtoolerr_t (*create_dir) (imgtool::partition &partition, const char *path); + imgtoolerr_t (*delete_dir) (imgtool::partition &partition, const char *path); + imgtoolerr_t (*list_attrs) (imgtool::partition &partition, const char *path, uint32_t *attrs, size_t len); + imgtoolerr_t (*get_attrs) (imgtool::partition &partition, const char *path, const uint32_t *attrs, imgtool_attribute *values); + imgtoolerr_t (*set_attrs) (imgtool::partition &partition, const char *path, const uint32_t *attrs, const imgtool_attribute *values); + imgtoolerr_t (*attr_name) (uint32_t attribute, const imgtool_attribute *attr, char *buffer, size_t buffer_len); + imgtoolerr_t (*get_iconinfo) (imgtool::partition &partition, const char *path, imgtool_iconinfo *iconinfo); + imgtoolerr_t (*suggest_transfer) (imgtool::partition &partition, const char *path, imgtool_transfer_suggestion *suggestions, size_t suggestions_length); + imgtoolerr_t (*get_chain) (imgtool::partition &partition, const char *path, imgtool_chainent *chain, size_t chain_size); + imgtoolerr_t (*get_geometry) (imgtool::image &image, uint32_t *tracks, uint32_t *heads, uint32_t *sectors); + imgtoolerr_t (*read_sector) (imgtool::image &image, uint32_t track, uint32_t head, uint32_t sector, std::vector &buffer); + imgtoolerr_t (*write_sector) (imgtool::image &image, uint32_t track, uint32_t head, uint32_t sector, const void *buffer, size_t len, int ddam); + imgtoolerr_t (*read_block) (imgtool::image &image, void *buffer, uint64_t block); + imgtoolerr_t (*write_block) (imgtool::image &image, const void *buffer, uint64_t block); + imgtoolerr_t (*list_partitions) (imgtool::image &image, std::vector &partitions); + int (*approve_filename_char)(char32_t ch); + int (*make_class)(int index, imgtool_class *imgclass); + + const util::option_guide *createimage_optguide; + const util::option_guide *writefile_optguide; + const imgtool::charconverter *charconverter; +}; + + + +static inline int64_t imgtool_get_info_int(const imgtool_class *imgclass, uint32_t state) +{ + union imgtoolinfo info; + info.i = 0; + imgclass->get_info(imgclass, state, &info); + return info.i; +} + +static inline void *imgtool_get_info_ptr(const imgtool_class *imgclass, uint32_t state) +{ + union imgtoolinfo info; + info.p = nullptr; + imgclass->get_info(imgclass, state, &info); + return info.p; +} + +static inline void *imgtool_get_info_fct(const imgtool_class *imgclass, uint32_t state) +{ + union imgtoolinfo info; + info.f = nullptr; + imgclass->get_info(imgclass, state, &info); + return info.f; +} + +static inline char *imgtool_get_info_string(const imgtool_class *imgclass, uint32_t state) +{ + union imgtoolinfo info; + info.s = nullptr; + imgclass->get_info(imgclass, state, &info); + return info.s; +} + +/* circular string buffer */ +char *imgtool_temp_str(void); + + + +struct imgtool_module +{ + imgtool_class imgclass = { 0 }; + + std::string name; + std::string description; + std::string extensions; + std::string eoln; + + size_t image_extra_bytes = 0; + + /* flags */ + bool initial_path_separator = false; + bool open_is_strict = false; + bool tracks_are_called_cylinders = false; /* used for hard drivers */ + bool writing_untested = false; /* used when we support writing, but not in main build */ + bool creation_untested = false; /* used when we support creation, but not in main build */ + + imgtoolerr_t (*open) (imgtool::image &image, std::unique_ptr &&stream) = nullptr; + void (*close) (imgtool::image &image) = nullptr; + void (*info) (imgtool::image &image, std::ostream &stream) = nullptr; + imgtoolerr_t (*create) (imgtool::image &image, std::unique_ptr &&stream, util::option_resolution *opts) = nullptr; + imgtoolerr_t (*get_geometry) (imgtool::image &image, uint32_t *track, uint32_t *heads, uint32_t *sectors) = nullptr; + imgtoolerr_t (*read_sector) (imgtool::image &image, uint32_t track, uint32_t head, uint32_t sector, std::vector &buffer) = nullptr; + imgtoolerr_t (*write_sector) (imgtool::image &image, uint32_t track, uint32_t head, uint32_t sector, const void *buffer, size_t len) = nullptr; + imgtoolerr_t (*read_block) (imgtool::image &image, void *buffer, uint64_t block) = nullptr; + imgtoolerr_t (*write_block) (imgtool::image &image, const void *buffer, uint64_t block) = nullptr; + imgtoolerr_t (*list_partitions)(imgtool::image &image, std::vector &partitions) = nullptr; + + uint32_t block_size = 0; + + const util::option_guide *createimage_optguide = nullptr; + std::string createimage_optspec; + + const void *extra = nullptr; +}; + +namespace imgtool { + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// imgtool "library" - equivalent to the MAME driver list +class library +{ +public: + typedef std::list > modulelist; + + enum class sort_type + { + NAME, + DESCRIPTION + }; + + library(); + ~library(); + + // adds a module to an imgtool library + void add(imgtool_get_info get_info); + + // seeks out and removes a module from an imgtool library + void unlink(const std::string &module_name); + + // sorts an imgtool library + void sort(sort_type sort); + + // finds a module + const imgtool_module *findmodule(const std::string &module_name); + + // module iteration + const modulelist &modules() { return m_modules; } + +private: + modulelist m_modules; + + // internal lookup and iteration + modulelist::iterator find(const std::string &module_name); + + // helpers + void add_class(const imgtool_class *imgclass); + int module_compare(const imgtool_module *m1, const imgtool_module *m2, sort_type sort); +}; + +} // namespace imgtool + +#endif // MAME_TOOLS_IMGTOOL_LIBRARY_H diff --git a/src/tools/imgtool/main.cpp b/src/tools/imgtool/main.cpp new file mode 100644 index 0000000..d8a166e --- /dev/null +++ b/src/tools/imgtool/main.cpp @@ -0,0 +1,966 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + main.cpp + + Imgtool command line front end + +***************************************************************************/ + +#include "imgtool.h" +#include "filter.h" +#include "main.h" +#include "modules.h" + +#include "corefile.h" +#include "corestr.h" +#include "opresolv.h" +#include "strformat.h" +#include "unicode.h" + +#include "osdcore.h" // osd_get_command_line + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#endif + +// ---------------------------------------------------------------------- + +static void writeusage(std::wostream &output, bool write_word_usage, const struct command *c, char *argv[]) +{ + std::string cmdname(core_filename_extract_base(argv[0])); + + util::stream_format(output, + L"%s %s %s %s\n", + (write_word_usage ? L"Usage:" : L" "), + wstring_from_utf8(cmdname), + wstring_from_utf8(c->name), + c->usage ? wstring_from_utf8(c->usage) : std::wstring()); +} + + +// ---------------------------------------------------------------------- + +static int parse_options(int argc, char *argv[], int minunnamed, int maxunnamed, + util::option_resolution *resolution, filter_getinfoproc *filter, const char **fork) +{ + int i; + int lastunnamed = 0; + char *s; + char *name = nullptr; + char *value = nullptr; + static char buf[256]; + + if (filter) + *filter = nullptr; + if (fork) + *fork = nullptr; + + for (i = 0; i < argc; i++) + { + /* Named or unamed arg */ + if ((argv[i][0] != '-') || (argv[i][1] != '-')) + { + /* Unnamed */ + if (i >= maxunnamed) + goto error; /* Too many unnamed */ + lastunnamed = i + 1; + } + else + { + /* Named */ + name = argv[i] + 2; + s = strchr(name, '='); + if (!s) + goto error; + *s = 0; + value = s + 1; + + if (!strcmp(name, "filter")) + { + /* filter option */ + if (!filter) + goto error; /* this command doesn't use filters */ + if (*filter) + goto optionalreadyspecified; + *filter = filter_lookup(value); + if (!(*filter)) + goto filternotfound; + + } + else if (!strcmp(name, "fork")) + { + /* fork option */ + if (!fork) + goto error; /* this command doesn't use filters */ + if (*fork) + goto optionalreadyspecified; + + snprintf(buf, std::size(buf), "%s", value); + *fork = buf; + } + else + { + /* Other named option */ + if (i < minunnamed) + goto error; /* Too few unnamed */ + + util::option_resolution::entry *entry = resolution->find(name); + if (entry->option_type() == util::option_guide::entry::option_type::ENUM_BEGIN) + { + const util::option_guide::entry *enum_value; + for (enum_value = entry->enum_value_begin(); enum_value != entry->enum_value_end(); enum_value++) + { + if (!strcmp (enum_value->identifier(), value)) + { + entry->set_value(enum_value->parameter()); + break; + } + } + if (enum_value == entry->enum_value_end()) + goto error; + } + else + entry->set_value(value); + } + } + } + return lastunnamed; + +filternotfound: + util::stream_format(std::wcerr, L"%s: Unknown filter type\n", wstring_from_utf8(value)); + return -1; + +optionalreadyspecified: + util::stream_format(std::wcerr, L"Cannot specify multiple %ss\n", wstring_from_utf8(name)); + return -1; + +error: + util::stream_format(std::wcerr, L"%s: Unrecognized option\n", wstring_from_utf8(argv[i])); + return -1; +} + + + +void reporterror(imgtoolerr_t err, const struct command *c, const char *format, const char *imagename, + const char *filename, const char *newname, util::option_resolution *opts) +{ + const char *src = "imgtool"; + const char *err_name; + + err_name = imgtool_error(err); + + switch(ERRORSOURCE(err)) { + case IMGTOOLERR_SRC_MODULE: + src = format; + break; + case IMGTOOLERR_SRC_FUNCTIONALITY: + src = c->name; + break; + case IMGTOOLERR_SRC_IMAGEFILE: + src = imagename; + break; + case IMGTOOLERR_SRC_FILEONIMAGE: + src = filename; + break; + case IMGTOOLERR_SRC_NATIVEFILE: + src = newname ? newname : filename; + break; + } + fflush(stdout); + fflush(stderr); + + if (!src) + src = c->name; + util::stream_format(std::wcerr, L"%s: %s\n", wstring_from_utf8(src), wstring_from_utf8(err_name)); +} + + + +static const char *interpret_filename(const char *filename) +{ + if (!strcmp(filename, "??BOOT??") + || !strcmp(filename, "\'??BOOT??\'") + || !strcmp(filename, "\"??BOOT??\"")) + filename = FILENAME_BOOTBLOCK; + return filename; +} + + + +// ---------------------------------------------------------------------- + +static int cmd_dir(const struct command *c, int argc, char *argv[]) +{ + imgtoolerr_t err; + int total_count, total_size, freespace_err; + uint64_t freespace; + imgtool::image::ptr image; + imgtool::partition::ptr partition; + imgtool::directory::ptr imgenum; + imgtool_dirent ent; + char last_modified[19]; + std::string path; + int partition_index = 0; + std::string info; + + // build the separator + const int columnwidth_filename = 30; + const int columnwidth_filesize = 8; + const int columnwidth_attributes = 15; + const int columnwidth_lastmodified = 18; + std::string separator = std::string(columnwidth_filename, '-') + " " + + std::string(columnwidth_filesize, '-') + " " + + std::string(columnwidth_attributes, '-') + " " + + std::string(columnwidth_lastmodified, '-'); + + // attempt to open image + err = imgtool::image::open(argv[0], argv[1], OSD_FOPEN_READ, image); + if (err) + goto done; + + /* attempt to open partition */ + err = imgtool::partition::open(*image, partition_index, partition); + if (err) + goto done; + + path = argc > 2 ? argv[2] : ""; + + err = imgtool::directory::open(*partition, path, imgenum); + if (err) + goto done; + + memset(&ent, 0, sizeof(ent)); + last_modified[0] = '\0'; + total_count = 0; + total_size = 0; + + util::stream_format(std::wcout, L"Contents of %s:%s\n", wstring_from_utf8(argv[1]), wstring_from_utf8(path)); + + info = image->info(); + if (!info.empty()) + util::stream_format(std::wcout, L"%s\n", wstring_from_utf8(info)); + + util::stream_format(std::wcout, L"%s\n", wstring_from_utf8(separator)); + + while (((err = imgenum->get_next(ent)) == 0) && !ent.eof) + { + std::string filesize_string = ent.directory + ? "" + : util::string_format("%u", (unsigned int) ent.filesize); + + if (!ent.lastmodified_time.empty()) + { + std::tm t = ent.lastmodified_time.localtime(); + strftime(last_modified, sizeof(last_modified), "%d-%b-%y %H:%M:%S", &t); + } + + if (ent.hardlink) + strcat(ent.filename, " "); + + util::stream_format(std::wcout, + L"%*s %*s %*s %*s\n", + -columnwidth_filename, wstring_from_utf8(ent.filename), + columnwidth_filesize, wstring_from_utf8(filesize_string), + columnwidth_attributes, wstring_from_utf8(ent.attr), + columnwidth_lastmodified, wstring_from_utf8(last_modified)); + + if (ent.softlink[0] != '\0') + util::stream_format(std::wcout, L"-> %s\n", wstring_from_utf8(ent.softlink)); + + if (ent.comment[0] != '\0') + util::stream_format(std::wcout, L": %s\n", wstring_from_utf8(ent.comment)); + + total_count++; + total_size += ent.filesize; + + memset(&ent, 0, sizeof(ent)); + } + + freespace_err = partition->get_free_space(freespace); + + if (err) + goto done; + + util::stream_format(std::wcout, L"%s\n", wstring_from_utf8(separator)); + util::stream_format(std::wcout, L"%8i File(s) %8i bytes", total_count, total_size); + if (!freespace_err) + util::stream_format(std::wcout, L" %8u bytes free\n", (unsigned int)freespace); + else + util::stream_format(std::wcout, L"\n"); + +done: + if (err) + reporterror(err, c, argv[0], argv[1], nullptr, nullptr, nullptr); + return err ? -1 : 0; +} + + + +static int cmd_get(const struct command *c, int argc, char *argv[]) +{ + imgtoolerr_t err; + imgtool::image::ptr image; + imgtool::partition::ptr partition; + const char *filename; + char *new_filename; + int unnamedargs = 0; + filter_getinfoproc filter; + const char *fork; + int partition_index = 0; + + err = imgtool::image::open(argv[0], argv[1], OSD_FOPEN_READ, image); + if (err) + goto done; + + err = imgtool::partition::open(*image, partition_index, partition); + if (err) + goto done; + + filename = interpret_filename(argv[2]); + + unnamedargs = parse_options(argc, argv, 3, 4, nullptr, &filter, &fork); + if (unnamedargs < 0) + goto done; + + new_filename = (unnamedargs == 4) ? argv[3] : nullptr; + + err = partition->get_file(filename, fork, new_filename, filter); + if (err) + goto done; + + err = IMGTOOLERR_SUCCESS; + +done: + if (err) + reporterror(err, c, argv[0], argv[1], argv[2], argv[3], nullptr); + return (err || (unnamedargs < 0)) ? -1 : 0; +} + + + +static int cmd_put(const struct command *c, int argc, char *argv[]) +{ + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + int i; + imgtool::image::ptr image; + imgtool::partition::ptr partition; + const char *filename = nullptr; + int unnamedargs; + filter_getinfoproc filter; + const imgtool_module *module; + std::unique_ptr resolution; + const char *fork; + const char *new_filename; + char **filename_list; + int filename_count; + int partition_index = 0; + const util::option_guide *writefile_optguide; + const char *writefile_optspec; + + module = imgtool_find_module(argv[0]); + if (!module) + { + err = (imgtoolerr_t)(IMGTOOLERR_MODULENOTFOUND | IMGTOOLERR_SRC_MODULE); + goto done; + } + + /* ugh I hate the way this function is set up, this is because the + * arguments depend on the partition; something that requires some + * rudimentary parsing */ + if (argc >= 2) + { + /* open up the image */ + err = imgtool::image::open(module, argv[1], OSD_FOPEN_RW, image); + if (err) + goto done; + + /* open up the partition */ + err = imgtool::partition::open(*image, partition_index, partition); + if (err) + goto done; + + writefile_optguide = (const util::option_guide *) partition->get_info_ptr(IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE); + writefile_optspec = (const char *)partition->get_info_ptr(IMGTOOLINFO_STR_WRITEFILE_OPTSPEC); + + if (writefile_optguide && writefile_optspec) + { + try { resolution.reset(new util::option_resolution(*writefile_optguide)); } + catch (...) + { + err = IMGTOOLERR_OUTOFMEMORY; + goto done; + } + resolution->set_specification(writefile_optspec); + } + } + + unnamedargs = parse_options(argc, argv, 4, 0xffff, resolution.get(), &filter, &fork); + if (unnamedargs < 0) + return -1; + + /* pick out which args are filenames, and which one is the destination */ + new_filename = interpret_filename(argv[unnamedargs - 1]); + filename_list = &argv[2]; + filename_count = unnamedargs - 3; + + /* loop through the filenames, and put them */ + for (i = 0; i < filename_count; i++) + { + filename = filename_list[i]; + util::stream_format(std::wcout, L"Putting file '%s'...\n", wstring_from_utf8(filename)); + err = partition->put_file(new_filename, fork, filename, resolution.get(), filter); + if (err) + goto done; + } + +done: + if (err) + reporterror(err, c, argv[0], argv[1], filename, nullptr, resolution.get()); + return err ? -1 : 0; +} + + + +static int cmd_getall(const struct command *c, int argc, char *argv[]) +{ + imgtoolerr_t err; + imgtool::image::ptr image; + imgtool::partition::ptr partition; + imgtool::directory::ptr imgenum; + imgtool_dirent ent; + filter_getinfoproc filter; + int unnamedargs; + const char *path = ""; + int arg; + int partition_index = 0; + + err = imgtool::image::open(argv[0], argv[1], OSD_FOPEN_READ, image); + if (err) + goto done; + + err = imgtool::partition::open(*image, partition_index, partition); + if (err) + goto done; + + arg = 2; + if ((argc > 2) && (argv[2][0] != '-')) + { + path = argv[arg++]; + } + + unnamedargs = parse_options(argc, argv, arg, arg, nullptr, &filter, nullptr); + if (unnamedargs < 0) + goto done; + + err = imgtool::directory::open(*partition, path, imgenum); + if (err) + goto done; + + memset(&ent, 0, sizeof(ent)); + + while (((err = imgenum->get_next(ent)) == 0) && !ent.eof) + { + util::stream_format(std::wcout, L"Retrieving %s (%u bytes)\n", wstring_from_utf8(ent.filename), (unsigned int)ent.filesize); + + err = partition->get_file(ent.filename, nullptr, nullptr, filter); + if (err) + goto done; + } + +done: + if (err) + reporterror(err, c, argv[0], argv[1], nullptr, nullptr, nullptr); + return err ? -1 : 0; +} + + + +static int cmd_del(const struct command *c, int argc, char *argv[]) +{ + imgtoolerr_t err; + imgtool::image::ptr image; + imgtool::partition::ptr partition; + int partition_index = 0; + + err = imgtool::image::open(argv[0], argv[1], OSD_FOPEN_RW, image); + if (err) + goto done; + + err = imgtool::partition::open(*image, partition_index, partition); + if (err) + goto done; + + err = partition->delete_file(argv[2]); + if (err) + goto done; + +done: + if (err) + reporterror(err, c, argv[0], argv[1], argv[2], nullptr, nullptr); + return err ? -1 : 0; +} + + + +static int cmd_mkdir(const struct command *c, int argc, char *argv[]) +{ + imgtoolerr_t err; + imgtool::image::ptr image; + imgtool::partition::ptr partition; + int partition_index = 0; + + err = imgtool::image::open(argv[0], argv[1], OSD_FOPEN_RW, image); + if (err) + goto done; + + err = imgtool::partition::open(*image, partition_index, partition); + if (err) + goto done; + + err = partition->create_directory(argv[2]); + if (err) + goto done; + +done: + if (err) + reporterror(err, c, argv[0], argv[1], argv[2], nullptr, nullptr); + return err ? -1 : 0; +} + + + +static int cmd_rmdir(const struct command *c, int argc, char *argv[]) +{ + imgtoolerr_t err; + imgtool::image::ptr image; + imgtool::partition::ptr partition; + int partition_index = 0; + + err = imgtool::image::open(argv[0], argv[1], OSD_FOPEN_RW, image); + if (err) + goto done; + + err = imgtool::partition::open(*image, partition_index, partition); + if (err) + goto done; + + err = partition->delete_directory(argv[2]); + if (err) + goto done; + +done: + if (err) + reporterror(err, c, argv[0], argv[1], argv[2], nullptr, nullptr); + return err ? -1 : 0; +} + + + +static int cmd_identify(const struct command *c, int argc, char *argv[]) +{ + imgtool_module *modules[128]; + imgtoolerr_t err; + int i; + + err = imgtool::image::identify_file(argv[0], modules, std::size(modules)); + if (err) + { + reporterror(err, c, nullptr, argv[0], nullptr, nullptr, nullptr); + return -1; + } + else + { + for (i = 0; modules[i]; i++) + { + util::stream_format(std::wcout, L"%.16s %s\n", wstring_from_utf8(modules[i]->name), wstring_from_utf8(modules[i]->description)); + } + + return 0; + } +} + + + +static int cmd_create(const struct command *c, int argc, char *argv[]) +{ + imgtoolerr_t err; + int unnamedargs; + const imgtool_module *module; + std::unique_ptr resolution; + + module = imgtool_find_module(argv[0]); + if (!module) + { + err = (imgtoolerr_t)(IMGTOOLERR_MODULENOTFOUND | IMGTOOLERR_SRC_MODULE); + goto error; + } + + if (module->createimage_optguide && !module->createimage_optspec.empty()) + { + try { resolution.reset(new util::option_resolution(*module->createimage_optguide)); } + catch (...) + { + err = IMGTOOLERR_OUTOFMEMORY; + goto error; + } + resolution->set_specification(module->createimage_optspec.c_str()); + } + + unnamedargs = parse_options(argc, argv, 2, 3, resolution.get(), nullptr, nullptr); + if (unnamedargs < 0) + return -1; + + err = imgtool::image::create(module, argv[1], resolution.get()); + if (err) + goto error; + + return 0; + +error: + reporterror(err, c, argv[0], argv[1], nullptr, nullptr, nullptr); + return -1; +} + + + +static int cmd_readsector(const struct command *c, int argc, char *argv[]) +{ + imgtoolerr_t err; + std::unique_ptr img; + imgtool::stream::ptr stream; + std::vector buffer; + uint32_t track, head, sector; + + /* attempt to open image */ + err = imgtool::image::open(argv[0], argv[1], OSD_FOPEN_READ, img); + if (err) + goto done; + + track = atoi(argv[2]); + head = atoi(argv[3]); + sector = atoi(argv[4]); + + err = img->read_sector(track, head, sector, buffer); + if (err) + goto done; + + stream = imgtool::stream::open(argv[5], OSD_FOPEN_WRITE); + if (!stream) + { + err = (imgtoolerr_t)(IMGTOOLERR_FILENOTFOUND | IMGTOOLERR_SRC_NATIVEFILE); + goto done; + } + + stream->write(&buffer[0], buffer.size()); + +done: + if (err) + reporterror(err, c, argv[0], argv[1], nullptr, nullptr, nullptr); + return err ? -1 : 0; +} + + + +static int cmd_writesector(const struct command *c, int argc, char *argv[]) +{ + imgtoolerr_t err; + std::unique_ptr img; + imgtool::stream::ptr stream; + std::vector buffer; + uint32_t size, track, head, sector; + + // attempt to open image + err = imgtool::image::open(argv[0], argv[1], OSD_FOPEN_RW, img); + if (err) + goto done; + + track = atoi(argv[2]); + head = atoi(argv[3]); + sector = atoi(argv[4]); + + stream = imgtool::stream::open(argv[5], OSD_FOPEN_READ); + if (!stream) + { + err = (imgtoolerr_t)(IMGTOOLERR_FILENOTFOUND | IMGTOOLERR_SRC_NATIVEFILE); + goto done; + } + + size = (uint32_t) stream->size(); + + buffer.resize(size); + + stream->read(&buffer[0], size); + + err = img->write_sector(track, head, sector, &buffer[0], size); + if (err) + goto done; + +done: + if (err) + reporterror(err, c, argv[0], argv[1], nullptr, nullptr, nullptr); + return err ? -1 : 0; +} + + + +static int cmd_listformats(const struct command *c, int argc, char *argv[]) +{ + util::stream_format(std::wcout, L"Image formats supported by imgtool:\n\n"); + + for (const auto &module : imgtool_get_modules()) + { + util::stream_format(std::wcout, L" %-25s%s\n", wstring_from_utf8(module->name), wstring_from_utf8(module->description)); + } + + return 0; +} + + + +static int cmd_listfilters(const struct command *c, int argc, char *argv[]) +{ + int i; + + util::stream_format(std::wcout, L"Filters supported by imgtool:\n\n"); + + for (i = 0; filters[i]; i++) + { + util::stream_format(std::wcout, L" %-11s%s\n", + wstring_from_utf8(filter_get_info_string(filters[i], FILTINFO_STR_NAME)), + wstring_from_utf8(filter_get_info_string(filters[i], FILTINFO_STR_HUMANNAME))); + } + + return 0; +} + +static void listoptions(const util::option_guide &opt_guide, const char *opt_spec) +{ + util::option_resolution resolution(opt_guide); + resolution.set_specification(opt_spec); + + util::stream_format(std::wcout, L"Option Allowed values Description\n"); + util::stream_format(std::wcout, L"---------------- ------------------------------ -----------\n"); + + for (auto iter = resolution.entries_begin(); iter != resolution.entries_end(); iter++) + { + const util::option_resolution::entry &entry = *iter; + std::stringstream description_buffer; + + std::string opt_name = util::string_format("--%s", entry.identifier()); + const char *opt_desc = entry.display_name(); + + // is this option relevant? + if (!strchr(opt_spec, entry.parameter())) + continue; + + switch (entry.option_type()) + { + case util::option_guide::entry::option_type::INT: + for (const auto &range : entry.ranges()) + { + if (!description_buffer.str().empty()) + description_buffer << "/"; + + if (range.min == range.max) + util::stream_format(description_buffer, "%d", range.min); + else + util::stream_format(description_buffer, "%d-%d", range.min, range.max); + } + break; + + case util::option_guide::entry::option_type::ENUM_BEGIN: + for (auto enum_value = entry.enum_value_begin(); enum_value != entry.enum_value_end(); enum_value++) + { + if (!description_buffer.str().empty()) + description_buffer << '/'; + description_buffer << enum_value->identifier(); + } + break; + + case util::option_guide::entry::option_type::STRING: + description_buffer << "(string)"; + break; + + default: + assert(0); + break; + } + + util::stream_format(std::wcout, L"%16s %-30s %s\n", + wstring_from_utf8(opt_name), + wstring_from_utf8(description_buffer.str()), + wstring_from_utf8(opt_desc)); + } +} + + + +static int cmd_listdriveroptions(const struct command *c, int argc, char *argv[]) +{ + const imgtool_module *mod; + const util::option_guide *opt_guide; + const char *opt_spec; + + mod = imgtool_find_module(argv[0]); + if (!mod) + { + reporterror((imgtoolerr_t)(IMGTOOLERR_MODULENOTFOUND|IMGTOOLERR_SRC_MODULE), c, argv[0], nullptr, nullptr, nullptr, nullptr); + return -1; + } + + util::stream_format(std::wcout, L"Driver specific options for module '%s':\n\n", wstring_from_utf8(argv[0])); + + /* list write options */ + opt_guide = (const util::option_guide *) imgtool_get_info_ptr(&mod->imgclass, IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE); + opt_spec = imgtool_get_info_string(&mod->imgclass, IMGTOOLINFO_STR_WRITEFILE_OPTSPEC); + if (opt_guide) + { + util::stream_format(std::wcout, L"Image specific file options (usable on the 'put' command):\n\n"); + listoptions(*opt_guide, opt_spec); + util::stream_format(std::wcout, L"\n"); + } + else + { + util::stream_format(std::wcout, L"No image specific file options\n\n"); + } + + /* list create options */ + opt_guide = mod->createimage_optguide; + if (opt_guide) + { + util::stream_format(std::wcout, L"Image specific creation options (usable on the 'create' command):\n\n"); + listoptions(*opt_guide, mod->createimage_optspec.c_str()); + util::stream_format(std::wcout, L"\n"); + } + else + { + util::stream_format(std::wcout, L"No image specific creation options\n\n"); + } + + return 0; +} + + + +/* ----------------------------------------------------------------------- */ + +static const struct command cmds[] = +{ + { "create", cmd_create, " [--(createoption)=value]", 2, 8, 0}, + { "dir", cmd_dir, " [path]", 2, 3, 0 }, + { "get", cmd_get, " [newname] [--filter=filter] [--fork=fork]", 3, 6, 0 }, + { "put", cmd_put, " ... [--(fileoption)==value] [--filter=filter] [--fork=fork]", 3, 0xffff, 0 }, + { "getall", cmd_getall, " [path] [--filter=filter]", 2, 3, 0 }, + { "del", cmd_del, " ...", 3, 3, 1 }, + { "mkdir", cmd_mkdir, " ", 3, 3, 0 }, + { "rmdir", cmd_rmdir, " ...", 3, 3, 1 }, + { "readsector", cmd_readsector, " ", 6, 6, 0 }, + { "writesector", cmd_writesector, " ", 6, 6, 0 }, + { "identify", cmd_identify, "", 1, 1 }, + { "listformats", cmd_listformats, nullptr, 0, 0, 0 }, + { "listfilters", cmd_listfilters, nullptr, 0, 0, 0 }, + { "listdriveroptions", cmd_listdriveroptions, "", 1, 1, 0 } +}; + + +// ---------------------------------------------------------------------- + +int main(int argc, char *argv[]) +{ + int i; + int result; + const struct command *c; + const char *sample_format = "coco_jvc_rsdos"; + std::string cmdname(core_filename_extract_base(argv[0])); + +#ifdef _WIN32 + _setmode(_fileno(stdout), _O_U8TEXT); +#endif // _WIN32 + +#ifdef MAME_DEBUG + if (imgtool_validitychecks()) + return -1; +#endif // MAME_DEBUG + + // convert arguments to UTF-8 + std::vector args = osd_get_command_line(argc, argv); + argv = (char **)alloca(sizeof(char *) * args.size()); + for (i = 0; i < args.size(); i++) + argv[i] = (char *)args[i].c_str(); + + util::stream_format(std::wcout, L"\n"); + + if (argc > 1) + { + /* figure out what command they are running, and run it */ + for (i = 0; i < std::size(cmds); i++) + { + c = &cmds[i]; + if (!core_stricmp(c->name, argv[1])) + { + /* check argument count */ + if (c->minargs > (argc - 2)) + goto cmderror; + + /* initialize the imgtool core */ + imgtool_init(true, nullptr); + + if (c->lastargrepeats && (argc > c->maxargs)) + { + for (i = c->maxargs+1; i < argc; i++) + { + argv[c->maxargs+1] = argv[i]; + + result = c->cmdproc(c, c->maxargs, argv + 2); + if (result) + goto done; + } + result = 0; + goto done; + } + else + { + if ((c->maxargs > 0) && (c->maxargs < (argc - 2))) + goto cmderror; + + result = c->cmdproc(c, argc - 2, argv + 2); + goto done; + } + } + } + } + + // Usage + util::stream_format(std::wcerr, L"imgtool - Generic image manipulation tool for use with MAME\n\n"); + for (i = 0; i < std::size(cmds); i++) + { + writeusage(std::wcerr, (i == 0), &cmds[i], argv); + } + util::stream_format(std::wcerr, L"\n is the image format, e.g. %s\n", wstring_from_utf8(sample_format)); + util::stream_format(std::wcerr, L" is the image filename; can specify a ZIP file for image name\n"); + + util::stream_format(std::wcerr, L"\nExample usage:\n"); + util::stream_format(std::wcerr, L"\t%s dir %s myimageinazip.zip\n", wstring_from_utf8(cmdname), wstring_from_utf8(sample_format)); + util::stream_format(std::wcerr, L"\t%s get %s myimage.dsk myfile.bin mynewfile.txt\n", wstring_from_utf8(cmdname), wstring_from_utf8(sample_format)); + util::stream_format(std::wcerr, L"\t%s getall %s myimage.dsk\n", wstring_from_utf8(cmdname), wstring_from_utf8(sample_format)); + result = 0; + goto done; + +cmderror: + writeusage(std::wcout, 1, &cmds[i], argv); + result = -1; + +done: + imgtool_exit(); + return result; +} diff --git a/src/tools/imgtool/main.h b/src/tools/imgtool/main.h new file mode 100644 index 0000000..70cb43e --- /dev/null +++ b/src/tools/imgtool/main.h @@ -0,0 +1,20 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +#include "imgtool.h" + +struct command +{ + const char *name; + int (*cmdproc)(const struct command *c, int argc, char *argv[]); + const char *usage; + int minargs; + int maxargs; + int lastargrepeats; +}; + +void reporterror(imgtoolerr_t err, const struct command *c, const char *format, const char *imagename, + const char *filename, const char *newname, util::option_resolution *opts); + +#ifdef MAME_DEBUG +int cmd_testsuite(struct command *c, int argc, char *argv[]); +#endif diff --git a/src/tools/imgtool/modules.cpp b/src/tools/imgtool/modules.cpp new file mode 100644 index 0000000..de1595c --- /dev/null +++ b/src/tools/imgtool/modules.cpp @@ -0,0 +1,104 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + modules.c + + List of Imgtool modules + +***************************************************************************/ + +#include "modules.h" + +#ifndef MODULES_RECURSIVE +#define MODULES_RECURSIVE + +/* step 1: declare all external references */ +#define MODULE(name) extern void name##_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info); +#include "modules.cpp" +#undef MODULE + +/* step 2: define the modules[] array */ +#define MODULE(name) name##_get_info, +static void (*const modules[])(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) = +{ +#include "modules.cpp" +}; + +/* step 3: declare imgtool_create_canonical_library() */ +imgtoolerr_t imgtool_create_canonical_library(bool omit_untested, std::unique_ptr &library) +{ + /* list of modules that we drop */ + static const char *const irrelevant_modules[] = + { + "coco_os9_rsdos" + }; + + library.reset(new imgtool::library()); + if (!library) + return IMGTOOLERR_OUTOFMEMORY; + + // create all modules + for (auto &module : modules) + library->add(module); + + // remove irrelevant modules + for (auto &module : irrelevant_modules) + library->unlink(module); + + // if we are omitting untested, go through and block out the functionality in question + if (omit_untested) + { + for (auto &module : library->modules()) + { + if (module->writing_untested) + { + module->write_sector = nullptr; + } + if (module->creation_untested) + { + module->create = nullptr; + module->createimage_optguide = nullptr; + module->createimage_optspec.clear(); + } + } + } + + return IMGTOOLERR_SUCCESS; +} + + +#else /* MODULES_RECURSIVE */ + +MODULE(amiga_floppy) +MODULE(concept) +MODULE(mac_mfs) +MODULE(mac_hfs) +MODULE(hd) +MODULE(rsdos) +MODULE(dgndos) +MODULE(vzdos) +MODULE(os9) +MODULE(ti99_old) +MODULE(ti99_v9t9) +MODULE(ti99_pc99fm) +MODULE(ti99_pc99mfm) +MODULE(ti99_ti99hd) +MODULE(ti990) +MODULE(pc_floppy) +MODULE(pc_chd) +MODULE(prodos_525) +MODULE(prodos_35) +MODULE(thom_fd_basic) +MODULE(thom_qd_basic) +MODULE(thom_sap_basic) +MODULE(cybiko) +MODULE(cybikoxt) +MODULE(psion) +MODULE(bml3) +MODULE(hp48) +MODULE(hp9845_tape) +MODULE(hp85_tape) +MODULE(rt11) + +#endif /* MODULES_RECURSIVE */ diff --git a/src/tools/imgtool/modules.h b/src/tools/imgtool/modules.h new file mode 100644 index 0000000..d58b5af --- /dev/null +++ b/src/tools/imgtool/modules.h @@ -0,0 +1,18 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/**************************************************************************** + + modules.h + + Code that creates the "canonical" Imgtool library + +****************************************************************************/ + +#ifndef MODULES_H +#define MODULES_H + +#include "library.h" + +imgtoolerr_t imgtool_create_canonical_library(bool omit_untested, std::unique_ptr &library); + +#endif /* MODULES_H */ diff --git a/src/tools/imgtool/modules/amiga.cpp b/src/tools/imgtool/modules/amiga.cpp new file mode 100644 index 0000000..aa5cc26 --- /dev/null +++ b/src/tools/imgtool/modules/amiga.cpp @@ -0,0 +1,2420 @@ +// license:BSD-3-Clause +// copyright-holders:Dirk Best +/**************************************************************************** + + amiga.cpp + + Amiga floppies + +****************************************************************************/ + + +/***************************************************************************** + Includes +*****************************************************************************/ + +#include "imgtool.h" +#include "charconv.h" +#include "iflopimg.h" + +#include "formats/imageutl.h" +#include "corestr.h" +#include "opresolv.h" + +#include +#include +#include + + + +/***************************************************************************** + Data structures +*****************************************************************************/ + + +#define BSIZE (512) /* Block size is always 512 bytes for floppies */ +#define TSIZE ((BSIZE/4) - 56) /* Size of data tables */ +#define MSIZE ((BSIZE/4) - 1) /* Size of bitmaps */ + + +enum disk_type +{ + DT_UNKNOWN = -1, + DT_OFS = 0, + DT_FFS = 1, + DT_OFS_INTL = 2, + DT_FFS_INTL = 3, + DT_OFS_INTL_DIRC = 4, + DT_FFS_INTL_DIRC = 5 +}; + + +enum +{ + T_INVALID = 0, + T_HEADER = 2, + T_DATA = 8, + T_LIST = 16, + T_DIRCACHE = 33 +}; + + +enum sec_type +{ + ST_INVALID = 0, + ST_ROOT = 1, + ST_USERDIR = 2, + ST_FILE = -3, + ST_LINKFILE = -4, + ST_LINKDIR = 4, + ST_SOFTLINK = 3 +}; + + +struct amiga_date +{ + uint32_t days; /* days since 1 jan 78 */ + uint32_t mins; /* minutes past midnight */ + uint32_t ticks; /* ticks (1/50 sec) past last minute */ +}; + + +struct root_block +{ + uint32_t ht_size; /* Hash table size in long */ + uint32_t chksum; /* Rootblock checksum */ + uint32_t ht[TSIZE]; /* Hash table (entry block number) */ + uint32_t bm_flag; /* bitmap flag, -1 means VALID */ + uint32_t bm_pages[25]; /* bitmap blocks pointers (first one at bm_pages[0]) */ + amiga_date r; /* last root alteration date */ + uint8_t name_len; /* volume name length */ + uint8_t diskname[30]; /* volume name */ + amiga_date v; /* last disk alteration date */ + amiga_date c; /* filesystem creation date */ + uint32_t extension; /* FFS: first directory cache block, 0 otherwise */ +}; + + +struct bitmap_block +{ + uint32_t chksum; /* checksum, normal algorithm */ + uint32_t map[MSIZE]; /* bitmap */ +}; + + +struct bitmap_ext_block +{ + uint32_t map[MSIZE]; /* bitmap */ + uint32_t next; /* next extension block */ +}; + + +struct file_block +{ + uint32_t header_key; /* self pointer (to this block) */ + uint32_t high_seq; /* number of data block ptr stored here */ + uint32_t first_data; /* first data block ptr */ + uint32_t chksum; /* same algorithm as rootblock */ + uint32_t data_blocks[TSIZE]; /* data blk ptr */ + uint16_t uid; /* UserID */ + uint16_t gid; /* GroupID */ + uint32_t protect; /* protection flags (0 by default) */ + uint32_t byte_size; /* file size in bytes */ + uint8_t comm_len; /* file comment length */ + uint8_t comment[79]; /* comment (max. 79 chars permitted) */ + amiga_date date; /* last change date */ + uint8_t name_len; /* filename length */ + uint8_t filename[30]; /* filename (max. 30 chars permitted) */ + uint32_t real_entry; /* FFS: unused, set to 0 */ + uint32_t next_link; /* FFS: hardlinks chained list (first == newest */ + uint32_t hash_chain; /* next entry ptr with same hash */ + uint32_t parent; /* parent directory */ + uint32_t extension; /* pointer to 1st file extension block */ +}; + + +struct file_ext_block +{ + uint32_t header_key; /* self pointer (to this block) */ + uint32_t high_seq; /* number of data block ptr stored here */ + uint32_t chksum; /* same algorithm as rootblock */ + uint32_t data_blocks[TSIZE]; /* data blk ptr */ + uint32_t parent; /* file header block */ + uint32_t extension; /* pointer to next file extension block */ +}; + + +struct data_block +{ + uint32_t header_key; /* self pointer (to this block) */ + uint32_t seq_num; /* file data block number */ + uint32_t data_size; /* data size */ + uint32_t next_data; /* next data block ptr */ + uint32_t chksum; /* checksum, rootblock algorithm */ + uint8_t data[BSIZE-24]; /* file data */ +}; + + +struct dir_block +{ + uint32_t header_key; /* self pointer (to this block) */ + uint32_t chksum; /* same algorithm as rootblock */ + uint32_t ht[TSIZE]; /* hash table (entry block number) */ + uint8_t uid; /* UserID */ + uint8_t gid; /* GroupID */ + uint32_t protect; /* protection flags (0 by default) */ + uint8_t comm_len; /* file comment length */ + uint8_t comment[79]; /* comment (max. 79 chars permitted) */ + amiga_date date; /* last access date */ + uint8_t name_len; /* directory name length */ + uint8_t dirname[30]; /* directory name (max. 30 chars permitted) */ + uint32_t next_link; /* FFS: hardlinks chained list (first == newest */ + uint32_t hash_chain; /* next entry ptr with same hash */ + uint32_t parent; /* parent directory */ + uint32_t extension; /* FFS: first directory cache block */ +}; + + +struct hardlink_block +{ + uint32_t header_key; /* self pointer (to this block) */ + uint32_t chksum; /* same algorithm as rootblock */ + uint32_t protect; /* protection flags (0 by default) */ + uint8_t comm_len; /* file comment length */ + uint8_t comment[79]; /* comment (max. 79 chars permitted) */ + amiga_date date; /* last access date */ + uint8_t name_len; /* hard link name length */ + uint8_t hlname[30]; /* hard link name (max. 30 chars permitted) */ + uint32_t real_entry; /* FFS: pointer to "real" file or directory */ + uint32_t next_link; /* FFS: hardlinks chained list (first == newest */ + uint32_t hash_chain; /* next entry ptr with same hash */ + uint32_t parent; /* parent directory */ + uint32_t sec_type; /* secondary type, ST_LINKFILE/ST_LINKDIR */ +}; + + +struct softlink_block +{ + uint32_t header_key; /* self pointer (to this block) */ + uint32_t chksum; /* same algorithm as rootblock */ + uint8_t symbolic_name[BSIZE-224]; /* path name to referenced object */ + uint32_t protect; /* protection flags (0 by default) */ + uint8_t comm_len; /* file comment length */ + uint8_t comment[79]; /* comment (max. 79 chars permitted) */ + amiga_date date; /* last access date */ + uint8_t name_len; /* soft link name length */ + uint8_t slname[30]; /* soft link name (max. 30 chars permitted) */ + uint32_t hash_chain; /* next entry ptr with same hash */ + uint32_t parent; /* parent directory */ +}; + + +/* Basic Amiga floppy disk image info */ +struct amiga_floppy +{ + imgtool::stream *stream; + uint8_t sectors; +}; + + +/* iterator used to walk through directory entries */ +struct amiga_iterator +{ + unsigned int index; /* current file index */ + int block; /* block number we are iterating */ + uint32_t next_block; /* next block in hash chain */ + int ht_index; /* current index in the hash table */ + unsigned int eof : 1; /* end of file listing reached? */ +}; + + +/***************************************************************************** + Prototypes +*****************************************************************************/ + + +static imgtoolerr_t amiga_image_read_sector(imgtool::image &img, + uint32_t track, uint32_t head, uint32_t sector, void *buf, size_t len); +static imgtoolerr_t amiga_image_read_sector(imgtool::image &img, + uint32_t track, uint32_t head, uint32_t sector, std::vector &buffer); +static imgtoolerr_t amiga_image_write_sector(imgtool::image &img, + uint32_t track, uint32_t head, uint32_t sector, const void *buf, size_t len, int ddam); + + + +/***************************************************************************** + Utility functions +*****************************************************************************/ + + +/* Amiga version of the toupper function */ +static int intl_toupper(int c) +{ + return (c>='a' && c<='z') || (c>=224 && c<=254 && c!=247) ? c - ('a'-'A') : c ; +} + + +/* Amiga filename case insensitive string compare */ +static int intl_stricmp(const char *s1, const char *s2) +{ + for (;;) + { + int c1 = intl_toupper(*s1++); + int c2 = intl_toupper(*s2++); + + if (c1 == 0 || c1 != c2) + return c1 - c2; + } +} + + +/* Calculate the hash value for a filename */ +static int hash_name(const char *name, int intl) +{ + int i, l = strlen(name); + uint32_t hash = l; + + for(i = 0; i < l; i++) + { + hash *= 13; + hash += (uint8_t) (intl ? intl_toupper(name[i]) : toupper(name[i])); + hash &= 0x7ff; + } + + return hash % TSIZE; /* 0 < hash < 71 in case of BSIZE=512 */ +} + + +/* Returns true if year is a leap year */ +static int is_leap(int year) +{ + return ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0); +} + + +/* Convert amiga time to standard time */ +static imgtool::datetime amiga_crack_time(amiga_date *date) +{ + int month_days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + int year = 1978, month = 1, year_days = 365; /* base date */ + int day = date->days; + + /* first calculate the year */ + while (day >= year_days) + { + day -= year_days; + year_days = is_leap(++year) ? 366 : 365; + } + + /* then the month */ + while(day >= month_days[month-1]) + { + day -= month_days[month-1]; + if (month == 2 && is_leap(year)) + day -= 1; + month++; + } + + // fill the struct with our calculated values + util::arbitrary_datetime dt; + dt.year = year; + dt.month = month; + dt.day_of_month = day; + dt.hour = date->mins / 60; + dt.minute = date->mins % 60; + dt.second = date->ticks / 50; + + return imgtool::datetime(imgtool::datetime::datetime_type::LOCAL, dt); +} + + +/* convert standard time to amiga time */ +static void amiga_setup_time(time_t time, amiga_date *dest) +{ + struct tm t = *localtime(&time); + int year; + + dest->days = 0; + + for (year = 1978; year < t.tm_year + 1900; year++) + { + dest->days += is_leap(year) ? 366 : 365; + } + + dest->days += t.tm_yday; + dest->mins = t.tm_hour * 60 + t.tm_min; + dest->ticks = t.tm_sec * 50; +} + + +/* convert flags to human readable form */ +static void amiga_decode_flags(uint32_t flags, char *dest) +{ + /* test for flags */ + dest[0] = (flags & 0x80) ? 'h' : '-'; + dest[1] = (flags & 0x40) ? 's' : '-'; + dest[2] = (flags & 0x20) ? 'p' : '-'; + dest[3] = (flags & 0x10) ? 'a' : '-'; + dest[4] = (flags & 0x08) ? '-' : 'r'; + dest[5] = (flags & 0x04) ? '-' : 'w'; + dest[6] = (flags & 0x02) ? '-' : 'e'; + dest[7] = (flags & 0x01) ? '-' : 'd'; + dest[8] = '\0'; +} + + +static void copy_integer_array_be(uint32_t *dest, const uint32_t *source, int size) +{ + int i; + + for (i = 0; i < size; i++) + { + dest[i] = big_endianize_int32(source[i]); + } +} + + +/* This function converts an array of UINT32s to an amiga_date */ +static void copy_date_be(amiga_date *dest, const uint32_t *source) +{ + dest->days = big_endianize_int32(source[0]); + dest->mins = big_endianize_int32(source[1]); + dest->ticks = big_endianize_int32(source[2]); +} + + +/* Calculate the block checksum of a byte array */ +static uint32_t block_checksum(uint8_t *buffer, int length) +{ + uint32_t chksum = 0; + int i; + + for (i = 0; i < length/4; i++) + { + chksum += pick_integer_be(buffer, i*4, 4); + } + + return -chksum; +} + + +/* Get Amiga floppy data */ +static amiga_floppy *get_amiga_floppy(imgtool::image &image) +{ + return (amiga_floppy *)image.extra_bytes(); +} + + +/* Returns the total number of blocks in the image */ +static int get_total_blocks(imgtool::image &img) +{ + amiga_floppy *f = get_amiga_floppy(img); + + return 2 * 80 * f->sectors; +} + + +/* Returns track, head and sector for a block */ +static void find_block(amiga_floppy *f, int block, int *track, + int *head, int *sector) +{ + *track = block / f->sectors; + *head = (block - *track * f->sectors) / f->sectors; + *sector = (block - *track * f->sectors) % f->sectors; +} + + +/* Generic read block */ +static imgtoolerr_t read_block(imgtool::image &img, int block, uint8_t *buffer) +{ + imgtoolerr_t ret; + int track, head, sector; + + find_block(get_amiga_floppy(img), block, &track, &head, §or); + + /* get block from image */ + ret = amiga_image_read_sector(img, track, head, sector, buffer, BSIZE); + if (ret) return ret; + + return IMGTOOLERR_SUCCESS; +} + + +/* Generic write block */ +static imgtoolerr_t write_block(imgtool::image &img, int block, const uint8_t *buffer) +{ + imgtoolerr_t ret; + int track, head, sector; + + find_block(get_amiga_floppy(img), block, &track, &head, §or); + + /* write block to image */ + ret = amiga_image_write_sector(img, track, head, sector, buffer, BSIZE, 0); + if (ret) return ret; + + return IMGTOOLERR_SUCCESS; +} + + +/* Return the type a block */ +static sec_type get_block_type(imgtool::image &img, int block) +{ + imgtoolerr_t ret; + uint8_t buffer[BSIZE]; + + /* get data */ + ret = read_block(img, block, buffer); + if (ret) return ST_INVALID; + + /* return type */ + switch ((int32_t) pick_integer_be(buffer, BSIZE-4, 4)) + { + case 1: return ST_ROOT; + case 2: return ST_USERDIR; + case -3: return ST_FILE; + case -4: return ST_LINKFILE; + case 4: return ST_LINKDIR; + case 3: return ST_SOFTLINK; + default: return ST_INVALID; + } +} + + +/* Read a bitmap block */ +static imgtoolerr_t read_bitmap_block(imgtool::image &img, int block, bitmap_block *bm) +{ + imgtoolerr_t ret; + uint8_t buffer[BSIZE]; + + /* read block */ + ret = read_block(img, block, buffer); + if (ret) return ret; + + /* fill in data */ + bm->chksum = pick_integer_be(buffer, 0, 4); + copy_integer_array_be(bm->map, (uint32_t *) &buffer[4], MSIZE); + + return IMGTOOLERR_SUCCESS; +} + + +/* Write a bitmap block */ +static imgtoolerr_t write_bitmap_block(imgtool::image &img, int block, const bitmap_block *bm) +{ + imgtoolerr_t ret; + uint8_t buffer[BSIZE]; + + /* Setup buffer */ + place_integer_be(buffer, 0, 4, bm->chksum); + copy_integer_array_be((uint32_t *) &buffer[4], bm->map, MSIZE); + + /* write block */ + ret = write_block(img, block, buffer); + if (ret) return ret; + + return IMGTOOLERR_SUCCESS; +} + + +#ifdef UNUSED_FUNCTION +/* Read a bitmap extended block */ +static imgtoolerr_t read_bitmap_ext_block(imgtool::image *img, int block, bitmap_ext_block *bm) +{ + imgtoolerr_t ret; + uint8_t buffer[BSIZE]; + + /* read block */ + ret = read_block(img, block, buffer); + if (ret) return ret; + + /* fill in data */ + copy_integer_array_be(bm->map, (uint32_t *) &buffer, MSIZE); + bm->next = pick_integer_be(buffer, BSIZE-4, 4); + + return IMGTOOLERR_SUCCESS; +} +#endif + + +/* Read the root block */ +static imgtoolerr_t read_root_block(imgtool::image &img, root_block *root) +{ + imgtoolerr_t ret; + uint8_t buffer[BSIZE]; + + /* get raw root block from image */ + ret = read_block(img, get_total_blocks(img)/2, buffer); + if (ret) return ret; + + /* copy data to root_block */ + memset(root, 0, sizeof(root_block)); + + root->ht_size = pick_integer_be(buffer, 12, 4); + root->chksum = pick_integer_be(buffer, 20, 4); + copy_integer_array_be(root->ht, (uint32_t *) &buffer[24], TSIZE); + root->bm_flag = pick_integer_be(buffer, BSIZE-200, 4); + copy_integer_array_be(root->bm_pages, (uint32_t *) &buffer[BSIZE-196], 25); + copy_date_be(&root->r, (uint32_t *) &buffer[BSIZE-92]); + root->name_len = pick_integer_be(buffer, BSIZE-80, 1); + memcpy(root->diskname, &buffer[BSIZE-79], 30); + copy_date_be(&root->v, (uint32_t *) &buffer[BSIZE-40]); + copy_date_be(&root->c, (uint32_t *) &buffer[BSIZE-28]); + root->extension = pick_integer_be(buffer, BSIZE-8, 4); + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t write_root_block(imgtool::image &img, const root_block *root) +{ + imgtoolerr_t ret; + uint8_t buffer[BSIZE]; + + /* Setup buffer */ + memset(buffer, 0, BSIZE); + + place_integer_be(buffer, 0, 4, T_HEADER); + place_integer_be(buffer, 12, 4, root->ht_size); + place_integer_be(buffer, 20, 4, root->chksum); + copy_integer_array_be((uint32_t *) &buffer[24], root->ht, TSIZE); + place_integer_be(buffer, BSIZE-200, 4, root->bm_flag); + copy_integer_array_be((uint32_t *) &buffer[BSIZE-196], root->bm_pages, 25); + place_integer_be(buffer, BSIZE-92, 4, root->r.days); + place_integer_be(buffer, BSIZE-88, 4, root->r.mins); + place_integer_be(buffer, BSIZE-84, 4, root->r.ticks); + place_integer_be(buffer, BSIZE-80, 1, root->name_len); + memcpy(&buffer[BSIZE-79], root->diskname, root->name_len); + place_integer_be(buffer, BSIZE-40, 4, root->v.days); + place_integer_be(buffer, BSIZE-36, 4, root->v.mins); + place_integer_be(buffer, BSIZE-32, 4, root->v.ticks); + place_integer_be(buffer, BSIZE-28, 4, root->c.days); + place_integer_be(buffer, BSIZE-24, 4, root->c.mins); + place_integer_be(buffer, BSIZE-20, 4, root->c.ticks); + place_integer_be(buffer, BSIZE-8, 4, root->extension); + place_integer_be(buffer, BSIZE-4, 4, ST_ROOT); + + /* write root block to image */ + ret = write_block(img, get_total_blocks(img)/2, buffer); + if (ret) return ret; + + return IMGTOOLERR_SUCCESS; +} + + +/* Read a file block */ +static imgtoolerr_t read_file_block(imgtool::image &img, int block, file_block *fb) +{ + imgtoolerr_t ret; + uint8_t buffer[BSIZE]; + + /* read block */ + ret = read_block(img, block, buffer); + if (ret) return ret; + + /* fill in data */ + fb->header_key = pick_integer_be(buffer, 4, 4); + fb->high_seq = pick_integer_be(buffer, 8, 4); + fb->first_data = pick_integer_be(buffer, 16, 4); + fb->chksum = pick_integer_be(buffer, 20, 4); + copy_integer_array_be(fb->data_blocks, (uint32_t *) &buffer[24], TSIZE); + fb->uid = pick_integer_be(buffer, BSIZE-196, 2); + fb->gid = pick_integer_be(buffer, BSIZE-194, 2); + fb->protect = pick_integer_be(buffer, BSIZE-192, 4); + fb->byte_size = pick_integer_be(buffer, BSIZE-188, 4); + fb->comm_len = pick_integer_be(buffer, BSIZE-184, 1); + memcpy(fb->comment, &buffer[BSIZE-183], 79); + copy_date_be(&fb->date, (uint32_t *) &buffer[BSIZE-92]); + fb->name_len = pick_integer_be(buffer, BSIZE-80, 1); + memcpy(fb->filename, (uint32_t *) &buffer[BSIZE-79], 30); + fb->real_entry = pick_integer_be(buffer, BSIZE-44, 4); + fb->next_link = pick_integer_be(buffer, BSIZE-40, 4); + fb->hash_chain = pick_integer_be(buffer, BSIZE-16, 4); + fb->parent = pick_integer_be(buffer, BSIZE-12, 4); + fb->extension = pick_integer_be(buffer, BSIZE-8, 4); + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t read_file_ext_block(imgtool::image &img, int block, file_ext_block *fe) +{ + imgtoolerr_t ret; + uint8_t buffer[BSIZE]; + + /* read block */ + ret = read_block(img, block, buffer); + if (ret) return ret; + + /* fill in data */ + fe->header_key = pick_integer_be(buffer, 4, 4); + fe->high_seq = pick_integer_be(buffer, 8, 4); + fe->chksum = pick_integer_be(buffer, 20, 4); + copy_integer_array_be(fe->data_blocks, (uint32_t *) &buffer[24], TSIZE); + fe->parent = pick_integer_be(buffer, BSIZE-12, 4); + fe->extension = pick_integer_be(buffer, BSIZE-8, 4); + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t read_data_block(imgtool::image &img, int block, data_block *d) +{ + imgtoolerr_t ret; + uint8_t buffer[BSIZE]; + + /* read block */ + ret = read_block(img, block, buffer); + if (ret) return ret; + + /* fill in data */ + d->header_key = pick_integer_be(buffer, 4, 4); + d->seq_num = pick_integer_be(buffer, 8, 4); + d->data_size = pick_integer_be(buffer, 12, 4); + d->next_data = pick_integer_be(buffer, 16, 4); + d->chksum = pick_integer_be(buffer, 20, 4); + memcpy(d->data, &buffer[24], BSIZE-24); + + return IMGTOOLERR_SUCCESS; +} + + +/* Read a directory block */ +static imgtoolerr_t read_dir_block(imgtool::image &img, int block, dir_block *db) +{ + imgtoolerr_t ret; + uint8_t buffer[BSIZE]; + + /* read block */ + ret = read_block(img, block, buffer); + if (ret) return ret; + + /* fill in data */ + db->header_key = pick_integer_be(buffer, 4, 4); + db->chksum = pick_integer_be(buffer, 20, 4); + copy_integer_array_be(db->ht, (uint32_t *) &buffer[24], TSIZE); + db->uid = pick_integer_be(buffer, BSIZE-196, 2); + db->gid = pick_integer_be(buffer, BSIZE-194, 2); + db->protect = pick_integer_be(buffer, BSIZE-192, 4); + db->comm_len = pick_integer_be(buffer, BSIZE-184, 1); + memcpy(db->comment, &buffer[BSIZE-183], 79); + copy_date_be(&db->date, (uint32_t *) &buffer[BSIZE-92]); + db->name_len = pick_integer_be(buffer, BSIZE-80, 1); + memcpy(db->dirname, (uint32_t *) &buffer[BSIZE-79], 30); + db->next_link = pick_integer_be(buffer, BSIZE-40, 4); + db->hash_chain = pick_integer_be(buffer, BSIZE-16, 4); + db->parent = pick_integer_be(buffer, BSIZE-12, 4); + db->extension = pick_integer_be(buffer, BSIZE-8, 4); + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t write_dir_block(imgtool::image &img, int block, const dir_block *db) +{ + uint8_t buffer[BSIZE]; + + /* Setup buffer */ + memset(buffer, 0, BSIZE); + + /* Copy data */ + place_integer_be(buffer, 0, 4, T_HEADER); + place_integer_be(buffer, 4, 4, db->header_key); + place_integer_be(buffer, 20, 4, db->chksum); + copy_integer_array_be((uint32_t *) &buffer[24], db->ht, TSIZE); + place_integer_be(buffer, BSIZE-196, 2, db->uid); + place_integer_be(buffer, BSIZE-194, 2, db->gid); + place_integer_be(buffer, BSIZE-192, 4, db->protect); + place_integer_be(buffer, BSIZE-184, 1, db->comm_len); + memcpy((uint32_t *) &buffer[BSIZE-183], db->comment, db->comm_len); + place_integer_be(buffer, BSIZE-92, 4, db->date.days); + place_integer_be(buffer, BSIZE-88, 4, db->date.mins); + place_integer_be(buffer, BSIZE-84, 4, db->date.ticks); + place_integer_be(buffer, BSIZE-80, 1, db->name_len); + memcpy((uint32_t *) &buffer[BSIZE-79], db->dirname, db->name_len); + place_integer_be(buffer, BSIZE-40, 4, db->next_link); + place_integer_be(buffer, BSIZE-16, 4, db->hash_chain); + place_integer_be(buffer, BSIZE-12, 4, db->parent); + place_integer_be(buffer, BSIZE-8, 4, db->extension); + place_integer_be(buffer, BSIZE-4, 4, ST_USERDIR); + + /* Write block to disk */ + return write_block(img, block, buffer); +} + + +static imgtoolerr_t read_hardlink_block(imgtool::image &img, int block, hardlink_block *hl) +{ + imgtoolerr_t ret; + uint8_t buffer[BSIZE]; + + /* read block */ + ret = read_block(img, block, buffer); + if (ret) return ret; + + /* fill in data */ + hl->header_key = pick_integer_be(buffer, 4, 4); + hl->chksum = pick_integer_be(buffer, 20, 4); + hl->protect = pick_integer_be(buffer, BSIZE-192, 4); + hl->comm_len = pick_integer_be(buffer, BSIZE-184, 1); + memcpy(hl->comment, &buffer[BSIZE-183], 79); + copy_date_be(&hl->date, (uint32_t *) &buffer[BSIZE-92]); + hl->name_len = pick_integer_be(buffer, BSIZE-80, 1); + memcpy(hl->hlname, (uint32_t *) &buffer[BSIZE-79], 30); + hl->real_entry = pick_integer_be(buffer, BSIZE-44, 4); + hl->next_link = pick_integer_be(buffer, BSIZE-40, 4); + hl->hash_chain = pick_integer_be(buffer, BSIZE-16, 4); + hl->parent = pick_integer_be(buffer, BSIZE-12, 4); + hl->sec_type = pick_integer_be(buffer, BSIZE-4, 4); + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t read_softlink_block(imgtool::image &img, int block, softlink_block *sl) +{ + imgtoolerr_t ret; + uint8_t buffer[BSIZE]; + + /* read block */ + ret = read_block(img, block, buffer); + if (ret) return ret; + + /* fill in data */ + sl->header_key = pick_integer_be(buffer, 4, 4); + sl->chksum = pick_integer_be(buffer, 20, 4); + memcpy(sl->symbolic_name, &buffer[24], BSIZE-224); + sl->protect = pick_integer_be(buffer, BSIZE-192, 4); + sl->comm_len = pick_integer_be(buffer, BSIZE-184, 1); + memcpy(sl->comment, &buffer[BSIZE-183], 79); + copy_date_be(&sl->date, (uint32_t *) &buffer[BSIZE-92]); + sl->name_len = pick_integer_be(buffer, BSIZE-80, 1); + memcpy(sl->slname, (uint32_t *) &buffer[BSIZE-79], 30); + sl->hash_chain = pick_integer_be(buffer, BSIZE-16, 4); + sl->parent = pick_integer_be(buffer, BSIZE-12, 4); + + return IMGTOOLERR_SUCCESS; +} + + +/* Returns the disk type */ +static disk_type get_disk_type(imgtool::image &img) +{ + imgtoolerr_t ret; + uint8_t buffer[BSIZE]; + + ret = read_block(img, 0, buffer); + if (ret) return DT_UNKNOWN; + + switch(buffer[3]) + { + case 0: return DT_OFS; + case 1: return DT_FFS; + case 2: return DT_OFS_INTL; + case 3: return DT_FFS_INTL; + case 4: return DT_OFS_INTL_DIRC; + case 5: return DT_FFS_INTL_DIRC; + default: return DT_UNKNOWN; + } +} + + +/* Returns true if the disk is formatted with the FastFileSystem */ +static int is_ffs(imgtool::image &img) +{ + disk_type t = get_disk_type(img); + + return ((t == DT_FFS || + t == DT_FFS_INTL || + t == DT_FFS_INTL_DIRC) ? true : false); +} + + +/* Returns true if the disk uses the international mode */ +static int is_intl(imgtool::image &img) +{ + disk_type t = get_disk_type(img); + + return ((t == DT_OFS_INTL || + t == DT_FFS_INTL || + t == DT_OFS_INTL_DIRC || + t == DT_FFS_INTL_DIRC) ? true : false); +} + +#ifdef UNUSED_FUNCTION +/* Returns true if the disk uses the directory cache mode */ +static int is_dirc(imgtool::image *img) +{ + disk_type t = get_disk_type(img); + + return ((t == DT_OFS_INTL_DIRC || + t == DT_FFS_INTL_DIRC) ? true : false); +} +#endif + +static imgtoolerr_t get_hash_table(imgtool::image &img, int block, uint32_t *ht) +{ + imgtoolerr_t ret; + + switch (get_block_type(img, block)) + { + case ST_USERDIR: + { + dir_block dir; + + /* get the directory block */ + ret = read_dir_block(img, block, &dir); + if (ret) return ret; + + /* copy data */ + memcpy(ht, &dir.ht, TSIZE*4); + + return IMGTOOLERR_SUCCESS; + } + + case ST_ROOT: + { + root_block root; + + /* get the root block */ + ret = read_root_block(img, &root); + if (ret) return ret; + + /* copy data */ + memcpy(ht, &root.ht, TSIZE*4); + + return IMGTOOLERR_SUCCESS; + } + + default: + return IMGTOOLERR_UNEXPECTED; + } +} + + +static imgtoolerr_t set_hash_table(imgtool::image &img, int block, const uint32_t *ht) +{ + uint8_t buffer[BSIZE]; + imgtoolerr_t ret; + + /* Read block */ + ret = read_block(img, block, buffer); + if (ret) return ret; + + /* Copy new hash table into it */ + copy_integer_array_be((uint32_t *) &buffer[24], ht, TSIZE); + + /* Write it back again */ + ret = write_block(img, block, buffer); + if (ret) return ret; + + return IMGTOOLERR_SUCCESS; +} + +#ifdef UNUSED_FUNCTION +static imgtoolerr_t get_root_hash_table(imgtool::image *img, uint32_t *ht) +{ + return get_hash_table(img, get_total_blocks(img)/2, ht); +} +#endif + +static imgtoolerr_t get_blockname(imgtool::image &img, int block, char *dest) +{ + uint8_t buffer[BSIZE]; + imgtoolerr_t ret; + + /* Read the block */ + ret = read_block(img, block, buffer); + if (ret) return ret; + + /* Copy filename out of the buffer */ + memset(dest, 0, 31); + memcpy(dest, &buffer[BSIZE-79], buffer[BSIZE-80]); + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t get_hash_chain(imgtool::image &img, int block, uint32_t *chain) +{ + uint8_t buffer[BSIZE]; + imgtoolerr_t ret; + + /* Read block */ + ret = read_block(img, block, buffer); + if (ret) return ret; + + /* Get chain value */ + *chain = pick_integer_be(buffer, BSIZE-16, 4); + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t set_hash_chain(imgtool::image &img, int block, uint32_t chain) +{ + uint8_t buffer[BSIZE]; + imgtoolerr_t ret; + + /* Read block */ + ret = read_block(img, block, buffer); + if (ret) return ret; + + /* Copy new hash chain value into it */ + place_integer_be(buffer, BSIZE-16, 4, chain); + + /* Write it back again */ + ret = write_block(img, block, buffer); + if (ret) return ret; + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t walk_hash_chain(imgtool::image &img, const char *path, int start_block, int *prev_block, int *block) +{ + imgtoolerr_t err; + uint32_t hash_chain; + char name[31]; + + /* choose compare function depending on intl mode */ + int (*cmp)(const char *, const char *) = is_intl(img) ? &intl_stricmp : &core_stricmp; + + /* initialize filenames */ + memset(name, 0, sizeof(name)); + + switch (get_block_type(img, start_block)) + { + case ST_USERDIR: + { + dir_block dir; + + /* read block */ + err = read_dir_block(img, start_block, &dir); + if (err) return err; + + /* copy filename string and next hash */ + memcpy(name, dir.dirname, dir.name_len); + hash_chain = dir.hash_chain; + + break; + } + + case ST_FILE: + { + file_block file; + + /* read block */ + err = read_file_block(img, start_block, &file); + if (err) return err; + + /* copy filename string and next hash */ + memcpy(name, file.filename, file.name_len); + hash_chain = file.hash_chain; + + break; + } + + case ST_SOFTLINK: + { + softlink_block sl; + + /* read block */ + err = read_softlink_block(img, start_block, &sl); + if (err) return err; + + /* copy filename string and next hash */ + memcpy(name, sl.slname, sl.name_len); + hash_chain = sl.hash_chain; + + break; + } + + case ST_LINKDIR: + case ST_LINKFILE: + { + hardlink_block hl; + + /* read block */ + err = read_hardlink_block(img, start_block, &hl); + if (err) return err; + + /* copy filename string and next hash */ + memcpy(name, hl.hlname, hl.name_len); + hash_chain = hl.hash_chain; + break; + } + + default: + return IMGTOOLERR_UNEXPECTED; + + } + + /* if we haven't found the right filename but there are linked entries, + * walk up the chain */ + if ((*cmp)(name, path) != 0 && hash_chain != 0) + { + *prev_block = start_block; + return walk_hash_chain(img, path, hash_chain, prev_block, block); + } + + /* found the correct block, return */ + if ((*cmp)(name, path) == 0) + { + *block = start_block; + return IMGTOOLERR_SUCCESS; + } + + /* we should never get here */ + return IMGTOOLERR_UNEXPECTED; +} + + +/* Returns the block number for a dir/file/link entry given as NUL delimited + * list of path parts, for example "dir1\0dir2\0dir3" returns the block number + * for directory "dir3" */ +static imgtoolerr_t find_entry(imgtool::image &img, const char *path, int start_block, int *block) +{ + imgtoolerr_t ret; + const char *next_path; + int current_block, prev; + uint32_t ht[TSIZE]; + + /* get the hash table */ + ret = get_hash_table(img, start_block, ht); + if (ret) return ret; + + /* calculate hash and get block for initial entry */ + current_block = ht[hash_name(path, is_intl(img))]; + + /* check if there was a match in the hash table */ + if (current_block == 0) + { + return IMGTOOLERR_PATHNOTFOUND; + } + + /* walk the linked hash list */ + ret = walk_hash_chain(img, path, current_block, &prev, block); + if (ret) return ret; + + /* follow links */ + switch (get_block_type(img, *block)) + { + case ST_SOFTLINK: + + /* TODO: Softlink support */ + return IMGTOOLERR_UNIMPLEMENTED; + + case ST_LINKDIR: + case ST_LINKFILE: + { + hardlink_block hl; + + ret = read_hardlink_block(img, *block, &hl); + if (ret) return ret; + + *block = hl.real_entry; + + break; + } + + default: + break; + } + + /* get next path part */ + next_path = path + strlen(path) + 1; + + /* if there are more path parts, search the next block */ + if (next_path[0]) + { + return find_entry(img, next_path, *block, block); + } + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t get_block_chksum(imgtool::image &img, int block, uint32_t *chksum, int bitmap) +{ + imgtoolerr_t ret; + uint8_t buffer[BSIZE]; + + /* get block data */ + ret = read_block(img, block, buffer); + if (ret) return ret; + + /* clear old checksum */ + if (bitmap) + { + memset(buffer, 0, 4); + } + else + { + memset(&buffer[20], 0, 4); + } + + /* calulate checksum */ + *chksum = block_checksum(buffer, BSIZE); + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t fix_chksum(imgtool::image &img, int block, int bitmap) +{ + imgtoolerr_t ret; + uint8_t buffer[BSIZE]; + uint32_t chksum; + + /* calculate block checksum */ + ret = get_block_chksum(img, block, &chksum, bitmap); + if (ret) return ret; + + /* read block data */ + ret = read_block(img, block, buffer); + if (ret) return ret; + + /* update checksum */ + if (bitmap) + { + place_integer_be(buffer, 0, 4, chksum); + } + else + { + place_integer_be(buffer, 20, 4, chksum); + } + + /* write back new block data */ + ret = write_block(img, block, buffer); + if (ret) return ret; + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t fix_block_chksum(imgtool::image &img, int block) +{ + return fix_chksum(img, block, false); +} + + +static imgtoolerr_t fix_bitmap_chksum(imgtool::image &img, int block) +{ + return fix_chksum(img, block, true); +} + + +/* Set a block as used */ +static imgtoolerr_t bitmap_mark(imgtool::image &img, int block, int used) +{ + imgtoolerr_t ret; + bitmap_block bm; + root_block root; + int page; + + block -= 2; /* subtract boot block sectors, 2 only for floppies! */ + + ret = read_root_block(img, &root); + if (ret) return ret; + + /* figure out bitmap block location */ + page = root.bm_pages[block / (MSIZE * 32)]; + + /* get bitmap */ + ret = read_bitmap_block(img, page, &bm); + if (ret) return ret; + + /* subtract pages we skip */ + block -= MSIZE * 32 * (block / (MSIZE * 32)); + + /* mark as used or free */ + if (used) + { + bm.map[block/32] &= ~(1 << block % 32); + } + else + { + bm.map[block/32] |= (1 << block % 32); + } + + /* write changed bitmap block back to disk */ + ret = write_bitmap_block(img, page, &bm); + if (ret) return ret; + + /* update checksum */ + ret = fix_bitmap_chksum(img, page); + if (ret) return ret; + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t bitmap_mark_used(imgtool::image &img, int block) +{ + return bitmap_mark(img, block, true); +} + + +static imgtoolerr_t bitmap_mark_free(imgtool::image &img, int block) +{ + return bitmap_mark(img, block, false); +} + + +static imgtoolerr_t update_block_modified_date(imgtool::image &img, int block) +{ + uint8_t buffer[BSIZE]; + imgtoolerr_t ret; + amiga_date date; + time_t now; + + ret = read_block(img, block, buffer); + if (ret) return ret; + + /* Set new time */ + time(&now); + amiga_setup_time(now, &date); + + /* Write new time into block */ + place_integer_be(buffer, BSIZE-92, 4, date.days); + place_integer_be(buffer, BSIZE-88, 4, date.mins); + place_integer_be(buffer, BSIZE-84, 4, date.ticks); + + /* Write block back to disk */ + ret = write_block(img, block, buffer); + if (ret) return ret; + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t clear_hash_table_entry(imgtool::image &img, int parent, char *filename) +{ + imgtoolerr_t ret; + uint32_t ht[TSIZE], chain; + int index, entry, prev, block; + + ret = get_hash_table(img, parent, ht); + if (ret) return ret; + + /* Calculate hash and get block for initial entry */ + index = hash_name(filename, is_intl(img)); + entry = ht[index]; + + /* Walk the hash chain to get the real entry */ + ret = walk_hash_chain(img, filename, entry, &prev, &block); + if (ret) return ret; + + /* Get chained value from block */ + ret = get_hash_chain(img, block, &chain); + if (ret) return ret; + + /* Check if we need to change the hash table */ + if (entry == block) + { + /* Set new value (might be 0 if there were no linked entries) */ + ht[index] = chain; + + /* Save changed hash table */ + ret = set_hash_table(img, parent, ht); + if (ret) return ret; + + /* Update last modified date */ + ret = update_block_modified_date(img, parent); + if (ret) return ret; + + /* Calculate new checksum */ + ret = fix_block_chksum(img, parent); + if (ret) return ret; + } + else + { + /* Save chained value to previous chain element */ + ret = set_hash_chain(img, prev, chain); + if (ret) return ret; + + /* Calculate new checksum */ + ret = fix_block_chksum(img, prev); + if (ret) return ret; + } + + /* Mark our block as free */ + ret = bitmap_mark_free(img, block); + if (ret) return ret; + + return IMGTOOLERR_SUCCESS; +} + + +/* Returns the number of the first bit that is set in the array */ +static int get_first_bit(uint32_t *array, int size) +{ + int i; + + for (i = 0; i < size; i++) + { + if (array[i] != 0) + { + uint32_t v = array[i]; + int c; + + /* get first bit that is set */ + for (c = 0; (v & 1) == 0; c++) v >>= 1; + + /* return bit number */ + return i * 32 + c; + } + } + + return -1; +} + + +#ifdef UNUSED_FUNCTION +static imgtoolerr_t walk_bitmap_ext_blocks(imgtool::image *img, int start, int *block) +{ + imgtoolerr_t ret; + bitmap_ext_block bm_ext; + int bit; + + /* if we don't have a valid block, bail out */ + if (start == 0) + { + return IMGTOOLERR_NOSPACE; + } + + /* get the extended bitmap block */ + ret = read_bitmap_ext_block(img, start, &bm_ext); + if (ret) return ret; + + /* get the first bit that is set in the map */ + bit = get_first_bit(bm_ext.map, MSIZE); + + /* if we found one, return */ + if (bit != -1) + { + *block += bit; + return IMGTOOLERR_SUCCESS; + } + + /* increase on each call */ + *block += MSIZE * 32; + + /* else continue walking the list */ + return walk_bitmap_ext_blocks(img, bm_ext.next, block); +} +#endif + + +/* Searches for a block marked as free + * TODO: bm_ext support for HDs */ +static imgtoolerr_t find_free_block(imgtool::image &img, int *block) +{ + imgtoolerr_t ret; + root_block root; + int i; + + /* get root block */ + ret = read_root_block(img, &root); + if (ret) return ret; + + /* iterate bitmap pointers */ + for (i = 0; i < 25; i++) + { + bitmap_block bm; + + /* get bitmap block pointed to */ + ret = read_bitmap_block(img, root.bm_pages[i], &bm); + if (ret) return ret; + + *block = i * 32 * MSIZE + get_first_bit(bm.map, MSIZE); + + if (*block != -1) + { + *block += 2; + return IMGTOOLERR_SUCCESS; + } + } + + /* if we get here we haven't found a free block */ + return IMGTOOLERR_NOSPACE; +} + + +static imgtoolerr_t add_entry(imgtool::image &img, int parent, int block) +{ + imgtoolerr_t ret; + uint32_t ht[TSIZE]; + char name[31]; + int hash; + + ret = get_blockname(img, block, name); + if (ret) return ret; + + ret = get_hash_table(img, parent, ht); + if (ret) return ret; + + hash = hash_name(name, is_intl(img)); + + /* Check if there is already an entry with that name */ + if (ht[hash] != 0) + { + /* Save the old value to our hash chain */ + ret = set_hash_chain(img, block, ht[hash]); + if (ret) return ret; + } + + /* Write our block number into the table */ + ht[hash] = block; + + /* Write table back to disk */ + ret = set_hash_table(img, parent, ht); + if (ret) return ret; + + return IMGTOOLERR_SUCCESS; +} + + +/* Recursively create new directory entries */ +static imgtoolerr_t makedir(imgtool::image &img, const char *path, int parent) +{ + imgtoolerr_t ret; + dir_block dir; + time_t now; + int block; + + if (!path) + { + return IMGTOOLERR_PARAMNEEDED; + } + + if (!path[0]) + { + return IMGTOOLERR_SUCCESS; + } + + /* Get a free block */ + ret = find_free_block(img, &block); + if (ret) return ret; + + /* Initialize entry */ + memset(&dir, 0, sizeof(dir_block)); + + /* Copy data */ + time(&now); + amiga_setup_time(now, &dir.date); + dir.name_len = strlen(path); + memcpy(dir.dirname, path, dir.name_len); + dir.header_key = block; + dir.parent = parent; + + /* Write block */ + ret = write_dir_block(img, block, &dir); + if (ret) return ret; + + /* Fix checksum */ + ret = fix_block_chksum(img, block); + if (ret) return ret; + + /* Link the new entry in the parent */ + ret = add_entry(img, parent, block); + if (ret) return ret; + + /* Mark it as used */ + ret = bitmap_mark_used(img, block); + if (ret) return ret; + + /* Create the next entry */ + return makedir(img, path + strlen(path) + 1, block); +} + + +/* Recursively checks the path parts and creates directories for them */ +static imgtoolerr_t checkdir(imgtool::image &img, const char *path, int parent) +{ + imgtoolerr_t ret; + int block; + char first_part[31]; + + memset(first_part, 0, sizeof(first_part)); + strcpy(first_part, path); + + /* Directories all the way down, bail out */ + if (!path[0]) + { + return IMGTOOLERR_CANNOTUSEPATH; + } + + /* Search for the entry */ + ret = find_entry(img, first_part, parent, &block); + + switch (ret) + { + case IMGTOOLERR_PATHNOTFOUND: + + /* There is no entry with this name yet, so we can just create them */ + return makedir(img, path, parent); + + case IMGTOOLERR_SUCCESS: + + if (get_block_type(img, block) == ST_USERDIR) + { + /* Go down one level */ + return checkdir(img, path + strlen(path) + 1, block); + } + else + { + /* There is an entry but it's not a directory, create it */ + return makedir(img, path, parent); + } + + default: return ret; + } +} + + +/* Writes the file data from the specified block into the stream */ +static imgtoolerr_t write_file_block_data(imgtool::image &img, int block, int size, imgtool::stream &destf) +{ + imgtoolerr_t ret; + uint8_t buffer[BSIZE]; + + /* Check if we even need to write something */ + if (size == 0) + { + return IMGTOOLERR_SUCCESS; + } + + if (is_ffs(img)) + { + /* Get block and read directly into buffer */ + ret = read_block(img, block, buffer); + if (ret) return ret; + } + else + { + data_block db; + uint32_t chksum; + + ret = read_data_block(img, block, &db); + if (ret) return ret; + + /* Verify data checksum */ + ret = get_block_chksum(img, block, &chksum, false); + if (ret) return ret; + + if (db.chksum != chksum) + { + return IMGTOOLERR_CORRUPTFILE; + } + + /* Copy data to buffer */ + memcpy(buffer, db.data, size); + } + + /* Write data to stream */ + if (destf.write(buffer, size) != size) + { + return IMGTOOLERR_WRITEERROR; + } + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t walk_data_block_ptr(imgtool::image &img, uint32_t *ptr, int *filesize, imgtool::stream *destf, bool write) +{ + int i, blocksize = is_ffs(img) ? BSIZE : BSIZE-24; + imgtoolerr_t ret; + + for (i = TSIZE-1; i >= 0; i--) + { + /* We write either blocksize bytes or whats remaining */ + int bytes_left = (*filesize >= blocksize) ? blocksize : *filesize; + + if (write) + { + ret = write_file_block_data(img, ptr[i], bytes_left, *destf); + if (ret) return ret; + } + else + { + ret = bitmap_mark_free(img, ptr[i]); + if (ret) return ret; + } + + *filesize -= bytes_left; + + /* Check if we are finished early */ + if (*filesize == 0) break; + } + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t write_data_block_ptr(imgtool::image &img, uint32_t *ptr, int *filesize, imgtool::stream &destf) +{ + return walk_data_block_ptr(img, ptr, filesize, &destf, true); +} + + +/* Marks all blocks pointed to by the data block pointers as free */ +static imgtoolerr_t clear_data_block_ptr(imgtool::image &img, uint32_t *ptr, int *filesize) +{ + return walk_data_block_ptr(img, ptr, filesize, nullptr, false); +} + + +static imgtoolerr_t walk_file_ext_data(imgtool::image &img, int block, int *filesize, imgtool::stream *destf, int write) +{ + file_ext_block file_ext; + imgtoolerr_t ret; + + /* Get file extension block */ + ret = read_file_ext_block(img, block, &file_ext); + if (ret) return ret; + + /* Write all data pointers in the table */ + ret = walk_data_block_ptr(img, file_ext.data_blocks, filesize, destf, write); + if (ret) return ret; + + /* Check if we are finished */ + if (*filesize != 0) + { + if (file_ext.extension == 0) + { + /* We are not finished, but there are no more extension blocks */ + return IMGTOOLERR_CORRUPTFILE; + } + else + { + /* Write the next file extension block */ + return walk_file_ext_data(img, file_ext.extension, filesize, destf, write); + } + } + + /* Mark ourself as free if we not writing */ + if (!write) + { + ret = bitmap_mark_free(img, block); + if (ret) return ret; + } + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t write_file_ext_data(imgtool::image &img, int block, int *filesize, imgtool::stream *destf) +{ + return walk_file_ext_data(img, block, filesize, destf, true); +} + + +static imgtoolerr_t clear_file_ext_data(imgtool::image &img, int block, int *filesize) +{ + return walk_file_ext_data(img, block, filesize, NULL, false); +} + + +/* Updates the disk alteration date stored in the root block */ +static imgtoolerr_t update_disk_alteration_date(imgtool::image &img) +{ + imgtoolerr_t ret; + root_block root; + time_t now; + + /* Get root block */ + ret = read_root_block(img, &root); + if (ret) return ret; + + /* Get current time */ + time(&now); + amiga_setup_time(now, &root.v); + + /* Write back new root block */ + ret = write_root_block(img, &root); + if (ret) return ret; + + /* And update its checksum */ + ret = fix_block_chksum(img, get_total_blocks(img)/2); + if (ret) return ret; + + return IMGTOOLERR_SUCCESS; +} + + +/***************************************************************************** + Imgtool functions +*****************************************************************************/ + + +static imgtoolerr_t amiga_image_open(imgtool::image &img, imgtool::stream::ptr &&stream) +{ + amiga_floppy *f = get_amiga_floppy(img); + uint64_t size = stream->size(); + + f->sectors = size/BSIZE/80/2; + + if (f->sectors != 11 && f->sectors != 22) + { + return IMGTOOLERR_CORRUPTIMAGE; + } + + f->stream = stream.release(); + return IMGTOOLERR_SUCCESS; +} + + +static void amiga_image_exit(imgtool::image &img) +{ + amiga_floppy *f = get_amiga_floppy(img); + if (f->stream) + delete f->stream; +} + + +static void amiga_image_info(imgtool::image &img, std::ostream &stream) +{ + imgtoolerr_t ret; + root_block root; + char info[255]; + time_t t_c, t_v, t_r; + char c[19], v[19], r[19]; + + ret = read_root_block(img, &root); + if (ret) return; + + t_c = amiga_crack_time(&root.c).to_time_t(); + t_v = amiga_crack_time(&root.v).to_time_t(); + t_r = amiga_crack_time(&root.r).to_time_t(); + + strftime(c, sizeof(c), "%d-%b-%y %H:%M:%S", localtime(&t_c)); + strftime(v, sizeof(v), "%d-%b-%y %H:%M:%S", localtime(&t_v)); + strftime(r, sizeof(r), "%d-%b-%y %H:%M:%S", localtime(&t_r)); + + strcpy(info, "Volume name: "); + strncat(info, (char *)root.diskname, root.name_len); + strcat(info, "\nVolume created: "); + strcat(info, c); + strcat(info, "\nVolume modified: "); + strcat(info, v); + strcat(info, "\n Root modified: "); + strcat(info, r); + + stream << info; +} + + +static imgtoolerr_t amiga_image_read_sector(imgtool::image &img, uint32_t track, uint32_t head, uint32_t sector, void *buf, size_t len) +{ + amiga_floppy *f = get_amiga_floppy(img); + + /* skip ahead to the area we want to read */ + f->stream->seek(track * (head+1) * f->sectors * BSIZE + sector * BSIZE, SEEK_CUR); + + if (f->stream->read(buf, len) != len) + { + return IMGTOOLERR_READERROR; + } + + /* reset stream */ + f->stream->seek(0, 0); + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t amiga_image_read_sector(imgtool::image &img, + uint32_t track, uint32_t head, uint32_t sector, std::vector &buffer) +{ + try { buffer.resize(BSIZE); } + catch (std::bad_alloc const &) { return IMGTOOLERR_OUTOFMEMORY; } + + return amiga_image_read_sector(img, track, head, sector, &buffer[0], buffer.size()); +} + + +static imgtoolerr_t amiga_image_write_sector(imgtool::image &img, uint32_t track, uint32_t head, uint32_t sector, const void *buf, size_t len, int ddam) +{ + amiga_floppy *f = get_amiga_floppy(img); + + /* skip ahead to the area we want to write */ + f->stream->seek(track * (head+1) * f->sectors * BSIZE + sector * BSIZE, SEEK_CUR); + + /* write data */ + if (f->stream->write(buf, len) != len) + { + return IMGTOOLERR_WRITEERROR; + } + + /* reset stream */ + f->stream->seek(0, 0); + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t amiga_image_beginenum(imgtool::directory &enumeration, const char *path) +{ + int blocks = get_total_blocks(enumeration.image()); + imgtoolerr_t ret; + amiga_iterator *iter; + + iter = (amiga_iterator *) enumeration.extra_bytes(); + if (!iter) return IMGTOOLERR_OUTOFMEMORY; + + iter->index = 1; + iter->ht_index = 0; + iter->eof = 0; + + if (path[0]) + { + /* search for the directory block, start with the root block */ + ret = find_entry(enumeration.image(), path, blocks/2, &iter->block); + if (ret) return ret; + } + else + { + /* we didn't get a path, use the root directory */ + iter->block = blocks / 2; + } + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t amiga_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent) +{ + amiga_iterator *iter = (amiga_iterator *) enumeration.extra_bytes(); + imgtoolerr_t ret; + uint32_t ht[TSIZE]; + int block; + + /* finished listing all entries? */ + if (iter->eof == 1 || iter->ht_index == TSIZE) + { + ent.eof = 1; + return IMGTOOLERR_SUCCESS; + } + + /* get hash table */ + ret = get_hash_table(enumeration.image(), iter->block, ht); + if (ret) return ret; + + /* skip empty hash table entries */ + while (ht[iter->ht_index] == 0) + { + iter->ht_index++; + /* check if we are already at the end */ + if (iter->ht_index == TSIZE) + { + ent.eof = 1; + return IMGTOOLERR_SUCCESS; + } + } + + /* get block number */ + block = (iter->next_block == 0) ? ht[iter->ht_index] : iter->next_block; + + switch (get_block_type(enumeration.image(), block)) + { + case ST_FILE: + { + file_block file; + + /* get block */ + ret = read_file_block(enumeration.image(), block, &file); + if (ret) return ret; + + /* fill directory entry */ + strncpyz(ent.filename, (char *)file.filename, file.name_len + 1); + ent.filesize = file.byte_size; + ent.lastmodified_time = amiga_crack_time(&file.date); + amiga_decode_flags(file.protect, ent.attr); + strncpyz(ent.comment, (char *)file.comment, file.comm_len + 1); + + iter->next_block = file.hash_chain; + + break; + } + + case ST_USERDIR: + { + dir_block dir; + + /* get block */ + ret = read_dir_block(enumeration.image(), block, &dir); + if (ret) return ret; + + /* fill directory entry */ + strncpyz(ent.filename, (char *)dir.dirname, dir.name_len + 1); + ent.lastmodified_time = amiga_crack_time(&dir.date); + amiga_decode_flags(dir.protect, ent.attr); + strncpyz(ent.comment, (char *)dir.comment, dir.comm_len + 1); + ent.directory = 1; + + iter->next_block = dir.hash_chain; + + break; + } + + case ST_SOFTLINK: + { + softlink_block sl; + + /* get block */ + ret = read_softlink_block(enumeration.image(), block, &sl); + if (ret) return ret; + + /* fill directory entry */ + strncpyz(ent.filename, (char *)sl.slname, sl.name_len + 1); + ent.lastmodified_time = amiga_crack_time(&sl.date); + amiga_decode_flags(sl.protect, ent.attr); + strncpyz(ent.comment, (char *)sl.comment, sl.comm_len + 1); + strcpy(ent.softlink, (char *)sl.symbolic_name); + + iter->next_block = sl.hash_chain; + + break; + } + + case ST_LINKDIR: + + ent.directory = 1; + [[fallthrough]]; + + case ST_LINKFILE: + { + hardlink_block hl; + + /* get block */ + ret = read_hardlink_block(enumeration.image(), block, &hl); + if (ret) return ret; + + /* get filesize from linked file */ + if (!ent.directory) + { + file_block file; + ret = read_file_block(enumeration.image(), hl.real_entry, &file); + if (ret) return ret; + ent.filesize = file.byte_size; + } + + /* fill directory entry */ + strncpyz(ent.filename, (char *)hl.hlname, hl.name_len + 1); + ent.lastmodified_time = amiga_crack_time(&hl.date); + amiga_decode_flags(hl.protect, ent.attr); + strncpyz(ent.comment, (char *)hl.comment, hl.comm_len + 1); + ent.hardlink = 1; + + iter->next_block = hl.hash_chain; + + break; + } + + default: + return IMGTOOLERR_UNIMPLEMENTED; + } + + /* if there are no linked entries, go to the next hash table entry */ + if (iter->next_block == 0) + { + iter->ht_index++; + } + + /* jump to next index */ + iter->index++; + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t amiga_image_freespace(imgtool::partition &partition, uint64_t *size) +{ + imgtoolerr_t ret; + imgtool::image &image(partition.image()); + const int data_size = is_ffs(image) ? BSIZE : BSIZE-24; + root_block root; + bitmap_block bm; + int blocks, blocks_processed = 0, pages, i, c; + uint32_t v; + + /* initialize size */ + *size = 0; + + /* get root block */ + ret = read_root_block(image, &root); + if (ret) return ret; + + /* get total number of blocks in the image */ + blocks = get_total_blocks(image); + + /* subtract the two bootblock blocks (only for floppies!) */ + blocks -= 2; + + /* iterate all bitmap pages */ + for (pages = 0; pages < 25; pages++) + { + ret = read_bitmap_block(image, root.bm_pages[pages], &bm); + if (ret) return ret; + + for (i = 0; i < MSIZE; i++) + { + v = bm.map[i]; + + /* clear half used value */ + if ((blocks_processed + 32) > blocks) + v &= ~(~0 << (blocks - blocks_processed)); + + /* count bits */ + for (c = 0; v; c++) + v &= v - 1; + + *size += c * data_size; + + blocks_processed += 32; + + if (blocks_processed >= blocks) + return IMGTOOLERR_SUCCESS; + } + } + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t amiga_image_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf) +{ + imgtool::image &img(partition.image()); + imgtoolerr_t ret; + file_block file; + int filesize, block; + + /* Search for the block number */ + ret = find_entry(img, filename, get_total_blocks(img)/2, &block); + if (ret == IMGTOOLERR_PATHNOTFOUND) return IMGTOOLERR_FILENOTFOUND; + if (ret) return ret; /* Other errors */ + + /* Phase 1: Follow data pointers */ + ret = read_file_block(img, block, &file); + if (ret) return ret; + + filesize = file.byte_size; + + /* Write out file data pointed to by data block pointers */ + ret = write_data_block_ptr(img, file.data_blocks, &filesize, destf); + if (ret) return ret; + + /* Check if we are done already */ + if (filesize == 0) return IMGTOOLERR_SUCCESS; + + /* Phase 2: Follow file extension blocks */ + ret = write_file_ext_data(img, file.extension, &filesize, &destf); + if (ret) return ret; + + return IMGTOOLERR_SUCCESS; +} + + +/* When a file is deleted, only its File header block number is cleared from + * the Directory block (or from the same-hash-value list) and the bitmap is + * updated. File header block, Data blocks and File extension blocks are not + * cleared, but the bitmap blocks are updated. */ +static imgtoolerr_t amiga_image_deletefile(imgtool::partition &partition, const char *fname) +{ + imgtool::image &img(partition.image()); + imgtoolerr_t ret; + int parent, block; + char filename[31]; + + /* Initialize filename */ + memset(filename, 0, sizeof(filename)); + + /* Search for the block number */ + ret = find_entry(img, fname, get_total_blocks(img)/2, &block); + if (ret == IMGTOOLERR_PATHNOTFOUND) return IMGTOOLERR_FILENOTFOUND; + if (ret) return ret; + + /* Get the parent block, where we need to clear the hash */ + switch (get_block_type(img, block)) + { + case ST_FILE: + { + file_block file; + int filesize; + + ret = read_file_block(img, block, &file); + if (ret) return ret; + + filesize = file.byte_size; + parent = file.parent; + memcpy(filename, file.filename, file.name_len); + + /* Clear all linked data sectors */ + ret = clear_data_block_ptr(img, file.data_blocks, &filesize); + if (ret) return ret; + + /* Clear extended file data sectors */ + if (filesize != 0) + { + ret = clear_file_ext_data(img, file.extension, &filesize); + if (ret) return ret; + } + + break; + } + + case ST_LINKFILE: + { + softlink_block link; + + ret = read_softlink_block(img, block, &link); + if (ret) return ret; + + parent = link.parent; + memcpy(filename, link.slname, link.name_len); + + break; + } + + default: + return IMGTOOLERR_UNEXPECTED; + } + + /* Clear hash table entry */ + ret = clear_hash_table_entry(img, parent, filename); + if (ret) return ret; + + /* Update disk alteration date */ + ret = update_disk_alteration_date(img); + if (ret) return ret; + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t amiga_image_writefile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts) +{ + return IMGTOOLERR_UNIMPLEMENTED; +} + + +static imgtoolerr_t amiga_image_create(imgtool::image &img, imgtool::stream::ptr &&stream, util::option_resolution *opts) +{ + amiga_floppy *f = get_amiga_floppy(img); + const std::string &dskname = opts->lookup_string('N'); + imgtoolerr_t ret; + uint8_t buffer[BSIZE]; + root_block root; + bitmap_block bm; + time_t now; + int blocks; + + f->stream = stream.get(); + + switch (opts->lookup_int('S')) + { + case 0: f->sectors = 11; break; + case 1: f->sectors = 22; break; + default: return IMGTOOLERR_PARAMCORRUPT; + } + + /* initialize with zeros */ + memset(buffer, 0, BSIZE); + + /* add DOS magic string and flags */ + buffer[0] = 'D'; + buffer[1] = 'O'; + buffer[2] = 'S'; + buffer[3] = 0; + + /* File system */ + buffer[3] += (opts->lookup_int('F')); + + /* File system mode */ + buffer[3] += (opts->lookup_int('M')); + + /* write first bootblock sector */ + ret = write_block(img, 0, buffer); + if (ret) return ret; + + /* reset with zeros */ + memset(buffer, 0, BSIZE); + + /* write second bootblock sector */ + ret = write_block(img, 1, buffer); + if (ret) return ret; + + /* rootblock */ + memset(&root, 0, sizeof(root_block)); + + blocks = get_total_blocks(img); + + root.chksum = 0; + root.ht_size = TSIZE; + root.bm_flag = -1; + root.bm_pages[0] = blocks/2 + 1; /* generally it's located here */ + + time(&now); + amiga_setup_time(now, &root.r); + amiga_setup_time(now, &root.v); + amiga_setup_time(now, &root.c); + + /* volume name */ + if (!dskname.empty()) + { + root.name_len = dskname.length(); + memcpy(&root.diskname, dskname.c_str(), root.name_len); + } + else + { + root.name_len = strlen("Empty"); + memcpy(&root.diskname, "Empty", root.name_len); + } + + /* write root block to disk */ + ret = write_root_block(img, &root); + if (ret) return ret; + + /* calculate block checksum */ + ret = fix_block_chksum(img, blocks/2); + if (ret) return ret; + + /* bitmap block */ + memset(&bm, 0xff, sizeof(bitmap_block)); + + /* write bitmap block to disk */ + ret = write_bitmap_block(img, root.bm_pages[0], &bm); + if (ret) return ret; + + /* set root and bitmap block as used */ + ret = bitmap_mark_used(img, blocks/2); + if (ret) return ret; + + ret = bitmap_mark_used(img, root.bm_pages[0]); + if (ret) return ret; + + /* write empty last block so that we don't get a truncated image */ + memset(buffer, 0, BSIZE); + + ret = write_block(img, blocks - 1, buffer); + if (ret) return ret; + + f->stream = stream.release(); + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t amiga_image_createdir(imgtool::partition &partition, const char *path) +{ + imgtool::image &img(partition.image()); + imgtoolerr_t ret; + + /* Create directories */ + ret = checkdir(img, path, get_total_blocks(img)/2); + if (ret) return ret; + + /* Update disk alteration date */ + ret = update_disk_alteration_date(img); + if (ret) return ret; + + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t amiga_image_getattrs(imgtool::partition &partition, const char *path, const uint32_t *attrs, imgtool_attribute *values) +{ + return IMGTOOLERR_UNIMPLEMENTED; +} + + +static imgtoolerr_t amiga_image_setattrs(imgtool::partition &partition, const char *path, const uint32_t *attrs, const imgtool_attribute *values) +{ + return IMGTOOLERR_UNIMPLEMENTED; +} + + +static imgtoolerr_t amiga_image_geticoninfo(imgtool::partition &partition, const char *path, imgtool_iconinfo *iconinfo) +{ + return IMGTOOLERR_UNIMPLEMENTED; +} + + +static imgtoolerr_t amiga_image_suggesttransfer(imgtool::partition &partition, const char *fname, imgtool_transfer_suggestion *suggestions, size_t suggestions_length) +{ + return IMGTOOLERR_UNIMPLEMENTED; +} + + + +/***************************************************************************** + Create image options +*****************************************************************************/ + + +OPTION_GUIDE_START(amiga_createimage_optionguide) + OPTION_STRING( 'N', "name", "Volume name" ) + OPTION_ENUM_START( 'S', "density", "Density" ) + OPTION_ENUM( 0, "dd", "Double Density" ) + OPTION_ENUM( 1, "hd", "High Density" ) + OPTION_ENUM_END + OPTION_ENUM_START( 'F', "filesystem", "File system" ) + OPTION_ENUM( 0, "ofs", "OldFileSystem" ) + OPTION_ENUM( 1, "ffs", "FastFileSystem" ) + OPTION_ENUM_END + OPTION_ENUM_START( 'M', "mode", "File system options" ) + OPTION_ENUM( 0, "none", "None" ) + OPTION_ENUM( 2, "intl", "International Mode" ) + OPTION_ENUM( 4, "dirc", "International Mode with Directory Cache" ) + OPTION_ENUM_END +OPTION_GUIDE_END + + + +/***************************************************************************** + Imgtool module declaration +*****************************************************************************/ + + +/* Amiga floppy disk attributes */ +void amiga_floppy_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(amiga_floppy); break; + case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(amiga_iterator); break; + case IMGTOOLINFO_INT_SUPPORTS_LASTMODIFIED_TIME: info->i = 1; break; + case IMGTOOLINFO_INT_PATH_SEPARATOR: info->i = '/'; break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "amiga_floppy"); break; + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "Amiga floppy disk image (OFS/FFS format)"); break; + case IMGTOOLINFO_STR_FILE_EXTENSIONS: strcpy(info->s = imgtool_temp_str(), "adf"); break; + case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break; + case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), EOLN_LF); break; + case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC: strcpy(info->s = imgtool_temp_str(), "S[0]-1;F[0]-1;M[0]/2/4"); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case IMGTOOLINFO_PTR_OPEN: info->open = amiga_image_open; break; + case IMGTOOLINFO_PTR_CLOSE: info->close = amiga_image_exit; break; + case IMGTOOLINFO_PTR_READ_SECTOR: info->read_sector = amiga_image_read_sector; break; + case IMGTOOLINFO_PTR_WRITE_SECTOR: info->write_sector = amiga_image_write_sector; break; + case IMGTOOLINFO_PTR_CREATE: info->create = amiga_image_create; break; + case IMGTOOLINFO_PTR_INFO: info->info = amiga_image_info; break; + case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = amiga_image_beginenum; break; + case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = amiga_image_nextenum; break; + case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = amiga_image_freespace; break; + case IMGTOOLINFO_PTR_READ_FILE: info->read_file = amiga_image_readfile; break; + case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = amiga_image_writefile; break; + case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = amiga_image_deletefile; break; + case IMGTOOLINFO_PTR_CREATE_DIR: info->create_dir = amiga_image_createdir; break; + case IMGTOOLINFO_PTR_GET_ATTRS: info->get_attrs = amiga_image_getattrs; break; + case IMGTOOLINFO_PTR_SET_ATTRS: info->set_attrs = amiga_image_setattrs; break; + case IMGTOOLINFO_PTR_GET_ICON_INFO: info->get_iconinfo = amiga_image_geticoninfo; break; + case IMGTOOLINFO_PTR_SUGGEST_TRANSFER: info->suggest_transfer = amiga_image_suggesttransfer; break; + case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE: info->createimage_optguide = &amiga_createimage_optionguide; break; + case IMGTOOLINFO_PTR_CHARCONVERTER: info->charconverter = &imgtool::charconverter_iso_8859_1; break; + } +} diff --git a/src/tools/imgtool/modules/bml3.cpp b/src/tools/imgtool/modules/bml3.cpp new file mode 100644 index 0000000..ecb33c0 --- /dev/null +++ b/src/tools/imgtool/modules/bml3.cpp @@ -0,0 +1,932 @@ +// license:GPL-2.0+ +// copyright-holders:Jonathan Edwards +/**************************************************************************** + + bml3.c + + Hitachi bml3 disk images + + By Jonathan Edwards, based on rsdos.c (both use Microsoft BASIC) + +****************************************************************************/ + +/* Supported Hitachi floppy formats are: + - 3" or 5"1/4 single density, single-sided: 40 tracks, 16 sectors/track, 128 bytes/sector + - (used with MP-1805 floppy disk controller card) + - 5"1/4 double density, double-sided: 40 tracks, 16 sectors/track, 256 bytes/sector + - (first track on first head may be single density) + - (used with MP-1802 floppy disk controller card) +*/ + +#include "imgtool.h" +#include "filter.h" +#include "iflopimg.h" + +#include "corestr.h" +#include "opresolv.h" + +#include +#include +#include + +#define MAX_SECTOR_SIZE 256 + +struct bml3_diskinfo +{ + uint16_t sector_size; /* 128 or 256 */ + uint8_t heads; /* 1 or 2 */ + uint8_t fat_start_sector; /* in cylinder 20, start sector of FAT */ + uint8_t fat_start_offset; /* start byte of FAT in sector */ + uint8_t fat_sectors; /* the number of sectors in the FAT */ + uint8_t dirent_start_sector; /* in cylinder 20, start sector of directory entries */ + uint8_t granule_sectors; /* how many sectors per granule */ + uint8_t first_granule_cylinder; /* the number of the first cylinder with granule numbers assigned */ + uint8_t variant; /* 0 - older version, uses EOF to terminate files, 1 - newer version, stores file length */ +}; + +/* this structure mirrors the structure of a directory entry on disk */ +struct bml3_dirent +{ + char fname[8]; + char fext[3]; + uint8_t ftype; + uint8_t asciiflag; + uint8_t first_granule; + uint16_t lastsectorbytes; + // TODO there are some 'unused' bytes here that are sometimes used to store a timestamp, maybe support this? +}; + +struct bml3_direnum +{ + int index; + int eof; +}; + +#define MAX_GRANULEMAP_SIZE 256 + +struct granule_list_t { + uint8_t granules[MAX_GRANULEMAP_SIZE]; + uint8_t granule_count; + uint8_t last_granule_sectors; +}; + +#define BML3_OPTIONS_FTYPE 'T' +#define BML3_OPTIONS_ASCII 'M' + +static imgtoolerr_t bml3_diskimage_deletefile(imgtool::partition &partition, const char *fname); + + + +/********************************************************************* + Imgtool module code +*********************************************************************/ + +static bml3_diskinfo *bml3_get_diskinfo(imgtool::image &image) +{ + return (bml3_diskinfo *) imgtool_floppy_extrabytes(image); +} + + + +static int max_dirents(imgtool::image &image) +{ + bml3_diskinfo *info = bml3_get_diskinfo(image); + return (16 * info->heads + 1 - info->dirent_start_sector)*(info->sector_size/32); +} + + + +static void dirent_location(imgtool::image &image, int index_loc, uint8_t *head, uint8_t *track, uint8_t *sector, uint8_t *offset) +{ + bml3_diskinfo *info = bml3_get_diskinfo(image); + *track = 20; + *sector = info->dirent_start_sector + index_loc / (info->sector_size / 32); + *head = 0; + if (*sector > 16) { + // wrap to second head + *sector -= 16; + (*head)++; + } + *offset = index_loc % (info->sector_size/32) * 32; +} + + + +static floperr_t get_bml3_dirent(imgtool::image &f, int index_loc, struct bml3_dirent *ent) +{ + floperr_t err; + uint8_t head, track, sector, offset; + uint8_t buf[32]; + bml3_diskinfo *info = bml3_get_diskinfo(f); + dirent_location(f, index_loc, &head, &track, §or, &offset); + err = floppy_read_sector(imgtool_floppy(f), head, track, sector, offset, (void *) buf, sizeof(buf)); + memset(ent, 0, sizeof(*ent)); + switch (info->variant) { + case 0: + memcpy(&ent->fname, &buf[0], 8); + ent->ftype = buf[11]; + ent->asciiflag = buf[12]; + ent->first_granule = buf[14]; + break; + case 1: + memcpy(&ent->fname, &buf[0], 8); + memcpy(&ent->fext, &buf[8], 3); + ent->ftype = buf[11]; + ent->asciiflag = buf[12]; + ent->first_granule = buf[13]; + ent->lastsectorbytes = (buf[14] << 8) | buf[15]; + break; + default: + return FLOPPY_ERROR_INVALIDIMAGE; + } + return err; +} + + + +static floperr_t put_bml3_dirent(imgtool::image &f, int index_loc, const struct bml3_dirent *ent) +{ + floperr_t err; + uint8_t head, track, sector, offset; + uint8_t buf[32]; + bml3_diskinfo *info = bml3_get_diskinfo(f); + if (index_loc >= max_dirents(f)) + return (floperr_t)IMGTOOLERR_FILENOTFOUND; + dirent_location(f, index_loc, &head, &track, §or, &offset); + memset(buf, 0, sizeof(buf)); + switch (info->variant) { + case 0: + memcpy(&buf[0], &ent->fname, 8); + buf[11] = ent->ftype; + buf[12] = ent->asciiflag; + buf[14] = ent->first_granule; + break; + case 1: + memcpy(&buf[0], &ent->fname, 8); + memcpy(&buf[8], &ent->fext, 3); + buf[11] = ent->ftype; + buf[12] = ent->asciiflag; + buf[13] = ent->first_granule; + buf[14] = ent->lastsectorbytes >> 8; + buf[15] = ent->lastsectorbytes & 0xff; + break; + default: + return FLOPPY_ERROR_INVALIDIMAGE; + } + err = floppy_write_sector(imgtool_floppy(f), head, track, sector, offset, (void *) buf, sizeof(buf), 0); /* TODO: pass ddam argument from imgtool */ + return err; +} + + + +/* fnamebuf must have at least 13 bytes */ +static void get_dirent_fname(char *fnamebuf, const struct bml3_dirent *ent) +{ + char *s; + + memset(fnamebuf, 0, 13); + memcpy(fnamebuf, ent->fname, sizeof(ent->fname)); + rtrim(fnamebuf); + s = fnamebuf + strlen(fnamebuf); + *(s++) = '.'; + memcpy(s, ent->fext, sizeof(ent->fext)); + rtrim(s); + + /* If no extension, remove period */ + if (*s == '\0') + s[-1] = '\0'; +} + + + +static imgtoolerr_t lookup_bml3_file(imgtool::image &f, const char *fname, struct bml3_dirent *ent, int *position) +{ + int i; + floperr_t ferr; + char fnamebuf[13]; + + i = 0; + fnamebuf[0] = '\0'; + + do + { + do + { + ferr = get_bml3_dirent(f, i++, ent); + if (ferr) + return imgtool_floppy_error(ferr); + } + while(ent->fname[0] == '\0'); + + + if (ent->fname[0] != -1) + get_dirent_fname(fnamebuf, ent); + } + while((ent->fname[0] != -1) && core_stricmp(fnamebuf, fname)); + + if (ent->fname[0] == -1) + return IMGTOOLERR_FILENOTFOUND; + + if (position) + *position = i - 1; + return (imgtoolerr_t)0; +} + + + +static uint8_t get_granule_count(imgtool::image &img) +{ + // uint16_t tracks; + uint16_t disk_granules; + bml3_diskinfo *info = bml3_get_diskinfo(img); + + // This always returns 82 for D88, so not quite right + // tracks = floppy_get_tracks_per_disk(imgtool_floppy(img)); + + // The number of granules is primarily constrained by the disk capacity. + disk_granules = (40 - 1 - info->first_granule_cylinder) * info->heads * (16 / info->granule_sectors); + // Also, granule numbers from 0xC0 upwards are reserved for terminating a granule chain + return (uint8_t)((disk_granules < 0xC0) ? disk_granules : 0xC0); +} + +/* granule_map must be an array of MAX_GRANULEMAP_SIZE bytes */ +static floperr_t get_granule_map(imgtool::image &img, uint8_t *granule_map, uint8_t *granule_count) +{ + bml3_diskinfo *info = bml3_get_diskinfo(img); + uint8_t count; + + count = get_granule_count(img); + if (granule_count) + *granule_count = count; + + // The first byte of the granule map sector is ignored (and expected to be 0) + return floppy_read_sector(imgtool_floppy(img), 0, 20, info->fat_start_sector, info->fat_start_offset, granule_map, count); +} + + + +static floperr_t put_granule_map(imgtool::image &img, const uint8_t *granule_map, uint8_t granule_count) +{ + bml3_diskinfo *info = bml3_get_diskinfo(img); + return floppy_write_sector(imgtool_floppy(img), 0, 20, info->fat_start_sector, info->fat_start_offset, granule_map, granule_count, 0); /* TODO: pass ddam argument from imgtool */ +} + + + + +static void granule_location(imgtool::image &image, uint8_t granule, uint8_t *head, uint8_t *track, uint8_t *sector) +{ + bml3_diskinfo *info = bml3_get_diskinfo(image); + uint16_t abs_track = granule * info->granule_sectors / 16; + *head = abs_track % info->heads; + *track = abs_track / info->heads + info->first_granule_cylinder; + // skip filesystem cylinder + if (*track >= 20) + (*track)++; + *sector = granule * info->granule_sectors % 16 + 1; +} + + + +static imgtoolerr_t transfer_granule(imgtool::image &img, uint8_t granule, int length, imgtool::stream &f, imgtoolerr_t (*proc)(imgtool::image &, int, int, int, int, size_t, imgtool::stream &)) +{ + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + uint8_t head, track, sector; + granule_location(img, granule, &head, &track, §or); + if (length > 0) + err = proc(img, head, track, sector, 0, length, f); + return err; +} + + +static imgtoolerr_t transfer_from_granule(imgtool::image &img, uint8_t granule, int length, imgtool::stream &destf) +{ + return transfer_granule(img, granule, length, destf, imgtool_floppy_read_sector_to_stream); +} + + + +static imgtoolerr_t transfer_to_granule(imgtool::image &img, uint8_t granule, int length, imgtool::stream &sourcef) +{ + return transfer_granule(img, granule, length, sourcef, imgtool_floppy_write_sector_from_stream); +} + + + +static floperr_t read_granule(imgtool::image &img, uint8_t granule, int offset, int length, uint8_t *buf) +{ + uint8_t head, track, sector; + granule_location(img, granule, &head, &track, §or); + return floppy_read_sector(imgtool_floppy(img), head, track, sector, offset, buf, length); +} + + + +static floperr_t write_granule(imgtool::image &img, uint8_t granule, int offset, int length, const uint8_t *buf) +{ + uint8_t head, track, sector; + granule_location(img, granule, &head, &track, §or); + return floppy_write_sector(imgtool_floppy(img), head, track, sector, offset, buf, length, 0); /* TODO: pass ddam argument from imgtool */ +} + + + +static imgtoolerr_t list_granules(struct bml3_dirent *ent, imgtool::image &img, struct granule_list_t *granule_list) +{ + floperr_t ferr; + uint8_t max_granules; + uint8_t granule; + uint8_t usedmap[MAX_GRANULEMAP_SIZE]; /* Used to detect infinite loops */ + uint8_t granule_map[MAX_GRANULEMAP_SIZE]; + bml3_diskinfo *info = bml3_get_diskinfo(img); + + ferr = get_granule_map(img, granule_map, &max_granules); + if (ferr) + return imgtool_floppy_error(ferr); + + memset(usedmap, 0, max_granules); + + granule = ent->first_granule; + granule_list->granule_count = 0; + + while(!usedmap[granule] && granule < max_granules) + { + usedmap[granule] = 1; + granule_list->granules[granule_list->granule_count++] = granule; + granule = granule_map[granule]; + } + + granule_list->last_granule_sectors = granule - 0xc0; + if (info->variant == 0) { + // add final incomplete sector + granule_list->last_granule_sectors++; + } + + // A value of zero (variant 1) and max (variant 0) seem to indicate a file open for writing. + // Strictly speaking this means the image is corrupt, although a real system will happily read + // garbage from the file. + if (granule_list->last_granule_sectors > info->granule_sectors) + return IMGTOOLERR_CORRUPTIMAGE; + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t get_file_size(struct bml3_dirent *ent, imgtool::image &img, const struct granule_list_t *granule_list, size_t *size) +{ + floperr_t ferr; + size_t last_sector_bytes = 0; + bml3_diskinfo *info = bml3_get_diskinfo(img); + + // TODO are these special cases valid, or maybe indicate a corrupt image? + if (granule_list->granule_count == 0) { + *size = 0; + return IMGTOOLERR_SUCCESS; + } + else if (granule_list->last_granule_sectors == 0) { + *size = info->sector_size * ((granule_list->granule_count - 1) * info->granule_sectors); + return IMGTOOLERR_SUCCESS; + } + + // determine size excluding final sector + *size = info->sector_size * ((granule_list->granule_count - 1) * info->granule_sectors + granule_list->last_granule_sectors - 1); + + // determine bytes used in final sector + switch (info->variant) { + case 0: + // look for EOF (ASCII SUB) and trailing NULs in final sector + { + uint8_t buf[MAX_SECTOR_SIZE]; + ferr = read_granule(img, granule_list->granules[granule_list->granule_count-1], info->sector_size * (granule_list->last_granule_sectors - 1), info->sector_size, buf); + if (ferr) + return imgtool_floppy_error(ferr); + for (last_sector_bytes = info->sector_size - 1; ; last_sector_bytes--) { + if (buf[last_sector_bytes] != 0) + break; + if (last_sector_bytes == 0) + break; + } + if (buf[last_sector_bytes] != 0x1a) { + last_sector_bytes++; + } + } + break; + case 1: + last_sector_bytes = ent->lastsectorbytes; + break; + } + + // TODO is it valid for last_sector_bytes == 0? + if (last_sector_bytes > info->sector_size) { + return IMGTOOLERR_CORRUPTIMAGE; + } + *size += last_sector_bytes; + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t process_bml3_file(struct bml3_dirent *ent, imgtool::image &img, imgtool::stream *destf, size_t *size) +{ + imgtoolerr_t err; + size_t remaining_size, granule_size; + bml3_diskinfo *info = bml3_get_diskinfo(img); + struct granule_list_t granule_list; + granule_list.granule_count = 0; + + err = list_granules(ent, img, &granule_list); + if (err) + return err; + err = get_file_size(ent, img, &granule_list, size); + if (err) + return err; + + if (destf) { + remaining_size = *size; + granule_size = info->granule_sectors * info->sector_size; + + for (int c = 0; c < granule_list.granule_count; c++) { + if (granule_size >= remaining_size) + granule_size = remaining_size; + transfer_from_granule(img, granule_list.granules[c], granule_size, *destf); + remaining_size -= granule_size; + } + } + return IMGTOOLERR_SUCCESS; +} + + + +/* create a new directory entry with a specified name */ +static imgtoolerr_t prepare_dirent(uint8_t variant, struct bml3_dirent *ent, const char *fname) +{ + const char *fname_end; + const char *fname_ext; + int fname_ext_len; + + memset(ent, '\0', sizeof(*ent)); + memset(ent->fname, ' ', sizeof(ent->fname)); + memset(ent->fext, ' ', sizeof(ent->fext)); + + fname_end = strchr(fname, '.'); + if (fname_end) + fname_ext = fname_end + 1; + else + fname_end = fname_ext = fname + strlen(fname); + + fname_ext_len = strlen(fname_ext); + + switch (variant) { + case 0: + /* 8-character max filename */ + if (((fname_end - fname) > 8) || (fname_ext_len > 0)) + return IMGTOOLERR_BADFILENAME; + break; + case 1: + /*8.3 filename */ + if (((fname_end - fname) > 8) || (fname_ext_len > 3)) + return IMGTOOLERR_BADFILENAME; + break; + default: + return IMGTOOLERR_CORRUPTIMAGE; + } + + memcpy(ent->fname, fname, fname_end - fname); + memcpy(ent->fext, fname_ext, fname_ext_len); + + /* By default, set as a type 2 binary file */ + ent->ftype = 2; + ent->asciiflag = 0; + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t bml3_diskimage_open(imgtool::image &image, imgtool::stream::ptr &&dummy) +{ + // imgtoolerr_t err; + floperr_t ferr; + bml3_diskinfo *info = bml3_get_diskinfo(image); + floppy_image_legacy *floppy = imgtool_floppy(image); + const struct FloppyCallbacks *callbacks = floppy_callbacks(floppy); + + // probe disk geometry to guess format + int heads_per_disk = callbacks->get_heads_per_disk(floppy); + uint32_t sector_length; + ferr = callbacks->get_sector_length(floppy, 0, 20, 1, §or_length); + if (ferr) + return imgtool_floppy_error(ferr); + int sectors_per_track = callbacks->get_sectors_per_track(floppy, 0, 20); + + if (heads_per_disk == 2 && sector_length == 128 && sectors_per_track == 16) { + // single-sided, single-density + info->sector_size = 128; + info->heads = 1; + info->fat_start_sector = 1; + info->fat_start_offset = 5; + info->fat_sectors = 2; + info->dirent_start_sector = 7; + info->granule_sectors = 4; + info->first_granule_cylinder = 0; + info->variant = 0; + } + else if (heads_per_disk == 2 && sector_length == 256 && sectors_per_track == 16) { + // double-sided, double-density + info->sector_size = 256; + info->heads = 2; + info->fat_start_sector = 2; + info->fat_start_offset = 1; + info->fat_sectors = 1; + info->dirent_start_sector = 5; + info->granule_sectors = 8; + info->first_granule_cylinder = 1; + info->variant = 1; + } + else { + // invalid or unsupported format + return IMGTOOLERR_CORRUPTIMAGE; + } + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t bml3_diskimage_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent) +{ + floperr_t ferr; + imgtoolerr_t err; + size_t filesize; + struct bml3_direnum *rsenum; + struct bml3_dirent rsent; + char fname[13]; + imgtool::image &image(enumeration.image()); + + rsenum = (struct bml3_direnum *) enumeration.extra_bytes(); + + /* Did we hit the end of file before? */ + if (rsenum->eof) + goto eof; + + do + { + if (rsenum->index >= max_dirents(image)) + goto eof; + + ferr = get_bml3_dirent(image, rsenum->index++, &rsent); + if (ferr) + return imgtool_floppy_error(ferr); + } + while(rsent.fname[0] == '\0'); + + /* Now are we at the eof point? */ + if (rsent.fname[0] == -1) + { + rsenum->eof = 1; +eof: + ent.eof = 1; + } + else + { + /* Not the end of file */ + err = process_bml3_file(&rsent, image, nullptr, &filesize); + if (err) + return err; + + if (filesize == ((size_t) -1)) + { + /* corrupt! */ + ent.filesize = 0; + ent.corrupt = 1; + } + else + { + ent.filesize = filesize; + ent.corrupt = 0; + } + ent.eof = 0; + + get_dirent_fname(fname, &rsent); + + snprintf(ent.filename, std::size(ent.filename), "%s", fname); + snprintf(ent.attr, std::size(ent.attr), "%d %c", (int) rsent.ftype, (char) (rsent.asciiflag + 'B')); + } + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t bml3_diskimage_freespace(imgtool::partition &partition, uint64_t *size) +{ + floperr_t ferr; + uint8_t i; + size_t s = 0; + uint8_t granule_count; + uint8_t granule_map[MAX_GRANULEMAP_SIZE]; + imgtool::image &image(partition.image()); + bml3_diskinfo *info = bml3_get_diskinfo(image); + + ferr = get_granule_map(image, granule_map, &granule_count); + if (ferr) + return imgtool_floppy_error(ferr); + + for (i = 0; i < granule_count; i++) + { + if (granule_map[i] == 0xff) + s += (info->granule_sectors * info->sector_size); + } + *size = s; + return (imgtoolerr_t)FLOPPY_ERROR_SUCCESS; +} + + + +static imgtoolerr_t delete_entry(imgtool::image &img, struct bml3_dirent *ent, int pos) +{ + floperr_t ferr; + unsigned char g, i; + uint8_t granule_count; + uint8_t granule_map[MAX_GRANULEMAP_SIZE]; + + /* Write a NUL in the filename, marking it deleted */ + ent->fname[0] = 0; + ferr = put_bml3_dirent(img, pos, ent); + if (ferr) + return imgtool_floppy_error(ferr); + + ferr = get_granule_map(img, granule_map, &granule_count); + if (ferr) + return imgtool_floppy_error(ferr); + + /* Now free up the granules */ + g = ent->first_granule; + while (g < granule_count) + { + i = granule_map[g]; + granule_map[g] = 0xff; + g = i; + } + + ferr = put_granule_map(img, granule_map, granule_count); + if (ferr) + return imgtool_floppy_error(ferr); + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t bml3_diskimage_readfile(imgtool::partition &partition, const char *fname, const char *fork, imgtool::stream &destf) +{ + imgtoolerr_t err; + struct bml3_dirent ent; + size_t size; + imgtool::image &img(partition.image()); + + err = lookup_bml3_file(img, fname, &ent, nullptr); + if (err) + return err; + + err = process_bml3_file(&ent, img, &destf, &size); + if (err) + return err; + + if (size == (size_t) -1) + return IMGTOOLERR_CORRUPTIMAGE; + + return (imgtoolerr_t)0; +} + + + +static imgtoolerr_t bml3_diskimage_writefile(imgtool::partition &partition, const char *fname, const char *fork, imgtool::stream &sourcef, util::option_resolution *writeoptions) +{ + floperr_t ferr; + imgtoolerr_t err; + imgtool::image &img(partition.image()); + bml3_diskinfo *info = bml3_get_diskinfo(img); + struct bml3_dirent ent, ent2; + size_t i; + uint64_t sz, read_sz; + uint64_t freespace = 0; + unsigned char *gptr; + uint8_t granule_count; + uint8_t granule_map[MAX_GRANULEMAP_SIZE]; + uint8_t eof_buf[MAX_SECTOR_SIZE]; + + // one-time setup of eof_buf + memset(eof_buf, 0, sizeof(eof_buf)); + eof_buf[0] = 0x1A; + + /* can we write to this image? */ + if (floppy_is_read_only(imgtool_floppy(img))) + return IMGTOOLERR_READONLY; + + err = bml3_diskimage_freespace(partition, &freespace); + if (err) + return err; + + /* is there enough space? */ + sz = read_sz = sourcef.size(); + if (info->variant == 0) { + // also need to write EOF + sz++; + } + if (sz > freespace) + return IMGTOOLERR_NOSPACE; + + /* setup our directory entry */ + err = prepare_dirent(info->variant, &ent, fname); + if (err) + return err; + + ent.ftype = writeoptions->lookup_int(BML3_OPTIONS_FTYPE); + ent.asciiflag = uint8_t(writeoptions->lookup_int(BML3_OPTIONS_ASCII)) - 1; + gptr = &ent.first_granule; + + ferr = get_granule_map(img, granule_map, &granule_count); + if (ferr) + return imgtool_floppy_error(ferr); + + unsigned char g = 0x00; + uint32_t granule_bytes = info->granule_sectors * info->sector_size; + + do + { + while (granule_map[g] != 0xff) + { + g++; + if ((g >= granule_count) || (g == 0)) + return IMGTOOLERR_UNEXPECTED; /* We should have already verified that there is enough space */ + } + *gptr = g; + gptr = &granule_map[g]; + + + i = std::min(read_sz, uint64_t(granule_bytes)); + if (i > 0) { + err = transfer_to_granule(img, g, i, sourcef); + if (err) + return err; + read_sz -= i; + sz -= i; + } + if (i < info->granule_sectors * info->sector_size && sz > 0) { + // write EOF and trailing NULs in the final sector + ferr = write_granule(img, g, i, (info->granule_sectors * info->sector_size - i - 1) % info->sector_size + 1, eof_buf); + if (ferr) + return imgtool_floppy_error(ferr); + sz--; + i++; + } + + /* Go to next granule */ + g++; + } + while(sz > 0); + + /* Now that we are done with the file, we need to specify the final entry + * in the file allocation table + */ + *gptr = 0xc0 + ((i + info->sector_size-1) / info->sector_size) - (info->variant == 0 ? 1 : 0); + ent.lastsectorbytes = (i - 1) % info->sector_size + 1; + + /* delete file if it already exists */ + err = bml3_diskimage_deletefile(partition, fname); + if (err && err != IMGTOOLERR_FILENOTFOUND) + return err; + + /* Now we need to find an empty directory entry */ + i = -1; + do + { + ferr = get_bml3_dirent(img, ++i, &ent2); + if (ferr) + return imgtool_floppy_error(ferr); + } + while(ent2.fname[0] != '\0' && ent2.fname[0] != -1); + + ferr = put_bml3_dirent(img, i, &ent); + if (ferr) + return imgtool_floppy_error(ferr); + + /* write the granule map back out */ + ferr = put_granule_map(img, granule_map, granule_count); + if (ferr) + return imgtool_floppy_error(ferr); + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t bml3_diskimage_deletefile(imgtool::partition &partition, const char *fname) +{ + imgtoolerr_t err; + imgtool::image &image(partition.image()); + int pos = 0; + struct bml3_dirent ent; + + err = lookup_bml3_file(image, fname, &ent, &pos); + if (err) + return err; + + return delete_entry(image, &ent, pos); +} + + + +static imgtoolerr_t bml3_diskimage_suggesttransfer(imgtool::partition &partition, const char *fname, imgtool_transfer_suggestion *suggestions, size_t suggestions_length) +{ + imgtoolerr_t err; + imgtool::image &image(partition.image()); + struct bml3_dirent ent; + int pos; + + if (fname) + { + err = lookup_bml3_file(image, fname, &ent, &pos); + if (err) + return err; + + if (ent.asciiflag == 0xFF) + { + /* ASCII file */ + suggestions[0].viability = SUGGESTION_RECOMMENDED; + suggestions[0].filter = filter_eoln_getinfo; + suggestions[1].viability = SUGGESTION_POSSIBLE; + suggestions[1].filter = NULL; + } + else if (ent.ftype == 0) + { + /* tokenized BASIC file */ + suggestions[0].viability = SUGGESTION_RECOMMENDED; + suggestions[0].filter = NULL; + suggestions[1].viability = SUGGESTION_POSSIBLE; + suggestions[1].filter = filter_bml3bas_getinfo; + } + } + else + { + suggestions[0].viability = SUGGESTION_RECOMMENDED; + suggestions[0].filter = NULL; + suggestions[1].viability = SUGGESTION_POSSIBLE; + suggestions[1].filter = filter_eoln_getinfo; + suggestions[2].viability = SUGGESTION_POSSIBLE; + suggestions[2].filter = filter_bml3bas_getinfo; + } + + return IMGTOOLERR_SUCCESS; +} + + + +/********************************************************************* + Imgtool module declaration +*********************************************************************/ + +OPTION_GUIDE_START( bml3_writefile_optionguide ) + OPTION_ENUM_START( BML3_OPTIONS_FTYPE, "ftype", "File type" ) + OPTION_ENUM( 0, "basic", "Basic" ) + OPTION_ENUM( 1, "data", "Data" ) + OPTION_ENUM( 2, "binary", "Binary" ) + OPTION_ENUM( 3, "assembler", "Assembler Source" ) + OPTION_ENUM_END + OPTION_ENUM_START( BML3_OPTIONS_ASCII, "ascii", "Ascii flag" ) + OPTION_ENUM( 0, "ascii", "Ascii" ) + OPTION_ENUM( 1, "binary", "Binary" ) + OPTION_ENUM_END +OPTION_GUIDE_END + + + +void bml3_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case IMGTOOLINFO_INT_PREFER_UCASE: info->i = 1; break; + case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(bml3_diskinfo); break; + case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(struct bml3_direnum); break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "bml3"); break; + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "Basic Master Level 3 format"); break; + case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break; + case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), "\r"); break; + case IMGTOOLINFO_STR_WRITEFILE_OPTSPEC: strcpy(info->s = imgtool_temp_str(), "T0-[2]-3;M0-[1]"); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case IMGTOOLINFO_PTR_MAKE_CLASS: info->make_class = imgtool_floppy_make_class; break; + case IMGTOOLINFO_PTR_FLOPPY_OPEN: info->open = bml3_diskimage_open; break; + case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = bml3_diskimage_nextenum; break; + case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = bml3_diskimage_freespace; break; + case IMGTOOLINFO_PTR_READ_FILE: info->read_file = bml3_diskimage_readfile; break; + case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = bml3_diskimage_writefile; break; + case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = bml3_diskimage_deletefile; break; + case IMGTOOLINFO_PTR_SUGGEST_TRANSFER: info->suggest_transfer = bml3_diskimage_suggesttransfer; break; + case IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE: info->writefile_optguide = &bml3_writefile_optionguide; break; + case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_default; break; + } +} diff --git a/src/tools/imgtool/modules/concept.cpp b/src/tools/imgtool/modules/concept.cpp new file mode 100644 index 0000000..7bfa7b4 --- /dev/null +++ b/src/tools/imgtool/modules/concept.cpp @@ -0,0 +1,499 @@ +// license:BSD-3-Clause +// copyright-holders:Raphael Nabet +/* + Handlers for concept floppy images + + Disk images are in MESS format. + + Raphael Nabet, 2003 +*/ + +#include "imgtool.h" + +#include "opresolv.h" + +#include +#include +#include +#include + +struct UINT16xE +{ + uint8_t bytes[2]; +}; + +/* + get_UINT16xE + + Read a 16-bit word, whether it is little-endian or big-endian + + little_endian (I): non-zero if word is little-endian, zero if word is + big-endian + word (I): pointer to word to read + + Returns value of word in native format +*/ +static inline uint16_t get_UINT16xE(int little_endian, UINT16xE word) +{ + return little_endian ? (word.bytes[0] | (word.bytes[1] << 8)) : ((word.bytes[0] << 8) | word.bytes[1]); +} + +#if 0 +/* + set_UINT16xE + + Write a 16-bit word, whether it is little-endian or big-endian + + little_endian (I): non-zero if word is little-endian, zero if word is + big-endian + word (O): pointer to word to write + data (I): value to write in word, in native format +*/ +static inline void set_UINT16xE(int little_endian, UINT16xE *word, uint16_t data) +{ + if (little_endian) + { + word->bytes[0] = data & 0xff; + word->bytes[1] = (data >> 8) & 0xff; + } + else + { + word->bytes[0] = (data >> 8) & 0xff; + word->bytes[1] = data & 0xff; + } +} +#endif + +/* + Disk structure: + + Track 0 Sector 0 & 1: bootstrap loader + Track 0 Sector 2 through 5: disk directory + Remaining sectors are used for data. +*/ + +/* + device directory record (Disk sector 2-5) +*/ + +struct concept_vol_hdr_entry +{ + UINT16xE first_block; + UINT16xE next_block; + UINT16xE ftype; + + unsigned char volname[8]; + UINT16xE last_block; + UINT16xE num_files; + UINT16xE last_boot; + UINT16xE last_access; + char mem_flipped; + char disk_flipped; + UINT16xE unused; +}; + +struct concept_file_dir_entry +{ + UINT16xE first_block; + UINT16xE next_block; + UINT16xE ftype; + + unsigned char filename[16]; + UINT16xE last_byte; + UINT16xE last_access; +}; + +struct concept_dev_dir +{ + concept_vol_hdr_entry vol_hdr; + concept_file_dir_entry file_dir[77]; + char unused[20]; +}; + +/* + concept disk image descriptor +*/ +struct concept_image +{ + imgtool::stream *file_handle; /* imgtool file handle */ + concept_dev_dir dev_dir; /* cached copy of device directory */ +}; + +/* + concept catalog iterator, used when imgtool reads the catalog +*/ +struct concept_iterator +{ + concept_image *image; + int index; /* current index */ +}; + + +static imgtoolerr_t concept_image_init(imgtool::image &img, imgtool::stream::ptr &&stream); +static void concept_image_exit(imgtool::image &img); +static void concept_image_info(imgtool::image &img, std::ostream &stream); +static imgtoolerr_t concept_image_beginenum(imgtool::directory &enumeration, const char *path); +static imgtoolerr_t concept_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent); +static void concept_image_closeenum(imgtool::directory &enumeration); +static imgtoolerr_t concept_image_freespace(imgtool::partition &partition, uint64_t *size); +static imgtoolerr_t concept_image_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf); +#if 0 +static imgtoolerr_t concept_image_writefile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream *sourcef, util::option_resolution *writeoptions); +static imgtoolerr_t concept_image_deletefile(imgtool::partition &partition, const char *filename); +static imgtoolerr_t concept_image_create(const imgtool_module *mod, imgtool::stream *f, util::option_resolution *createoptions); +#endif + +void concept_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(concept_image); break; + case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(concept_iterator); break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "concept"); break; + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "Concept floppy disk image"); break; + case IMGTOOLINFO_STR_FILE_EXTENSIONS: strcpy(info->s = imgtool_temp_str(), "img"); break; + case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), "\r"); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case IMGTOOLINFO_PTR_OPEN: info->open = concept_image_init; break; + case IMGTOOLINFO_PTR_CLOSE: info->close = concept_image_exit; break; + case IMGTOOLINFO_PTR_INFO: info->info = concept_image_info; break; + case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = concept_image_beginenum; break; + case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = concept_image_nextenum; break; + case IMGTOOLINFO_PTR_CLOSE_ENUM: info->close_enum = concept_image_closeenum; break; + case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = concept_image_freespace; break; + case IMGTOOLINFO_PTR_READ_FILE: info->read_file = concept_image_readfile; break; + case IMGTOOLINFO_PTR_WRITE_FILE: /* info->write_file = concept_image_writefile */; break; + case IMGTOOLINFO_PTR_DELETE_FILE: /* info->delete_file = concept_image_deletefile */; break; + case IMGTOOLINFO_PTR_CREATE: /* info->create = concept_image_create */; break; + } +} + +static concept_image *get_concept_image(imgtool::image &image) +{ + return (concept_image *)image.extra_bytes(); +} + +/* + read_physical_record + + Read one 512-byte physical record from a disk image + + file_handle: imgtool file handle + secnum: physical record address + dest: pointer to destination buffer + + Return non-zero on error +*/ +static int read_physical_record(imgtool::stream &file_handle, int secnum, void *dest) +{ + int reply; + + /* seek to sector */ + reply = file_handle.seek(secnum*512, SEEK_SET); + if (reply) + return 1; + /* read it */ + reply = file_handle.read(dest, 512); + if (reply != 512) + return 1; + + return 0; +} + +#ifdef UNUSED_FUNCTION +/* + write_physical_record + + Write one 512-byte physical record to a disk image + + file_handle: imgtool file handle + secnum: logical sector address + src: pointer to source buffer + + Return non-zero on error +*/ +static int write_physical_record(imgtool::stream *file_handle, int secnum, const void *src) +{ + int reply; + + /* seek to sector */ + reply = file_handle->seek(secnum*512, SEEK_SET); + if (reply) + return 1; + /* read it */ + reply = file_handle->write(src, 512); + if (reply != 512) + return 1; + + return 0; +} +#endif + +/* + Search for a file name on a concept_image + + image (I): image reference + filename (I): name of the file to search + entry_index (O): index of file in disk catalog + + Return non-zero on error +*/ +static int get_catalog_entry(concept_image *image, const unsigned char *filename, int *entry_index) +{ + int filename_len = filename[0]; + int i; + + if (filename_len > 15) + /* file name is bad */ + return 1; + + for (i = 0; i < 77; i++) + { + if (!memcmp(filename, image->dev_dir.file_dir[i].filename, filename_len+1)) + { + /* file found */ + *entry_index = i; + return 0; + } + } + + /* file not found */ + return 1; +} + +/* + Open a file as a concept_image. +*/ +static imgtoolerr_t concept_image_init(imgtool::image &img, imgtool::stream::ptr &&stream) +{ + concept_image *image = get_concept_image(img); + int reply; + int i; + unsigned totphysrecs; + + /* read device directory */ + for (i=0; i<4; i++) + { + reply = read_physical_record(*stream, i+2, ((char *) & image->dev_dir)+i*512); + if (reply) + return IMGTOOLERR_READERROR; + } + + /* do primitive checks */ + totphysrecs = get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.vol_hdr.last_block) + - get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.vol_hdr.first_block); + + if ((get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.vol_hdr.first_block) != 0) + || (get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.vol_hdr.next_block) != 6) + || (totphysrecs < 6) /*|| (f->size() != totphysrecs*512)*/ + || (image->dev_dir.vol_hdr.volname[0] > 7)) + { + return IMGTOOLERR_CORRUPTIMAGE; + } + + image->file_handle = stream.release(); + return IMGTOOLERR_SUCCESS; +} + +/* + close a concept_image +*/ +static void concept_image_exit(imgtool::image &img) +{ + /*concept_image *image = get_concept_image(img);*/ +} + +/* + get basic information on a concept_image + + Currently returns the volume name +*/ +static void concept_image_info(imgtool::image &img, std::ostream &stream) +{ + concept_image *image = get_concept_image(img); + char vol_name[8]; + + memcpy(vol_name, image->dev_dir.vol_hdr.volname + 1, image->dev_dir.vol_hdr.volname[0]); + vol_name[image->dev_dir.vol_hdr.volname[0]] = 0; + + stream << vol_name; +} + +/* + Open the disk catalog for enumeration +*/ +static imgtoolerr_t concept_image_beginenum(imgtool::directory &enumeration, const char *path) +{ + concept_iterator *iter; + + iter = (concept_iterator *) enumeration.extra_bytes(); + iter->image = (concept_image *) enumeration.image().extra_bytes(); + iter->index = 0; + return IMGTOOLERR_SUCCESS; +} + +/* + Enumerate disk catalog next entry +*/ +static imgtoolerr_t concept_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent) +{ + concept_iterator *iter = (concept_iterator *) enumeration.extra_bytes(); + + + ent.corrupt = 0; + ent.eof = 0; + + if ((iter->image->dev_dir.file_dir[iter->index].filename[0] == 0) || (iter->index > 77)) + { + ent.eof = 1; + } + else if (iter->image->dev_dir.file_dir[iter->index].filename[0] > 15) + { + ent.corrupt = 1; + } + else + { + int len = iter->image->dev_dir.file_dir[iter->index].filename[0]; + const char *type; + + if (len > std::size(ent.filename)) + len = std::size(ent.filename); + memcpy(ent.filename, iter->image->dev_dir.file_dir[iter->index].filename + 1, len); + ent.filename[len] = 0; + + /* parse flags */ + switch (get_UINT16xE(iter->image->dev_dir.vol_hdr.disk_flipped, iter->image->dev_dir.file_dir[iter->index].ftype) & 0xf) + { + case 0: + case 8: + type = "DIRHDR"; + break; + case 2: + type = "CODE"; + break; + case 3: + type = "TEXT"; + break; + case 5: + type = "DATA"; + break; + default: + type = "???"; + break; + } + snprintf(ent.attr, std::size(ent.attr), "%s", type); + + /* len in physrecs */ + ent.filesize = get_UINT16xE(iter->image->dev_dir.vol_hdr.disk_flipped, iter->image->dev_dir.file_dir[iter->index].next_block) + - get_UINT16xE(iter->image->dev_dir.vol_hdr.disk_flipped, iter->image->dev_dir.file_dir[iter->index].first_block); + + iter->index++; + } + + return (imgtoolerr_t)0; +} + +/* + Free enumerator +*/ +static void concept_image_closeenum(imgtool::directory &enumeration) +{ +} + +/* + Compute free space on disk image +*/ +static imgtoolerr_t concept_image_freespace(imgtool::partition &partition, uint64_t *size) +{ + imgtool::image &img(partition.image()); + concept_image *image = get_concept_image(img); + int free_blocks; + int i; + + /* first get number of data blocks */ + free_blocks = get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.vol_hdr.last_block) + - get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.vol_hdr.next_block); + + /* next substract length of each file */ + for (i=0; (image->dev_dir.file_dir[i].filename[0] != 0) && (i <= 77); i++) + { + free_blocks -= get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.file_dir[i].next_block) + - get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.file_dir[i].first_block); + } + + *size = free_blocks; + + return IMGTOOLERR_SUCCESS; +} + +/* + Extract a file from a concept_image. +*/ +static imgtoolerr_t concept_image_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf) +{ + imgtool::image &img(partition.image()); + concept_image *image = get_concept_image(img); + size_t filename_len = strlen(filename); + unsigned char concept_fname[16]; + int catalog_index; + int i; + uint8_t buf[512]; + + if (filename_len > 15) + return IMGTOOLERR_BADFILENAME; + + concept_fname[0] = filename_len; + memcpy(concept_fname+1, filename, filename_len); + + if (get_catalog_entry(image, concept_fname, &catalog_index)) + return IMGTOOLERR_FILENOTFOUND; + + for (i = get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.file_dir[catalog_index].first_block); + i < get_UINT16xE(image->dev_dir.vol_hdr.disk_flipped, image->dev_dir.file_dir[catalog_index].next_block); + i++) + { + if (read_physical_record(*image->file_handle, i, buf)) + return IMGTOOLERR_READERROR; + + if (destf.write(buf, 512) != 512) + return IMGTOOLERR_WRITEERROR; + } + + return (imgtoolerr_t)0; +} + +#if 0 +/* + Add a file to a concept_image. +*/ +static imgtoolerr_t concept_image_writefile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream *sourcef, util::option_resolution *writeoptions) +{ + /* ... */ + + return 0; +} + +/* + Delete a file from a concept_image. +*/ +static imgtoolerr_t concept_image_deletefile(imgtool::partition &partition, const char *filename) +{ + /* ... */ + + return 0; +} + +/* + Create a blank concept_image. +*/ +static imgtoolerr_t concept_image_create(const imgtool_module *mod, imgtool::stream *f, util::option_resolution *createoptions) +{ + /* ... */ + + return 0; +} +#endif diff --git a/src/tools/imgtool/modules/cybiko.cpp b/src/tools/imgtool/modules/cybiko.cpp new file mode 100644 index 0000000..f508e42 --- /dev/null +++ b/src/tools/imgtool/modules/cybiko.cpp @@ -0,0 +1,583 @@ +// license:BSD-3-Clause +// copyright-holders:Tim Schuerewegen +/* + + Cybiko Classic File System + + (c) 2007 Tim Schuerewegen + +*/ + +#include "imgtool.h" + +#include "opresolv.h" + +#include + +struct cybiko_file_system +{ + imgtool::stream *stream; + uint32_t page_count, page_size, block_count_boot, block_count_file; + uint16_t write_count; +}; + +struct cybiko_iter +{ + uint16_t block; +}; + +struct cfs_file +{ + char name[64]; // name of the file + uint32_t date; // date/time of the file (seconds since 1900/01/01) + uint32_t size; // size of the file + uint32_t blocks; // number of blocks occupied by the file +}; + +enum +{ + BLOCK_TYPE_INVALID, + BLOCK_TYPE_BOOT, + BLOCK_TYPE_FILE +}; + +#define MAX_PAGE_SIZE (264 * 2) + +#define INVALID_FILE_ID 0xFFFF + +enum +{ + FLASH_TYPE_INVALID, + FLASH_TYPE_AT45DB041, + FLASH_TYPE_AT45DB081, + FLASH_TYPE_AT45DB161 +}; + +#define BLOCK_USED(x) (x[0] & 0x80) +#define BLOCK_FILE_ID(x) buffer_read_16_be( x + 2) +#define BLOCK_PART_ID(x) buffer_read_16_be( x + 4) +#define BLOCK_FILENAME(x) (char*)(x + 7) + +#define FILE_HEADER_SIZE 0x48 + +static cybiko_file_system *get_cfs(imgtool::image &image) +{ + return (cybiko_file_system*)image.extra_bytes(); +} + +// 2208988800 is the number of seconds between 1900/01/01 and 1970/01/01 +typedef util::arbitrary_clock > cybiko_clock; + +imgtool::datetime cybiko_time_crack(uint32_t cfs_time) +{ + cybiko_clock::duration d(cfs_time); + std::chrono::time_point tp(d); + return imgtool::datetime(imgtool::datetime::datetime_type::LOCAL, tp); +} + +uint32_t cybiko_time_setup(const imgtool::datetime &t) +{ + auto cybiko_time_point = cybiko_clock::from_arbitrary_time_point(t.time_point()); + return cybiko_time_point.time_since_epoch().count(); +} + +static uint32_t buffer_read_32_be( uint8_t *buffer) +{ + return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | (buffer[3] << 0); +} + +static uint16_t buffer_read_16_be( uint8_t *buffer) +{ + return (buffer[0] << 8) | (buffer[1] << 0); +} + +static void buffer_write_32_be( uint8_t *buffer, uint32_t data) +{ + buffer[0] = (data >> 24) & 0xFF; + buffer[1] = (data >> 16) & 0xFF; + buffer[2] = (data >> 8) & 0xFF; + buffer[3] = (data >> 0) & 0xFF; +} + +static void buffer_write_16_be( uint8_t *buffer, uint16_t data) +{ + buffer[0] = (data >> 8) & 0xFF; + buffer[1] = (data >> 0) & 0xFF; +} + +// page = crc1 (4) + wcnt (2) + crc2 (2) + data (x) + unk (2) + +static uint32_t page_buffer_calc_checksum_1( uint8_t *buffer, uint32_t size, int block_type) +{ + return crc32( 0, buffer + 8, (block_type == BLOCK_TYPE_BOOT) ? 250 : size - 10); +} + +static uint16_t page_buffer_calc_checksum_2( uint8_t *buffer) +{ + uint16_t val = 0xAF17; + val ^= buffer_read_16_be( buffer + 0); + val ^= buffer_read_16_be( buffer + 2); + val ^= buffer_read_16_be( buffer + 4); + return swapendian_int16(val); +} + +static int page_buffer_verify( uint8_t *buffer, uint32_t size, int block_type) +{ + uint32_t checksum_page, checksum_calc; + // checksum 1 + checksum_calc = page_buffer_calc_checksum_1( buffer, size, block_type); + checksum_page = buffer_read_32_be( buffer + 0); + if (checksum_calc != checksum_page) return false; + // checksum 2 + checksum_calc = page_buffer_calc_checksum_2( buffer); + checksum_page = buffer_read_16_be( buffer + 6); + if (checksum_calc != checksum_page) return false; + // ok + return true; +} + +static int cfs_block_to_page( cybiko_file_system *cfs, int block_type, uint32_t block, uint32_t *page) +{ + switch (block_type) + { + case BLOCK_TYPE_BOOT : if (page) *page = block; return true; + case BLOCK_TYPE_FILE : if (page) *page = block + cfs->block_count_boot; return true; + default : return false; + } +} + +static int cfs_page_to_block( cybiko_file_system *cfs, uint32_t page, int *block_type, uint32_t *block) +{ + uint32_t tmp = page; + // boot block + if (tmp < cfs->block_count_boot) + { + if (block_type) *block_type = BLOCK_TYPE_BOOT; + if (block) *block = tmp; + return true; + } + tmp -= cfs->block_count_boot; + // file block + if (tmp < cfs->block_count_file) + { + if (block_type) *block_type = BLOCK_TYPE_FILE; + if (block) *block = tmp; + return true; + } + tmp -= cfs->block_count_file; + // error + return false; +} + +static int cfs_page_read( cybiko_file_system *cfs, uint8_t *buffer, uint32_t page) +{ + if (page >= cfs->page_count) return false; + cfs->stream->seek(page * cfs->page_size, SEEK_SET); + cfs->stream->read(buffer, cfs->page_size); + return true; +} + +static int cfs_page_write( cybiko_file_system *cfs, uint8_t *buffer, uint32_t page) +{ + if (page >= cfs->page_count) return false; + cfs->stream->seek(page * cfs->page_size, SEEK_SET); + cfs->stream->write(buffer, cfs->page_size); + return true; +} + +static int cfs_block_read( cybiko_file_system *cfs, uint8_t *buffer, int block_type, uint32_t block) +{ + uint8_t buffer_page[MAX_PAGE_SIZE]; + uint32_t page; + if (!cfs_block_to_page( cfs, block_type, block, &page)) return false; + if (!cfs_page_read( cfs, buffer_page, page)) return false; + memcpy( buffer, buffer_page + 8, cfs->page_size - 10); + return true; +} + +static int cfs_block_write( cybiko_file_system *cfs, uint8_t *buffer, int block_type, uint32_t block) +{ + uint8_t buffer_page[MAX_PAGE_SIZE]; + uint32_t page; + memcpy( buffer_page + 8, buffer, cfs->page_size - 10); + buffer_write_32_be( buffer_page + 0, page_buffer_calc_checksum_1( buffer_page, cfs->page_size, block_type)); + buffer_write_16_be( buffer_page + 4, cfs->write_count++); + buffer_write_16_be( buffer_page + 6, page_buffer_calc_checksum_2( buffer_page)); + buffer_write_16_be( buffer_page + cfs->page_size - 2, 0xFFFF); + if (!cfs_block_to_page( cfs, block_type, block, &page)) return false; + if (!cfs_page_write( cfs, buffer_page, page)) return false; + return true; +} + +static int cfs_file_delete( cybiko_file_system *cfs, uint16_t file_id) +{ + uint8_t buffer[MAX_PAGE_SIZE]; + int i; + for (i=0;iblock_count_file;i++) + { + if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return false; + if (BLOCK_USED(buffer) && (BLOCK_FILE_ID(buffer) == file_id)) + { + buffer[0] &= ~0x80; + if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_FILE, i)) return false; + } + } + return true; +} + +static int cfs_file_info( cybiko_file_system *cfs, uint16_t file_id, cfs_file *file) +{ + uint8_t buffer[MAX_PAGE_SIZE]; + int i; + file->blocks = file->size = 0; + for (i=0;iblock_count_file;i++) + { + if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return false; + if (BLOCK_USED(buffer) && (BLOCK_FILE_ID(buffer) == file_id)) + { + if (BLOCK_PART_ID(buffer) == 0) + { + strcpy( file->name, BLOCK_FILENAME(buffer)); + file->date = buffer_read_32_be( buffer + 6 + FILE_HEADER_SIZE - 4); + } + file->size += buffer[1]; + file->blocks++; + } + } + return (file->blocks > 0) ? true : false; +} + +static int cfs_file_find( cybiko_file_system *cfs, const char *filename, uint16_t *file_id) +{ + uint8_t buffer[MAX_PAGE_SIZE]; + int i; + for (i=0;iblock_count_file;i++) + { + if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return false; + if (BLOCK_USED(buffer) && (strncmp( filename, BLOCK_FILENAME(buffer), 40) == 0)) + { + *file_id = i; + return true; + } + } + return false; +} + +static int cfs_verify(cybiko_file_system &cfs) +{ + uint8_t buffer[MAX_PAGE_SIZE]; + int i, block_type; + for (i = 0; i < cfs.page_count; i++) + { + if (!cfs_page_read(&cfs, buffer, i)) return false; + if (!cfs_page_to_block(&cfs, i, &block_type, NULL)) return false; + if (!page_buffer_verify(buffer, cfs.page_size, block_type)) return false; + } + return true; +} + +static int cfs_init(cybiko_file_system &cfs, imgtool::stream::ptr &&stream, int flash_type) +{ + cfs.stream = stream.release(); + switch (flash_type) + { + case FLASH_TYPE_AT45DB041 : cfs.page_count = 2048; cfs.page_size = 264; break; + case FLASH_TYPE_AT45DB081 : cfs.page_count = 4096; cfs.page_size = 264; break; + case FLASH_TYPE_AT45DB161 : cfs.page_count = 4096; cfs.page_size = 528; break; + default : return false; + } + cfs.block_count_boot = 5; + cfs.block_count_file = cfs.page_count - cfs.block_count_boot; + cfs.write_count = 0; + return true; +} + +static int cfs_format( cybiko_file_system *cfs) +{ + uint8_t buffer[MAX_PAGE_SIZE]; + int i; + // boot blocks + memset( buffer, 0xFF, sizeof( buffer)); + for (i=0;iblock_count_boot;i++) + { + if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_BOOT, i)) return false; + } + // file blocks + memset( buffer, 0xFF, sizeof( buffer)); + buffer[0] &= ~0x80; + for (i=0;iblock_count_file;i++) + { + if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_FILE, i)) return false; + } + // ok + return true; +} + +static uint16_t cfs_calc_free_blocks( cybiko_file_system *cfs) +{ + uint8_t buffer[MAX_PAGE_SIZE]; + int i; + uint16_t blocks = 0; + for (i=0;iblock_count_file;i++) + { + if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return 0; + if (!BLOCK_USED(buffer)) blocks++; + } + return blocks; +} + +static uint32_t cfs_calc_free_space( cybiko_file_system *cfs, uint16_t blocks) +{ + uint32_t free_space; + free_space = blocks * (cfs->page_size - 0x10); + if (free_space > 0) free_space -= FILE_HEADER_SIZE; + return free_space; +} + +static int flash_size_to_flash_type( size_t size) +{ + switch (size) + { + case 0x084000 : return FLASH_TYPE_AT45DB041; + case 0x108000 : return FLASH_TYPE_AT45DB081; + case 0x210000 : return FLASH_TYPE_AT45DB161; + default : return FLASH_TYPE_INVALID; + } +} + +static int flash_option_to_flash_type( int option) +{ + switch (option) + { + case 0 : return FLASH_TYPE_AT45DB041; + case 1 : return FLASH_TYPE_AT45DB081; + case 2 : return FLASH_TYPE_AT45DB161; + default : return FLASH_TYPE_INVALID; + } +} + +static imgtoolerr_t cybiko_image_open(imgtool::image &image, imgtool::stream::ptr &&stream) +{ + cybiko_file_system *cfs = get_cfs(image); + int flash_type; + // init + flash_type = flash_size_to_flash_type(stream->size()); + if (!cfs_init(*cfs, std::move(stream), flash_type)) return IMGTOOLERR_CORRUPTIMAGE; + // verify + if (!cfs_verify(*cfs)) return IMGTOOLERR_CORRUPTIMAGE; + // ok + return IMGTOOLERR_SUCCESS; +} + +static void cybiko_image_close(imgtool::image &image) +{ + cybiko_file_system *cfs = get_cfs(image); + delete cfs->stream; +} + +static imgtoolerr_t cybiko_image_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *opts) +{ + cybiko_file_system *cfs = get_cfs(image); + int flash_type; + // init + flash_type = flash_option_to_flash_type(opts->lookup_int('F')); + if (!cfs_init(*cfs, std::move(stream), flash_type)) return IMGTOOLERR_CORRUPTIMAGE; + // format + if (!cfs_format(cfs)) return IMGTOOLERR_CORRUPTIMAGE; + // ok + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t cybiko_image_begin_enum(imgtool::directory &enumeration, const char *path) +{ + cybiko_iter *iter = (cybiko_iter*)enumeration.extra_bytes(); + iter->block = 0; + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t cybiko_image_next_enum(imgtool::directory &enumeration, imgtool_dirent &ent) +{ + imgtool::image &image(enumeration.image()); + cybiko_file_system *cfs = get_cfs(image); + cybiko_iter *iter = (cybiko_iter*)enumeration.extra_bytes(); + uint8_t buffer[MAX_PAGE_SIZE]; + uint16_t file_id = INVALID_FILE_ID; + cfs_file file; + // find next file + while (iter->block < cfs->block_count_file) + { + if (!cfs_block_read(cfs, buffer, BLOCK_TYPE_FILE, iter->block++)) return IMGTOOLERR_READERROR; + if (BLOCK_USED(buffer) && (BLOCK_PART_ID(buffer) == 0)) + { + file_id = BLOCK_FILE_ID(buffer); + break; + } + } + // get file information + if ((file_id != INVALID_FILE_ID) && cfs_file_info(cfs, file_id, &file)) + { + strcpy(ent.filename, file.name); + ent.filesize = file.size; + ent.lastmodified_time = cybiko_time_crack(file.date); + ent.filesize = file.size; + } + else + { + ent.eof = 1; + } + // ok + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t cybiko_image_free_space(imgtool::partition &partition, uint64_t *size) +{ + imgtool::image &image(partition.image()); + cybiko_file_system *cfs = get_cfs(image); + if (size) *size = cfs_calc_free_space( cfs, cfs_calc_free_blocks( cfs)); + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t cybiko_image_read_file(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf) +{ + imgtool::image &image(partition.image()); + cybiko_file_system *cfs = get_cfs(image); + uint8_t buffer[MAX_PAGE_SIZE]; + uint16_t file_id, part_id = 0, old_part_id; + int i; + // find file + if (!cfs_file_find( cfs, filename, &file_id)) return IMGTOOLERR_FILENOTFOUND; + // read file + do + { + old_part_id = part_id; + for (i=0;iblock_count_file;i++) + { + if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return IMGTOOLERR_READERROR; + if (BLOCK_USED(buffer) && (BLOCK_FILE_ID(buffer) == file_id) && (BLOCK_PART_ID(buffer) == part_id)) + { + destf.write(buffer + 6 + ((part_id == 0) ? FILE_HEADER_SIZE : 0), buffer[1]); + part_id++; + } + } + } while (old_part_id != part_id); + // ok + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t cybiko_image_write_file(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts) +{ + imgtool::image &image(partition.image()); + cybiko_file_system *cfs = get_cfs(image); + uint8_t buffer[MAX_PAGE_SIZE]; + uint16_t file_id, part_id = 0, free_blocks; + uint64_t bytes_left; + cfs_file file; + int i; + // find file + if (!cfs_file_find(cfs, filename, &file_id)) file_id = INVALID_FILE_ID; + // check free space + free_blocks = cfs_calc_free_blocks( cfs); + if (file_id != INVALID_FILE_ID) + { + if (!cfs_file_info(cfs, file_id, &file)) return IMGTOOLERR_UNEXPECTED; + free_blocks += file.blocks; + } + if (cfs_calc_free_space(cfs, free_blocks) < sourcef.size()) return IMGTOOLERR_NOSPACE; + // delete file + if (file_id != INVALID_FILE_ID) + { + if (!cfs_file_delete( cfs, file_id)) return IMGTOOLERR_UNEXPECTED; + } + // create/write destination file + bytes_left = sourcef.size(); + i = 0; + while ((bytes_left > 0) && (i < cfs->block_count_file)) + { + if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return IMGTOOLERR_READERROR; + if (!BLOCK_USED(buffer)) + { + if (part_id == 0) file_id = i; + memset( buffer, 0xFF, cfs->page_size - 0x10); + buffer[0] = 0x80; + buffer[1] = cfs->page_size - 0x10 - ((part_id == 0) ? FILE_HEADER_SIZE : 0); + if (bytes_left < buffer[1]) buffer[1] = bytes_left; + buffer_write_16_be( buffer + 2, file_id); + buffer_write_16_be( buffer + 4, part_id); + if (part_id == 0) + { + buffer[6] = 0; + strcpy(BLOCK_FILENAME(buffer), filename); + buffer_write_32_be(buffer + 6 + FILE_HEADER_SIZE - 4, cybiko_time_setup(imgtool::datetime::now(imgtool::datetime::datetime_type::LOCAL))); + sourcef.read(buffer + 6 + FILE_HEADER_SIZE, buffer[1]); + } + else + { + sourcef.read(buffer + 6, buffer[1]); + } + if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_FILE, i)) return IMGTOOLERR_WRITEERROR; + bytes_left -= buffer[1]; + part_id++; + } + i++; + } + // ok + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t cybiko_image_delete_file(imgtool::partition &partition, const char *filename) +{ + imgtool::image &image(partition.image()); + cybiko_file_system *cfs = get_cfs(image); + uint16_t file_id; + // find file + if (!cfs_file_find( cfs, filename, &file_id)) return IMGTOOLERR_FILENOTFOUND; + // delete file + if (!cfs_file_delete( cfs, file_id)) return IMGTOOLERR_UNEXPECTED; + // ok + return IMGTOOLERR_SUCCESS; +} + +OPTION_GUIDE_START( cybiko_image_createimage_optguide ) + OPTION_ENUM_START( 'F', "flash", "Flash Type" ) + OPTION_ENUM( 0, "AT45DB041", "AT45DB041 (528 KByte)" ) + OPTION_ENUM( 1, "AT45DB081", "AT45DB081 (1056 KByte)" ) + OPTION_ENUM( 2, "AT45DB161", "AT45DB161 (2112 KByte)" ) + OPTION_ENUM_END +OPTION_GUIDE_END + +//OPTION_GUIDE_START( cybiko_image_writefile_optguide ) +// OPTION_INT( 'B', "boot", "Boot Flag" ) +//OPTION_GUIDE_END + +void cybiko_get_info( const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES : info->i = sizeof( cybiko_file_system); break; + case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES : info->i = sizeof( cybiko_iter); break; +// case IMGTOOLINFO_INT_SUPPORTS_CREATION_TIME : info->i = 1; break; + case IMGTOOLINFO_INT_SUPPORTS_LASTMODIFIED_TIME : info->i = 1; break; +// case IMGTOOLINFO_INT_BLOCK_SIZE : info->i = 264; break; + // --- the following bits of info are returned as pointers to data or functions --- + case IMGTOOLINFO_PTR_OPEN : info->open = cybiko_image_open; break; + case IMGTOOLINFO_PTR_CREATE : info->create = cybiko_image_create; break; + case IMGTOOLINFO_PTR_CLOSE : info->close = cybiko_image_close; break; + case IMGTOOLINFO_PTR_BEGIN_ENUM : info->begin_enum = cybiko_image_begin_enum; break; + case IMGTOOLINFO_PTR_NEXT_ENUM : info->next_enum = cybiko_image_next_enum; break; + case IMGTOOLINFO_PTR_FREE_SPACE : info->free_space = cybiko_image_free_space; break; + case IMGTOOLINFO_PTR_READ_FILE : info->read_file = cybiko_image_read_file; break; + case IMGTOOLINFO_PTR_WRITE_FILE : info->write_file = cybiko_image_write_file; break; + case IMGTOOLINFO_PTR_DELETE_FILE : info->delete_file = cybiko_image_delete_file; break; + case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE : info->createimage_optguide = &cybiko_image_createimage_optguide; break; +// case IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE : info->writefile_optguide = cybiko_image_writefile_optguide; break; + // --- the following bits of info are returned as NULL-terminated strings --- + case IMGTOOLINFO_STR_NAME : strcpy( info->s = imgtool_temp_str(), "cybiko"); break; + case IMGTOOLINFO_STR_DESCRIPTION : strcpy( info->s = imgtool_temp_str(), "Cybiko Classic File System"); break; + case IMGTOOLINFO_STR_FILE : strcpy( info->s = imgtool_temp_str(), __FILE__); break; + case IMGTOOLINFO_STR_FILE_EXTENSIONS : strcpy( info->s = imgtool_temp_str(), "bin,nv"); break; + case IMGTOOLINFO_STR_EOLN : strcpy( info->s = imgtool_temp_str(), "\r\n"); break; + case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC : strcpy( info->s = imgtool_temp_str(), "F[0]-2"); break; +// case IMGTOOLINFO_STR_WRITEFILE_OPTSPEC : strcpy( info->s = imgtool_temp_str(), "B[0]-1"); break; + } +} diff --git a/src/tools/imgtool/modules/cybikoxt.cpp b/src/tools/imgtool/modules/cybikoxt.cpp new file mode 100644 index 0000000..8c2980d --- /dev/null +++ b/src/tools/imgtool/modules/cybikoxt.cpp @@ -0,0 +1,529 @@ +// license:BSD-3-Clause +// copyright-holders:Tim Schuerewegen +/* + + Cybiko Xtreme File System + + (c) 2010 Tim Schuerewegen + +*/ + +#include "imgtool.h" + +#include + +struct cybiko_file_system +{ + imgtool::stream *stream; + uint32_t page_count, page_size, block_count_boot, block_count_file; + uint16_t write_count; +}; + +struct cybiko_iter +{ + uint16_t block; +}; + +struct cfs_file +{ + char name[64]; // name of the file + uint32_t date; // date/time of the file (seconds since 1900/01/01) + uint32_t size; // size of the file + uint32_t blocks; // number of blocks occupied by the file +}; + +enum +{ + BLOCK_TYPE_INVALID, + BLOCK_TYPE_BOOT, + BLOCK_TYPE_FILE +}; + +#define MAX_PAGE_SIZE (264 * 2) + +#define INVALID_FILE_ID 0xFFFF + +#define BLOCK_USED(x) (x[0] & 0x80) +#define BLOCK_FILE_ID(x) buffer_read_16_be( x + 2) +#define BLOCK_PART_ID(x) buffer_read_16_be( x + 4) +#define BLOCK_FILENAME(x) (char*)(x + 7) + +#define FILE_HEADER_SIZE 0x48 + +static cybiko_file_system *get_cfs(imgtool::image &image) +{ + return (cybiko_file_system*)image.extra_bytes(); +} + +extern imgtool::datetime cybiko_time_crack(uint32_t cfs_time); +extern uint32_t cybiko_time_setup(const imgtool::datetime &t); + +static uint32_t buffer_read_32_be( uint8_t *buffer) +{ + return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | (buffer[3] << 0); +} + +static uint16_t buffer_read_16_be( uint8_t *buffer) +{ + return (buffer[0] << 8) | (buffer[1] << 0); +} + +static void buffer_write_32_be( uint8_t *buffer, uint32_t data) +{ + buffer[0] = (data >> 24) & 0xFF; + buffer[1] = (data >> 16) & 0xFF; + buffer[2] = (data >> 8) & 0xFF; + buffer[3] = (data >> 0) & 0xFF; +} + +static void buffer_write_16_be( uint8_t *buffer, uint16_t data) +{ + buffer[0] = (data >> 8) & 0xFF; + buffer[1] = (data >> 0) & 0xFF; +} + +// page = crc (2) + data (x) + +static uint16_t page_buffer_calc_checksum( uint8_t *data, uint32_t size) +{ + int i; + uint32_t val = 0; + for (i = 0; i < size; i++) + { + val = (val ^ data[i] ^ i) << 1; + val = val | ((val >> 16) & 0x0001); + } + return val; +} + +static int page_buffer_verify( uint8_t *buffer, uint32_t size, int block_type) +{ + // checksum + if (block_type == BLOCK_TYPE_FILE) + { + uint32_t checksum_page, checksum_calc; + checksum_calc = page_buffer_calc_checksum( buffer + 2, size - 2); + checksum_page = buffer_read_16_be( buffer + 0); + if (checksum_calc != checksum_page) return false; + } + // ok + return true; +} + +static int cfs_block_to_page( cybiko_file_system *cfs, int block_type, uint32_t block, uint32_t *page) +{ + switch (block_type) + { + case BLOCK_TYPE_BOOT : if (page) *page = block; return true; + case BLOCK_TYPE_FILE : if (page) *page = block + cfs->block_count_boot; return true; + default : return false; + } +} + +static int cfs_page_to_block( cybiko_file_system *cfs, uint32_t page, int *block_type, uint32_t *block) +{ + uint32_t tmp = page; + // boot block + if (tmp < cfs->block_count_boot) + { + if (block_type) *block_type = BLOCK_TYPE_BOOT; + if (block) *block = tmp; + return true; + } + tmp -= cfs->block_count_boot; + // file block + if (tmp < cfs->block_count_file) + { + if (block_type) *block_type = BLOCK_TYPE_FILE; + if (block) *block = tmp; + return true; + } + tmp -= cfs->block_count_file; + // error + return false; +} + +static int cfs_page_read( cybiko_file_system *cfs, uint8_t *buffer, uint32_t page) +{ + if (page >= cfs->page_count) return false; + cfs->stream->seek(page * cfs->page_size, SEEK_SET); + cfs->stream->read(buffer, cfs->page_size); + return true; +} + +static int cfs_page_write( cybiko_file_system *cfs, uint8_t *buffer, uint32_t page) +{ + if (page >= cfs->page_count) return false; + cfs->stream->seek(page * cfs->page_size, SEEK_SET); + cfs->stream->write(buffer, cfs->page_size); + return true; +} + +static int cfs_block_read( cybiko_file_system *cfs, uint8_t *buffer, int block_type, uint32_t block) +{ + uint8_t buffer_page[MAX_PAGE_SIZE]; + uint32_t page; + if (!cfs_block_to_page( cfs, block_type, block, &page)) return false; + if (!cfs_page_read( cfs, buffer_page, page)) return false; + memcpy( buffer, buffer_page + 2, cfs->page_size - 2); + return true; +} + +static int cfs_block_write( cybiko_file_system *cfs, uint8_t *buffer, int block_type, uint32_t block) +{ + uint8_t buffer_page[MAX_PAGE_SIZE]; + uint32_t page; + uint16_t checksum; + memcpy( buffer_page + 2, buffer, cfs->page_size - 2); + if (block_type == BLOCK_TYPE_BOOT) + { + checksum = 0xFFFF; + } + else + { + checksum = page_buffer_calc_checksum( buffer_page + 2, cfs->page_size - 2); + } + buffer_write_16_be( buffer_page + 0, checksum); + if (!cfs_block_to_page( cfs, block_type, block, &page)) return false; + if (!cfs_page_write( cfs, buffer_page, page)) return false; + return true; +} + +static int cfs_file_delete( cybiko_file_system *cfs, uint16_t file_id) +{ + uint8_t buffer[MAX_PAGE_SIZE]; + int i; + for (i=0;iblock_count_file;i++) + { + if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return false; + if (BLOCK_USED(buffer) && (BLOCK_FILE_ID(buffer) == file_id)) + { + buffer[0] &= ~0x80; + if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_FILE, i)) return false; + } + } + return true; +} + +static int cfs_file_info( cybiko_file_system *cfs, uint16_t file_id, cfs_file *file) +{ + uint8_t buffer[MAX_PAGE_SIZE]; + int i; + file->blocks = file->size = 0; + for (i=0;iblock_count_file;i++) + { + if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return false; + if (BLOCK_USED(buffer) && (BLOCK_FILE_ID(buffer) == file_id)) + { + if (BLOCK_PART_ID(buffer) == 0) + { + strcpy( file->name, BLOCK_FILENAME(buffer)); + file->date = buffer_read_32_be( buffer + 6 + FILE_HEADER_SIZE - 4); + } + file->size += buffer[1]; + file->blocks++; + } + } + return (file->blocks > 0) ? true : false; +} + +static int cfs_file_find( cybiko_file_system *cfs, const char *filename, uint16_t *file_id) +{ + uint8_t buffer[MAX_PAGE_SIZE]; + int i; + for (i=0;iblock_count_file;i++) + { + if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return false; + if (BLOCK_USED(buffer) && (BLOCK_PART_ID(buffer) == 0) && (strcmp( filename, BLOCK_FILENAME(buffer)) == 0)) + { + *file_id = i; + return true; + } + } + return false; +} + +static bool cfs_verify(cybiko_file_system &cfs) +{ + uint8_t buffer[MAX_PAGE_SIZE]; + int i, block_type; + for (i = 0; i < cfs.page_count; i++) + { + if (!cfs_page_read(&cfs, buffer, i)) return false; + if (!cfs_page_to_block(&cfs, i, &block_type, NULL)) return false; + if (!page_buffer_verify(buffer, cfs.page_size, block_type)) return false; + } + return true; +} + +static bool cfs_init(cybiko_file_system &cfs, imgtool::stream::ptr &&stream) +{ + cfs.stream = stream.release(); + cfs.page_count = 2005; + cfs.page_size = 258; + cfs.block_count_boot = 5; + cfs.block_count_file = cfs.page_count - cfs.block_count_boot; + cfs.write_count = 0; + return true; +} + +static int cfs_format(cybiko_file_system *cfs) +{ + uint8_t buffer[MAX_PAGE_SIZE]; + int i; + // boot blocks + memset( buffer, 0xFF, sizeof( buffer)); + for (i=0;iblock_count_boot;i++) + { + if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_BOOT, i)) return false; + } + // file blocks + memset( buffer, 0xFF, sizeof( buffer)); + buffer[0] &= ~0x80; + for (i=0;iblock_count_file;i++) + { + if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_FILE, i)) return false; + } + // padding + buffer[0] = 0xFF; + for (i=0;i<0x1B56;i++) + { + cfs->stream->write(buffer, 1); + } + // ok + return true; +} + +static uint16_t cfs_calc_free_blocks( cybiko_file_system *cfs) +{ + uint8_t buffer[MAX_PAGE_SIZE]; + int i; + uint16_t blocks = 0; + for (i=0;iblock_count_file;i++) + { + if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return 0; + if (!BLOCK_USED(buffer)) blocks++; + } + return blocks; +} + +static uint32_t cfs_calc_free_space( cybiko_file_system *cfs, uint16_t blocks) +{ + uint32_t free_space; + free_space = blocks * ((cfs->page_size - 2) - 6); + if (free_space > 0) free_space -= FILE_HEADER_SIZE; + return free_space; +} + +static imgtoolerr_t cybiko_image_open(imgtool::image &image, imgtool::stream::ptr &&stream) +{ + cybiko_file_system *cfs = get_cfs(image); + // init + if (!cfs_init(*cfs, std::move(stream))) return IMGTOOLERR_CORRUPTIMAGE; + // verify + if (!cfs_verify(*cfs)) return IMGTOOLERR_CORRUPTIMAGE; + // ok + return IMGTOOLERR_SUCCESS; +} + +static void cybiko_image_close(imgtool::image &image) +{ + cybiko_file_system *cfs = get_cfs(image); + delete cfs->stream; +} + +static imgtoolerr_t cybiko_image_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *opts) +{ + cybiko_file_system *cfs = get_cfs(image); + // init + if (!cfs_init(*cfs, std::move(stream))) return IMGTOOLERR_CORRUPTIMAGE; + // format + if (!cfs_format(cfs)) return IMGTOOLERR_CORRUPTIMAGE; + // ok + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t cybiko_image_begin_enum(imgtool::directory &enumeration, const char *path) +{ + cybiko_iter *iter = (cybiko_iter*)enumeration.extra_bytes(); + iter->block = 0; + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t cybiko_image_next_enum(imgtool::directory &enumeration, imgtool_dirent &ent) +{ + imgtool::image &image(enumeration.image()); + cybiko_file_system *cfs = get_cfs(image); + cybiko_iter *iter = (cybiko_iter*)enumeration.extra_bytes(); + uint8_t buffer[MAX_PAGE_SIZE]; + uint16_t file_id = INVALID_FILE_ID; + cfs_file file; + // find next file + while (iter->block < cfs->block_count_file) + { + if (!cfs_block_read(cfs, buffer, BLOCK_TYPE_FILE, iter->block++)) return IMGTOOLERR_READERROR; + if (BLOCK_USED(buffer) && (BLOCK_PART_ID(buffer) == 0)) + { + file_id = BLOCK_FILE_ID(buffer); + break; + } + } + // get file information + if ((file_id != INVALID_FILE_ID) && cfs_file_info(cfs, file_id, &file)) + { + strcpy(ent.filename, file.name); + ent.filesize = file.size; + ent.lastmodified_time = cybiko_time_crack(file.date); + ent.filesize = file.size; + } + else + { + ent.eof = 1; + } + // ok + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t cybiko_image_free_space(imgtool::partition &partition, uint64_t *size) +{ + imgtool::image &image(partition.image()); + cybiko_file_system *cfs = get_cfs(image); + if (size) *size = cfs_calc_free_space( cfs, cfs_calc_free_blocks( cfs)); + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t cybiko_image_read_file(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf) +{ + imgtool::image &image(partition.image()); + cybiko_file_system *cfs = get_cfs(image); + uint8_t buffer[MAX_PAGE_SIZE]; + uint16_t file_id, part_id = 0, old_part_id; + int i; + // check filename + if (strlen( filename) > 58) return IMGTOOLERR_BADFILENAME; + // find file + if (!cfs_file_find( cfs, filename, &file_id)) return IMGTOOLERR_FILENOTFOUND; + // read file + do + { + old_part_id = part_id; + for (i=0;iblock_count_file;i++) + { + if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return IMGTOOLERR_READERROR; + if (BLOCK_USED(buffer) && (BLOCK_FILE_ID(buffer) == file_id) && (BLOCK_PART_ID(buffer) == part_id)) + { + destf.write(buffer + 6 + ((part_id == 0) ? FILE_HEADER_SIZE : 0), buffer[1]); + part_id++; + } + } + } while (old_part_id != part_id); + // ok + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t cybiko_image_write_file(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts) +{ + imgtool::image &image(partition.image()); + cybiko_file_system *cfs = get_cfs(image); + uint8_t buffer[MAX_PAGE_SIZE]; + uint16_t file_id, part_id = 0, free_blocks; + uint64_t bytes_left; + cfs_file file; + int i; + // check filename + if (strlen( filename) > 58) return IMGTOOLERR_BADFILENAME; + // find file + if (!cfs_file_find( cfs, filename, &file_id)) file_id = INVALID_FILE_ID; + // check free space + free_blocks = cfs_calc_free_blocks(cfs); + if (file_id != INVALID_FILE_ID) + { + if (!cfs_file_info(cfs, file_id, &file)) return IMGTOOLERR_UNEXPECTED; + free_blocks += file.blocks; + } + if (cfs_calc_free_space(cfs, free_blocks) < sourcef.size()) return IMGTOOLERR_NOSPACE; + // delete file + if (file_id != INVALID_FILE_ID) + { + if (!cfs_file_delete(cfs, file_id)) return IMGTOOLERR_UNEXPECTED; + } + // create/write destination file + bytes_left = sourcef.size(); + i = 0; + while (i < cfs->block_count_file) + { + if (!cfs_block_read( cfs, buffer, BLOCK_TYPE_FILE, i)) return IMGTOOLERR_READERROR; + if (!BLOCK_USED(buffer)) + { + if (part_id == 0) file_id = i; + memset( buffer, 0xFF, cfs->page_size - 0x02); + buffer[0] = 0x80; + buffer[1] = (cfs->page_size - 2) - 6 - ((part_id == 0) ? FILE_HEADER_SIZE : 0); + if (bytes_left < buffer[1]) buffer[1] = bytes_left; + buffer_write_16_be( buffer + 2, file_id); + buffer_write_16_be( buffer + 4, part_id); + if (part_id == 0) + { + buffer[6] = 0x20; + strcpy(BLOCK_FILENAME(buffer), filename); + buffer_write_32_be( buffer + 6 + FILE_HEADER_SIZE - 4, cybiko_time_setup(imgtool::datetime::now(imgtool::datetime::datetime_type::LOCAL))); + sourcef.read(buffer + 6 + FILE_HEADER_SIZE, buffer[1]); + } + else + { + sourcef.read(buffer + 6, buffer[1]); + } + if (!cfs_block_write( cfs, buffer, BLOCK_TYPE_FILE, i)) return IMGTOOLERR_WRITEERROR; + bytes_left -= buffer[1]; + if (bytes_left == 0) break; + part_id++; + } + i++; + } + // ok + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t cybiko_image_delete_file(imgtool::partition &partition, const char *filename) +{ + imgtool::image &image(partition.image()); + cybiko_file_system *cfs = get_cfs(image); + uint16_t file_id; + // check filename + if (strlen(filename) > 58) return IMGTOOLERR_BADFILENAME; + // find file + if (!cfs_file_find(cfs, filename, &file_id)) return IMGTOOLERR_FILENOTFOUND; + // delete file + if (!cfs_file_delete(cfs, file_id)) return IMGTOOLERR_UNEXPECTED; + // ok + return IMGTOOLERR_SUCCESS; +} + +void cybikoxt_get_info( const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES : info->i = sizeof( cybiko_file_system); break; + case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES : info->i = sizeof( cybiko_iter); break; +// case IMGTOOLINFO_INT_SUPPORTS_CREATION_TIME : info->i = 1; break; + case IMGTOOLINFO_INT_SUPPORTS_LASTMODIFIED_TIME : info->i = 1; break; +// case IMGTOOLINFO_INT_BLOCK_SIZE : info->i = 264; break; + // --- the following bits of info are returned as pointers to data or functions --- + case IMGTOOLINFO_PTR_OPEN : info->open = cybiko_image_open; break; + case IMGTOOLINFO_PTR_CREATE : info->create = cybiko_image_create; break; + case IMGTOOLINFO_PTR_CLOSE : info->close = cybiko_image_close; break; + case IMGTOOLINFO_PTR_BEGIN_ENUM : info->begin_enum = cybiko_image_begin_enum; break; + case IMGTOOLINFO_PTR_NEXT_ENUM : info->next_enum = cybiko_image_next_enum; break; + case IMGTOOLINFO_PTR_FREE_SPACE : info->free_space = cybiko_image_free_space; break; + case IMGTOOLINFO_PTR_READ_FILE : info->read_file = cybiko_image_read_file; break; + case IMGTOOLINFO_PTR_WRITE_FILE : info->write_file = cybiko_image_write_file; break; + case IMGTOOLINFO_PTR_DELETE_FILE : info->delete_file = cybiko_image_delete_file; break; + // --- the following bits of info are returned as NULL-terminated strings --- + case IMGTOOLINFO_STR_NAME : strcpy( info->s = imgtool_temp_str(), "cybikoxt"); break; + case IMGTOOLINFO_STR_DESCRIPTION : strcpy( info->s = imgtool_temp_str(), "Cybiko Xtreme File System"); break; + case IMGTOOLINFO_STR_FILE : strcpy( info->s = imgtool_temp_str(), __FILE__); break; + case IMGTOOLINFO_STR_FILE_EXTENSIONS : strcpy( info->s = imgtool_temp_str(), "bin,nv"); break; + case IMGTOOLINFO_STR_EOLN : strcpy( info->s = imgtool_temp_str(), "\r\n"); break; + } +} diff --git a/src/tools/imgtool/modules/dgndos.cpp b/src/tools/imgtool/modules/dgndos.cpp new file mode 100644 index 0000000..13fbbfc --- /dev/null +++ b/src/tools/imgtool/modules/dgndos.cpp @@ -0,0 +1,1256 @@ +// license:BSD-3-Clause +// copyright-holders:tim lindner +/**************************************************************************** + + dgndos.cpp + + Dragon DOS disk images + + I am not happy with the sector allocation algorithm + +****************************************************************************/ + +#include "imgtool.h" +#include "filter.h" +#include "iflopimg.h" + +#include "formats/coco_dsk.h" +#include "corestr.h" +#include "opresolv.h" + +#include +#include +#include + +#ifdef _MSC_VER +#pragma pack(push,1) +#define A_PACKED +#else +#define A_PACKED __attribute__((packed)) +#endif + +typedef struct A_PACKED dngdos_sector_allocation_format { + uint16_t lsn; + uint8_t count; +} dngdos_sector_allocation_format; + +typedef struct A_PACKED dngdos_file_header_block { + char filename[11]; + dngdos_sector_allocation_format block[4]; +} dngdos_file_header_block; + +typedef struct A_PACKED dngdos_file_continuation_block { + dngdos_sector_allocation_format block[7]; + uint16_t unused; +} dngdos_file_continuation_block; + +struct A_PACKED dgndos_dirent +{ + unsigned char flag_byte; + union block { + dngdos_file_header_block header; + dngdos_file_continuation_block continuation; + } block; + uint8_t dngdos_last_or_next; +}; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif + +struct dgndos_direnum +{ + int index; + bool eof; +}; + +/********************************************************************* + Imgtool module code +*********************************************************************/ + +#define DGNDOS_OPTIONS_PROTECT 'P' +#define MAX_DIRENTS 160 +#define HEADER_EXTENTS_COUNT 4 +#define CONT_EXTENTS_COUNT 7 + +#define DGNDOS_DELETED_BIT 0x80 // deleted entry +#define DGNDOS_CONTINUED_BIT 0x20 // byte at offset 0x18 give next entry number +#define DGNDOS_END_BIT 0x08 // end of directory +#define DGNDOS_PROTECT_BIT 0x02 // ignored +#define DGNDOS_CONTINUATION_BIT 0x01 // this is a continuation block + +static imgtoolerr_t get_dgndos_dirent(uint8_t *track, int index_loc, dgndos_dirent &ent) +{ + if (index_loc >= MAX_DIRENTS) + return IMGTOOLERR_FILENOTFOUND; + + int sector = 3 + (index_loc / 10); + int offset = (index_loc * 25) % 250; + + memcpy( (void *)&ent, track + (256 * (sector-1)) + offset, sizeof(ent)); + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t put_dgndos_dirent(uint8_t *track, int index_loc, const dgndos_dirent &ent) +{ + if (index_loc >= MAX_DIRENTS) + return IMGTOOLERR_FILENOTFOUND; + + int sector = 3 + (index_loc / 10); + int offset = (index_loc * 25) % 250; + + memcpy( track + (256 * (sector-1)) + offset, (void *)&ent, sizeof(ent)); + + return IMGTOOLERR_SUCCESS; +} + +static std::string get_dirent_fname(const dgndos_dirent &ent) +{ + return extract_padded_filename(ent.block.header.filename, 8, 3, '\0'); +} + +static bool dgndos_real_file( dgndos_dirent &ent ) +{ + if( ent.flag_byte & DGNDOS_DELETED_BIT) return false; + if( ent.flag_byte & DGNDOS_CONTINUATION_BIT) return false; + + return true; +} + +static imgtoolerr_t lookup_dgndos_file(uint8_t *entire_track, const char *fname, dgndos_dirent &ent, int *position = nullptr) +{ + int i = 0; + imgtoolerr_t err; + std::string fnamebuf; + + do + { + do + { + err = get_dgndos_dirent( entire_track, i++, ent ); + if( err ) return err; + + if( ent.flag_byte & DGNDOS_END_BIT ) return IMGTOOLERR_FILENOTFOUND; + } + while( ! dgndos_real_file(ent) ); + + fnamebuf = get_dirent_fname(ent); + } + while(core_stricmp(fnamebuf.c_str(), fname)); + + if (position) + *position = i - 1; + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t dgndos_get_geometry(uint8_t *entire_track, int *bitmap_count, int *heads, int *tracks_on_disk, int *sectors_per_track) +{ + unsigned int tod, spt, sides; + + tod = entire_track[0xfc]; + spt = entire_track[0xfd]; + + if( (~tod & 0xff) != entire_track[0xfe]) + { +// fprintf( stderr, "tracks_on_disk check failed: %u == %u\n", (~tod & 0xff), entire_track[0xfe] ); + return IMGTOOLERR_CORRUPTIMAGE; + } + + if( (~spt & 0xff) != entire_track[0xff]) + { +// fprintf( stderr, "sectors_per_track check failed: %u == %u\n", (~spt & 0xff), entire_track[0xff] ); + return IMGTOOLERR_CORRUPTIMAGE; + } + + if(spt == 36) + { + sides = 1; + } + else if(spt == 18 ) + { + sides = 0; + } + else + { +// fprintf( stderr, "sides check failed\n" ); + return IMGTOOLERR_CORRUPTIMAGE; + } + + if( heads ) *heads = sides; + if( tracks_on_disk) *tracks_on_disk = tod; + if( sectors_per_track ) *sectors_per_track = spt; + if( bitmap_count ) *bitmap_count = (tod * sides * 18) + (tod * 18); + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t dgndos_convert_lsn(uint8_t *entire_track, int lsn, int *head, int *track, int *sector ) +{ + int tracks_on_disk; + int sectors_per_track; + int sides; + + imgtoolerr_t err = dgndos_get_geometry(entire_track, nullptr, &sides, &tracks_on_disk, §ors_per_track); + if(err) return err; + + if( sides == 0 ) + { + *head = 0; + *track = lsn / 18; + *sector = (lsn % 18) + 1; + } + else + { + *head = (lsn / 18) % 2; + *track = lsn / 36; + *sector = (lsn % 18) + 1; + } + + return IMGTOOLERR_SUCCESS; +} + +#define ALLOCATE_BIT 1 +#define QUERY_BIT 0 +#define DEALLOCATE_BIT -1 + +static int dgndos_set_reset_bitmap( uint8_t *entire_track, int lsn, int set ) +{ + int startbyte = lsn / 8; + int startbit = lsn % 8; + + if( lsn > 1439 ) startbyte += 76; + + if(set > QUERY_BIT) // ALLOCATE_BIT + { + // 0 = used, 1 = free + entire_track[startbyte] &= ~(1 << startbit); + } + else if( set < QUERY_BIT ) // DEALLOCATE_BIT + { + // 0 = used, 1 = free + entire_track[startbyte] |= (1 << startbit); + } + + // 0 = used, 1 = free + return (entire_track[startbyte] & (1 << startbit)) != 0; +} + +static int dgndos_is_sector_avaiable(uint8_t *entire_track, int lsn) +{ + return dgndos_set_reset_bitmap( entire_track, lsn, QUERY_BIT ); +} + +static int dgndos_fat_allocate_sector(uint8_t *entire_track, int lsn) +{ + return dgndos_set_reset_bitmap( entire_track, lsn, ALLOCATE_BIT ); +} + +static int dgndos_fat_deallocate_sector(uint8_t *entire_track, int lsn) +{ + return dgndos_set_reset_bitmap( entire_track, lsn, DEALLOCATE_BIT ); +} + +static int dgndos_fat_deallocate_span(uint8_t *entire_track, int lsn, int count) +{ + int value = 0; + + for( int i=lsn; i= 0 ) + { + if( dgndos_is_sector_avaiable(entire_track, i) ) + { + *lsn = i; + return IMGTOOLERR_SUCCESS; + } + + i -= neg_diff; + } + + i = start; + while( i < bitmap_count ) + { + if( dgndos_is_sector_avaiable(entire_track, i) ) + { + *lsn = i; + return IMGTOOLERR_SUCCESS; + } + + i += pos_diff; + } + + start--; + } + while( start >= 335 ); + + return IMGTOOLERR_NOSPACE; +} + +static imgtoolerr_t dgndos_count_dirents(uint8_t *entire_track, dgndos_dirent dgnent, int *result) +{ + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + *result = 1; + + while( (dgnent.flag_byte & DGNDOS_CONTINUED_BIT) && *result < MAX_DIRENTS ) + { + (*result)++; + err = get_dgndos_dirent(entire_track, dgnent.dngdos_last_or_next, dgnent); + } + + return err; +} + +static imgtoolerr_t dgndos_get_file_size(uint8_t *entire_track, dgndos_dirent dgnent, size_t &filesize) +{ + imgtoolerr_t err; + int directory_entry_count = 0; + int extent = 0; + int block_size; + int done; + int count; + int i; + + filesize = 0; + + do + { + if(directory_entry_count>MAX_DIRENTS) return IMGTOOLERR_CORRUPTDIR; + + count = dgnent.flag_byte & DGNDOS_CONTINUATION_BIT ? dgnent.block.continuation.block[extent].count : dgnent.block.header.block[extent].count; + + if( count == 0 ) break; + + i = 0; + + // count extents except last sector + while (i < count - 1) + { + filesize += 256; + i++; + } + + block_size = 256; + done = false; + + if( extent < (dgnent.flag_byte & DGNDOS_CONTINUATION_BIT ? CONT_EXTENTS_COUNT : HEADER_EXTENTS_COUNT) - 1 ) + { + if( (dgnent.flag_byte & DGNDOS_CONTINUATION_BIT ? dgnent.block.continuation.block[extent+1].count : dgnent.block.header.block[extent+1].count) == 0 ) + { + // not last extent, and yes continuation + if( dgnent.dngdos_last_or_next == 0 ) + block_size = 256; + else + block_size = dgnent.dngdos_last_or_next; + + done = true; + } + } + else if( extent == (dgnent.flag_byte & DGNDOS_CONTINUATION_BIT ? CONT_EXTENTS_COUNT : HEADER_EXTENTS_COUNT) - 1) + { + if( !(dgnent.flag_byte & DGNDOS_CONTINUED_BIT) ) + { + // is last extent, and no continuation + if( dgnent.dngdos_last_or_next == 0 ) + block_size = 256; + else + block_size = dgnent.dngdos_last_or_next; + + done = true; + } + } + + filesize += block_size; + + extent++; + + if( extent == (dgnent.flag_byte & DGNDOS_CONTINUATION_BIT ? CONT_EXTENTS_COUNT : HEADER_EXTENTS_COUNT) ) + { + if( dgnent.flag_byte & DGNDOS_CONTINUED_BIT) + { + err = get_dgndos_dirent(entire_track, dgnent.dngdos_last_or_next, *(&dgnent) ); + if(err) return err; + + extent = 0; + directory_entry_count++; + } + } + } + while( !done ); + + return IMGTOOLERR_SUCCESS; +} + +static floperr_t dgndos_get_directory_track( imgtool::image &image, int track, uint8_t *buffer ) +{ + floperr_t ferr; + + for(int i=1; i<=18; i++ ) + { + ferr = floppy_read_sector(imgtool_floppy(image), 0, track, i, 0, &(buffer[(i-1)*256]), 256); + if(ferr) break; + } + + return ferr; +} + +static floperr_t dgndos_put_directory_track( imgtool::image &image, int track, uint8_t *buffer ) +{ + floperr_t ferr; + + for(int i=1; i<=18; i++ ) + { + ferr = floppy_write_sector(imgtool_floppy(image), 0, track, i, 0, &(buffer[(i-1)*256]), 256, 0); + if(ferr) break; + } + + return ferr; +} + +static imgtoolerr_t dgndos_diskimage_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent) +{ + floperr_t ferr; + imgtoolerr_t err; + size_t filesize; + dgndos_direnum *dgnenum; + dgndos_dirent dgnent; + int dir_ent_count; + + imgtool::image &image(enumeration.image()); + + uint8_t entire_track20[18*256]; + + ferr = dgndos_get_directory_track( image, 20, entire_track20 ); + if (ferr) return imgtool_floppy_error(ferr); + + dgnenum = (dgndos_direnum *) enumeration.extra_bytes(); + + /* Did we hit the end of file before? */ + if (dgnenum->eof) return IMGTOOLERR_SUCCESS; + + do + { + if (dgnenum->index >= MAX_DIRENTS) + { + dgnenum->eof = 1; + ent.eof = 1; + return IMGTOOLERR_SUCCESS; + } + + err = get_dgndos_dirent(entire_track20, dgnenum->index++, dgnent); + if (err) return err; + + if( dgndos_real_file( dgnent ) ) break; + } + while( ! (dgnent.flag_byte & DGNDOS_END_BIT) ); + + // now are we at the eof point? + if (dgnent.flag_byte & DGNDOS_END_BIT) + { + dgnenum->eof = 1; + ent.eof = 1; + } + else + { + err = dgndos_get_file_size(entire_track20, dgnent, filesize); + if (err) return err; + + if (filesize == ((size_t) -1)) + { + /* corrupt! */ + ent.filesize = 0; + ent.corrupt = 1; + } + else + { + ent.filesize = filesize; + ent.corrupt = 0; + } + ent.eof = 0; + + std::string fname = get_dirent_fname(dgnent); + + err = dgndos_count_dirents(entire_track20, dgnent, &dir_ent_count); + if (err) return err; + + snprintf(ent.filename, std::size(ent.filename), "%s", fname.c_str()); + snprintf(ent.attr, std::size(ent.attr), "%c (%03d)", + (char) (dgnent.flag_byte & DGNDOS_PROTECT_BIT ? 'P' : '.'), + dir_ent_count); + } + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t dgndos_diskimage_freespace(imgtool::partition &partition, uint64_t *size) +{ + floperr_t ferr; + imgtool::image &image(partition.image()); + int bitmap_count(0); + + uint8_t entire_track20[18*256]; + + ferr = dgndos_get_directory_track( image, 20, entire_track20 ); + if (ferr) return imgtool_floppy_error(ferr); + + *size = 0; + + dgndos_get_geometry(entire_track20, &bitmap_count, nullptr, nullptr, nullptr); + + for( int i=0; iMAX_DIRENTS) return IMGTOOLERR_CORRUPTDIR; + + lsn = big_endianize_int16( ent.flag_byte & DGNDOS_CONTINUATION_BIT ? ent.block.continuation.block[extent].lsn : ent.block.header.block[extent].lsn ); + count = ent.flag_byte & DGNDOS_CONTINUATION_BIT ? ent.block.continuation.block[extent].count : ent.block.header.block[extent].count; + + if( count == 0 ) break; + + i = 0; + + // get extents except last sector + while (i < count - 1) + { + err = dgndos_convert_lsn(entire_track20, lsn + i, &head, &track, §or ); + if(err) return err; + + err = imgtool_floppy_read_sector_to_stream(image, head, track, sector, 0, 256, destf); + if (err) return err; + + i++; + } + + block_size = 256; + done = false; + + if( extent < (ent.flag_byte & DGNDOS_CONTINUATION_BIT ? CONT_EXTENTS_COUNT : HEADER_EXTENTS_COUNT) - 1 ) + { + if( (ent.flag_byte & DGNDOS_CONTINUATION_BIT ? ent.block.continuation.block[extent+1].count : ent.block.header.block[extent+1].count) == 0 ) + { + // not last extent, and yes continuation + if( ent.dngdos_last_or_next == 0 ) + block_size = 256; + else + block_size = ent.dngdos_last_or_next; + + done = true; + } + } + else if( extent == (ent.flag_byte & DGNDOS_CONTINUATION_BIT ? CONT_EXTENTS_COUNT : HEADER_EXTENTS_COUNT) - 1) + { + if( !(ent.flag_byte & DGNDOS_CONTINUED_BIT) ) + { + // is last extent, and no continuation + if( ent.dngdos_last_or_next == 0 ) + block_size = 256; + else + block_size = ent.dngdos_last_or_next; + + done = true; + } + } + + err = dgndos_convert_lsn(entire_track20, lsn + i, &head, &track, §or ); + if(err) return err; + + err = imgtool_floppy_read_sector_to_stream(image, head, track, sector, 0, block_size, destf); + if (err) return err; + + extent++; + + if( extent == (ent.flag_byte & DGNDOS_CONTINUATION_BIT ? CONT_EXTENTS_COUNT : HEADER_EXTENTS_COUNT) ) + { + if( ent.flag_byte & DGNDOS_CONTINUED_BIT) + { + position = ent.dngdos_last_or_next; + err = get_dgndos_dirent(entire_track20, ent.dngdos_last_or_next, ent ); + if(err) return err; + + extent = 0; + directory_entry_count++; + } + } + } + while( !done ); + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t dgndos_diskimage_deletefile(imgtool::partition &partition, const char *fname) +{ + imgtoolerr_t err; + floperr_t ferr; + imgtool::image &image(partition.image()); + int pos; + struct dgndos_dirent ent; + int directory_entry_count = 0; + + uint8_t entire_track20[18*256]; + uint8_t entire_track16[18*256]; + bool write_20_to_16; + + ferr = dgndos_get_directory_track( image, 20, entire_track20 ); + if (ferr) return imgtool_floppy_error(ferr); + + ferr = dgndos_get_directory_track( image, 16, entire_track16 ); + if (ferr) return imgtool_floppy_error(ferr); + + write_20_to_16 = memcmp(entire_track20, entire_track16, 256*18 ); + + err = lookup_dgndos_file(entire_track20, fname, ent, &pos); + if (err) return err; + + ent.flag_byte |= DGNDOS_DELETED_BIT; + ent.flag_byte &= ~DGNDOS_PROTECT_BIT; + + for( int i=0; i MAX_DIRENTS ) return IMGTOOLERR_CORRUPTDIR; + + int dirent_index = continue_dirent_index; + err = get_dgndos_dirent(entire_track20, dirent_index, ent); + if (err) return err; + + for( int i=0; i 8) || (fname_ext_len > 3)) + return IMGTOOLERR_BADFILENAME; + + memcpy(&ent.block.header.filename[0], fname, fname_end - fname); + memcpy(&ent.block.header.filename[8], fname_ext, fname_ext_len); + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t dgndos_diskimage_writefile(imgtool::partition &partition, const char *fname, const char *fork, imgtool::stream &sourcef, util::option_resolution *writeoptions) +{ + floperr_t ferr; + imgtoolerr_t err; + imgtool::image &image(partition.image()); + dgndos_dirent ent; + int position; + uint64_t written = 0; + int fat_block, first_lsn, sectors_avaiable, current_sector_index; + int last_sector_size = 0; + int bitmap_count; + + if( sourcef.size() == 0 ) return IMGTOOLERR_BUFFERTOOSMALL; + + uint8_t entire_track20[18*256]; + uint8_t entire_track16[18*256]; + bool write_20_to_16; + + ferr = dgndos_get_directory_track( image, 20, entire_track20 ); + if (ferr) return imgtool_floppy_error(ferr); + + ferr = dgndos_get_directory_track( image, 16, entire_track16 ); + if (ferr) return imgtool_floppy_error(ferr); + + write_20_to_16 = memcmp(entire_track20, entire_track16, 18*256 ); + + err = dgndos_get_geometry(entire_track20, &bitmap_count, nullptr, nullptr, nullptr); + if(err) return err; + + // find directory entry with same file name + err = lookup_dgndos_file(entire_track20, fname, ent, &position); + + if( err == IMGTOOLERR_FILENOTFOUND ) + { + int new_lsn; + + // get new directory entry + err = dgndos_get_avaiable_dirent(entire_track20, ent, &position); + if (err) return err; + + err = dgndos_prepare_dirent(entire_track20, ent, fname); + if(err) return err; + + err = dgndos_get_avaiable_sector( entire_track20, &new_lsn ); + if (err) + { + return err; + } + + dgndos_fat_allocate_sector(entire_track20, new_lsn); + + ent.block.header.block[0].lsn = big_endianize_int16(new_lsn); + ent.block.header.block[0].count = 1; + + err = put_dgndos_dirent(entire_track20, position, ent); + if(err) return err; + } + else if(err != IMGTOOLERR_SUCCESS ) + { + return err; + } + + if( writeoptions->lookup_int(DGNDOS_OPTIONS_PROTECT)) + { + ent.flag_byte |= DGNDOS_PROTECT_BIT; + } + else + { + ent.flag_byte &= ~DGNDOS_PROTECT_BIT; + } + + fat_block = 0; + + /* get next available fat entry */ + first_lsn = big_endianize_int16(ent.block.header.block[fat_block].lsn); + sectors_avaiable = ent.block.header.block[fat_block].count; + current_sector_index = 0; + + do + { + while ((current_sector_index < sectors_avaiable) && (written < sourcef.size())) + { + int head, track, sector, write_count; + + err = dgndos_convert_lsn(entire_track20, first_lsn + current_sector_index, &head, &track, §or ); + if (err) return err; + + if( sourcef.size() - written > 256 ) + { + write_count = 256; + } + else if( sourcef.size() - written == 256 ) + { + write_count = 256; + last_sector_size = 0; + } + else + { + write_count = sourcef.size() - written; + last_sector_size = write_count; + } + + err = imgtool_floppy_write_sector_from_stream(image, head, track, sector, 0, write_count, sourcef); + if (err) return err; + + current_sector_index++; + written += write_count; + } + + if( written == sourcef.size()) // are we are done writing file + { + int de_count = 0; + int de_dont_delete = position; + int lsn, count; + int save_next_de; + + save_next_de = ent.dngdos_last_or_next; + ent.dngdos_last_or_next = last_sector_size; + + // yes, truncate this allocation block if necessary + if( ent.flag_byte & DGNDOS_CONTINUATION_BIT) + { + for( int i = current_sector_index; i < ent.block.continuation.block[fat_block].count; i++) + { + dgndos_fat_deallocate_sector(entire_track20, first_lsn + i); + } + + ent.block.continuation.block[fat_block].count = current_sector_index; + } + else + { + for( int i = current_sector_index; i < ent.block.header.block[fat_block].count; i++) + { + dgndos_fat_deallocate_sector(entire_track20, first_lsn + i); + } + + ent.block.header.block[fat_block].count = current_sector_index; + } + + do // check remaining allocation blocks and zero them + { + fat_block++; + + if( (fat_block < (ent.flag_byte & DGNDOS_CONTINUATION_BIT ? CONT_EXTENTS_COUNT : HEADER_EXTENTS_COUNT)) ) + { + lsn = big_endianize_int16( ent.flag_byte & DGNDOS_CONTINUATION_BIT ? ent.block.continuation.block[fat_block].lsn : ent.block.header.block[fat_block].lsn ); + count = ent.flag_byte & DGNDOS_CONTINUATION_BIT ? ent.block.continuation.block[fat_block].count : ent.block.header.block[fat_block].count; + + if( count > 0 ) + { + dgndos_fat_deallocate_span(entire_track20, lsn, count); + + (ent.flag_byte & DGNDOS_CONTINUATION_BIT ? ent.block.continuation.block[fat_block].lsn : ent.block.header.block[fat_block].lsn) = 0; + (ent.flag_byte & DGNDOS_CONTINUATION_BIT ? ent.block.continuation.block[fat_block].count : ent.block.header.block[fat_block].count) = 0; + } + } + else + { + uint8_t save_flag = ent.flag_byte; + + ent.flag_byte &= ~DGNDOS_CONTINUED_BIT; + + if( ent.flag_byte & DGNDOS_CONTINUATION_BIT ) + { + if( de_dont_delete != position ) + { + ent.flag_byte |= DGNDOS_DELETED_BIT; + } + } + + err = put_dgndos_dirent(entire_track20, position, ent); + if (err) return err; + + if( save_flag & DGNDOS_CONTINUED_BIT ) + { + position = save_next_de; + + err = get_dgndos_dirent(entire_track20, position, ent); + if (err) return err; + + save_next_de = ent.dngdos_last_or_next; + fat_block = -1; + } + else + { + break; + } + } + + de_count++; + } + while( de_count < MAX_DIRENTS ); + + if( de_count == MAX_DIRENTS ) return IMGTOOLERR_CORRUPTDIR; + } + else // more to write + { + // check if I can extend the allocation count + if( (current_sector_index < 254) && (first_lsn + current_sector_index < bitmap_count) && (dgndos_is_sector_avaiable( entire_track20, first_lsn + current_sector_index )) ) + { + sectors_avaiable++; + + (ent.flag_byte & DGNDOS_CONTINUATION_BIT ? ent.block.continuation.block[fat_block].count : ent.block.header.block[fat_block].count) = sectors_avaiable; + + dgndos_fat_allocate_sector( entire_track20, first_lsn + current_sector_index ); + } + else // this allocation list is full - check next allocation list + { + fat_block++; + + if( fat_block == (ent.flag_byte & DGNDOS_CONTINUATION_BIT ? CONT_EXTENTS_COUNT : HEADER_EXTENTS_COUNT)) + { + if( ent.flag_byte & DGNDOS_CONTINUED_BIT) + { + // go to next directory entry. + err = put_dgndos_dirent(entire_track20, position, ent); + if (err) return err; + + position = ent.dngdos_last_or_next; + err = get_dgndos_dirent(entire_track20, position, ent); + if (err) + { + return err; + } + + fat_block = 0; + } + else + { + // need a or another continuation directory entry + int save_position = position, new_lsn; + + err = put_dgndos_dirent(entire_track20, position, ent); + if (err) return err; + + err = dgndos_get_avaiable_dirent_position( entire_track20, &position ); + if (err) + { + return err; + } + + ent.flag_byte |= DGNDOS_CONTINUED_BIT; + ent.dngdos_last_or_next = position; + err = put_dgndos_dirent(entire_track20, save_position, ent); + if (err) return err; + + err = get_dgndos_dirent(entire_track20, position, ent); + if (err) + { + return err; + } + + memset( (void *)&ent, 0, sizeof(dgndos_dirent) ); + ent.flag_byte |= DGNDOS_CONTINUATION_BIT; + fat_block = 0; + + err = dgndos_get_avaiable_sector( entire_track20, &new_lsn ); + if (err) + { + return err; + } + } + } + + sectors_avaiable = ent.flag_byte & DGNDOS_CONTINUATION_BIT ? ent.block.continuation.block[fat_block].count : ent.block.header.block[fat_block].count; + + // check if this block is empty + if( sectors_avaiable == 0) + { + int new_lsn; + + err = dgndos_get_avaiable_sector( entire_track20, &new_lsn ); + if (err) return err; + + (ent.flag_byte & DGNDOS_CONTINUATION_BIT ? ent.block.continuation.block[fat_block].lsn : ent.block.header.block[fat_block].lsn) = big_endianize_int16(new_lsn); + (ent.flag_byte & DGNDOS_CONTINUATION_BIT ? ent.block.continuation.block[fat_block].count : ent.block.header.block[fat_block].count) = 1; + + dgndos_fat_allocate_sector(entire_track20, new_lsn); + } + + first_lsn = ent.flag_byte & DGNDOS_CONTINUATION_BIT ? big_endianize_int16(ent.block.continuation.block[fat_block].lsn) : big_endianize_int16(ent.block.header.block[fat_block].lsn); + sectors_avaiable = ent.flag_byte & DGNDOS_CONTINUATION_BIT ? ent.block.continuation.block[fat_block].count : ent.block.header.block[fat_block].count; + current_sector_index = 0; + } + } + } + while( written < sourcef.size() ); + + ferr = dgndos_put_directory_track( image, 20, entire_track20 ); + if (ferr) return imgtool_floppy_error(ferr); + + if( write_20_to_16 ) + { + ferr = dgndos_put_directory_track( image, 16, entire_track20 ); + if (ferr) return imgtool_floppy_error(ferr); + } + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t dgndos_diskimage_suggesttransfer(imgtool::partition &partition, const char *fname, imgtool_transfer_suggestion *suggestions, size_t suggestions_length) +{ + floperr_t ferr; + imgtoolerr_t err; + imgtool::image &image(partition.image()); + struct dgndos_dirent ent; + int pos; + + uint8_t entire_track20[18*256]; + + ferr = dgndos_get_directory_track( image, 20, entire_track20 ); + if (ferr) return imgtool_floppy_error(ferr); + + if (fname) + { + err = lookup_dgndos_file(entire_track20, fname, ent, &pos); + if (err) + return err; + + if (strcmp(ent.block.header.filename+8,"DAT") == 0) + { + /* ASCII file */ + suggestions[0].viability = SUGGESTION_RECOMMENDED; + suggestions[0].filter = filter_eoln_getinfo; + suggestions[1].viability = SUGGESTION_POSSIBLE; + suggestions[1].filter = NULL; + } + else if (strcmp(ent.block.header.filename+8,"BAS") == 0) + { + /* tokenized BASIC file */ + suggestions[0].viability = SUGGESTION_RECOMMENDED; + suggestions[0].filter = NULL; + suggestions[1].viability = SUGGESTION_POSSIBLE; + suggestions[1].filter = filter_dragonbas_getinfo; + } + } + else + { + suggestions[0].viability = SUGGESTION_RECOMMENDED; + suggestions[0].filter = NULL; + suggestions[1].viability = SUGGESTION_POSSIBLE; + suggestions[1].filter = filter_eoln_getinfo; + suggestions[2].viability = SUGGESTION_POSSIBLE; + suggestions[2].filter = filter_dragonbas_getinfo; + } + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t dgndos_diskimage_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *opts) +{ + imgtoolerr_t err; + uint32_t heads, tracks, sectors, first_sector_id, sector_bytes, total_sector_count; + uint8_t sector[512]; + struct dgndos_dirent *ents; + + heads = opts->lookup_int('H'); + tracks = opts->lookup_int('T'); + sectors = opts->lookup_int('S'); + first_sector_id = opts->lookup_int('F'); + sector_bytes = opts->lookup_int('L'); + + if(sector_bytes!=256) return IMGTOOLERR_UNIMPLEMENTED; + + // create FAT sectors + memset( sector, 0, 512 ); + + total_sector_count = (heads * tracks * sectors); + + for( int i=0; ii = 1; break; + case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(struct dgndos_direnum); break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "dgndos"); break; + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "Dragon DOS format"); break; + case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break; + case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), "\r"); break; + case IMGTOOLINFO_STR_WRITEFILE_OPTSPEC: strcpy(info->s = imgtool_temp_str(), "P[0]-1"); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case IMGTOOLINFO_PTR_MAKE_CLASS: info->make_class = imgtool_floppy_make_class; break; + case IMGTOOLINFO_PTR_FLOPPY_CREATE: info->create = dgndos_diskimage_create; break; + case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = dgndos_diskimage_nextenum; break; + case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = dgndos_diskimage_freespace; break; + case IMGTOOLINFO_PTR_READ_FILE: info->read_file = dgndos_diskimage_readfile; break; + case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = dgndos_diskimage_writefile; break; + case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = dgndos_diskimage_deletefile; break; + case IMGTOOLINFO_PTR_SUGGEST_TRANSFER: info->suggest_transfer = dgndos_diskimage_suggesttransfer; break; + case IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE: info->writefile_optguide = &dragon_dgndos_writefile_optionguide; break; + case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_coco; break; + } +} diff --git a/src/tools/imgtool/modules/fat.cpp b/src/tools/imgtool/modules/fat.cpp new file mode 100644 index 0000000..3b5b670 --- /dev/null +++ b/src/tools/imgtool/modules/fat.cpp @@ -0,0 +1,2111 @@ +// license:BSD-3-Clause +// copyright-holders:Raphael Nabet +/**************************************************************************** + + fat.cpp + + PC FAT disk images + +***************************************************************************** + + Master boot record format: + + Offset Length Description + ------ ------ ----------- + 0 446 Boot machine code + 446 16 Partion #1 info + 462 16 Partion #2 info + 478 16 Partion #3 info + 494 16 Partion #4 info + 510 2 Magic bytes (0x55 0xAA) + + + Partition info format: + + Offset Length Description + ------ ------ ----------- + 0 1 Active byte (0x80=active 0x00=inactive) + 1 1 Starting head + 2 1 Starting sector (bits 5-0) and high bits of starting track (bits 6-5) + 3 1 Low bits of starting track + 4 1 Partition type: + 0x00 Unused + 0x?1 FAT12 (0-15 MB) + 0x?2 XENIX + 0x?4 FAT16 (16-32 MB) + 0x?6 FAT16` (32 MB-2 GB) + 0x?7 HPFS or NTFS + 0x?A Boot Manager + 0x?B FAT32 (512 MB-2 TB) + 0x?C FAT32 (512 MB-2 TB LBA) + 0x1? OS/2 Boot manager/Win95 hidden + 0xC? DR-DOS secured partition + 0xD? Multiuser DOS secured partition + 0xE? SpeedStor extended partition + 5 1 Ending head + 6 1 Ending sector (bits 5-0) and high bits of ending track (bits 6-5) + 7 1 Low bits of ending track + 8 4 Sector index of beginning of partition + 12 4 Total sectors in partition + + + Boot sector format: + + Offset Length Description + ------ ------ ----------- + 0 3 Jump instruction (to skip over header on boot) + 3 8 OEM Name + 11 2 Bytes per sector + 13 1 Sectors per cluster + 14 2 Reserved sector count (including boot sector) + 16 1 Number of FATs (file allocation tables) + 17 2 Number of root directory entries + 19 2 Total sectors (bits 0-15) + 21 1 Media descriptor + 22 2 Sectors per FAT + 24 2 Sectors per track + 26 2 Number of heads + 28 4 Hidden sectors + 32 4 Total sectors (bits 16-47) + 36 1 Physical drive number + 37 1 Current head + 38 1 Signature + 39 4 ID + 43 11 Volume Label + 54 8 FAT file system type + 62 448 Boot machine code + 510 2 Magic bytes (0x55 0xAA) + + For more information: + http://support.microsoft.com/kb/q140418/ + + + Directory Entry Format: + + Offset Length Description + ------ ------ ----------- + 0 8 DOS File Name (padded with spaces) + 8 3 DOS File Extension (padded with spaces) + 11 1 File Attributes + 12 2 Unknown + 14 4 Time of Creation + 18 2 Last Access Time + 20 2 EA-Index (OS/2 stuff) + 22 4 Last Modified Time + 26 2 First Cluster + 28 4 File Size + + + Dates and times are stored in separate words; when together, the time is + first and the date is second. + + Time: + bits 15-11 Hour + bits 10- 5 Minute + bits 4- 0 Second / 2 + + Date: + bits 15- 9 Year - 1980 + bits 8- 5 Month + bits 4- 0 Day + + LFN Entry Format: + + Offset Length Description + ------ ------ ----------- + 0 1 Sequence Number (bit 6 is set on highest sequence) + 1 10 Name characters (five UTF-16LE chars) + 11 1 Attributes (always 0x0F) + 12 1 Reserved (always 0x00) + 13 1 Checksum of short filename entry + 14 12 Name characters (six UTF-16LE chars) + 26 2 Entry Cluster (always 0x00) + 28 4 Name characters (two UTF-16LE chars) + + Valid characters in DOS file names: + - Upper case letters A-Z + - Numbers 0-9 + - Space (though there is no way to identify a trailing space) + - ! # $ % & ( ) - @ ^ _ ` { } ~ + - Characters 128-255 (though the code page is indeterminate) + + For more information: + http://en.wikipedia.org/wiki/File_Allocation_Table + +****************************************************************************/ + +#include "fat.h" + +#include "formats/imageutl.h" + +#include "corestr.h" +#include "unicode.h" + +#include +#include +#include + +#define FAT_DIRENT_SIZE 32 +#define FAT_SECLEN 512 + +#define LOG(x) + +struct fat_partition_info +{ + uint32_t fat_bits; + uint32_t sectors_per_cluster; + uint32_t cluster_size; + uint32_t reserved_sectors; + uint32_t fat_count; + uint32_t root_entries; + uint32_t sectors_per_fat; + uint64_t total_sectors; + uint32_t total_clusters; +}; + +struct fat_file +{ + unsigned int root : 1; + unsigned int directory : 1; + unsigned int eof : 1; + uint32_t index; + uint32_t filesize; + uint32_t first_cluster; + uint32_t parent_first_cluster; + uint32_t cluster; + uint32_t cluster_index; + uint32_t dirent_sector_index; + uint32_t dirent_sector_offset; +}; + +struct fat_dirent +{ + char long_filename[512]; + char short_filename[13]; + unsigned int directory : 1; + unsigned int eof : 1; + uint32_t filesize; + uint32_t first_cluster; + uint32_t dirent_sector_index; + uint32_t dirent_sector_offset; + imgtool::datetime creation_time; + imgtool::datetime lastmodified_time; +}; + +struct fat_freeentry_info +{ + uint32_t required_size; + uint32_t candidate_position; + uint32_t position; +}; + +struct fat_mediatype +{ + uint8_t media_descriptor; + uint8_t heads; + uint8_t tracks; + uint8_t sectors; +}; + +enum creation_policy_t +{ + CREATE_NONE, + CREATE_FILE, + CREATE_DIR +}; + + + +static const fat_mediatype known_media[] = +{ + { 0xF0, 2, 80, 36 }, + { 0xF0, 2, 80, 18 }, + { 0xF9, 2, 80, 9 }, + { 0xF9, 2, 80, 15 }, + { 0xFD, 2, 40, 9 }, + { 0xFF, 2, 40, 8 }, + { 0xFC, 1, 40, 9 }, + { 0xFE, 1, 40, 8 }, + { 0xF8, 0, 0, 0 } +}; + +/* boot sector code taken from FreeDOS */ +static const uint8_t boot_sector_code[] = +{ + 0xfa, 0xfc, 0x31, 0xc0, 0x8e, 0xd8, 0xbd, 0x00, 0x7c, 0xb8, 0xe0, 0x1f, + 0x8e, 0xc0, 0x89, 0xee, 0x89, 0xef, 0xb9, 0x00, 0x01, 0xf3, 0xa5, 0xea, + 0x5e, 0x7c, 0xe0, 0x1f, 0x00, 0x00, 0x60, 0x00, 0x8e, 0xd8, 0x8e, 0xd0, + 0x8d, 0x66, 0xa0, 0xfb, 0x80, 0x7e, 0x24, 0xff, 0x75, 0x03, 0x88, 0x56, + 0x24, 0xc7, 0x46, 0xc0, 0x10, 0x00, 0xc7, 0x46, 0xc2, 0x01, 0x00, 0x8b, + 0x76, 0x1c, 0x8b, 0x7e, 0x1e, 0x03, 0x76, 0x0e, 0x83, 0xd7, 0x00, 0x89, + 0x76, 0xd2, 0x89, 0x7e, 0xd4, 0x8a, 0x46, 0x10, 0x98, 0xf7, 0x66, 0x16, + 0x01, 0xc6, 0x11, 0xd7, 0x89, 0x76, 0xd6, 0x89, 0x7e, 0xd8, 0x8b, 0x5e, + 0x0b, 0xb1, 0x05, 0xd3, 0xeb, 0x8b, 0x46, 0x11, 0x31, 0xd2, 0xf7, 0xf3, + 0x89, 0x46, 0xd0, 0x01, 0xc6, 0x83, 0xd7, 0x00, 0x89, 0x76, 0xda, 0x89, + 0x7e, 0xdc, 0x8b, 0x46, 0xd6, 0x8b, 0x56, 0xd8, 0x8b, 0x7e, 0xd0, 0xc4, + 0x5e, 0x5a, 0xe8, 0xac, 0x00, 0xc4, 0x7e, 0x5a, 0xb9, 0x0b, 0x00, 0xbe, + 0xf1, 0x7d, 0x57, 0xf3, 0xa6, 0x5f, 0x26, 0x8b, 0x45, 0x1a, 0x74, 0x0b, + 0x83, 0xc7, 0x20, 0x26, 0x80, 0x3d, 0x00, 0x75, 0xe7, 0x72, 0x68, 0x50, + 0xc4, 0x5e, 0x5a, 0x8b, 0x7e, 0x16, 0x8b, 0x46, 0xd2, 0x8b, 0x56, 0xd4, + 0xe8, 0x7e, 0x00, 0x58, 0x1e, 0x07, 0x8e, 0x5e, 0x5c, 0xbf, 0x00, 0x20, + 0xab, 0x89, 0xc6, 0x01, 0xf6, 0x01, 0xc6, 0xd1, 0xee, 0xad, 0x73, 0x04, + 0xb1, 0x04, 0xd3, 0xe8, 0x80, 0xe4, 0x0f, 0x3d, 0xf8, 0x0f, 0x72, 0xe8, + 0x31, 0xc0, 0xab, 0x0e, 0x1f, 0xc4, 0x5e, 0x5a, 0xbe, 0x00, 0x20, 0xad, + 0x09, 0xc0, 0x75, 0x05, 0x88, 0xd3, 0xff, 0x6e, 0x5a, 0x48, 0x48, 0x8b, + 0x7e, 0x0d, 0x81, 0xe7, 0xff, 0x00, 0xf7, 0xe7, 0x03, 0x46, 0xda, 0x13, + 0x56, 0xdc, 0xe8, 0x34, 0x00, 0xeb, 0xe0, 0x5e, 0xac, 0x56, 0xb4, 0x0e, + 0xcd, 0x10, 0x3c, 0x2e, 0x75, 0xf5, 0xc3, 0xe8, 0xf1, 0xff, 0x45, 0x72, + 0x72, 0x6f, 0x72, 0x21, 0x20, 0x48, 0x69, 0x74, 0x20, 0x61, 0x20, 0x6b, + 0x65, 0x79, 0x20, 0x74, 0x6f, 0x20, 0x72, 0x65, 0x62, 0x6f, 0x6f, 0x74, + 0x2e, 0x30, 0xe4, 0xcd, 0x13, 0xcd, 0x16, 0xcd, 0x19, 0x56, 0x89, 0x46, + 0xc8, 0x89, 0x56, 0xca, 0x8c, 0x46, 0xc6, 0x89, 0x5e, 0xc4, 0xe8, 0xbe, + 0xff, 0x2e, 0xb4, 0x41, 0xbb, 0xaa, 0x55, 0x8a, 0x56, 0x24, 0x84, 0xd2, + 0x74, 0x19, 0xcd, 0x13, 0x72, 0x15, 0xd1, 0xe9, 0x81, 0xdb, 0x54, 0xaa, + 0x75, 0x0d, 0x8d, 0x76, 0xc0, 0x89, 0x5e, 0xcc, 0x89, 0x5e, 0xce, 0xb4, + 0x42, 0xeb, 0x26, 0x8b, 0x4e, 0xc8, 0x8b, 0x56, 0xca, 0x8a, 0x46, 0x18, + 0xf6, 0x66, 0x1a, 0x91, 0xf7, 0xf1, 0x92, 0xf6, 0x76, 0x18, 0x89, 0xd1, + 0x88, 0xc6, 0x86, 0xe9, 0xd0, 0xc9, 0xd0, 0xc9, 0x08, 0xe1, 0x41, 0xc4, + 0x5e, 0xc4, 0xb8, 0x01, 0x02, 0x8a, 0x56, 0x24, 0xcd, 0x13, 0x0f, 0x82, + 0x75, 0xff, 0x8b, 0x46, 0x0b, 0xf6, 0x76, 0xc0, 0x01, 0x46, 0xc6, 0x83, + 0x46, 0xc8, 0x01, 0x83, 0x56, 0xca, 0x00, 0x4f, 0x75, 0x98, 0x8e, 0x46, + 0xc6, 0x5e, 0xc3, 0x4d, 0x45, 0x54, 0x41, 0x4b, 0x45, 0x52, 0x4e, 0x53, + 0x59, 0x53, 0x00, 0x00, 0x55, 0xaa +}; + +static const char fat8_string[8] = { 'F', 'A', 'T', ' ', ' ', ' ', ' ', ' ' }; +static const char fat12_string[8] = { 'F', 'A', 'T', '1', '2', ' ', ' ', ' ' }; +static const char fat16_string[8] = { 'F', 'A', 'T', '1', '6', ' ', ' ', ' ' }; +static const char fat32_string[8] = { 'F', 'A', 'T', '3', '2', ' ', ' ', ' ' }; + + + +static fat_partition_info *fat_get_partition_info(imgtool::partition &partition) +{ + return (fat_partition_info *)partition.extra_bytes(); +} + + + +static imgtoolerr_t fat_read_sector(imgtool::partition &partition, uint32_t sector_index, + int offset, void *buffer, size_t buffer_len) +{ + //const fat_partition_info *disk_info; + imgtoolerr_t err; + uint8_t data[FAT_SECLEN]; + uint32_t block_size; + size_t len; + + //disk_info = fat_get_partition_info(partition); + + /* sanity check */ + err = partition.get_block_size(block_size); + if (err) + return err; + assert(block_size == sizeof(data)); + + while(buffer_len > 0) + { + err = partition.read_block(sector_index++, data); + if (err) + return err; + + len = std::min(buffer_len, sizeof(data) - offset); + memcpy(buffer, data + offset, len); + + buffer = ((uint8_t *) buffer) + len; + buffer_len -= len; + offset = 0; + } + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t fat_write_sector(imgtool::partition &partition, uint32_t sector_index, + int offset, const void *buffer, size_t buffer_len) +{ + //const fat_partition_info *disk_info; + imgtoolerr_t err; + uint8_t data[FAT_SECLEN]; + const void *write_data; + uint32_t block_size; + size_t len; + + //disk_info = fat_get_partition_info(partition); + + /* sanity check */ + err = partition.get_block_size(block_size); + if (err) + return err; + assert(block_size == sizeof(data)); + + while(buffer_len > 0) + { + len = std::min(buffer_len, sizeof(data) - offset); + + if ((offset != 0) || (buffer_len < sizeof(data))) + { + err = partition.read_block(sector_index, data); + if (err) + return err; + memcpy(data + offset, buffer, len); + write_data = data; + } + else + { + write_data = buffer; + } + + err = partition.write_block(sector_index++, write_data); + if (err) + return err; + + buffer = ((uint8_t *) buffer) + len; + buffer_len -= len; + offset = 0; + } + return IMGTOOLERR_SUCCESS; +} + + +#ifdef UNUSED_FUNCTION +static imgtoolerr_t fat_clear_sector(imgtool::partition &partition, uint32_t sector_index, uint8_t data) +{ + char buf[FAT_SECLEN]; + memset(buf, data, sizeof(buf)); + return fat_write_sector(partition, sector_index, 0, buf, sizeof(buf)); +} +#endif + + +static imgtoolerr_t fat_partition_open(imgtool::partition &partition, uint64_t first_block, uint64_t block_count) +{ + uint8_t header[FAT_SECLEN]; + imgtoolerr_t err; + fat_partition_info *info; + uint32_t fat_bits, total_sectors_l, total_sectors_h, sector_size; + uint64_t available_sectors; + //int has_extended_bios_param_block = true; + + info = fat_get_partition_info(partition); + + /* read the boot/root sector */ + err = fat_read_sector(partition, 0, 0, header, sizeof(header)); + if (err) + return err; + + /* magic bytes present? */ + if ((header[510] != 0x55) || (header[511] != 0xAA)) + return IMGTOOLERR_CORRUPTIMAGE; + + /* determine which type of FAT is on this disk */ + if (!memcmp(&header[54], fat8_string, sizeof(fat8_string))) + fat_bits = 8; + else if (!memcmp(&header[54], fat12_string, sizeof(fat12_string))) + fat_bits = 12; + else if (!memcmp(&header[54], fat16_string, sizeof(fat16_string))) + fat_bits = 16; + else if (!memcmp(&header[54], fat32_string, sizeof(fat32_string))) + fat_bits = 32; + else + { + fat_bits = 8; + //has_extended_bios_param_block = false; + } + + info->fat_bits = fat_bits; + sector_size = pick_integer_le(header, 11, 2); + info->sectors_per_cluster = pick_integer_le(header, 13, 1); + info->reserved_sectors = pick_integer_le(header, 14, 2); + info->fat_count = pick_integer_le(header, 16, 1); + info->root_entries = pick_integer_le(header, 17, 2); + total_sectors_l = pick_integer_le(header, 19, 2); + info->sectors_per_fat = pick_integer_le(header, 22, 2); + total_sectors_h = pick_integer_le(header, 32, 4); + + if (info->sectors_per_cluster == 0) + return IMGTOOLERR_CORRUPTIMAGE; + + info->total_sectors = total_sectors_l + (((uint64_t) total_sectors_h) << 16); + available_sectors = info->total_sectors - info->reserved_sectors + - (info->sectors_per_fat * info->fat_count) + - (info->root_entries * FAT_DIRENT_SIZE + FAT_SECLEN - 1) / FAT_SECLEN; + info->total_clusters = (available_sectors + info->sectors_per_cluster - 1) / info->sectors_per_cluster; + info->cluster_size = FAT_SECLEN * info->sectors_per_cluster; + + if (info->fat_count == 0) + return IMGTOOLERR_CORRUPTIMAGE; + if (sector_size != FAT_SECLEN) + return IMGTOOLERR_CORRUPTIMAGE; + if (info->sectors_per_fat == 0) + return IMGTOOLERR_CORRUPTIMAGE; + if (info->sectors_per_cluster == 0) + return IMGTOOLERR_CORRUPTIMAGE; + if (info->reserved_sectors == 0) + return IMGTOOLERR_CORRUPTIMAGE; + if (info->total_clusters * info->fat_bits > info->sectors_per_fat * FAT_SECLEN * 8) + return IMGTOOLERR_CORRUPTIMAGE; + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t fat_partition_create(imgtool::image &image, uint64_t first_block, uint64_t block_count) +{ + imgtoolerr_t err; + uint32_t heads, tracks, sectors_per_track; + uint32_t fat_bits, sectors_per_cluster, reserved_sectors, hidden_sectors; + uint32_t root_dir_count, root_dir_sectors; + uint32_t sectors_per_fat, fat_count, i; + uint32_t boot_sector_offset; + uint64_t total_clusters; + uint8_t media_descriptor; + //const char *title; + const char *fat_bits_string; + uint8_t header[FAT_SECLEN]; +#if 0 + uint64_t first_fat_entries; +#endif + + /* check for limits */ + if (block_count > 0xFFFFFFFFFFFFU) + return IMGTOOLERR_PARAMTOOLARGE; + + /* get the geometry */ + err = image.get_geometry(&tracks, &heads, §ors_per_track); + if (err) + return err; + + /* cap our sector count so that we only use FAT12/16 */ + sectors_per_cluster = (block_count + 65524 - 1) / 65524; + + /* compute the FAT file system type */ + if ((block_count / sectors_per_cluster) <= 4084) + { + fat_bits = 12; + fat_bits_string = fat12_string; + } + else if ((block_count / sectors_per_cluster) <= 65524) + { + fat_bits = 16; + fat_bits_string = fat16_string; + } + else + { + fat_bits = 32; + fat_bits_string = fat32_string; + } + + /* figure out media type */ + i = 0; + while((known_media[i].heads > 0) && ((known_media[i].heads != heads) + || (known_media[i].tracks != tracks) + || (known_media[i].sectors != sectors_per_track))) + { + i++; + } + media_descriptor = known_media[i].media_descriptor; + + /* other miscellaneous settings */ + //title = ""; + fat_count = 2; + root_dir_count = 512; + hidden_sectors = 0; + reserved_sectors = 1; + + /* calculated settings */ + root_dir_sectors = (root_dir_count * FAT_DIRENT_SIZE + FAT_SECLEN - 1) / FAT_SECLEN; + total_clusters = (block_count - reserved_sectors - hidden_sectors - root_dir_sectors) + / sectors_per_cluster; + sectors_per_fat = (total_clusters * fat_bits + (FAT_SECLEN * 8) - 1) + / (FAT_SECLEN * 8); + + /* prepare the header */ + memset(header, 0, sizeof(header)); + memcpy(&header[3], "IMGTOOL ", 8); + place_integer_le(header, 11, 2, FAT_SECLEN); + place_integer_le(header, 13, 1, sectors_per_cluster); + place_integer_le(header, 14, 1, reserved_sectors); + place_integer_le(header, 16, 1, fat_count); + place_integer_le(header, 17, 2, root_dir_count); + place_integer_le(header, 19, 2, (uint16_t) (block_count >> 0)); + place_integer_le(header, 21, 1, media_descriptor); + place_integer_le(header, 22, 2, sectors_per_fat); + place_integer_le(header, 24, 2, sectors_per_track); + place_integer_le(header, 26, 2, heads); + place_integer_le(header, 28, 4, hidden_sectors); + place_integer_le(header, 32, 4, (uint32_t) (block_count >> 16)); + place_integer_le(header, 36, 1, 0xFF); + place_integer_le(header, 38, 1, 0x28); + place_integer_le(header, 39, 1, std::rand()); + place_integer_le(header, 40, 1, std::rand()); + place_integer_le(header, 41, 1, std::rand()); + place_integer_le(header, 42, 1, std::rand()); + memcpy(&header[43], " ", 11); + memcpy(&header[54], fat_bits_string, 8); + + /* store boot code */ + boot_sector_offset = sizeof(header) - sizeof(boot_sector_code); + if (boot_sector_offset < 62) + return IMGTOOLERR_UNEXPECTED; /* sanity check */ + if (boot_sector_offset > 510) + return IMGTOOLERR_UNEXPECTED; /* sanity check */ + memcpy(&header[boot_sector_offset], boot_sector_code, sizeof(boot_sector_code)); + + /* specify jump instruction */ + if (boot_sector_offset <= 129) + { + header[0] = 0xEB; /* JMP rel8 */ + header[1] = (uint8_t) (boot_sector_offset - 2); /* (offset) */ + header[2] = 0x90; /* NOP */ + } + else + { + header[0] = 0xE9; /* JMP rel16 */ + header[1] = (uint8_t) ((boot_sector_offset - 2) >> 0); /* (offset) */ + header[2] = (uint8_t) ((boot_sector_offset - 2) >> 8); /* (offset) */ + } + + err = image.write_block(first_block, header); + if (err) + return err; + + /* clear out file allocation table */ + for (i = reserved_sectors; i < (reserved_sectors + sectors_per_fat * fat_count + root_dir_sectors); i++) + { + err = image.clear_block(first_block + i, 0); + if (err) + return err; + } + + // FIXME: this causes a corrupt PC floppy image since it doubles the FAT partition header - works without it though +#if 0 + /* set first two FAT entries */ + first_fat_entries = ((uint64_t) media_descriptor) | 0xFFFFFF00; + first_fat_entries &= (((uint64_t) 1) << fat_bits) - 1; + first_fat_entries |= ((((uint64_t) 1) << fat_bits) - 1) << fat_bits; + first_fat_entries = little_endianize_int64(first_fat_entries); + + for (i = 0; i < fat_count; i++) + { + err = image->write_block(first_block + 1 + (i * sectors_per_fat), &first_fat_entries); + if (err) + return err; + } +#endif + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t fat_load_fat(imgtool::partition &partition, uint8_t **fat_table) +{ + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + const fat_partition_info *disk_info; + uint8_t *table; + uint32_t table_size; + uint32_t pos, len; + uint32_t sector_index; + + disk_info = fat_get_partition_info(partition); + + table_size = disk_info->sectors_per_fat * disk_info->fat_count * FAT_SECLEN; + + /* allocate the table with extra bytes, in case we "overextend" our reads */ + table = (uint8_t*)malloc(table_size + sizeof(uint64_t)); + if (!table) + { + err = IMGTOOLERR_OUTOFMEMORY; + goto done; + } + memset(table, 0, table_size + sizeof(uint64_t)); + + pos = 0; + sector_index = disk_info->reserved_sectors; + + while(pos < table_size) + { + len = std::min(table_size - pos, uint32_t(FAT_SECLEN)); + + err = fat_read_sector(partition, sector_index++, 0, &table[pos], len); + if (err) + goto done; + + pos += FAT_SECLEN; + } + +done: + if (err && table) + { + free(table); + table = NULL; + } + *fat_table = table; + return err; +} + + + +static imgtoolerr_t fat_save_fat(imgtool::partition &partition, const uint8_t *fat_table) +{ + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + const fat_partition_info *disk_info; + uint32_t table_size; + uint32_t pos, len; + uint32_t sector_index; + + disk_info = fat_get_partition_info(partition); + + table_size = disk_info->sectors_per_fat * disk_info->fat_count * FAT_SECLEN; + + pos = 0; + sector_index = disk_info->reserved_sectors; + + while(pos < table_size) + { + len = std::min(table_size - pos, uint32_t(FAT_SECLEN)); + + err = fat_write_sector(partition, sector_index++, 0, &fat_table[pos], len); + if (err) + goto done; + + pos += FAT_SECLEN; + } + +done: + return err; +} + + + +static uint32_t fat_get_fat_entry(imgtool::partition &partition, const uint8_t *fat_table, uint32_t fat_entry) +{ + const fat_partition_info *disk_info; + uint64_t entry; + uint32_t bit_index, i; + uint32_t last_entry = 0; + uint32_t bit_mask; + + disk_info = fat_get_partition_info(partition); + bit_index = fat_entry * disk_info->fat_bits; + bit_mask = 0xFFFFFFFF >> (32 - disk_info->fat_bits); + + if (fat_entry >= disk_info->total_clusters) + { + assert(0); + return 1; + } + + /* make sure that the cluster is free in all fats */ + for (i = 0; i < disk_info->fat_count; i++) + { + memcpy(&entry, fat_table + (i * FAT_SECLEN + * disk_info->sectors_per_fat) + (bit_index / 8), sizeof(entry)); + + /* we've extracted the bytes; we now need to normalize it */ + entry = little_endianize_int64(entry); + entry >>= bit_index % 8; + entry &= bit_mask; + + if (i == 0) + last_entry = (uint32_t) entry; + else if (last_entry != (uint32_t) entry) + return 1; /* if the FATs disagree; mark this as reserved */ + } + + /* normalize special clusters */ + if (last_entry >= (0xFFFFFFF0 & bit_mask)) + { + last_entry |= 0xFFFFFFF0; + if (last_entry >= 0xFFFFFFF8) + last_entry = 0xFFFFFFFF; + } + return last_entry; +} + + + +static void fat_set_fat_entry(imgtool::partition &partition, uint8_t *fat_table, uint32_t fat_entry, uint32_t value) +{ + const fat_partition_info *disk_info; + uint64_t entry; + uint32_t bit_index, i; + + disk_info = fat_get_partition_info(partition); + bit_index = fat_entry * disk_info->fat_bits; + value &= 0xFFFFFFFF >> (32 - disk_info->fat_bits); + + for (i = 0; i < disk_info->fat_count; i++) + { + memcpy(&entry, fat_table + (i * FAT_SECLEN + * disk_info->sectors_per_fat) + (bit_index / 8), sizeof(entry)); + + entry = little_endianize_int64(entry); + entry &= (~((uint64_t) 0xFFFFFFFF >> (32 - disk_info->fat_bits)) << (bit_index % 8)) | ((1 << (bit_index % 8)) - 1); + entry |= ((uint64_t) value) << (bit_index % 8); + entry = little_endianize_int64(entry); + + memcpy(fat_table + (i * FAT_SECLEN + * disk_info->sectors_per_fat) + (bit_index / 8), &entry, sizeof(entry)); + } +} + + + +static void fat_debug_integrity_check(imgtool::partition &partition, const uint8_t *fat_table, const fat_file *file) +{ +#ifdef MAME_DEBUG + /* debug function to test the integrity of a file */ + uint32_t cluster; + const fat_partition_info *disk_info; + + disk_info = fat_get_partition_info(partition); + cluster = file->first_cluster ? file->first_cluster : 0xFFFFFFFF; + + if (!file->root) + { + while(cluster != 0xFFFFFFFF) + { + assert((cluster >= 2) && (cluster < disk_info->total_clusters)); + cluster = fat_get_fat_entry(partition, fat_table, cluster); + } + } +#endif +} + + + +static imgtoolerr_t fat_seek_file(imgtool::partition &partition, fat_file *file, uint32_t pos) +{ + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + const fat_partition_info *disk_info; + uint32_t new_cluster; + uint8_t *fat_table = NULL; + + disk_info = fat_get_partition_info(partition); + + /* can't seek past end of file */ + if (!file->directory && (pos > file->filesize)) + pos = file->filesize; + + if (file->first_cluster == 0) + { + /* special case; the root directory */ + file->index = pos; + } + else + { + /* first, we need to check to see if we have to go back to the beginning */ + if (pos < file->index) + { + file->cluster = file->first_cluster; + file->cluster_index = 0; + file->eof = 0; + } + + /* skip ahead clusters */ + while((file->cluster_index + disk_info->cluster_size) <= pos) + { + if (!fat_table) + { + err = fat_load_fat(partition, &fat_table); + if (err) + goto done; + } + + new_cluster = fat_get_fat_entry(partition, fat_table, file->cluster); + + file->cluster = new_cluster; + file->cluster_index += disk_info->cluster_size; + + /* are we at the end of the file? */ + if (new_cluster == 0xFFFFFFFF) + { + pos = file->cluster_index; + file->eof = 1; + } + } + file->index = pos; + } + +done: + if (fat_table) + free(fat_table); + return err; +} + + + +static uint32_t fat_get_filepos_sector_index(imgtool::partition &partition, fat_file *file) +{ + uint32_t sector_index; + const fat_partition_info *disk_info; + + disk_info = fat_get_partition_info(partition); + + sector_index = disk_info->reserved_sectors + (disk_info->sectors_per_fat * disk_info->fat_count); + if (file->root) + { + /* special case for the root file */ + sector_index += file->index / FAT_SECLEN; + } + else + { + /* cluster out of range? */ + if ((file->cluster < 2) || (file->cluster >= disk_info->total_clusters)) + return 0; + + sector_index += (disk_info->root_entries * FAT_DIRENT_SIZE + FAT_SECLEN - 1) / FAT_SECLEN; + sector_index += (file->cluster - 2) * disk_info->sectors_per_cluster; + sector_index += (file->index / FAT_SECLEN) % disk_info->sectors_per_cluster; + } + return sector_index; +} + + + +static imgtoolerr_t fat_corrupt_file_error(const fat_file *file) +{ + imgtoolerr_t err; + if (file->root) + err = IMGTOOLERR_CORRUPTIMAGE; + else if (file->directory) + err = IMGTOOLERR_CORRUPTDIR; + else + err = IMGTOOLERR_CORRUPTFILE; + return err; +} + + + +static imgtoolerr_t fat_readwrite_file(imgtool::partition &partition, fat_file *file, + void *buffer, size_t buffer_len, size_t *bytes_read, int read_or_write) +{ + imgtoolerr_t err; +// const fat_partition_info *disk_info; + uint32_t sector_index; + int offset; + size_t len; + +// disk_info = + fat_get_partition_info(partition); + if (bytes_read) + *bytes_read = 0; + if (!file->directory) + buffer_len = std::min(buffer_len, size_t(file->filesize - file->index)); + + while(!file->eof && (buffer_len > 0)) + { + sector_index = fat_get_filepos_sector_index(partition, file); + if (sector_index == 0) + return fat_corrupt_file_error(file); + + offset = file->index % FAT_SECLEN; + len = std::min(buffer_len, size_t(FAT_SECLEN - offset)); + + /* read or write the data from the disk */ + if (read_or_write) + err = fat_write_sector(partition, sector_index, offset, buffer, len); + else + err = fat_read_sector(partition, sector_index, offset, buffer, len); + if (err) + return err; + + /* and move the file pointer ahead */ + err = fat_seek_file(partition, file, file->index + len); + if (err) + return err; + + buffer = ((uint8_t *) buffer) + len; + buffer_len -= len; + if (bytes_read) + *bytes_read += len; + } + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t fat_read_file(imgtool::partition &partition, fat_file *file, + void *buffer, size_t buffer_len, size_t *bytes_read) +{ + return fat_readwrite_file(partition, file, buffer, buffer_len, bytes_read, 0); +} + + + +static imgtoolerr_t fat_write_file(imgtool::partition &partition, fat_file *file, + const void *buffer, size_t buffer_len, size_t *bytes_read) +{ + return fat_readwrite_file(partition, file, (void *) buffer, buffer_len, bytes_read, 1); +} + + + +static uint32_t fat_allocate_cluster(imgtool::partition &partition, uint8_t *fat_table) +{ + const fat_partition_info *disk_info; + uint32_t i, val; + + disk_info = fat_get_partition_info(partition); + + for (i = 2; i < disk_info->total_clusters; i++) + { + val = fat_get_fat_entry(partition, fat_table, i); + if (val == 0) + { + fat_set_fat_entry(partition, fat_table, i, 1); + return i; + } + } + return 0; +} + + + +/* sets the size of a file; ~0 means 'delete' */ +static imgtoolerr_t fat_set_file_size(imgtool::partition &partition, fat_file *file, + uint32_t new_size) +{ + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + const fat_partition_info *disk_info; + uint32_t new_cluster_count; + uint32_t old_cluster_count; + uint32_t cluster, write_cluster, last_cluster, new_pos, i; + uint8_t *fat_table = NULL; + uint8_t dirent[32]; + size_t clear_size; + void *clear_buffer = NULL; + int delete_file = false; + int rest_free = false; + + disk_info = fat_get_partition_info(partition); + + LOG(("fat_set_file_size(): file->first_cluster=%d new_size=0x%08x\n", file->first_cluster, new_size)); + + /* special case */ + if (new_size == ~0) + { + delete_file = true; + new_size = 0; + } + + /* if this is the trivial case (not changing the size), succeed */ + if (!delete_file && (file->filesize == new_size)) + { + err = IMGTOOLERR_SUCCESS; + goto done; + } + + /* what is the new position? */ + new_pos = std::min(file->index, new_size); + + if (file->root) + { + /* this is the root directory; this is a special case */ + if (new_size > (disk_info->root_entries * FAT_DIRENT_SIZE)) + { + err = IMGTOOLERR_NOSPACE; + goto done; + } + } + else + { + old_cluster_count = (file->filesize + disk_info->cluster_size - 1) / disk_info->cluster_size; + new_cluster_count = (new_size + disk_info->cluster_size - 1) / disk_info->cluster_size; + cluster = 0; + + /* load the dirent */ + err = fat_read_sector(partition, file->dirent_sector_index, file->dirent_sector_offset, dirent, sizeof(dirent)); + if (err) + goto done; + + /* need to load the FAT whether we are growing or shrinking the file */ + if (old_cluster_count != new_cluster_count) + { + err = fat_load_fat(partition, &fat_table); + if (err) + goto done; + + cluster = 0; + i = 0; + do + { + last_cluster = cluster; + write_cluster = 0; + + /* identify the next cluster */ + if (cluster != 0) + cluster = fat_get_fat_entry(partition, fat_table, cluster); + else + cluster = file->first_cluster ? file->first_cluster : 0xFFFFFFFF; + + /* do we need to grow the file by a cluster? */ + if (i < new_cluster_count && ((cluster < 2) || (cluster >= disk_info->total_clusters))) + { + /* grow this file by a cluster */ + cluster = fat_allocate_cluster(partition, fat_table); + if (cluster == 0) + { + err = IMGTOOLERR_NOSPACE; + goto done; + } + + write_cluster = cluster; + } + else if (i >= new_cluster_count) + { + /* we are shrinking the file; we need to unlink this node */ + if ((cluster < 2) || (cluster >= disk_info->total_clusters)) + cluster = 0xFFFFFFFF; /* ack file is corrupt! recover */ + write_cluster = 0xFFFFFFFF; + } + + /* write out the entry, if appropriate */ + if (write_cluster != 0) + { + /* are we tieing up loose ends? */ + if (rest_free && (write_cluster == 0xFFFFFFFF)) + write_cluster = 0; + + if (last_cluster == 0) + file->first_cluster = (write_cluster != 0xFFFFFFFF) ? write_cluster : 0; + else + fat_set_fat_entry(partition, fat_table, last_cluster, write_cluster); + + /* did we write the last cluster? if so, the rest (if any) are free */ + if (write_cluster == 0xFFFFFFFF) + rest_free = true; + } + } + while((++i < new_cluster_count) || (cluster != 0xFFFFFFFF)); + } + + /* record the new file size */ + place_integer_le(dirent, 26, 2, file->first_cluster); + place_integer_le(dirent, 28, 4, new_size); + + /* delete the file, if appropriate */ + if (delete_file) + dirent[0] = 0xE5; + + /* save the dirent */ + err = fat_write_sector(partition, file->dirent_sector_index, file->dirent_sector_offset, dirent, sizeof(dirent)); + if (err) + goto done; + + /* if we've modified the FAT, save it out */ + if (fat_table) + { + err = fat_save_fat(partition, fat_table); + if (err) + goto done; + } + + /* update the file structure */ + if (!file->directory) + file->filesize = new_size; + file->cluster = file->first_cluster; + file->index = 0; + file->cluster_index = 0; + file->eof = (new_cluster_count == 0); + } + + /* special case; clear out stale bytes on non-root directories */ + if (file->directory && !delete_file) + { + if (file->root) + clear_size = std::min(file->filesize - new_size, uint32_t(FAT_DIRENT_SIZE)); + else + clear_size = (disk_info->cluster_size - (new_size % disk_info->cluster_size)) % disk_info->cluster_size; + + if (clear_size > 0) + { + err = fat_seek_file(partition, file, new_size); + if (err) + goto done; + + clear_buffer = malloc(clear_size); + if (!clear_buffer) + { + err = IMGTOOLERR_OUTOFMEMORY; + goto done; + } + memset(clear_buffer, '\0', clear_size); + + err = fat_write_file(partition, file, clear_buffer, clear_size, NULL); + if (err) + goto done; + } + } + + /* seek back to original pos */ + err = fat_seek_file(partition, file, new_pos); + if (err) + goto done; + + if (fat_table) + fat_debug_integrity_check(partition, fat_table, file); + +done: + if (fat_table) + free(fat_table); + if (clear_buffer) + free(clear_buffer); + return err; +} + + + +static void prepend_lfn_bytes(char16_t *lfn_buf, size_t lfn_buflen, size_t *lfn_len, + const uint8_t *entry, int offset, int chars) +{ + uint16_t w; + int i; + size_t move_len; + + move_len = std::min(*lfn_len + 1, lfn_buflen - chars - 1); + memmove(&lfn_buf[chars], &lfn_buf[0], move_len * sizeof(*lfn_buf)); + + for (i = 0; i < chars; i++) + { + /* read the character */ + memcpy(&w, &entry[offset + i * 2], 2); + w = little_endianize_int16(w); + + /* append to buffer */ + lfn_buf[i] = (w != 0xFFFF) ? w : 0; + } + *lfn_len += chars; +} + + + +static uint8_t fat_calc_filename_checksum(const uint8_t *short_filename) +{ + uint8_t checksum; + int i, j; + + checksum = 0; + for (i = 0; i < 11; i++) + { + j = checksum & 1; + checksum >>= 1; + if (j) + checksum |= 0x80; + checksum += short_filename[i]; + } + return checksum; +} + + + +static void fat_calc_dirent_lfnchecksum(uint8_t *entry, size_t entry_len) +{ + uint8_t checksum; + int i; + + checksum = fat_calc_filename_checksum(entry + entry_len - FAT_DIRENT_SIZE); + + for (i = 0; i < (entry_len / FAT_DIRENT_SIZE - 1); i++) + entry[i * FAT_DIRENT_SIZE + 13] = checksum; +} + + + +static char fat_canonicalize_sfn_char(char ch) +{ + /* return the display version of this short file name character */ + return tolower(ch); +} + + + +static void fat_canonicalize_sfn(char *sfn, const uint8_t *sfn_bytes) +{ + /* return the display version of this short file name */ + int i; + + memset(sfn, '\0', 13); + memcpy(sfn, sfn_bytes, 8); + rtrim(sfn); + if (sfn[0] == 0x05) + sfn[0] = (char) 0xE5; + if ((sfn_bytes[8] != ' ') || (sfn_bytes[9] != ' ') || (sfn_bytes[10] != ' ')) + { + strcat(sfn, "."); + memcpy(sfn + strlen(sfn), &sfn_bytes[8], 3); + rtrim(sfn); + } + for (i = 0; sfn[i]; i++) + sfn[i] = fat_canonicalize_sfn_char(sfn[i]); +} + + + +static imgtool::datetime fat_crack_time(uint32_t fat_time) +{ + util::arbitrary_datetime dt; + dt.second = ((fat_time >> 0) & 0x001F) * 2; + dt.minute = ((fat_time >> 5) & 0x003F); + dt.hour = ((fat_time >> 11) & 0x001F); + dt.day_of_month = ((fat_time >> 16) & 0x001F); + dt.month = ((fat_time >> 21) & 0x000F); + dt.year = ((fat_time >> 25) & 0x007F) + 1980; + return imgtool::datetime(imgtool::datetime::LOCAL, dt); +} + + + +static uint32_t fat_setup_time(time_t ansi_time) +{ + std::tm t = *localtime(&ansi_time); + + uint32_t result = 0; + result |= (((uint32_t) (t.tm_sec / 2)) & 0x001F) << 0; + result |= (((uint32_t) t.tm_min) & 0x003F) << 5; + result |= (((uint32_t) t.tm_hour) & 0x001F) << 11; + result |= (((uint32_t) t.tm_mday) & 0x001F) << 16; + result |= (((uint32_t) t.tm_mon) & 0x000F) << 21; + result |= (((uint32_t) (t.tm_year + 1900 - 1980)) & 0x007F) << 25; + + return result; +} + + + +static imgtoolerr_t fat_read_dirent(imgtool::partition &partition, fat_file *file, + fat_dirent &ent, fat_freeentry_info *freeent) +{ + imgtoolerr_t err; + //const fat_partition_info *disk_info; + uint8_t entry[FAT_DIRENT_SIZE]; + size_t bytes_read; + int i, j; + char32_t ch; + char16_t lfn_buf[512]; + size_t lfn_len = 0; + int lfn_lastentry = 0; + uint8_t lfn_checksum = 0; + uint32_t entry_index, entry_sector_index, entry_sector_offset; + + assert(file->directory); + lfn_buf[0] = '\0'; + memset(&ent, 0, sizeof(ent)); + //disk_info = fat_get_partition_info(partition); + + /* The first eight bytes of a FAT directory entry is a blank padded name + * + * The first byte can be special: + * 0x00 - entry is available and no further entry is used + * 0x05 - first character is actually 0xe5 + * 0x2E - dot entry; either '.' or '..' + * 0xE5 - entry has been erased and is available + * + * Byte 11 is the attributes; and 0x0F denotes a LFN entry + */ + do + { + entry_index = file->index; + entry_sector_index = fat_get_filepos_sector_index(partition, file); + entry_sector_offset = file->index % FAT_SECLEN; + + err = fat_read_file(partition, file, entry, sizeof(entry), &bytes_read); + if (err) + return err; + if (bytes_read < sizeof(entry)) + memset(entry, 0, sizeof(entry)); + + if (entry[11] == 0x0F) + { + /* this is an LFN entry */ + if ((lfn_lastentry == 0) + || ((entry[0] & 0x3F) != (lfn_lastentry - 1)) + || (lfn_checksum != entry[13])) + { + lfn_buf[0] = 0; + lfn_len = 0; + lfn_checksum = entry[13]; + } + lfn_lastentry = entry[0] & 0x3F; + prepend_lfn_bytes(lfn_buf, std::size(lfn_buf), + &lfn_len, entry, 28, 2); + prepend_lfn_bytes(lfn_buf, std::size(lfn_buf), + &lfn_len, entry, 14, 6); + prepend_lfn_bytes(lfn_buf, std::size(lfn_buf), + &lfn_len, entry, 1, 5); + } + else if (freeent && (freeent->position == ~0)) + { + /* do a quick check to find out if we found space */ + if ((entry[0] == '\0') || (entry[0] == 0xE5)) + { + if (freeent->candidate_position > entry_index) + freeent->candidate_position = entry_index; + + if ((entry[0] == '\0') || (freeent->candidate_position + freeent->required_size < file->index)) + freeent->position = freeent->candidate_position; + } + else + { + freeent->candidate_position = ~0; + } + } + } + while((entry[0] == 0x2E) || (entry[0] == 0xE5) || (entry[11] == 0x0F)); + + /* no more directory entries? */ + if (entry[0] == '\0') + { + ent.eof = 1; + return IMGTOOLERR_SUCCESS; + } + + /* pick apart short filename */ + fat_canonicalize_sfn(ent.short_filename, entry); + + /* and the long filename */ + if (lfn_lastentry == 1) + { + /* only use the LFN if the checksum passes */ + if (lfn_checksum == fat_calc_filename_checksum(entry)) + { + i = 0; + j = 0; + do + { + i += uchar_from_utf16(&ch, &lfn_buf[i], std::size(lfn_buf) - i); + j += utf8_from_uchar(&ent.long_filename[j], std::size(ent.long_filename) - j, ch); + } + while(ch != 0); + } + } + + /* other attributes */ + ent.filesize = pick_integer_le(entry, 28, 4); + ent.directory = (entry[11] & 0x10) ? 1 : 0; + ent.first_cluster = pick_integer_le(entry, 26, 2); + ent.dirent_sector_index = entry_sector_index; + ent.dirent_sector_offset = entry_sector_offset; + ent.creation_time = fat_crack_time(pick_integer_le(entry, 14, 4)); + ent.lastmodified_time = fat_crack_time(pick_integer_le(entry, 22, 4)); + return IMGTOOLERR_SUCCESS; +} + + + +enum sfn_disposition_t +{ + SFN_SUFFICIENT, /* name fully representable in short file name */ + SFN_DERIVATIVE, /* name not fully representable in short file name, but no need to tildize */ + SFN_MANGLED /* name not representable in short file name; must tildize */ +}; + +static imgtoolerr_t fat_construct_dirent(const char *filename, creation_policy_t create, + uint8_t **entry, size_t *entry_len) +{ + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + uint8_t *created_entry = NULL; + uint8_t *new_created_entry; + uint32_t now; + size_t created_entry_len = FAT_DIRENT_SIZE; + size_t created_entry_pos = 0; + char32_t ch; + char last_short_char = ' '; + char short_char = '\0'; + char canonical_short_char; + char16_t buf[UTF16_CHAR_MAX]; + int i, len; + int sfn_pos = 0; + sfn_disposition_t sfn_disposition = SFN_SUFFICIENT; + int sfn_in_extension = 0; + + /* sanity check */ + if (*filename == '\0') + { + err = IMGTOOLERR_BADFILENAME; + goto done; + } + + /* construct intial entry */ + created_entry = (uint8_t *) malloc(FAT_DIRENT_SIZE); + if (!created_entry) + { + err = IMGTOOLERR_OUTOFMEMORY; + goto done; + } + + /* set up the basics for the new dirent */ + memset(created_entry + 0, ' ', 11); + memset(created_entry + 12, '\0', FAT_DIRENT_SIZE - 12); + created_entry[11] = (create == CREATE_DIR) ? 0x10 : 0x00; + + /* set up file dates in the new dirent */ + now = fat_setup_time(time(NULL)); + place_integer_le(created_entry, 14, 4, now); + place_integer_le(created_entry, 18, 2, now); + place_integer_le(created_entry, 22, 4, now); + + while(*filename) + { + filename += uchar_from_utf8(&ch, filename, UTF8_CHAR_MAX); + + /* append to short filename, if possible */ + if ((ch < 32) || (ch > 128)) + short_char = '\0'; + else if (isalnum((char) ch)) + short_char = toupper((char) ch); + else if (strchr(".!#$%^()-@^_`{}~", (char) ch)) + short_char = (char) ch; + else + short_char = '\0'; /* illegal SFN char */ + canonical_short_char = fat_canonicalize_sfn_char((char) ch); + if (!short_char || (short_char != canonical_short_char)) + { + if (toupper(short_char) == toupper(canonical_short_char)) + sfn_disposition = std::max(sfn_disposition, SFN_DERIVATIVE); + else + sfn_disposition = SFN_MANGLED; + } + + /* append the short filename char */ + if (short_char == '.') + { + /* multiple extensions or trailing spaces? */ + if (sfn_in_extension) + sfn_disposition = SFN_MANGLED; + else if (last_short_char == ' ') + sfn_disposition = std::max(sfn_disposition, SFN_DERIVATIVE); + + sfn_in_extension = 1; + sfn_pos = 8; + created_entry[created_entry_len - FAT_DIRENT_SIZE + 8] = ' '; + created_entry[created_entry_len - FAT_DIRENT_SIZE + 9] = ' '; + created_entry[created_entry_len - FAT_DIRENT_SIZE + 10] = ' '; + } + else if (sfn_pos == (sfn_in_extension ? 11 : 8)) + { + /* ran out of characters for short filename */ + sfn_disposition = SFN_MANGLED; + } + else if (short_char != '\0') + { + created_entry[created_entry_len - FAT_DIRENT_SIZE + sfn_pos++] = short_char; + } + last_short_char = short_char; + + + /* convert to UTF-16 and add a long filename entry */ + len = utf16le_from_uchar(buf, UTF16_CHAR_MAX, ch); + for (i = 0; i < len; i++) + { + switch(created_entry_pos) + { + case 0: + case 32: + /* need to grow */ + new_created_entry = (uint8_t *) malloc(created_entry_len + FAT_DIRENT_SIZE); + if (!new_created_entry) + { + err = IMGTOOLERR_OUTOFMEMORY; + goto done; + } + + /* move existing entries forward */ + memmove(new_created_entry + 32, created_entry, created_entry_len); + + if (created_entry) free(created_entry); + created_entry = new_created_entry; + created_entry_len += 32; + + /* set up this LFN */ + memset(created_entry, '\0', 32); + memset(&created_entry[1], '\xFF', 10); + memset(&created_entry[14], '\xFF', 12); + memset(&created_entry[28], '\xFF', 4); + created_entry[11] = 0x0F; + + /* specify entry index */ + created_entry[0] = (created_entry_len / 32) - 1; + if (created_entry[0] >= 0x40) + { + err = IMGTOOLERR_BADFILENAME; + goto done; + } + created_entry_pos = 1; + break; + + case 11: + created_entry_pos = 14; + break; + + case 26: + created_entry_pos = 28; + break; + } + + memcpy(&created_entry[created_entry_pos], &buf[i], 2); + created_entry_pos += 2; + } + } + + /* trailing spaces? */ + if (short_char == ' ') + sfn_disposition = std::max(sfn_disposition, SFN_DERIVATIVE); + + if (sfn_disposition == SFN_SUFFICIENT) + { + /* the short filename suffices; remove the LFN stuff */ + memcpy(created_entry, created_entry + created_entry_len - FAT_DIRENT_SIZE, FAT_DIRENT_SIZE); + created_entry_len = FAT_DIRENT_SIZE; + free(created_entry); + created_entry = NULL; + new_created_entry = (uint8_t *) malloc(created_entry_len); + if (!new_created_entry) + { + err = IMGTOOLERR_OUTOFMEMORY; + goto done; + } + created_entry = new_created_entry; + } + else + { + /* need to do finishing touches on the LFN */ + created_entry[0] |= 0x40; + + /* if necessary, mangle the name */ + if (sfn_disposition == SFN_MANGLED) + { + i = 6; + while((i > 0) && isspace(created_entry[created_entry_len - FAT_DIRENT_SIZE + i - 1])) + i--; + created_entry[created_entry_len - FAT_DIRENT_SIZE + i + 0] = '~'; + created_entry[created_entry_len - FAT_DIRENT_SIZE + i + 1] = '1'; + } + fat_calc_dirent_lfnchecksum(created_entry, created_entry_len); + } + +done: + if (err && created_entry) + { + free(created_entry); + created_entry = NULL; + } + *entry = created_entry; + *entry_len = created_entry_len; + return err; +} + + + +static void fat_bump_dirent(imgtool::partition &partition, uint8_t *entry, size_t entry_len) +{ + uint8_t *sfn_entry; + int pos, digit_count, i; + uint32_t digit_place, val = 0; + + sfn_entry = &entry[entry_len - FAT_DIRENT_SIZE]; + + digit_place = 1; + for (pos = 7; (pos >= 0) && isdigit((char) sfn_entry[pos]); pos--) + { + val += (sfn_entry[pos] - '0') * digit_place; + digit_place *= 10; + } + val++; + pos++; + + /* count the digits */ + digit_place = 1; + digit_count = 1; + while(val >= digit_place * 10) + { + digit_count++; + digit_place *= 10; + } + + /* give us some more space, if necessary */ + while ((pos > 0) && ((pos + digit_count) > 8)) + pos--; + + /* have we actually ran out of digits */ + if ((pos + digit_count) > 8) + { + /* extreme degenerate case; simply randomize the filename */ + for (i = 0; i < 6; i++) + sfn_entry[i] = 'A' + (std::rand() % 26); + sfn_entry[6] = '~'; + sfn_entry[7] = '0'; + } + else + { + /* write the tilde, if possible */ + if (pos > 0) + sfn_entry[pos - 1] = '~'; + + /* write out the number */ + while(digit_place > 0) + { + sfn_entry[pos++] = (val / digit_place) + '0'; + val %= digit_place; + digit_place /= 10; + } + } + + /* since we changed the short file name, we need to recalc the checksums + * in the LFN entries */ + fat_calc_dirent_lfnchecksum(entry, entry_len); +} + + + +static imgtoolerr_t fat_lookup_path(imgtool::partition &partition, const char *path, + creation_policy_t create, fat_file *file) +{ + imgtoolerr_t err; + const fat_partition_info *disk_info; + fat_dirent ent; + fat_freeentry_info freeent = { 0, }; + const char *next_path_part; + uint8_t *created_entry = NULL; + size_t created_entry_len = 0; + uint32_t entry_sector_index, entry_sector_offset; + uint32_t parent_first_cluster; + int bumped_sfn; + char sfn[13]; + + disk_info = fat_get_partition_info(partition); + + memset(file, 0, sizeof(*file)); + file->root = 1; + file->directory = 1; + file->filesize = disk_info->root_entries * FAT_DIRENT_SIZE; + + while(*path) + { + if (!file->directory) + { + err = IMGTOOLERR_PATHNOTFOUND; + goto done; + } + + next_path_part = path + strlen(path) + 1; + if (create && (*next_path_part == '\0')) + { + /* this is the last entry, and we are creating a file */ + err = fat_construct_dirent(path, create, &created_entry, &created_entry_len); + if (err) + goto done; + + freeent.required_size = created_entry_len; + freeent.candidate_position = ~0; + freeent.position = ~0; + } + + do + { + err = fat_read_dirent(partition, file, ent, created_entry ? &freeent : NULL); + if (err) + goto done; + + LOG(("fat_lookup_path(): %s/%s: %d\n", ent.short_filename, ent.long_filename, ent.dirent_sector_offset)); + } + while(!ent.eof && core_stricmp(path, ent.short_filename) && core_stricmp(path, ent.long_filename)); + + parent_first_cluster = file->first_cluster; + + if (ent.eof) + { + /* it seems that we have reached the end of this directory */ + if (!created_entry) + { + err = IMGTOOLERR_FILENOTFOUND; + goto done; + } + + if (created_entry_len > FAT_DIRENT_SIZE) + { + /* must ensure uniqueness of the short filename */ + do + { + /* rewind to the beginning of the directory */ + err = fat_seek_file(partition, file, 0); + if (err) + goto done; + + bumped_sfn = false; + fat_canonicalize_sfn(sfn, &created_entry[created_entry_len - FAT_DIRENT_SIZE]); + + do + { + err = fat_read_dirent(partition, file, ent, NULL); + if (err) + goto done; + + if (!core_stricmp(sfn, ent.short_filename)) + { + bumped_sfn = true; + fat_bump_dirent(partition, created_entry, created_entry_len); + fat_canonicalize_sfn(sfn, &created_entry[created_entry_len - FAT_DIRENT_SIZE]); + } + } + while(!ent.eof); + } + while(bumped_sfn); + } + + LOG(("fat_lookup_path(): creating entry; pos=%u length=%u\n", freeent.position, freeent.required_size)); + + err = fat_set_file_size(partition, file, std::max(file->filesize, uint32_t(freeent.position + created_entry_len))); + if (err) + goto done; + + err = fat_seek_file(partition, file, freeent.position); + if (err) + goto done; + + err = fat_write_file(partition, file, created_entry, created_entry_len, NULL); + if (err) + goto done; + + /* we have to do a special seek operation to get the main dirent */ + err = fat_seek_file(partition, file, freeent.position + created_entry_len - FAT_DIRENT_SIZE); + if (err) + goto done; + entry_sector_index = fat_get_filepos_sector_index(partition, file); + entry_sector_offset = file->index % FAT_SECLEN; + + /* build the file struct for the newly created file/directory */ + memset(file, 0, sizeof(*file)); + file->directory = (created_entry[created_entry_len - FAT_DIRENT_SIZE + 11] & 0x10) ? 1 : 0; + file->dirent_sector_index = entry_sector_index; + file->dirent_sector_offset = entry_sector_offset; + } + else + { + /* update the current file */ + memset(file, 0, sizeof(*file)); + file->directory = ent.directory; + file->filesize = ent.filesize; + file->cluster = ent.first_cluster; + file->first_cluster = ent.first_cluster; + file->dirent_sector_index = ent.dirent_sector_index; + file->dirent_sector_offset = ent.dirent_sector_offset; + } + + path = next_path_part; + file->parent_first_cluster = parent_first_cluster; + } + + err = IMGTOOLERR_SUCCESS; + +done: + if (created_entry) + free(created_entry); + return err; +} + + + +static imgtoolerr_t fat_partition_beginenum(imgtool::directory &enumeration, const char *path) +{ + imgtoolerr_t err; + fat_file *file; + + file = (fat_file *) enumeration.extra_bytes(); + + err = fat_lookup_path(enumeration.partition(), path, CREATE_NONE, file); + if (err) + return err; + if (!file->directory) + return IMGTOOLERR_PATHNOTFOUND; + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t fat_partition_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent) +{ + imgtoolerr_t err; + fat_file *file; + fat_dirent fatent; + + file = (fat_file *) enumeration.extra_bytes(); + err = fat_read_dirent(enumeration.partition(), file, fatent, NULL); + if (err) + return err; + + /* copy stuff from the FAT dirent to the Imgtool dirent */ + snprintf(ent.filename, std::size(ent.filename), "%s", fatent.long_filename[0] + ? fatent.long_filename : fatent.short_filename); + ent.filesize = fatent.filesize; + ent.directory = fatent.directory; + ent.eof = fatent.eof; + ent.creation_time = fatent.creation_time; + ent.lastmodified_time = fatent.lastmodified_time; + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t fat_read_bootblock(imgtool::partition &partition, imgtool::stream &stream) +{ + imgtoolerr_t err; + uint8_t block[FAT_SECLEN]; + + err = fat_read_sector(partition, 0, 0, block, sizeof(block)); + if (err) + return err; + + stream.write(block, sizeof(block)); + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t fat_write_bootblock(imgtool::partition &partition, imgtool::stream &stream) +{ + imgtoolerr_t err; + uint8_t block[FAT_SECLEN]; + uint8_t new_block[FAT_SECLEN]; + + if (stream.size() != sizeof(new_block)) + return IMGTOOLERR_UNEXPECTED; + stream.read(new_block, sizeof(new_block)); + + if (new_block[510] != 0x55) + return IMGTOOLERR_UNEXPECTED; + if (new_block[511] != 0xAA) + return IMGTOOLERR_UNEXPECTED; + + /* read current boot sector */ + err = fat_read_sector(partition, 0, 0, block, sizeof(block)); + if (err) + return err; + + /* merge in the new stuff */ + memcpy(&block[ 0], &new_block[ 0], 3); + memcpy(&block[62], &new_block[62], 448); + + /* and write it out */ + err = fat_write_sector(partition, 0, 0, block, sizeof(block)); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t fat_partition_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf) +{ + imgtoolerr_t err; + fat_file file; + size_t bytes_read; + char buffer[1024]; + + /* special case for bootblock */ + if (filename == FILENAME_BOOTBLOCK) + return fat_read_bootblock(partition, destf); + + err = fat_lookup_path(partition, filename, CREATE_NONE, &file); + if (err) + return err; + + if (file.directory) + return IMGTOOLERR_FILENOTFOUND; + + do + { + err = fat_read_file(partition, &file, buffer, sizeof(buffer), &bytes_read); + if (err) + return err; + + destf.write(buffer, bytes_read); + } + while(bytes_read > 0); + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t fat_partition_writefile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts) +{ + imgtoolerr_t err; + fat_file file; + uint32_t bytes_left, len; + char buffer[1024]; + + /* special case for bootblock */ + if (filename == FILENAME_BOOTBLOCK) + return fat_write_bootblock(partition, sourcef); + + err = fat_lookup_path(partition, filename, CREATE_FILE, &file); + if (err) + return err; + + if (file.directory) + return IMGTOOLERR_FILENOTFOUND; + + bytes_left = (uint32_t) sourcef.size(); + + err = fat_set_file_size(partition, &file, bytes_left); + if (err) + return err; + + while(bytes_left > 0) + { + len = (std::min)(bytes_left, sizeof(buffer)); + sourcef.read(buffer, len); + + err = fat_write_file(partition, &file, buffer, len, NULL); + if (err) + return err; + + bytes_left -= len; + } + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t fat_partition_delete(imgtool::partition &partition, const char *filename, unsigned int dir) +{ + imgtoolerr_t err; + fat_file file; + fat_dirent ent; + + err = fat_lookup_path(partition, filename, CREATE_NONE, &file); + if (err) + return err; + if (file.directory != dir) + return IMGTOOLERR_FILENOTFOUND; + + if (dir) + { + err = fat_read_dirent(partition, &file, ent, NULL); + if (err) + return err; + if (!ent.eof) + return IMGTOOLERR_DIRNOTEMPTY; + } + + err = fat_set_file_size(partition, &file, ~0); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t fat_partition_deletefile(imgtool::partition &partition, const char *filename) +{ + return fat_partition_delete(partition, filename, 0); +} + + + +static imgtoolerr_t fat_partition_freespace(imgtool::partition &partition, uint64_t *size) +{ + imgtoolerr_t err; + const fat_partition_info *disk_info; + uint8_t *fat_table; + uint32_t i; + + disk_info = fat_get_partition_info(partition); + + err = fat_load_fat(partition, &fat_table); + if (err) + goto done; + + *size = 0; + for (i = 2; i < disk_info->total_clusters; i++) + { + if (fat_get_fat_entry(partition, fat_table, i) == 0) + *size += disk_info->cluster_size; + } + +done: + if (fat_table) + free(fat_table); + return err; +} + + + +static imgtoolerr_t fat_partition_createdir(imgtool::partition &partition, const char *path) +{ + imgtoolerr_t err; + fat_file file; + uint8_t initial_data[64]; + + err = fat_lookup_path(partition, path, CREATE_DIR, &file); + if (err) + return err; + if (!file.directory) + return IMGTOOLERR_FILENOTFOUND; + + err = fat_set_file_size(partition, &file, sizeof(initial_data)); + if (err) + return err; + + /* set up the two directory entries in all directories */ + memset(initial_data, 0, sizeof(initial_data)); + memcpy(&initial_data[0], ". ", 11); + place_integer_le(initial_data, 11, 1, 0x10); + place_integer_le(initial_data, 26, 2, file.first_cluster); + memcpy(&initial_data[32], ". ", 11); + place_integer_le(initial_data, 43, 1, 0x10); + place_integer_le(initial_data, 58, 2, file.parent_first_cluster); + + err = fat_write_file(partition, &file, initial_data, sizeof(initial_data), NULL); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t fat_partition_deletedir(imgtool::partition &partition, const char *path) +{ + return fat_partition_delete(partition, path, 1); +} + + + +void fat_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case IMGTOOLINFO_INT_INITIAL_PATH_SEPARATOR: info->i = 1; break; + case IMGTOOLINFO_INT_OPEN_IS_STRICT: info->i = 1; break; + case IMGTOOLINFO_INT_SUPPORTS_CREATION_TIME: info->i = 1; break; + case IMGTOOLINFO_INT_SUPPORTS_LASTMODIFIED_TIME: info->i = 1; break; + case IMGTOOLINFO_INT_SUPPORTS_BOOTBLOCK: info->i = 1; break; + case IMGTOOLINFO_INT_PATH_SEPARATOR: info->i = '\\'; break; + case IMGTOOLINFO_INT_ALTERNATE_PATH_SEPARATOR: info->i = '/'; break; + case IMGTOOLINFO_INT_PARTITION_EXTRA_BYTES: info->i = sizeof(fat_partition_info); break; + case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(fat_file); break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break; + case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), "\r\n"); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case IMGTOOLINFO_PTR_CREATE_PARTITION: info->create_partition = fat_partition_create; break; + case IMGTOOLINFO_PTR_OPEN_PARTITION: info->open_partition = fat_partition_open; break; + case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = fat_partition_beginenum; break; + case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = fat_partition_nextenum; break; + case IMGTOOLINFO_PTR_READ_FILE: info->read_file = fat_partition_readfile; break; + case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = fat_partition_writefile; break; + case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = fat_partition_deletefile; break; + case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = fat_partition_freespace; break; + case IMGTOOLINFO_PTR_CREATE_DIR: info->create_dir = fat_partition_createdir; break; + case IMGTOOLINFO_PTR_DELETE_DIR: info->delete_dir = fat_partition_deletedir; break; + } +} diff --git a/src/tools/imgtool/modules/fat.h b/src/tools/imgtool/modules/fat.h new file mode 100644 index 0000000..8e7c60d --- /dev/null +++ b/src/tools/imgtool/modules/fat.h @@ -0,0 +1,6 @@ +// license:BSD-3-Clause +// copyright-holders:Raphael Nabet + +#include "imgtool.h" + +void fat_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info); diff --git a/src/tools/imgtool/modules/hp48.cpp b/src/tools/imgtool/modules/hp48.cpp new file mode 100644 index 0000000..4a008ad --- /dev/null +++ b/src/tools/imgtool/modules/hp48.cpp @@ -0,0 +1,741 @@ +// license:BSD-3-Clause +// copyright-holders:Antoine Mine +/**************************************************************************** + + hp48.cpp + + Memory cards for HP48 SX/GX + + Antoine Mine' 2014 + +***************************************************************************** +*/ + +/* + HP memory cards can contain two kinds of objects: + - attached libraries + - backup objects (encapsulating any kind of object) + + Attached libraries appear with a LIB attribute in imgtool's listing, and in + the global "LIBRARY" menu on the HP48. + Backup objects appear with a BAK attribute in imgtool's listing, and + in the relevant "PORTS" menu on the HP48. + + Currently, we only support storing host files into backup objects and + retreiving backup objects into files. + For attached library objects in the memory card, we only show their name, but + cannot retrieve their contents. + + To install a library contained in a host file, copy it with imgtool to + the card; it creates a backup object visible in the "PORTS" menu; + then evalue the object, store it into some port (e.g, 0 STO), + turn off the HP48 and back on; the library should now be attached and + visible in the "LIBRARY" menu. + (The process is similar to installing a library from a file you get + from the serial interface.) +*/ + + +/* + HP memory cards have size from 32 KB to 4096 KB. + However, cards with size greater than 128 KB are seen as multiple ports of + size 128 KB on the HP48 GX. In imgtool, we see them as distinct 128 KB + partitions. + Unfortunately, at the moment, partitions do not seem to be supported in the + command-line tool... + */ + + +/* + Here is the format of backup objects (addresses are in 4-bit nibbles): + + offset field size value + 0 prolog 5 02B62 prolog for overall backup object + 5 size 5 len total size in nibble without prolog + 10 name length 2 nbcar object name, in characters + 12 name 2*nbcar - + 12+2*nbcar name length 2 nbcar same value as name length at 10 + 14+2*nbcar object - - + len-5 prolog 5 02911 prolog for inlined system integer + len - 1 0 + len+1 CRC 4 - CRC from 5 to len + + total length: len+4 + + i.e., the backup object is a container containing the object name + and two objects: the backuped object itself and a system integer + that contains the CRC. + + HP48 host files start with a "HPHP48-X" header (where X can be any + single letter, which denotes a ROM revision and, implicitly, whether + the program is for the S/SX or the G/GX family). + When storing a file to the memory card, we strip the header and + embed the object into a backup object with the correct CRC. + When copying from the memory card to a host file, we extract the object + from the backup container, stripping its CRC, and add the HPHP48-X header. + + */ + + + + + +/***************************************************************************** + Includes +*****************************************************************************/ + +#include "imgtool.h" + +#include "opresolv.h" + + + +/***************************************************************************** + Data structures +*****************************************************************************/ + + +struct hp48_card +{ + imgtool::stream *stream; + int modified; + + /* size, in bytes of card data: from 32 KB to 4 MB */ + int size; + + /* we store each nibble (4-bit) into its own byte, for simpler addressing; + hence, data has 2*size + */ + uint8_t* data; + +}; + +struct hp48_partition +{ + /* pointer to the beginning of the partition inside the hp48_card */ + uint8_t* data; + + /* size, in bytes (128 KB or less) */ + int size; + +}; + +struct hp48_directory +{ + int pos; +}; + + +#define PROLOG_BACKUP 0x02B62 +#define PROLOG_LIBRARY 0x02B40 +#define PROLOG_SYSINT 0x02911 + +/* memory cards are composed solely of libraries and backup objects */ +#define IS_OBJECT(prolog) ((prolog) == PROLOG_LIBRARY || (prolog) == PROLOG_BACKUP) + +/* host files begin with this prefix (up to the last letter) */ +static const char* hp48_prefix = "HPHP48-R"; + + + +OPTION_GUIDE_START( hp48_create_optionguide ) + OPTION_INT('S', "size", "Size in KB" ) +OPTION_GUIDE_END + +/* size in KB, 128 KB being the default */ +static const char hp48_create_optionspec[] = "S32/64/[128]/256/512/1024/2048/4096"; + + + +/***************************************************************************** + Utility functions +*****************************************************************************/ + + +static hp48_card *get_hp48_card(imgtool::image &image) +{ + return (hp48_card*) image.extra_bytes(); +} + +/* byes to nibbles */ +static void unpack(uint8_t* dst, uint8_t* src, int nsize) +{ + int i; + + if ( nsize & 1 ) + { + dst[nsize-1] = src[nsize/2] & 0xf; + } + + for ( i = nsize/2-1; i >= 0; i-- ) + { + dst[2*i+1] = src[i] >> 4; + dst[2*i ] = src[i] & 0xf; + } +} + +/* nibbles to bytes */ +static void pack(uint8_t* dst, uint8_t* src, int nsize) +{ + int i; + + for ( i = 0 ; i < nsize/2; i++ ) + { + dst[i] = (src[2*i] & 0xf) | (src[2*i+1] << 4); + } + + if ( nsize & 1 ) + { + dst[nsize/2] = src[nsize-1] & 0xf; + } + } + + +static int read20(uint8_t* data) +{ + return data[0] | (data[1] << 4) | (data[2] << 8) | (data[3] << 12) | (data[4] << 16); +} + +static int read8(uint8_t* data) +{ + return data[0] | (data[1] << 4); +} + +static void readstring(char* dst, uint8_t* data, int nb) +{ + int i; + for ( i = 0; i < nb; i++ ) + { + dst[i] = read8( data + 2*i ); + } + dst[nb] = 0; +} + +static void write20(uint8_t* data, int v) +{ + data[0] = v & 0xf; + data[1] = (v >> 4) & 0xf; + data[2] = (v >> 8) & 0xf; + data[3] = (v >> 12) & 0xf; + data[4] = (v >> 16) & 0xf; +} + +static void write8(uint8_t* data, int v) +{ + data[0] = v & 0xf; + data[1] = (v >> 4) & 0xf; +} + +static void writestring(uint8_t* data, const char* str, int nb) +{ + int i; + for ( i = 0; i < nb; i++ ) + { + write8( data + 2*i, str[i] ); + } +} + + +/* go to the end, return its offset */ +static int find_end(hp48_partition* p) +{ + int pos = 0; + while (1) + { + if ( pos + 10 > 2*p->size) break; + + int prolog = read20( p->data + pos ); + if ( !IS_OBJECT( prolog )) break; + + pos += read20( p->data + pos + 5 ) + 5; + } + + if ( pos > 2*p->size ) pos = 2*p->size; + return pos; +} + + +/* find the backup object with the given name, returns its offset or -1 (not found) */ +static int find_file(hp48_partition* p, const char* filename, int *ptotalsize, int* pstart, int* pcontentsize) +{ + uint8_t* data = p->data; + int pos = 0; + + /* find file */ + while (1) + { + if ( pos + 10 > 2*p->size) return -1; + + /* get prolog */ + int prolog = read20( data+pos ); + if ( !IS_OBJECT(prolog)) return -1; + + /* get size */ + int totalsize = read20( data+pos+5 ); + if ( totalsize < 14) return -1; + if ( pos + 5 + totalsize > 2*p->size ) return -1; + + if ( prolog == PROLOG_BACKUP ) + { + /* get name */ + int namelen = read8( data + pos + 10 ); + char name[257]; + if ( 9 + 2*namelen > totalsize ) return -1; + readstring( name, data + pos + 12, namelen ); + + /* check name */ + if ( !strcmp( name, filename ) ) + { + /* found! */ + if ( ptotalsize ) *ptotalsize = totalsize; + if ( pstart ) *pstart = pos + 14 + 2*namelen; + if ( pcontentsize ) *pcontentsize = totalsize - (9 + 2*namelen); + return pos; + } + else + { + /* skip */ + pos += totalsize + 5; + } + } + else + { + /* skip */ + pos += totalsize + 5; + } + + } + + // never executed + //return -1; +} + + +/* CRC computing. + This is the same CRC that is computed by the HP48 hardware. + */ +static uint16_t crc(uint8_t* data, int len) +{ + uint16_t crc = 0; + int i; + + for ( i = 0; i < len; i++ ) + { + crc = (crc >> 4) ^ (((crc ^ data[i]) & 0xf) * 0x1081); + } + + return crc; +} + + + +/***************************************************************************** + Imgtool functions +*****************************************************************************/ + + +static imgtoolerr_t hp48_open(imgtool::image &img, imgtool::stream::ptr &&stream) +{ + hp48_card* c = get_hp48_card(img); + int size = stream->size(); + + /* check that size is a power of 2 between 32 KB and 4 MG */ + if ( (size < 32 * 1024) || + (size > 4 * 1024 * 1024) || + (size & (size-1)) ) + { + return IMGTOOLERR_CORRUPTIMAGE; + } + + /* store info */ + c->stream = stream.get(); + c->modified = 0; + c->size = size; + c->data = (uint8_t*) malloc( 2 * size ); + if ( !c->data ) + { + return IMGTOOLERR_READERROR; + } + + /* fully load image */ + c->stream->seek(0, SEEK_SET); + if (c->stream->read(c->data, size) < size) + { + return IMGTOOLERR_READERROR; + } + unpack( c->data, c->data, 2 * size ); + + c->stream = stream.release(); + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t hp48_create(imgtool::image &img, + imgtool::stream::ptr &&stream, + util::option_resolution *opts) +{ + hp48_card* c = get_hp48_card(img); + int size; + + size = opts->lookup_int('S'); + + c->stream = stream.get(); + c->modified = 1; + c->size = size * 1024; + c->data = (uint8_t*) malloc( 2 * c->size ); + if ( !c->data ) + { + return IMGTOOLERR_READERROR; + } + + /* zeroing the image seems fine */ + memset( c->data, 0, 2 * c->size ); + + c->stream = stream.release(); + return IMGTOOLERR_SUCCESS; +} + + + +static void hp48_close(imgtool::image &img) +{ + hp48_card* c = get_hp48_card(img); + + if ( c->modified ) + { + /* save image */ + pack( c->data, c->data, 2 * c->size ); + c->stream->seek(0, SEEK_SET); + c->stream->write(c->data, c->size); + } + + /* clean up */ + free( c->data ); + delete c->stream; +} + + + +/* each 128 KB chunk is a distinct partition */ +#define MAX_PORT_SIZE (128*1024) + +void hp48_partition_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info); + +static imgtoolerr_t hp48_list_partitions(imgtool::image &img, std::vector &partitions) +{ + hp48_card* c = get_hp48_card(img); + + int i; + for (i = 0; i * MAX_PORT_SIZE < c->size ; i++) + { + // offset and size in bytes + uint64_t base_block = i * MAX_PORT_SIZE; + uint64_t block_count = std::min((uint64_t)c->size - base_block, (uint64_t)MAX_PORT_SIZE); + + // append the partition + partitions.emplace_back(hp48_partition_get_info, base_block, block_count); + } + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t hp48_open_partition(imgtool::partition &part, + uint64_t first_block, uint64_t block_count) +{ + imgtool::image &img(part.image()); + hp48_card* c = get_hp48_card(img); + hp48_partition* p = (hp48_partition*) part.extra_bytes(); + + if ( first_block + block_count > c->size ) + return IMGTOOLERR_INVALIDPARTITION; + + /* store partition position */ + p->data = c->data + first_block; + p->size = block_count; + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t hp48_beginenum(imgtool::directory &enumeration, const char *path) +{ + hp48_directory* d = (hp48_directory*) enumeration.extra_bytes(); + + d->pos = 0; + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t hp48_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent) +{ + imgtool::partition &part(enumeration.partition()); + //imgtool::image &img(part.image()); + //hp48_card* c = get_hp48_card(img); + hp48_partition* p = (hp48_partition*) part.extra_bytes(); + hp48_directory* d = (hp48_directory*) enumeration.extra_bytes(); + + uint8_t* data = p->data; + int pos = d->pos; + + if ( pos < 0 || pos+12 > 2*p->size ) + { + ent.eof = 1; + return IMGTOOLERR_SUCCESS; + } + + int prolog = read20( data+pos ); + + if ( IS_OBJECT(prolog) ) + { + pos += 5; + int totalsize = read20( data+pos ); + pos += 5; + + int namelen = read8( data+pos ); + pos += 2; + if ( (pos + 2*namelen > 2*p->size) || + (namelen >= sizeof(ent.filename)) ) + { + ent.eof = 1; + return IMGTOOLERR_CORRUPTFILE; + } + readstring( ent.filename, data+pos, namelen ); + + /* compute size in bytes, removing name, length & CRC fields */ + ent.filesize = ((totalsize - 19 - 2*namelen) + 1) / 2; + + switch (prolog) + { + case PROLOG_LIBRARY: strncpy( ent.attr, "LIB", sizeof(ent.attr) ); break; + case PROLOG_BACKUP: strncpy( ent.attr, "BAK", sizeof(ent.attr) ); break; + default: strncpy( ent.attr, "?", sizeof(ent.attr) ); + } + + d->pos = d->pos + totalsize + 5; + } + else + { + /* 0 or unknown object => end */ + ent.eof = 1; + } + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t hp48_freespace(imgtool::partition &part, uint64_t *size) +{ + //imgtool::image &img(part.image()); + //hp48_card* c = get_hp48_card(img); + hp48_partition* p = (hp48_partition*) part.extra_bytes(); + + *size = p->size - (find_end(p)+1)/2; + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t hp48_readfile(imgtool::partition &part, + const char *filename, + const char *fork, + imgtool::stream &destf) +{ + //imgtool::image &img(part.image()); + //hp48_card* c = get_hp48_card(img); + hp48_partition* p = (hp48_partition*) part.extra_bytes(); + + /* find entry */ + int totalsize, start, size; + int pos = find_file(p, filename, &totalsize, &start, &size); + if ( pos == -1 ) + { + return IMGTOOLERR_FILENOTFOUND; + } + + /* CRC check */ + uint16_t objcrc = read20( p->data + pos + totalsize ) >> 4; + uint16_t mycrc = crc( p->data + pos + 5, totalsize - 4); + if ( objcrc != mycrc ) + { + return IMGTOOLERR_CORRUPTIMAGE; + } + size -= 10; + + /* save header */ + destf.write(hp48_prefix, 8); + + /* save contents to host file */ + int bytesize = (size + 1) / 2; + uint8_t* buf = (uint8_t*) malloc( bytesize ); + if (!buf) + { + return IMGTOOLERR_FILENOTFOUND; + } + pack( buf, p->data + start, size ); + destf.write(buf, bytesize); + free( buf ); + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t hp48_deletefile(imgtool::partition &part, + const char *filename) +{ + imgtool::image &img(part.image()); + hp48_card* c = get_hp48_card(img); + hp48_partition* p = (hp48_partition*) part.extra_bytes(); + + /* find entry */ + int totalsize; + int pos = find_file(p, filename, &totalsize, NULL, NULL ); + if ( pos == -1 ) + { + return IMGTOOLERR_FILENOTFOUND; + } + + /* move */ + totalsize += 5; + memmove( p->data+pos, p->data+pos+totalsize, 2*p->size-(pos+totalsize) ); + memset( p->data + 2*p->size-totalsize, 0, totalsize); + c->modified = 1; + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t hp48_writefile(imgtool::partition &part, + const char *filename, + const char *fork, + imgtool::stream &sourcef, + util::option_resolution *opts) +{ + imgtool::image &img(part.image()); + hp48_card* c = get_hp48_card(img); + hp48_partition* p = (hp48_partition*) part.extra_bytes(); + + /* check header */ + char head[8]; + sourcef.read(head, 8); + if ( memcmp( head, hp48_prefix, 7) ) + { + return IMGTOOLERR_READERROR; + } + + /* ensure that the file does not exist */ + /* TODO: resize the existing file instead, to keep it in place? */ + hp48_deletefile( part, filename ); + + /* goto end */ + //uint8_t* data = p->data; + int pos = find_end(p); + + int len = strlen( filename ); + if ( len > 255 ) len = 255; + + /* check size */ + int filesize = sourcef.size() - 8; + if ( pos + 2*filesize + 24 + 2*len > 2 * p->size ) + { + return IMGTOOLERR_NOSPACE; + } + + /* load file */ + uint8_t* buf = (uint8_t*) malloc( filesize ); + if ( !buf ) return IMGTOOLERR_NOSPACE; + sourcef.read(buf, filesize); + + /* store backup object */ + int org = pos; + int totalsize = 2*filesize + 19 + 2*len; + write20( p->data+pos, PROLOG_BACKUP ); + pos +=5; + write20( p->data+pos, totalsize ); + pos +=5; + write8( p->data+pos, len ); + pos += 2; + writestring( p->data+pos, filename, len ); + pos += 2*len; + write8( p->data+pos, len ); + pos += 2; + unpack( p->data+pos, buf, 2*filesize ); + pos += 2*filesize; + + /* store crc */ + write20( p->data+pos, PROLOG_SYSINT ); + pos += 5; + p->data[pos] = 0; + write20( p->data+pos, crc(p->data+org+5, totalsize-4) << 4 ); + + free(buf); + + c->modified = 1; + + return IMGTOOLERR_SUCCESS; +} + + + + +/***************************************************************************** + Imgtool module declaration +*****************************************************************************/ + + + +void hp48_partition_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "hp48"); break; + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "HP48 SX/GX memory card"); break; + case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break; + case IMGTOOLINFO_STR_FILE_EXTENSIONS: strcpy(info->s = imgtool_temp_str(), "crd"); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case IMGTOOLINFO_PTR_OPEN_PARTITION: info->open_partition = hp48_open_partition; break; + case IMGTOOLINFO_PTR_CLOSE: info->close = hp48_close; break; + case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = hp48_beginenum; break; + case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = hp48_nextenum; break; + case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = hp48_freespace; break; + case IMGTOOLINFO_PTR_READ_FILE: info->read_file = hp48_readfile; break; + case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = hp48_writefile; break; + case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = hp48_deletefile; break; + + case IMGTOOLINFO_INT_PARTITION_EXTRA_BYTES: info->i = sizeof(hp48_partition); break; + case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(hp48_directory); break; + + } +} + +void hp48_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "hp48"); break; + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "HP48 SX/GX memory card"); break; + case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break; + case IMGTOOLINFO_STR_FILE_EXTENSIONS: strcpy(info->s = imgtool_temp_str(), "crd"); break; + case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC: strcpy(info->s = imgtool_temp_str(), hp48_create_optionspec); break; + + case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE: info->createimage_optguide = &hp48_create_optionguide; break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case IMGTOOLINFO_PTR_OPEN: info->open = hp48_open; break; + case IMGTOOLINFO_PTR_CREATE: info->create = hp48_create; break; + case IMGTOOLINFO_PTR_CLOSE: info->close = hp48_close; break; + case IMGTOOLINFO_PTR_LIST_PARTITIONS: info->list_partitions = hp48_list_partitions; break; + + case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(hp48_card); break; + } +} diff --git a/src/tools/imgtool/modules/hp85_tape.cpp b/src/tools/imgtool/modules/hp85_tape.cpp new file mode 100644 index 0000000..14103fc --- /dev/null +++ b/src/tools/imgtool/modules/hp85_tape.cpp @@ -0,0 +1,1164 @@ +// license:BSD-3-Clause +// copyright-holders:F. Ulivi +/********************************************************************* + + hp85_tape.cpp + + HP-85 tape format + +*********************************************************************/ +#include "imgtool.h" + +#include "formats/hti_tape.h" +#include "formats/imageutl.h" + +#include "ioprocs.h" +#include "opresolv.h" + +#include +#include + + +// Constants +static constexpr unsigned CHARS_PER_FNAME = 6; // Characters in a filename +static constexpr unsigned CHARS_PER_EXT = 4; // Characters in (simulated) file extension +static constexpr unsigned CHARS_PER_FNAME_EXT = CHARS_PER_FNAME + 1 + CHARS_PER_EXT; // Characters in filename + extension +static constexpr unsigned MAX_FILE_NO = 42; // Maximum file # +static constexpr unsigned MAX_RECORD_SIZE = 256; // Maximum size of record body +static constexpr unsigned MAX_N_RECORDS = 436 * 2 - 5; // Total user-available capacity of tape (in physical records) +static constexpr unsigned DIR_RECORDS = 4; // Records reserved to directory + +// Words stored on tape +using tape_word_t = hti_format_t::tape_word_t; + +// Tape position, 1 unit = 1 inch / (968 * 1024) +using tape_pos_t = hti_format_t::tape_pos_t; + +// File number [0..42] +typedef uint8_t file_no_t; + +// Minimum gap size to detect IFGs: 1.25" +static constexpr tape_pos_t MIN_IFG_SIZE = 1.25 * hti_format_t::ONE_INCH_POS; + +// Minimum gap size to detect IRGs: 1/32 " +static constexpr tape_pos_t MIN_IRG_SIZE = hti_format_t::ONE_INCH_POS / 32; + +// Formatted size of IFGs: 2.5" +static constexpr tape_pos_t FMT_IFG_SIZE = 2.5 * hti_format_t::ONE_INCH_POS; + +// Formatted size of IRGs: 1" +static constexpr tape_pos_t FMT_IRG_SIZE = hti_format_t::ONE_INCH_POS; + +// Formatted size of records: 2.67" +static constexpr tape_pos_t FMT_REC_SIZE = 2.67 * hti_format_t::ONE_INCH_POS; + +// Starting position on tracks: 74" from beginning of tape +static constexpr tape_pos_t TRACK_START = 74 * hti_format_t::ONE_INCH_POS; + +// Sync word: 0x0001 +static constexpr tape_word_t SYNC_WORD = 1; + +// Name of erased (NULL) files +static const char *const NULL_FILENAME = "==NULL=="; + +// Masks of bits in file type +static constexpr uint8_t FT_NEXT_AV_MASK = 0x80; // Next available slot +static constexpr uint8_t FT_NULL_FILE_MASK = 0x40; // Erased file +static constexpr uint8_t FT_PROG_MASK = 0x20; // PROG (BASIC) file +static constexpr uint8_t FT_DATA_MASK = 0x10; // DATA file +static constexpr uint8_t FT_BPGM_MASK = 0x08; // BPGM file +static constexpr uint8_t FT_WP_MASK = 0x02; // Write protection +static constexpr uint8_t FT_HIDDEN_MASK = 0x01; // Hidden file + +/******************************************************************************** + * Directory entries + ********************************************************************************/ +struct dir_entry_85 { + uint8_t filename[ CHARS_PER_FNAME ]; // Filename (left justified, space-padded on the right) + file_no_t file_no; // File # + uint8_t filetype; // File type + uint16_t n_recs; // Physical records + uint16_t record_len; // Length of logical records +}; + +/******************************************************************************** + * Tape image + ********************************************************************************/ +class tape_image_85 { +public: + tape_image_85(void); + + bool is_dirty(void) const { return dirty; } + + void format_img(void); + + imgtoolerr_t load_from_file(imgtool::stream *stream); + typedef std::vector sif_file_t; + typedef std::unique_ptr sif_file_ptr_t; + bool load_sif_file(file_no_t file_no , sif_file_t& out); + imgtoolerr_t save_to_file(imgtool::stream *stream); + + bool get_dir_entry(unsigned idx , const dir_entry_85*& entry) const; + bool find_file(const char *filename , bool ignore_ext , unsigned& idx) const; + bool alloc_new_file(dir_entry_85*& entry , sif_file_ptr_t&& file_data); + bool delete_dir_entry(unsigned idx); + bool finalize_allocation(); + static void get_filename_and_ext(const dir_entry_85& ent , bool inc_ext , char *out); + static void split_filename_and_ext(const char *filename , char *fname , char *ext); + +private: + // Tape image + hti_format_t image; + bool dirty; + // Directory + std::vector dir; + // Content + std::vector content; + // First file on track 1 + file_no_t file_track_1; + // No. of first record on track 1 + uint16_t record_track_1; + + bool dec_rec_header(const tape_word_t *hdr , file_no_t& file_no , uint16_t& rec_no , bool& has_body , unsigned& body_len); + bool load_whole_tape(); + static tape_word_t checksum(const tape_word_t *block , unsigned block_len); + bool decode_dir(const sif_file_t& file_0); + void encode_dir(sif_file_t& file_0) const; + void save_words(unsigned track , tape_pos_t& pos , const tape_word_t *block , unsigned block_len); + void save_sif_file(unsigned& track , tape_pos_t& pos , file_no_t file_no , const sif_file_t& in); + static bool filename_char_check(uint8_t c); + static bool filename_check(const uint8_t *filename); +}; + +/******************************************************************************** + * Image state + ********************************************************************************/ +typedef struct { + imgtool::stream *stream; + tape_image_85 *img; +} tape_state_t; + +/******************************************************************************** + * Directory enumeration + ********************************************************************************/ +typedef struct { + unsigned dir_idx; +} dir_state_t; + +/******************************************************************************** + * Internal functions + ********************************************************************************/ +tape_image_85::tape_image_85(void) + : dirty(false) +{ + image.set_image_format(hti_format_t::HTI_DELTA_MOD_16_BITS); +} + +void tape_image_85::format_img(void) +{ + // Create an empty directory + dir.clear(); + content.clear(); + + // Allocate space + finalize_allocation(); +} + +imgtoolerr_t tape_image_85::load_from_file(imgtool::stream *stream) +{ + auto io = imgtool::stream_read(*stream, 0); + if (!io || !image.load_tape(*io)) { + return IMGTOOLERR_READERROR; + } + io.reset(); + + // Prevent track boundary crossing when reading directory + file_track_1 = 0; + + // Get directory (file #0) + sif_file_t file_0; + if (!load_sif_file(0 , file_0)) { + return IMGTOOLERR_CORRUPTDIR; + } + + if (!decode_dir(file_0)) { + return IMGTOOLERR_CORRUPTDIR; + } + + dirty = false; + + return IMGTOOLERR_SUCCESS; +} + +bool tape_image_85::load_sif_file(file_no_t file_no , sif_file_t& out) +{ + unsigned track; + unsigned gaps_to_go; + + // What track is the file on? + if (!file_track_1 || file_no < file_track_1 || (file_no == file_track_1 && record_track_1)) { + track = 0; + gaps_to_go = file_no + 1; + } else { + track = 1; + gaps_to_go = file_no - file_track_1; + if (!record_track_1) { + gaps_to_go++; + } + } + tape_pos_t pos = 0; + + hti_format_t::track_iterator_t it; + while (gaps_to_go--) { + // Search for IFG + if (!image.next_data(track , pos , true , false , it)) { + return false; + } + pos = it->first; + if (!image.next_gap(track , pos , true , MIN_IFG_SIZE)) { + return false; + } + } + + // Get back to the start of record before the gap + image.next_data(track , pos , false , false , it); + pos = it->first; + bool direction = false; + + uint16_t expected_rec_no = 0; + + out.clear(); + + while (true) { + image.next_gap(track , pos , direction , MIN_IRG_SIZE); + // Read record header + if (!image.next_data(track , pos , true , false , it)) { + break; + } + unsigned bit_idx = 15; + if (!image.sync_with_record(track , it , bit_idx)) { + // Couldn't align + return false; + } + + // 0 File word + // 1 Record word + // 2 Length word + // 3 Checksum + tape_word_t hdr[ 4 ]; + for (unsigned i = 0; i < 4; i++) { + auto res = image.next_word(track , it , bit_idx , hdr[ i ]); + if (res != hti_format_t::ADV_CONT_DATA) { + return false; + } + } + if (checksum(&hdr[ 0 ] , 3) != hdr[ 3 ]) { + return false; + } + + file_no_t hdr_file_no; + uint16_t hdr_rec_no; + bool hdr_has_body; + unsigned hdr_body_len; + + if (!dec_rec_header(&hdr[ 0 ] , hdr_file_no , hdr_rec_no , hdr_has_body , hdr_body_len)) { + return false; + } + + if (hdr_file_no != file_no) { + break; + } + if (!hdr_has_body || !hdr_body_len) { + return true; + } + if (hdr_rec_no != expected_rec_no) { + return false; + } + + tape_word_t body[ MAX_RECORD_SIZE / 2 + 1 ]; + unsigned word_no = (hdr_body_len + 1) / 2 + 1; + for (unsigned i = 0; i < word_no; i++) { + auto res = image.next_word(track , it , bit_idx , body[ i ]); + if (res != hti_format_t::ADV_CONT_DATA) { + return false; + } + } + if (checksum(&body[ 0 ] , word_no - 1) != body[ word_no - 1 ]) { + return false; + } + for (unsigned i = 0; i < hdr_body_len; i++) { + tape_word_t tmp = body[ i / 2 ]; + out.push_back((uint8_t)(tmp >> 8)); + i++; + if (i < hdr_body_len) { + out.push_back((uint8_t)(tmp & 0xff)); + } + } + + // Move to next record (possibly crossing into track 1) + expected_rec_no++; + if (file_no == file_track_1 && expected_rec_no == record_track_1) { + track = 1; + pos = 0; + } else { + pos = it->first; + } + direction = true; + + if (hdr_body_len < MAX_RECORD_SIZE) { + break; + } + } + + return expected_rec_no != 0; +} + +bool tape_image_85::load_whole_tape() +{ + content.clear(); + + for (const auto& i : dir) { + sif_file_ptr_t file; + if (!(i.filetype & FT_NULL_FILE_MASK)) { + file = std::make_unique(); + if (!load_sif_file(i.file_no, *file)) { + return false; + } + } + content.push_back(std::move(file)); + } + + return true; +} + +bool tape_image_85::dec_rec_header(const tape_word_t *hdr , file_no_t& file_no , uint16_t& rec_no , bool& has_body , unsigned& body_len) +{ + if ((hdr[ 0 ] & 0x6000) != 0x2000 || + (hdr[ 0 ] & 0x07ff) > MAX_FILE_NO || + (hdr[ 1 ] & 0xf000) != 0x1000) { + return false; + } + file_no = (file_no_t)(hdr[ 0 ] & 0xff); + rec_no = (uint16_t)(hdr[ 1 ] & 0xfff); + has_body = (hdr[ 0 ] & 0x1800) != 0; + + bool has_file_id = (hdr[ 0 ] & 0x8000) != 0; + if (has_file_id != (rec_no == 0)) { + return false; + } + + if (has_body) { + if ((hdr[ 2 ] & 0xff00) != 0xff00) { + return false; + } + body_len = hdr[ 2 ] & 0xff; + if (body_len) { + body_len++; + } + } else { + body_len = 0; + } + + return true; +} + +tape_word_t tape_image_85::checksum(const tape_word_t *block , unsigned block_len) +{ + tape_word_t csum = 0; + for (unsigned i = 0; i < block_len; i++) { + csum += *block++; + } + return csum & 0xffff; +} + +imgtoolerr_t tape_image_85::save_to_file(imgtool::stream *stream) +{ + sif_file_t file_0; + + encode_dir(file_0); + + unsigned track = 0; + tape_pos_t pos = TRACK_START; + + image.clear_tape(); + + save_sif_file(track , pos , 0 , file_0); + + for (auto i = dir.cbegin(); i != dir.cend(); i++) { + file_no_t file_no = i - dir.cbegin(); + save_sif_file(track , pos , file_no + 1 , *content[ file_no ]); + } + + // Empty file at the end + file_0.clear(); + save_sif_file(track , pos , dir.size() + 1 , file_0); + + image.save_tape(*imgtool::stream_read_write(*stream, 0)); + + return IMGTOOLERR_SUCCESS; +} + +bool tape_image_85::get_dir_entry(unsigned idx , const dir_entry_85*& entry) const +{ + if (idx >= dir.size()) { + return false; + } else { + entry = &dir[ idx ]; + return true; + } +} + +bool tape_image_85::find_file(const char *filename , bool ignore_ext , unsigned& idx) const +{ + if (strcmp(filename , NULL_FILENAME) == 0) { + return false; + } + + char fname[ CHARS_PER_FNAME_EXT + 1 ]; + char ext[ CHARS_PER_EXT + 1 ]; + + split_filename_and_ext(filename, fname, ext); + + bool has_ext = !ignore_ext && *ext != '\0'; + + if (has_ext) { + strcat(fname , "."); + strcat(fname , ext); + } + + for (auto i = dir.cbegin(); i < dir.cend(); i++) { + if (i->filetype & FT_NULL_FILE_MASK) { + continue; + } + char full_fname[ CHARS_PER_FNAME_EXT + 1 ]; + + get_filename_and_ext(*i, has_ext, full_fname); + + if (strcmp(fname , full_fname) == 0) { + idx = i - dir.cbegin(); + return true; + } + } + + return false; +} + +bool tape_image_85::alloc_new_file(dir_entry_85*& entry , sif_file_ptr_t&& file_data) +{ + if (file_data->size() > MAX_N_RECORDS * MAX_RECORD_SIZE) { + // File bigger than tape capacity + return false; + } + + if (!load_whole_tape()) { + return false; + } + + dir_entry_85 new_entry; + memset(&new_entry , 0 , sizeof(new_entry)); + + unsigned idx = MAX_FILE_NO; + for (auto i = dir.cbegin(); i != dir.cend(); i++) { + if (i->filetype & FT_NULL_FILE_MASK) { + idx = i - dir.cbegin(); + break; + } + } + if (idx >= MAX_FILE_NO) { + idx = dir.size(); + if (idx >= MAX_FILE_NO) { + return false; + } + dir.push_back(new_entry); + content.push_back(std::make_unique()); + } else { + dir[ idx ] = new_entry; + } + entry = &dir[ idx ]; + + content[ idx ] = std::move(file_data); + + return true; +} + +bool tape_image_85::delete_dir_entry(unsigned idx) +{ + if (idx < dir.size()) { + dir[ idx ].filetype = FT_NULL_FILE_MASK; + return load_whole_tape(); + } else { + return false; + } +} + +bool tape_image_85::finalize_allocation() +{ + tape_pos_t hole_pos = hti_format_t::next_hole(TRACK_START , true); + + for (unsigned i = 0; i < dir.size(); i++) { + if (dir[ i ].filetype & FT_NULL_FILE_MASK) { + dir.erase(dir.begin() + i); + content.erase(content.begin() + i); + i--; + } + } + + unsigned track = 0; + // Position where file #1 starts (that is, after directory) + tape_pos_t pos = TRACK_START + FMT_REC_SIZE * DIR_RECORDS + FMT_IRG_SIZE * (DIR_RECORDS - 1) + FMT_IFG_SIZE; + + file_track_1 = 0; + record_track_1 = 0; + + for (auto i = dir.begin(); i != dir.end(); i++) { + file_no_t file_no = i - dir.begin(); + unsigned recs = (content[ file_no ]->size() + MAX_RECORD_SIZE - 1) / MAX_RECORD_SIZE; + if (!recs) { + // Always at least 1 record + recs = 1; + } + i->file_no = file_no + 1; + i->n_recs = recs; + // Size of file on tape: "recs" records, 1 IFG between record 0 and 1, IRGs between all + // other records + tape_pos_t file_size = FMT_IFG_SIZE + recs * FMT_REC_SIZE + (recs - 1) * FMT_IRG_SIZE; + tape_pos_t rec1_start = pos + FMT_REC_SIZE + FMT_IFG_SIZE; + tape_pos_t next_track_pos = 0; + if (pos <= hole_pos && hole_pos < rec1_start) { + // Hole on record #0 + file_track_1 = file_no + 1; + record_track_1 = 1; + next_track_pos = rec1_start; + } else if (rec1_start <= hole_pos && hole_pos < (pos + file_size)) { + // Hole in this file, records from 1 on + file_track_1 = file_no + 1; + record_track_1 = (hole_pos - rec1_start) / (FMT_REC_SIZE + FMT_IRG_SIZE) + 2; + next_track_pos = rec1_start + (record_track_1 - 1) * (FMT_REC_SIZE + FMT_IRG_SIZE); + } + if (next_track_pos) { + // Move to next track + if (++track >= 2) { + // Out of space + return false; + } + pos = pos + file_size - next_track_pos + TRACK_START; + } else { + pos += file_size; + } + } + dirty = true; + return true; +} + +typedef struct { + uint8_t filetype_mask; + const char *ext; +} file_attr_t; + +static const file_attr_t filetype_table[] = { + { FT_PROG_MASK , "PROG" }, + { FT_DATA_MASK , "DATA" }, + { FT_BPGM_MASK , "BPGM" } +}; + +void tape_image_85::get_filename_and_ext(const dir_entry_85& ent , bool inc_ext , char *out) +{ + if (ent.filetype & FT_NULL_FILE_MASK) { + // Empty directory slot + strcpy(out , NULL_FILENAME); + } else { + const uint8_t *s = &ent.filename[ 0 ]; + while (*s != '\0' && *s != ' ' && (s - &ent.filename[ 0 ]) < CHARS_PER_FNAME) { + *out++ = *s++; + } + *out = '\0'; + + // Decode filetype + if (inc_ext) { + const char *ext = nullptr; + for (const auto& i : filetype_table) { + if (ent.filetype & i.filetype_mask) { + ext = i.ext; + break; + } + } + if (ext != nullptr) { + strcat(out , "."); + strcat(out , ext); + } + } + } +} + +void tape_image_85::split_filename_and_ext(const char *filename , char *fname , char *ext) +{ + char *fname_fence = fname + CHARS_PER_FNAME; + + while (fname < fname_fence && *filename != '\0' && *filename != '.') { + *fname++ = *filename++; + } + + *fname = '\0'; + + while (*filename != '\0' && *filename != '.') { + filename++; + } + + if (*filename == '\0') { + *ext = '\0'; + } else { + filename++; + strncpy(ext , filename , CHARS_PER_EXT); + ext[ CHARS_PER_EXT ] = '\0'; + } +} + +bool tape_image_85::decode_dir(const sif_file_t& file_0) +{ + if (file_0.size() != DIR_RECORDS * MAX_RECORD_SIZE) { + return false; + } + dir.clear(); + + // Check DIRSEG (directory segment), i.e. record #0 or #1 of directory + // Check FL1TK1 (file & record no. where track 1 begins) + if (file_0[ 0xfc ] != 0 || + file_0[ 0x1fc ] != 1 || + file_0[ 0xfd ] != file_0[ 0x1fd ] || + file_0[ 0xfe ] != file_0[ 0x1fe ] || + file_0[ 0xff ] != file_0[ 0x1ff ] || + file_0[ 0xfd ] > MAX_FILE_NO) { + return false; + } + + // Get FL1TK1 + file_track_1 = file_0[ 0xfd ]; + record_track_1 = pick_integer_le(file_0.data() , 0xfe , 2); + + file_no_t file_no = 1; + + // Iterate over all entries of directory + for (unsigned i = 0; i < 0x1fc; i += 12) { + if (i == 0xfc) { + // Skip over 1st copy of DIRSEG/FL1TK1 + i = 0x100; + } + const uint8_t *p = file_0.data() + i; + dir_entry_85 new_entry; + + // File type + new_entry.filetype = p[ 7 ]; + if (new_entry.filetype & FT_NEXT_AV_MASK) { + // Directory ends + break; + } + + // Filename + memcpy(&new_entry.filename[ 0 ] , p , CHARS_PER_FNAME); + if (!filename_check(&new_entry.filename[ 0 ])) { + return false; + } + + // File # + // It is also stored at p[ 6 ] but HP85 firmware ignores it + new_entry.file_no = file_no++; + + // Physical records + new_entry.n_recs = pick_integer_le(p , 8 , 2); + + // Bytes per logical record + new_entry.record_len = pick_integer_le(p , 10 , 2); + if (new_entry.record_len < 4 || new_entry.record_len >= 32768) { + return false; + } + + dir.push_back(new_entry); + } + + return true; +} + +void tape_image_85::encode_dir(sif_file_t& file_0) const +{ + file_0.clear(); + file_0.resize(DIR_RECORDS * MAX_RECORD_SIZE , 0); + + // Set DIRSEG + file_0[ 0xfc ] = 0; + file_0[ 0x1fc ] = 1; + // Set FL1TK1 + file_0[ 0xfd ] = file_0[ 0x1fd ] = file_track_1; + place_integer_le(file_0.data() , 0xfe , 2 , record_track_1); + place_integer_le(file_0.data() , 0x1fe , 2 , record_track_1); + + unsigned i = 0; + file_no_t file_no = 1; + for (auto entry = dir.cbegin(); entry != dir.cend(); entry++, i += 12, file_no++) { + if (i == 0xfc) { + // Skip over 1st copy of DIRSEG/FL1TK1 + i = 0x100; + } + uint8_t *p_entry = file_0.data() + i; + memcpy(&p_entry[ 0 ] , &entry->filename[ 0 ] , CHARS_PER_FNAME); + p_entry[ 6 ] = file_no; + p_entry[ 7 ] = entry->filetype; + place_integer_le(p_entry , 8 , 2 , entry->n_recs); + place_integer_le(p_entry , 10 , 2 , entry->record_len); + } + + if (file_no <= MAX_FILE_NO) { + if (i == 0xfc) { + // Skip over 1st copy of DIRSEG/FL1TK1 + i = 0x100; + } + file_0[ i + 7 ] = FT_NEXT_AV_MASK; + } + + // Two identical copies of directory + memcpy(file_0.data() + (DIR_RECORDS / 2) * MAX_RECORD_SIZE , file_0.data() , (DIR_RECORDS / 2) * MAX_RECORD_SIZE); +} + +void tape_image_85::save_words(unsigned track , tape_pos_t& pos , const tape_word_t *block , unsigned block_len) +{ + tape_pos_t length; + for (unsigned i = 0; i < block_len; i++) { + image.write_word(track , pos , *block++ , length); + pos += length; + } +} + +void tape_image_85::save_sif_file(unsigned& track , tape_pos_t& pos , file_no_t file_no , const sif_file_t& in) +{ + unsigned rec_no = 0; + unsigned bytes_to_go = in.size(); + sif_file_t::const_iterator in_it = in.cbegin(); + + do { + if (file_track_1 != 0 && track == 0 && + ((file_no == file_track_1 && rec_no >= record_track_1) || file_no > file_track_1)) { + // Switch to track 1 + track = 1; + pos = TRACK_START; + } + tape_pos_t start_pos = pos; + + unsigned rec_size = std::min(bytes_to_go , MAX_RECORD_SIZE); + + tape_word_t hdr[ 5 ]; + hdr[ 0 ] = SYNC_WORD; + hdr[ 1 ] = (tape_word_t)file_no | 0x2000; + if (rec_no == 0) { + hdr[ 1 ] |= 0x8000; + } + if (rec_size) { + hdr[ 1 ] |= 0x1800; + } + hdr[ 2 ] = 0x1000 | (tape_word_t)rec_no; + hdr[ 3 ] = 0xff00; + if (rec_size) { + hdr[ 3 ] |= (rec_size - 1); + } + hdr[ 4 ] = checksum(&hdr[ 1 ], 3); + + save_words(track, pos, &hdr[ 0 ], 5); + + if (rec_size) { + tape_word_t body[ MAX_RECORD_SIZE / 2 + 1 ]; + unsigned words = 0; + while (words < MAX_RECORD_SIZE / 2 && in_it != in.cend()) { + tape_word_t w = (tape_word_t)*in_it++ << 8; + if (in_it != in.cend()) { + w |= *in_it++; + } + body[ words++ ] = w; + } + body[ words ] = checksum(&body[ 0 ], words); + save_words(track, pos, &body[ 0 ], words + 1); + } + + // Pad record up to FMT_REC_SIZE + tape_word_t filler = 0xffff; + while ((pos - start_pos) < FMT_REC_SIZE) { + save_words(track, pos, &filler, 1); + } + + if (rec_no == 0) { + // IFG + pos = start_pos + FMT_REC_SIZE + FMT_IFG_SIZE; + } else { + // IRG + pos = start_pos + FMT_REC_SIZE + FMT_IRG_SIZE; + } + bytes_to_go -= rec_size; + rec_no++; + } while (bytes_to_go > 0); +} + +bool tape_image_85::filename_char_check(uint8_t c) +{ + // Quotation marks are forbidden in file names + return 0x20 < c && c < 0x7f && c != '"'; +} + +bool tape_image_85::filename_check(const uint8_t *filename) +{ + bool ended = false; + + for (unsigned i = 0; i < CHARS_PER_FNAME; i++) { + uint8_t c = *filename++; + + if (ended) { + if (c != ' ') { + return false; + } + } else if (c == ' ') { + ended = true; + } else if (!filename_char_check(c)) { + return false; + } + } + + return true; +} + +namespace { + tape_state_t& get_tape_state(imgtool::image &img) + { + tape_state_t *ts = (tape_state_t*)img.extra_bytes(); + + return *ts; + } + + tape_image_85& get_tape_image(tape_state_t& ts) + { + if (ts.img == nullptr) { + ts.img = new tape_image_85; + } + + return *(ts.img); + } +} +/******************************************************************************** + * Imgtool functions + ********************************************************************************/ +namespace { + imgtoolerr_t hp85_tape_open(imgtool::image &image, imgtool::stream::ptr &&stream) + { + tape_state_t& state = get_tape_state(image); + + state.stream = stream.release(); + + tape_image_85& tape_image = get_tape_image(state); + + imgtoolerr_t err = tape_image.load_from_file(state.stream); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; + } + + imgtoolerr_t hp85_tape_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *opts) + { + tape_state_t& state = get_tape_state(image); + + state.stream = stream.release(); + + tape_image_85& tape_image = get_tape_image(state); + + tape_image.format_img(); + + return IMGTOOLERR_SUCCESS; + } + + void hp85_tape_close(imgtool::image &image) + { + tape_state_t& state = get_tape_state(image); + tape_image_85& tape_image = get_tape_image(state); + + if (tape_image.is_dirty()) { + (void)tape_image.save_to_file(state.stream); + } + + delete state.stream; + + // Free tape_image + delete &tape_image; + } + + imgtoolerr_t hp85_tape_begin_enum (imgtool::directory &enumeration, const char *path) + { + dir_state_t *ds = (dir_state_t*)enumeration.extra_bytes(); + + ds->dir_idx = 0; + + return IMGTOOLERR_SUCCESS; + } + + imgtoolerr_t hp85_tape_next_enum (imgtool::directory &enumeration, imgtool_dirent &ent) + { + tape_state_t& state = get_tape_state(enumeration.image()); + tape_image_85& tape_image = get_tape_image(state); + dir_state_t *ds = (dir_state_t*)enumeration.extra_bytes(); + + const dir_entry_85 *entry = nullptr; + + if (!tape_image.get_dir_entry(ds->dir_idx, entry)) { + ent.eof = 1; + } else { + ds->dir_idx++; + + unsigned filesize = entry->n_recs * MAX_RECORD_SIZE; + + if (entry->filetype & FT_NULL_FILE_MASK) { + ent.filesize = 0; + } else { + ent.filesize = filesize; + } + + tape_image_85::get_filename_and_ext(*entry, true, ent.filename); + snprintf(ent.attr , sizeof(ent.attr) , "%u %u %u %c%c" , entry->record_len , filesize / entry->record_len , entry->file_no , (entry->filetype & FT_WP_MASK) ? 'W' : ' ' , (entry->filetype & FT_HIDDEN_MASK) ? 'H' : ' '); + } + + + return IMGTOOLERR_SUCCESS; + } + + imgtoolerr_t hp85_tape_free_space(imgtool::partition &partition, uint64_t *size) + { + tape_state_t& state = get_tape_state(partition.image()); + tape_image_85& tape_image = get_tape_image(state); + unsigned used_recs = 0; + + const dir_entry_85 *entry = nullptr; + + for (unsigned i = 0; i < MAX_FILE_NO; i++) { + if (!tape_image.get_dir_entry(i, entry)) { + break; + } + // Ignore erased files + if (entry->filetype & FT_NULL_FILE_MASK) { + continue; + } + used_recs += entry->n_recs; + } + + if (used_recs >= MAX_N_RECORDS) { + *size = 0; + } else { + *size = (MAX_N_RECORDS - used_recs) * MAX_RECORD_SIZE; + } + + return IMGTOOLERR_SUCCESS; + } + + imgtoolerr_t hp85_tape_read_file(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf) + { + tape_state_t& state = get_tape_state(partition.image()); + tape_image_85& tape_image = get_tape_image(state); + + unsigned idx; + + if (!tape_image.find_file(filename , false , idx)) { + return IMGTOOLERR_FILENOTFOUND; + } + + const dir_entry_85 *ent = nullptr; + + tape_image.get_dir_entry(idx, ent); + + tape_image_85::sif_file_t file_data; + + if (!tape_image.load_sif_file(ent->file_no , file_data)) { + return IMGTOOLERR_READERROR; + } + + destf.write(file_data.data() , file_data.size()); + + return IMGTOOLERR_SUCCESS; + } + + imgtoolerr_t hp85_tape_write_file(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts) + { + tape_state_t& state = get_tape_state(partition.image()); + tape_image_85& tape_image = get_tape_image(state); + + unsigned idx; + + if (tape_image.find_file(filename , true , idx)) { + // When overwriting a file, delete its old version first + tape_image.delete_dir_entry(idx); + } + + unsigned file_size = sourcef.size(); + + if (!file_size) { + return IMGTOOLERR_SUCCESS; + } + if (file_size > (MAX_N_RECORDS * MAX_RECORD_SIZE)) { + return IMGTOOLERR_NOSPACE; + } + + auto p_in_file = std::make_unique(); + + p_in_file->resize(file_size); + + if (sourcef.read(p_in_file->data() , file_size) != file_size) { + return IMGTOOLERR_READERROR; + } + + dir_entry_85 *ent = nullptr; + + if (!tape_image.alloc_new_file(ent , std::move(p_in_file))) { + return IMGTOOLERR_NOSPACE; + } + + char fname[ CHARS_PER_FNAME + 1 ]; + char ext[ CHARS_PER_EXT + 1 ]; + + tape_image_85::split_filename_and_ext(filename, fname, ext); + + char *dest = (char *)&ent->filename[ 0 ]; + char *dest0 = dest; + char *src = &fname[ 0 ]; + + while ((dest - dest0) < CHARS_PER_FNAME && *src != '\0') { + *dest++ = *src++; + } + while ((dest - dest0) < CHARS_PER_FNAME) { + *dest++ = ' '; + } + + int bpr = opts->lookup_int('B'); + if (bpr < 4) { + bpr = MAX_RECORD_SIZE; + } else if (bpr > file_size) { + util::stream_format(std::wcerr, L"BPR value too large, using %u\n", MAX_RECORD_SIZE); + bpr = MAX_RECORD_SIZE; + } + ent->record_len = (uint16_t)bpr; + + if (opts->lookup_int('T') == 0) { + // File type defaults to DATA if no extension is given or extension is invalid + ent->filetype = FT_DATA_MASK; + for (const auto& i : filetype_table) { + if (strcmp(i.ext , ext) == 0) { + ent->filetype = i.filetype_mask; + break; + } + } + } else { + ent->filetype = filetype_table[ opts->lookup_int('T') - 1 ].filetype_mask; + } + + if (!tape_image.finalize_allocation()) { + return IMGTOOLERR_NOSPACE; + } + + return IMGTOOLERR_SUCCESS; + } + + imgtoolerr_t hp85_tape_delete_file(imgtool::partition &partition, const char *filename) + { + tape_state_t& state = get_tape_state(partition.image()); + tape_image_85& tape_image = get_tape_image(state); + + unsigned idx; + + if (!tape_image.find_file(filename , true , idx)) { + return IMGTOOLERR_FILENOTFOUND; + } + + if (!tape_image.delete_dir_entry(idx)) { + return IMGTOOLERR_READERROR; + } + if (!tape_image.finalize_allocation()) { + return IMGTOOLERR_NOSPACE; + } + + return IMGTOOLERR_SUCCESS; + } +} +#define HP85_WRITEFILE_OPTSPEC "B[0]-32767;T[0]-3" + +OPTION_GUIDE_START(hp85_write_optguide) + OPTION_INT('B' , "bpr" , "Bytes per record") + OPTION_ENUM_START('T' , "ftype" , "File type") + OPTION_ENUM(0 , "auto" , "Automatic (\"DATA\" or by extension)") + OPTION_ENUM(1 , "P" , "PROG") + OPTION_ENUM(2 , "D" , "DATA") + OPTION_ENUM(3 , "B" , "BPGM") + OPTION_ENUM_END +OPTION_GUIDE_END + +void hp85_tape_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch (state) { + case IMGTOOLINFO_STR_NAME: + strcpy(info->s = imgtool_temp_str(), "hp85_tape"); + break; + + case IMGTOOLINFO_STR_DESCRIPTION: + strcpy(info->s = imgtool_temp_str(), "HP85 tape image"); + break; + + case IMGTOOLINFO_STR_FILE: + strcpy(info->s = imgtool_temp_str(), __FILE__); + break; + + case IMGTOOLINFO_STR_FILE_EXTENSIONS: + strcpy(info->s = imgtool_temp_str(), "hti"); + break; + + case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: + info->i = sizeof(tape_state_t); + break; + + case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: + info->i = sizeof(dir_state_t); + break; + + case IMGTOOLINFO_PTR_OPEN: + info->open = hp85_tape_open; + break; + + case IMGTOOLINFO_PTR_CREATE: + info->create = hp85_tape_create; + break; + + case IMGTOOLINFO_PTR_CLOSE: + info->close = hp85_tape_close; + break; + + case IMGTOOLINFO_PTR_BEGIN_ENUM: + info->begin_enum = hp85_tape_begin_enum; + break; + + case IMGTOOLINFO_PTR_NEXT_ENUM: + info->next_enum = hp85_tape_next_enum; + break; + + case IMGTOOLINFO_PTR_FREE_SPACE: + info->free_space = hp85_tape_free_space; + break; + + case IMGTOOLINFO_PTR_READ_FILE: + info->read_file = hp85_tape_read_file; + break; + + case IMGTOOLINFO_PTR_WRITE_FILE: + info->write_file = hp85_tape_write_file; + break; + + case IMGTOOLINFO_PTR_DELETE_FILE: + info->delete_file = hp85_tape_delete_file; + break; + + case IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE: + info->writefile_optguide = &hp85_write_optguide; + break; + + case IMGTOOLINFO_STR_WRITEFILE_OPTSPEC: + strcpy(info->s = imgtool_temp_str(), HP85_WRITEFILE_OPTSPEC); + break; + } +} diff --git a/src/tools/imgtool/modules/hp9845_tape.cpp b/src/tools/imgtool/modules/hp9845_tape.cpp new file mode 100644 index 0000000..5254c26 --- /dev/null +++ b/src/tools/imgtool/modules/hp9845_tape.cpp @@ -0,0 +1,1540 @@ +// license:BSD-3-Clause +// copyright-holders:F. Ulivi +/********************************************************************* + + hp9845_tape.cpp + + HP-9845 tape format + + This imgtool module manipulates HTI files. These are image files + of the DC-100 tape cartridges that are simulated for the HP9845B + driver. + HP9845 filesystem for tapes has the following features: + * File names are 1 to 6 characters long. + * Case is significant in file names. + * There is no file "extension", file type is encoded separately + in file metadata. + * There are 8 file types. File type is encoded in 5 bits. + Only 8 out of the 32 possible values are valid. + * This module handles the file type as a fake file extension. + For example, a file named "TEST" having DATA type is get/put/shown + as "TEST.DATA". + * File type is deduced from host file extension when putting files + into image. File type can be overridden by the "ftype" option. + This table summarizes the file types. + + ftype Fake Type of file BASIC commands + switch extension for this file type + ======================================================== + U BKUP "Database backup" + No idea + D DATA Generic record-based data file + SAVE/GET/PRINT#/READ# + P PROG Program file (tokenized BASIC & other data) + STORE/LOAD + K KEYS KEY file (definition of soft keys) + STORE KEY/LOAD KEY + T BDAT Binary data file + ? + A ALL Full dump of system state + STORE ALL/LOAD ALL + B BPRG Binary program file + STORE BIN/LOAD BIN + O OPRM Option ROM specific file + ? + + * Files are always stored in units of 256-byte physical records. + * An important metadata of files is WPR: Words Per Record. This + is a numeric value that sets the length of each logical record of + the file (in units of 16-bit words). It defaults to 128 (i.e. + logical and physical records are the same thing). It can be + set by the "wpr" option when putting files into the image. + * There is no fragmentation map in the filesystem: each file + always occupy a contiguous set of physical records. This fact + could prevent the putting of a file into an image when there + is no single block of free records big enough to hold the file + even though the total amount of free space would be sufficient. + + Notes on commands + ================= + + **** dir command **** + The format of the "attr" part of file listing is as follows: + %c '*' if file has the protection bit set, else ' ' + %02x Hexadecimal value of file type (00-1f) + %c '?' if file type is not valid, else ' ' + %4u Number of logical records + %4u WPR * 2 (i.e. bytes per logical record) + %3u First physical record of file + + **** get command **** + A file can be extracted from an image with or without an explicit + extension. If an extension is given, it must match the one corresponding + to file type. + The "9845data" filter can be used on DATA files (see below). + + **** getall command **** + Files are extracted with their "fake" extension. + + **** put command **** + File type can be specified explicitly through the "ftype" option. + If this option is "auto" (the default), type is deduced from file + extension, if present. When extension is not given or it doesn't + match any known type, file type is set to "DATA". + WPR can be set through the "wpr" option. If it's 0 (the default), + WPR is set to 128. + The "9845data" filter can be used on DATA files (see below). + + **** del command **** + File extension is ignored, if present. + + "9845data" filter + ================= + + This filter can be applied to DATA files whose content is made + of strings only. BASIC programs that are saved with "SAVE" command + have this format. + This filter translates a DATA file into a standard ASCII text file + and viceversa. + Keep in mind that this translation is NOT lossless because all + non-ASCII & non printable characters are substituted with spaces. + This kind of characters must be removed because they may confuse + the line-by-line reading of file when translating in the opposite + direction. + The 9845 system has the capability to insert formatting characters + directly in the text strings to be displayed on screen. These + characters set things like inverse video or underline. + Turning a DATA file into a text file through this filter removes + these special characters. + +*********************************************************************/ +#include "imgtool.h" +#include "filter.h" + +#include "formats/hti_tape.h" +#include "formats/imageutl.h" + +#include "corefile.h" +#include "ioprocs.h" +#include "opresolv.h" + +#include +#include + + +// Constants +#define SECTOR_LEN 256 // Bytes in a sector +#define WORDS_PER_SECTOR (SECTOR_LEN / 2) // 16-bit words in a sector payload +#define SECTORS_PER_TRACK 426 // Sectors in a track +#define TRACKS_NO 2 // Number of tracks +#define TOT_SECTORS (SECTORS_PER_TRACK * TRACKS_NO) // Total number of sectors +#define DIR_WORD_0 0x0500 // First word of directories +#define DIR_WORD_1 0xffff // Second word of directories +#define DIR_LAST_WORD 0xffff // Last word of directories +#define FIRST_DIR_SECTOR 1 // First directory sector +#define SECTORS_PER_DIR 2 // Sectors per copy of directory +#define MAX_DIR_ENTRIES 42 // And the answer is.... the maximum number of entries in the directory! +#define DIR_COPIES 2 // Count of directory copies +#define CHARS_PER_FNAME 6 // Maximum characters in a filename +#define CHARS_PER_EXT 4 // Characters in file extension. Extension is encoded as file type, it's not actually stored in directory as characters. +#define CHARS_PER_FNAME_EXT (CHARS_PER_FNAME + 1 + CHARS_PER_EXT) // Characters in filename + extension +#define PAD_WORD 0xffff // Word value for padding +#define FIRST_FILE_SECTOR (FIRST_DIR_SECTOR + SECTORS_PER_DIR * DIR_COPIES) // First file sector +#define START_POS ((tape_pos_t)(72.25 * hti_format_t::ONE_INCH_POS)) // Start position on each track +#define DZ_WORDS 350 // Words in deadzone +#define IRG_SIZE hti_format_t::ONE_INCH_POS // Size of inter-record-gap: 1" +#define IFG_SIZE ((tape_pos_t)(2.5 * hti_format_t::ONE_INCH_POS)) // Size of inter-file-gap: 2.5" +#define HDR_W0_ZERO_MASK 0x4000 // Mask of zero bits in word 0 of header +#define RES_FREE_FIELD 0x2000 // Mask of "reserved free field" bit +#define FILE_ID_BIT 0x8000 // Mask of "file identifier" bit +#define SECTOR_IN_USE 0x1800 // Mask of "empty record indicator" (== !sector in use indicator) +#define SIF_FILE_NO 1 // SIF file # +#define SIF_FILE_NO_MASK 0x07ff // Mask of SIF file # +#define SIF_FREE_FIELD 0 // SIF free field +#define SIF_FREE_FIELD_MASK 0xf000 // Mask of SIF free field +#define BYTES_AVAILABLE 0xff00 // "bytes available" field = 256 +#define BYTES_AVAILABLE_MASK 0xff00 // Mask of "bytes available" field +#define BYTES_USED 0x00ff // "bytes used" field = 256 +#define BYTES_USED_MASK 0x00ff // Mask of "bytes used" field +#define FORMAT_SECT_SIZE ((tape_pos_t)(2.67 * hti_format_t::ONE_INCH_POS)) // Size of sectors including padding: 2.67" +#define PREAMBLE_WORD 0x0001 // Value of preamble word +#define WORDS_PER_HEADER_N_SECTOR (WORDS_PER_SECTOR + 5) +#define MIN_IRG_SIZE ((tape_pos_t)(16 * 1024)) // Minimum size of IRG gaps: 0.017" + +// File types +#define BKUP_FILETYPE 0 +#define BKUP_ATTR_STR "BKUP" +#define DATA_FILETYPE 1 +#define DATA_ATTR_STR "DATA" +#define PROG_FILETYPE 2 +#define PROG_ATTR_STR "PROG" +#define KEYS_FILETYPE 3 +#define KEYS_ATTR_STR "KEYS" +#define BDAT_FILETYPE 4 +#define BDAT_ATTR_STR "BDAT" +#define ALL_FILETYPE 5 +#define ALL_ATTR_STR "ALL" +#define BPRG_FILETYPE 6 +#define BPRG_ATTR_STR "BPRG" +#define OPRM_FILETYPE 7 +#define OPRM_ATTR_STR "OPRM" + +// Record type identifiers +#define REC_TYPE_EOR 0x1e // End-of-record +#define REC_TYPE_FULLSTR 0x3c // A whole (un-split) string +#define REC_TYPE_EOF 0x3e // End-of-file +#define REC_TYPE_1STSTR 0x1c // First part of a string +#define REC_TYPE_MIDSTR 0x0c // Middle part(s) of a string +#define REC_TYPE_ENDSTR 0x2c // Last part of a string + +// End-of-lines +#define EOLN (CRLF == 1 ? "\r" : (CRLF == 2 ? "\n" : (CRLF == 3 ? "\r\n" : NULL))) + +// Words stored on tape +using tape_word_t = hti_format_t::tape_word_t; + +// Tape position, 1 unit = 1 inch / (968 * 1024) +using tape_pos_t = hti_format_t::tape_pos_t; + +/******************************************************************************** + * Directory entries + ********************************************************************************/ +typedef struct { + uint8_t filename[ CHARS_PER_FNAME ]; // Filename (left justified, 0 padded on the right) + bool protection; // File protection + uint8_t filetype; // File type (00-1f) + uint16_t filepos; // File position (# of 1st sector) + uint16_t n_recs; // Number of records + uint16_t wpr; // Word-per-record + unsigned n_sects; // Count of sectors +} dir_entry_t; + +/******************************************************************************** + * Tape image + ********************************************************************************/ +class tape_image_t { +public: + tape_image_t(void); + + bool is_dirty(void) const { return dirty; } + + void format_img(void); + + imgtoolerr_t load_from_file(imgtool::stream *stream); + imgtoolerr_t save_to_file(imgtool::stream *stream); + + unsigned free_sectors(void) const; + + void set_sector(unsigned s_no , const tape_word_t *s_data); + void unset_sector(unsigned s_no); + bool get_sector(unsigned s_no , tape_word_t *s_data); + + bool get_dir_entry(unsigned idx , const dir_entry_t*& entry) const; + bool find_file(const char *filename , bool ignore_ext , unsigned& idx) const; + + void delete_dir_entry(unsigned idx); + + bool find_free_block(unsigned blocks , unsigned& first_s) const; + + bool alloc_new_file(unsigned blocks , dir_entry_t*& entry); + + static void tape_word_to_bytes(tape_word_t w , uint8_t& bh , uint8_t& bl); + static void bytes_to_tape_word(uint8_t bh , uint8_t bl , tape_word_t& w); + + static void get_filename_and_ext(const dir_entry_t& ent , bool inc_ext , char *out , bool& qmark); + static void split_filename_and_ext(const char *filename , char *fname , char *ext); + +private: + bool dirty; + // Tape image + tape_word_t img[ TOT_SECTORS ][ WORDS_PER_SECTOR ]; + // Map of sectors in use + std::bitset alloc_map; + // Directory + std::vector dir; + + static void wipe_sector(tape_word_t *s); + void dump_dir_sect(const tape_word_t *dir_sect , unsigned dir_sect_idx); + void fill_and_dump_dir_sect(tape_word_t *dir_sect , unsigned& idx , unsigned& dir_sect_idx , tape_word_t w) ; + void encode_dir(void); + bool read_sector_words(unsigned& sect_no , unsigned& sect_idx , size_t word_no , tape_word_t *out) const; + static bool filename_char_check(uint8_t c); + static bool filename_check(const uint8_t *filename); + bool decode_dir(void); + void save_words(hti_format_t& img , unsigned track , tape_pos_t& pos , const tape_word_t *block , unsigned block_len); + static tape_word_t checksum(const tape_word_t *block , unsigned block_len); +}; + +/******************************************************************************** + * Image state + ********************************************************************************/ +typedef struct { + imgtool::stream *stream; + tape_image_t *img; +} tape_state_t; + +/******************************************************************************** + * Directory enumeration + ********************************************************************************/ +typedef struct { + unsigned dir_idx; +} dir_state_t; + +/******************************************************************************** + * Internal functions + ********************************************************************************/ +tape_image_t::tape_image_t(void) + : dirty(false) +{ +} + +void tape_image_t::format_img(void) +{ + // Deallocate all sectors + alloc_map.reset(); + + // Create an empty directory + dir.clear(); + + dirty = true; +} + +imgtoolerr_t tape_image_t::load_from_file(imgtool::stream *stream) +{ + hti_format_t inp_image; + inp_image.set_image_format(hti_format_t::HTI_DELTA_MOD_16_BITS); + + auto io = imgtool::stream_read(*stream, 0); + if (!io || !inp_image.load_tape(*io)) { + return IMGTOOLERR_READERROR; + } + io.reset(); + + unsigned exp_sector = 0; + unsigned last_sector_on_track = SECTORS_PER_TRACK; + for (unsigned track = 0; track < TRACKS_NO; track++ , last_sector_on_track += SECTORS_PER_TRACK) { + tape_pos_t pos = 0; + // Loader state: + // 0 Wait for DZ + // 1 Wait for sector data + // 2 Wait for gap + unsigned state = 0; + + while (exp_sector != last_sector_on_track) { + switch (state) { + case 0: + case 1: + { + hti_format_t::track_iterator_t it; + + if (!inp_image.next_data(track , pos , true , false , it)) { + // No more data on tape + return IMGTOOLERR_CORRUPTIMAGE; + } + if (state == 1) { + // Extract record data + + // The top 8 bits are ignored by TACO when aligning with preamble + unsigned bit_idx = 7; + if (!inp_image.sync_with_record(track , it , bit_idx)) { + // Couldn't align + return IMGTOOLERR_CORRUPTIMAGE; + } + tape_word_t buffer[ WORDS_PER_HEADER_N_SECTOR ]; + for (unsigned i = 0; i < WORDS_PER_HEADER_N_SECTOR; i++) { + auto res = inp_image.next_word(track , it , bit_idx , buffer[ i ]); + if (res != hti_format_t::ADV_CONT_DATA) { + return IMGTOOLERR_CORRUPTIMAGE; + } + } + if (buffer[ 3 ] != checksum(&buffer[ 0 ], 3) || + buffer[ 4 + WORDS_PER_SECTOR ] != checksum(&buffer[ 4 ], WORDS_PER_SECTOR)) { + return IMGTOOLERR_CORRUPTIMAGE; + } + // Check record content + if (exp_sector != (buffer[ 1 ] & 0xfff)) { + return IMGTOOLERR_CORRUPTIMAGE; + } + if (((buffer[ 0 ] & FILE_ID_BIT) != 0) != (exp_sector == 0)) { + return IMGTOOLERR_CORRUPTIMAGE; + } + if ((buffer[ 0 ] & (HDR_W0_ZERO_MASK | RES_FREE_FIELD | SIF_FILE_NO_MASK)) != (RES_FREE_FIELD | SIF_FILE_NO)) { + return IMGTOOLERR_CORRUPTIMAGE; + } + if ((buffer[ 1 ] & SIF_FREE_FIELD_MASK) != SIF_FREE_FIELD) { + return IMGTOOLERR_CORRUPTIMAGE; + } + bool in_use = (buffer[ 0 ] & SECTOR_IN_USE) != 0; + if ((buffer[ 2 ] & BYTES_AVAILABLE_MASK) != BYTES_AVAILABLE || + (in_use && (buffer[ 2 ] & BYTES_USED_MASK) != BYTES_USED) || + (!in_use && (buffer[ 2 ] & BYTES_USED_MASK) != 0)) { + return IMGTOOLERR_CORRUPTIMAGE; + } + if (in_use) { + set_sector(exp_sector, &buffer[ 4 ]); + } else { + unset_sector(exp_sector); + } + exp_sector++; + } + pos = it->first; + state = 2; + } + break; + + case 2: + // Find next gap + if (!inp_image.next_gap(track , pos , true , MIN_IRG_SIZE)) { + return IMGTOOLERR_CORRUPTIMAGE; + } + state = 1; + break; + + } + } + } + + if (!decode_dir()) { + return IMGTOOLERR_CORRUPTDIR; + } + + dirty = false; + + return IMGTOOLERR_SUCCESS; +} + +void tape_image_t::save_words(hti_format_t& img , unsigned track , tape_pos_t& pos , const tape_word_t *block , unsigned block_len) +{ + // Preamble + tape_pos_t length; + img.write_word(track , pos , PREAMBLE_WORD , length); + pos += length; + // Words + for (unsigned i = 0; i < block_len; i++) { + img.write_word(track , pos , *block++ , length); + pos += length; + } +} + +tape_word_t tape_image_t::checksum(const tape_word_t *block , unsigned block_len) +{ + tape_word_t csum = 0; + for (unsigned i = 0; i < block_len; i++) { + csum += *block++; + } + return csum & 0xffff; +} + +imgtoolerr_t tape_image_t::save_to_file(imgtool::stream *stream) +{ + // Encode copies of directory into sectors + encode_dir(); + + // Store sectors into image + hti_format_t out_image; + + unsigned rec_no = 0; + for (unsigned track = 0; track < TRACKS_NO; track++) { + tape_pos_t pos = START_POS; + + // Start of either track + // Deadzone + 1" of gap + tape_word_t deadzone[ DZ_WORDS ]; + for (auto& dz : deadzone) { + dz = PAD_WORD; + } + save_words(out_image, track, pos, deadzone, DZ_WORDS); + pos += IRG_SIZE; + + for (unsigned i = 0; i < SECTORS_PER_TRACK; i++ , rec_no++) { + bool in_use = alloc_map[ rec_no ]; + // Sector header + tape_word_t sector[ WORDS_PER_HEADER_N_SECTOR ]; + + // Header word 0: file identifier bit, reserved free-field bit, empty record indicator & file # + sector[ 0 ] = RES_FREE_FIELD | SIF_FILE_NO; + if (rec_no == 0) { + sector[ 0 ] |= FILE_ID_BIT; + } + if (in_use) { + sector[ 0 ] |= SECTOR_IN_USE; + } + // Header word 1: free-field & sector # + sector[ 1 ] = SIF_FREE_FIELD | rec_no; + // Header word 2: bytes available & bytes used + sector[ 2 ] = BYTES_AVAILABLE; + if (in_use) { + sector[ 2 ] |= BYTES_USED; + } + // Checksum of header + sector[ 3 ] = checksum(§or[ 0 ] , 3); + // Sector payload + if (in_use) { + memcpy(§or[ 4 ] , &img[ rec_no ][ 0 ] , SECTOR_LEN); + } else { + for (unsigned j = 4; j < (4 + WORDS_PER_SECTOR); j++) { + sector[ j ] = PAD_WORD; + } + } + // Checksum of payload + sector[ 4 + WORDS_PER_SECTOR ] = checksum(§or[ 4 ] , WORDS_PER_SECTOR); + + tape_pos_t start_pos = pos; + save_words(out_image, track, pos, sector, WORDS_PER_HEADER_N_SECTOR); + + // Pad sector up to FORMAT_SECT_SIZE + while ((pos - start_pos) < FORMAT_SECT_SIZE) { + tape_pos_t length; + out_image.write_word(track , pos , PAD_WORD , length); + pos += length; + } + + // Gap between sectors + if (rec_no == 0) { + pos += IFG_SIZE; + } else { + pos += IRG_SIZE; + } + } + } + + out_image.save_tape(*imgtool::stream_read_write(*stream, 0)); + + return IMGTOOLERR_SUCCESS; +} + +unsigned tape_image_t::free_sectors(void) const +{ + std::bitset tmp(alloc_map); + + // Reserve sectors that cannot be allocated to files + for (unsigned i = 0; i < FIRST_FILE_SECTOR; i++) { + tmp[ i ] = true; + } + + return TOT_SECTORS - tmp.count(); +} + +void tape_image_t::set_sector(unsigned s_no , const tape_word_t *s_data) +{ + if (s_no < TOT_SECTORS) { + memcpy(&img[ s_no ][ 0 ] , s_data , SECTOR_LEN); + alloc_map.set(s_no); + dirty = true; + } +} + +void tape_image_t::unset_sector(unsigned s_no) +{ + if (s_no < TOT_SECTORS) { + alloc_map.reset(s_no); + dirty = true; + } +} + +bool tape_image_t::get_sector(unsigned s_no , tape_word_t *s_data) +{ + if (s_no < TOT_SECTORS && alloc_map[ s_no ]) { + memcpy(s_data , &img[ s_no ][ 0 ] , SECTOR_LEN); + return true; + } else { + return false; + } +} + +bool tape_image_t::get_dir_entry(unsigned idx , const dir_entry_t*& entry) const +{ + if (idx >= dir.size()) { + return false; + } else { + entry = &dir[ idx ]; + return true; + } +} + +bool tape_image_t::find_file(const char *filename , bool ignore_ext , unsigned& idx) const +{ + char fname[ CHARS_PER_FNAME_EXT + 1 ]; + char ext[ CHARS_PER_EXT + 1 ]; + + split_filename_and_ext(filename, fname, ext); + + bool has_ext = !ignore_ext && *ext != '\0'; + + if (has_ext) { + strcat(fname , "."); + strcat(fname , ext); + } + + for (auto i = dir.cbegin(); i < dir.cend(); i++) { + char full_fname[ CHARS_PER_FNAME_EXT + 1 ]; + bool qmark; + + get_filename_and_ext(*i, has_ext, full_fname, qmark); + + if (strcmp(fname , full_fname) == 0) { + idx = i - dir.cbegin(); + return true; + } + } + + return false; +} + +void tape_image_t::delete_dir_entry(unsigned idx) +{ + const dir_entry_t& ent = dir[ idx ]; + + // Release all sectors of file + for (unsigned i = ent.filepos; i < ent.filepos + ent.n_sects; i++) { + unset_sector(i); + } + + dir.erase(dir.begin() + idx); + dirty = true; +} + +bool tape_image_t::find_free_block(unsigned blocks , unsigned& first_s) const +{ + if (blocks >= (TOT_SECTORS - FIRST_FILE_SECTOR)) { + return false; + } + + std::bitset scanner; + + for (unsigned i = FIRST_FILE_SECTOR; i < (FIRST_FILE_SECTOR + blocks); i++) { + scanner[ i ] = true; + } + + for (unsigned i = FIRST_FILE_SECTOR; i <= (TOT_SECTORS - blocks); i++) { + std::bitset tmp_map(alloc_map & scanner); + if (tmp_map.none()) { + first_s = i; + return true; + } + scanner <<= 1; + } + + return false; +} + +bool tape_image_t::alloc_new_file(unsigned blocks , dir_entry_t*& entry) +{ + if (dir.size() >= MAX_DIR_ENTRIES) { + return false; + } + + dir_entry_t new_entry; + + memset(&new_entry.filename[ 0 ] , 0 , sizeof(new_entry.filename)); + new_entry.protection = 0; + new_entry.filetype = 0; + new_entry.n_recs = 0; + new_entry.wpr = 0; + + unsigned first_s; + + if (!find_free_block(blocks, first_s)) { + return false; + } + + new_entry.filepos = (uint16_t)first_s; + new_entry.n_sects = blocks; + + dir.push_back(new_entry); + entry = &dir.back(); + dirty = true; + + return true; +} + +void tape_image_t::split_filename_and_ext(const char *filename , char *fname , char *ext) +{ + char *fname_fence = fname + CHARS_PER_FNAME; + + while (fname < fname_fence && *filename != '\0' && *filename != '.') { + *fname++ = *filename++; + } + + *fname = '\0'; + + while (*filename != '\0' && *filename != '.') { + filename++; + } + + if (*filename == '\0') { + *ext = '\0'; + } else { + filename++; + strncpy(ext , filename , CHARS_PER_EXT); + ext[ CHARS_PER_EXT ] = '\0'; + } +} + +void tape_image_t::wipe_sector(tape_word_t *s) +{ + for (unsigned i = 0; i < WORDS_PER_SECTOR; i++) { + s[ i ] = PAD_WORD; + } +} + +void tape_image_t::tape_word_to_bytes(tape_word_t w , uint8_t& bh , uint8_t& bl) +{ + bh = (uint8_t)(w >> 8); + bl = (uint8_t)(w & 0xff); +} + +void tape_image_t::bytes_to_tape_word(uint8_t bh , uint8_t bl , tape_word_t& w) +{ + w = ((tape_word_t)bh << 8) | ((tape_word_t)bl); +} + +void tape_image_t::dump_dir_sect(const tape_word_t *dir_sect , unsigned dir_sect_idx) +{ + for (unsigned i = 0; i < DIR_COPIES; i++) { + set_sector(FIRST_DIR_SECTOR + i * SECTORS_PER_DIR + dir_sect_idx, dir_sect); + } +} + +void tape_image_t::fill_and_dump_dir_sect(tape_word_t *dir_sect , unsigned& idx , unsigned& dir_sect_idx , tape_word_t w) +{ + // Dump sector once it's full + if (idx >= WORDS_PER_SECTOR) { + dump_dir_sect(dir_sect, dir_sect_idx); + wipe_sector(dir_sect); + idx = 0; + dir_sect_idx++; + } + dir_sect[ idx++ ] = w; +} + +void tape_image_t::encode_dir(void) +{ + tape_word_t dir_sect[ WORDS_PER_SECTOR ]; + + wipe_sector(dir_sect); + + unsigned idx = 0; + unsigned dir_sect_idx = 0; + + fill_and_dump_dir_sect(dir_sect, idx, dir_sect_idx, DIR_WORD_0); + fill_and_dump_dir_sect(dir_sect, idx, dir_sect_idx, DIR_WORD_1); + + for (const dir_entry_t& ent : dir) { + tape_word_t tmp; + + // Filename + bytes_to_tape_word(ent.filename[ 0 ], ent.filename[ 1 ], tmp); + fill_and_dump_dir_sect(dir_sect, idx, dir_sect_idx, tmp); + bytes_to_tape_word(ent.filename[ 2 ], ent.filename[ 3 ], tmp); + fill_and_dump_dir_sect(dir_sect, idx, dir_sect_idx, tmp); + bytes_to_tape_word(ent.filename[ 4 ], ent.filename[ 5 ], tmp); + fill_and_dump_dir_sect(dir_sect, idx, dir_sect_idx, tmp); + // Protection, file type & file position + tmp = ((tape_word_t)ent.filetype << 10) | (tape_word_t)ent.filepos; + if (ent.protection) { + tmp |= 0x8000; + } + fill_and_dump_dir_sect(dir_sect, idx, dir_sect_idx, tmp); + // File size (# of records) + fill_and_dump_dir_sect(dir_sect, idx, dir_sect_idx, ent.n_recs); + // Words per record + fill_and_dump_dir_sect(dir_sect, idx, dir_sect_idx, ent.wpr); + } + + // Terminator + fill_and_dump_dir_sect(dir_sect, idx, dir_sect_idx, DIR_LAST_WORD); + + // Dump last partial sector + dump_dir_sect(dir_sect, dir_sect_idx); + + // Unset unused sectors + for (unsigned i = dir_sect_idx + 1; i < SECTORS_PER_DIR; i++) { + for (unsigned j = 0; j < DIR_COPIES; j++) { + unset_sector(FIRST_DIR_SECTOR + i + j * SECTORS_PER_DIR); + } + } +} + +bool tape_image_t::read_sector_words(unsigned& sect_no , unsigned& sect_idx , size_t word_no , tape_word_t *out) const +{ + while (word_no > 0) { + if (sect_idx >= WORDS_PER_SECTOR) { + sect_idx = 0; + sect_no++; + if (sect_no >= TOT_SECTORS || !alloc_map[ sect_no ]) { + return false; + } + } + *out++ = img[ sect_no ][ sect_idx ]; + sect_idx++; + word_no--; + } + + return true; +} + +bool tape_image_t::filename_char_check(uint8_t c) +{ + // Colons and quotation marks are forbidden in file names + return 0x20 < c && c < 0x7f && c != ':' && c != '"'; +} + +bool tape_image_t::filename_check(const uint8_t *filename) +{ + bool ended = false; + + for (unsigned i = 0; i < 6; i++) { + uint8_t c = *filename++; + + if (ended) { + if (c != 0) { + return false; + } + } else if (c == 0) { + ended = true; + } else if (!filename_char_check(c)) { + return false; + } + } + + return true; +} + +static const char *const filetype_attrs[] = { + BKUP_ATTR_STR, // 0 + DATA_ATTR_STR, // 1 + PROG_ATTR_STR, // 2 + KEYS_ATTR_STR, // 3 + BDAT_ATTR_STR, // 4 + ALL_ATTR_STR, // 5 + BPRG_ATTR_STR, // 6 + OPRM_ATTR_STR // 7 +}; + +void tape_image_t::get_filename_and_ext(const dir_entry_t& ent , bool inc_ext , char *out , bool& qmark) +{ + strncpy(&out[ 0 ] , (const char*)&ent.filename[ 0 ] , CHARS_PER_FNAME); + out[ CHARS_PER_FNAME ] = '\0'; + + // Decode filetype + uint8_t type_low = ent.filetype & 7; + uint8_t type_hi = (ent.filetype >> 3) & 3; + + const char *filetype_str = filetype_attrs[ type_low ]; + + // Same logic used by hp9845b to add a question mark next to filetype + qmark = (type_low == DATA_FILETYPE && type_hi == 3) || + (type_low != DATA_FILETYPE && type_hi != 2); + + if (inc_ext) { + strcat(out , "."); + strcat(out , filetype_str); + } +} + +bool tape_image_t::decode_dir(void) +{ + unsigned sect_no = FIRST_DIR_SECTOR - 1; + unsigned sect_idx = SECTOR_LEN; + + dir.clear(); + + tape_word_t tmp; + + if (!read_sector_words(sect_no, sect_idx, 1, &tmp)) { + return false; + } + if (tmp != DIR_WORD_0) { + return false; + } + if (!read_sector_words(sect_no, sect_idx, 1, &tmp)) { + return false; + } + if (tmp != DIR_WORD_1) { + return false; + } + + // This is to check for overlapping files + std::bitset sect_in_use; + + while (1) { + if (!read_sector_words(sect_no, sect_idx, 1, &tmp)) { + return false; + } + if (tmp == DIR_LAST_WORD) { + // End of directory + break; + } + + if (dir.size() >= MAX_DIR_ENTRIES) { + // Too many entries + return false; + } + + dir_entry_t new_entry; + + // Filename + tape_word_to_bytes(tmp, new_entry.filename[ 0 ], new_entry.filename[ 1 ]); + if (!read_sector_words(sect_no, sect_idx, 1, &tmp)) { + return false; + } + tape_word_to_bytes(tmp, new_entry.filename[ 2 ], new_entry.filename[ 3 ]); + if (!read_sector_words(sect_no, sect_idx, 1, &tmp)) { + return false; + } + tape_word_to_bytes(tmp, new_entry.filename[ 4 ], new_entry.filename[ 5 ]); + if (!filename_check(new_entry.filename)) { + return false; + } + + // Protection, file type & file position + if (!read_sector_words(sect_no, sect_idx, 1, &tmp)) { + return false; + } + new_entry.protection = (tmp & 0x8000) != 0; + new_entry.filetype = ((tmp >> 10) & 0x1f); + new_entry.filepos = tmp & 0x3ff; + if (new_entry.filepos < FIRST_FILE_SECTOR || new_entry.filepos >= TOT_SECTORS) { + return false; + } + + // File size (# of records) + if (!read_sector_words(sect_no, sect_idx, 1, &tmp)) { + return false; + } + new_entry.n_recs = tmp; + + // Words per record + if (!read_sector_words(sect_no, sect_idx, 1, &tmp)) { + return false; + } + new_entry.wpr = tmp; + if (new_entry.wpr < 1) { + return false; + } + + new_entry.n_sects = ((unsigned)new_entry.wpr * new_entry.n_recs * 2 + SECTOR_LEN - 1) / SECTOR_LEN; + if (new_entry.n_sects < 1 || (new_entry.n_sects + new_entry.filepos) > TOT_SECTORS) { + return false; + } + + for (unsigned i = new_entry.filepos; i < new_entry.n_sects + new_entry.filepos; i++) { + if (sect_in_use[ i ]) { + return false; + } + sect_in_use[ i ] = true; + } + + dir.push_back(new_entry); + } + + // Check for inconsistency between alloc_map & sect_in_use + for (unsigned i = 0; i < FIRST_FILE_SECTOR; i++) { + sect_in_use[ i ] = alloc_map[ i ]; + } + + std::bitset tmp_map(~alloc_map & sect_in_use); + if (tmp_map.any()) { + // There is at least 1 sector that is in use by a file but it's empty/unallocated + return false; + } + + alloc_map = sect_in_use; + + return true; +} + +static tape_state_t& get_tape_state(imgtool::image &img) +{ + tape_state_t *ts = (tape_state_t*)img.extra_bytes(); + + return *ts; +} + +static tape_image_t& get_tape_image(tape_state_t& ts) +{ + if (ts.img == nullptr) { + ts.img = new tape_image_t; + } + + return *(ts.img); +} + +/******************************************************************************** + * Imgtool functions + ********************************************************************************/ +static imgtoolerr_t hp9845_tape_open(imgtool::image &image, imgtool::stream::ptr &&stream) +{ + tape_state_t& state = get_tape_state(image); + + state.stream = stream.release(); + + tape_image_t& tape_image = get_tape_image(state); + + imgtoolerr_t err = tape_image.load_from_file(state.stream); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t hp9845_tape_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *opts) +{ + tape_state_t& state = get_tape_state(image); + + state.stream = stream.release(); + + tape_image_t& tape_image = get_tape_image(state); + + tape_image.format_img(); + + return IMGTOOLERR_SUCCESS; +} + +static void hp9845_tape_close(imgtool::image &image) +{ + tape_state_t& state = get_tape_state(image); + tape_image_t& tape_image = get_tape_image(state); + + if (tape_image.is_dirty()) { + (void)tape_image.save_to_file(state.stream); + } + + delete state.stream; + + // Free tape_image + delete &tape_image; +} + +static imgtoolerr_t hp9845_tape_begin_enum (imgtool::directory &enumeration, const char *path) +{ + dir_state_t *ds = (dir_state_t*)enumeration.extra_bytes(); + + ds->dir_idx = 0; + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t hp9845_tape_next_enum (imgtool::directory &enumeration, imgtool_dirent &ent) +{ + tape_state_t& state = get_tape_state(enumeration.image()); + tape_image_t& tape_image = get_tape_image(state); + dir_state_t *ds = (dir_state_t*)enumeration.extra_bytes(); + + const dir_entry_t *entry = nullptr; + + if (!tape_image.get_dir_entry(ds->dir_idx, entry)) { + ent.eof = 1; + } else { + ds->dir_idx++; + + bool qmark; + + tape_image_t::get_filename_and_ext(*entry, true, ent.filename, qmark); + + // "filename" and "attr" fields try to look like the output of the "CAT" command + snprintf(ent.attr , sizeof(ent.attr) , "%c %02x%c %4u %4u %3u" , entry->protection ? '*' : ' ' , entry->filetype , qmark ? '?' : ' ' , entry->n_recs , entry->wpr * 2 , entry->filepos); + + ent.filesize = entry->n_sects * SECTOR_LEN; + } + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t hp9845_tape_free_space(imgtool::partition &partition, uint64_t *size) +{ + tape_state_t& state = get_tape_state(partition.image()); + tape_image_t& tape_image = get_tape_image(state); + + *size = tape_image.free_sectors() * SECTOR_LEN; + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t hp9845_tape_read_file(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf) +{ + tape_state_t& state = get_tape_state(partition.image()); + tape_image_t& tape_image = get_tape_image(state); + + unsigned idx; + + if (!tape_image.find_file(filename , false , idx)) { + return IMGTOOLERR_FILENOTFOUND; + } + + const dir_entry_t *ent = nullptr; + + tape_image.get_dir_entry(idx, ent); + + unsigned sect_no = ent->filepos; + unsigned n_sects = ent->n_sects; + tape_word_t buff_w[ WORDS_PER_SECTOR ]; + uint8_t buff_b[ SECTOR_LEN ]; + + while (n_sects--) { + if (!tape_image.get_sector(sect_no++, &buff_w[ 0 ])) { + return IMGTOOLERR_READERROR; + } + for (unsigned i = 0; i < WORDS_PER_SECTOR; i++) { + tape_image_t::tape_word_to_bytes(buff_w[ i ], buff_b[ i * 2 ], buff_b[ i * 2 + 1 ]); + } + + destf.write(buff_b , SECTOR_LEN); + } + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t hp9845_tape_write_file(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts) +{ + tape_state_t& state = get_tape_state(partition.image()); + tape_image_t& tape_image = get_tape_image(state); + + unsigned idx; + + if (tape_image.find_file(filename , true , idx)) { + // When overwriting a file, delete its old version first + tape_image.delete_dir_entry(idx); + } + + unsigned blocks = (unsigned)((sourcef.size() + SECTOR_LEN - 1) / SECTOR_LEN); + + if (!blocks) { + fprintf(stderr , "Null file, not writing..\n"); + return IMGTOOLERR_SUCCESS; + } + + dir_entry_t *ent = nullptr; + + if (!tape_image.alloc_new_file(blocks, ent)) { + return IMGTOOLERR_NOSPACE; + } + + unsigned s_no = ent->filepos; + + char fname[ CHARS_PER_FNAME + 1 ]; + char ext[ CHARS_PER_EXT + 1 ]; + + tape_image_t::split_filename_and_ext(filename, fname, ext); + + strncpy((char*)&ent->filename[ 0 ] , fname , CHARS_PER_FNAME); + + for (unsigned i = 0; i < blocks; i++) { + tape_word_t buff_w[ WORDS_PER_SECTOR ]; + uint8_t buff_b[ SECTOR_LEN ]; + + memset(&buff_b[ 0 ] , 0 , sizeof(buff_b)); + + if (sourcef.read(buff_b , SECTOR_LEN) != SECTOR_LEN && i != (blocks - 1)) { + return IMGTOOLERR_READERROR; + } + for (unsigned j = 0; j < WORDS_PER_SECTOR; j++) { + tape_image_t::bytes_to_tape_word(buff_b[ 2 * j ], buff_b[ 2 * j + 1 ], buff_w[ j ]); + } + tape_image.set_sector(s_no, buff_w); + s_no++; + } + + int wpr = opts->lookup_int('W'); + if (wpr == 0) { + wpr = WORDS_PER_SECTOR; + } else if (wpr > (blocks * WORDS_PER_SECTOR)) { + fprintf(stderr , "WPR value too large, using %u\n" , WORDS_PER_SECTOR); + wpr = WORDS_PER_SECTOR; + } + ent->wpr = (uint16_t)wpr; + + ent->n_recs = (uint16_t)((blocks * WORDS_PER_SECTOR) / wpr); + + unsigned type_low; + + if (opts->lookup_int('T') == 0) { + // File type defaults to DATA if no extension is given or extension is invalid + type_low = DATA_FILETYPE; + for (unsigned i = 0; i < 8; i++) { + if (strcmp(filetype_attrs[ i ] , ext) == 0) { + type_low = i; + break; + } + } + } else { + type_low = opts->lookup_int('T') - 1; + } + + // See tape_image_t::get_filename_and_ext for the logic behind file type + if (type_low == DATA_FILETYPE) { + ent->filetype = (uint8_t)type_low + (1U << 3); + } else { + ent->filetype = (uint8_t)type_low + (2U << 3); + } + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t hp9845_tape_delete_file(imgtool::partition &partition, const char *filename) +{ + tape_state_t& state = get_tape_state(partition.image()); + tape_image_t& tape_image = get_tape_image(state); + + unsigned idx; + + if (!tape_image.find_file(filename , true , idx)) { + return IMGTOOLERR_FILENOTFOUND; + } + + tape_image.delete_dir_entry(idx); + + return IMGTOOLERR_SUCCESS; +} + +#define HP9845_WRITEFILE_OPTSPEC "W[0]-65535;T[0]-8" + +OPTION_GUIDE_START(hp9845_write_optguide) + OPTION_INT('W' , "wpr" , "Words per record") + OPTION_ENUM_START('T' , "ftype" , "File type") + OPTION_ENUM(0 , "auto" , "Automatic (\"DATA\" or by extension)") + OPTION_ENUM(1 , "U" , "BKUP") + OPTION_ENUM(2 , "D" , "DATA") + OPTION_ENUM(3 , "P" , "PROG") + OPTION_ENUM(4 , "K" , "KEYS") + OPTION_ENUM(5 , "T" , "BDAT") + OPTION_ENUM(6 , "A" , "ALL") + OPTION_ENUM(7 , "B" , "BPRG") + OPTION_ENUM(8 , "O" , "OPRM") + OPTION_ENUM_END +OPTION_GUIDE_END + +void hp9845_tape_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch (state) { + case IMGTOOLINFO_STR_NAME: + strcpy(info->s = imgtool_temp_str(), "hp9845_tape"); + break; + + case IMGTOOLINFO_STR_DESCRIPTION: + strcpy(info->s = imgtool_temp_str(), "HP9845 tape image"); + break; + + case IMGTOOLINFO_STR_FILE: + strcpy(info->s = imgtool_temp_str(), __FILE__); + break; + + case IMGTOOLINFO_STR_FILE_EXTENSIONS: + strcpy(info->s = imgtool_temp_str(), "hti"); + break; + + case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: + info->i = sizeof(tape_state_t); + break; + + case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: + info->i = sizeof(dir_state_t); + break; + + case IMGTOOLINFO_PTR_OPEN: + info->open = hp9845_tape_open; + break; + + case IMGTOOLINFO_PTR_CREATE: + info->create = hp9845_tape_create; + break; + + case IMGTOOLINFO_PTR_CLOSE: + info->close = hp9845_tape_close; + break; + + case IMGTOOLINFO_PTR_BEGIN_ENUM: + info->begin_enum = hp9845_tape_begin_enum; + break; + + case IMGTOOLINFO_PTR_NEXT_ENUM: + info->next_enum = hp9845_tape_next_enum; + break; + + case IMGTOOLINFO_PTR_FREE_SPACE: + info->free_space = hp9845_tape_free_space; + break; + + case IMGTOOLINFO_PTR_READ_FILE: + info->read_file = hp9845_tape_read_file; + break; + + case IMGTOOLINFO_PTR_WRITE_FILE: + info->write_file = hp9845_tape_write_file; + break; + + case IMGTOOLINFO_PTR_DELETE_FILE: + info->delete_file = hp9845_tape_delete_file; + break; + + case IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE: + info->writefile_optguide = &hp9845_write_optguide; + break; + + case IMGTOOLINFO_STR_WRITEFILE_OPTSPEC: + strcpy(info->s = imgtool_temp_str(), HP9845_WRITEFILE_OPTSPEC); + break; + } +} + +/******************************************************************************** + * Filter functions + ********************************************************************************/ +static unsigned len_to_eor(imgtool::stream &inp) +{ + return SECTOR_LEN - (unsigned)(inp.tell() % SECTOR_LEN); +} + +static bool get_record_part(imgtool::stream &inp , void *buf , unsigned len) +{ + // Reading must never cross sector boundary + if (len > len_to_eor(inp)) { + return false; + } + + return inp.read(buf, len) == len; +} + +static bool dump_string(imgtool::stream &inp, imgtool::stream &out , unsigned len , bool add_eoln) +{ + uint8_t tmp[ SECTOR_LEN ]; + + if (!get_record_part(inp , tmp , len)) { + return false; + } + + // Sanitize string + for (unsigned i = 0; i < len; i++) { + if (!isascii(tmp[ i ]) || !isprint(tmp[ i ])) { + tmp[ i ] = ' '; + } + } + + out.write(tmp , len); + if (add_eoln) { + out.puts(EOLN); + } + + return true; +} + +static imgtoolerr_t hp9845data_read_file(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf) +{ + imgtool::stream::ptr inp_data; + imgtoolerr_t res; + uint8_t tmp[ 2 ]; + + inp_data = imgtool::stream::open_mem(NULL , 0); + if (!inp_data) + return IMGTOOLERR_OUTOFMEMORY; + + res = hp9845_tape_read_file(partition , filename , fork , *inp_data); + if (res != IMGTOOLERR_SUCCESS) + return res; + + inp_data->seek(0, SEEK_SET); + + uint16_t rec_type; + unsigned rec_len; + unsigned tmp_len; + unsigned accum_len = 0; + + do { + // Get record type + if (!get_record_part(*inp_data , tmp , 2)) { + return IMGTOOLERR_READERROR; + } + rec_type = (uint16_t)pick_integer_be(tmp , 0 , 2); + switch (rec_type) { + case REC_TYPE_EOR: + // End of record: just skip it + break; + + case REC_TYPE_FULLSTR: + // A string in a single piece + case REC_TYPE_1STSTR: + // First piece of a split string + case REC_TYPE_MIDSTR: + // Mid piece(s) of a split string + case REC_TYPE_ENDSTR: + // Closing piece of a split string + if (((rec_type == REC_TYPE_FULLSTR || rec_type == REC_TYPE_1STSTR) && accum_len > 0) || + ((rec_type == REC_TYPE_MIDSTR || rec_type == REC_TYPE_ENDSTR) && accum_len == 0)) { + fputs("Wrong sequence of string pieces\n" , stderr); + return IMGTOOLERR_CORRUPTFILE; + } + + if (!get_record_part(*inp_data , tmp , 2)) { + return IMGTOOLERR_READERROR; + } + tmp_len = (unsigned)pick_integer_be(tmp , 0 , 2); + + if (rec_type == REC_TYPE_FULLSTR || rec_type == REC_TYPE_1STSTR) { + accum_len = tmp_len; + } else if (tmp_len != accum_len) { + fputs("Wrong length of string piece\n" , stderr); + return IMGTOOLERR_CORRUPTFILE; + } + + if (rec_type == REC_TYPE_FULLSTR || rec_type == REC_TYPE_ENDSTR) { + rec_len = accum_len; + } else { + rec_len = std::min(accum_len , len_to_eor(*inp_data)); + } + if (!dump_string(*inp_data , destf , rec_len , rec_type == REC_TYPE_FULLSTR || rec_type == REC_TYPE_ENDSTR)) { + return IMGTOOLERR_READERROR; + } + if (rec_len & 1) { + // Keep length of string pieces even + get_record_part(*inp_data , tmp , 1); + } + accum_len -= rec_len; + break; + + case REC_TYPE_EOF: + // End of file + break; + + default: + fprintf(stderr , "Unknown record type (%04x)\n" , rec_type); + return IMGTOOLERR_CORRUPTFILE; + } + } while (rec_type != REC_TYPE_EOF); + + return IMGTOOLERR_SUCCESS; +} + +static bool split_string_n_dump(const char *s , imgtool::stream &dest) +{ + unsigned s_len = strlen(s); + uint16_t rec_type = REC_TYPE_1STSTR; + uint8_t tmp[ 4 ]; + bool at_least_one = false; + + while (1) { + unsigned free_len = len_to_eor(dest); + if (free_len <= 4) { + // Not enough free space at end of current record: fill with EORs + place_integer_be(tmp , 0 , 2 , REC_TYPE_EOR); + while (free_len) { + if (dest.write(tmp , 2) != 2) { + return false; + } + free_len -= 2; + } + } else { + unsigned s_part_len = std::min(free_len - 4 , s_len); + if (s_part_len == s_len) { + // Free space to EOR enough for what's left of string + break; + } + place_integer_be(tmp , 0 , 2 , rec_type); + place_integer_be(tmp , 2 , 2 , s_len); + if (dest.write(tmp , 4) != 4 || + dest.write(s, s_part_len) != s_part_len) { + return false; + } + rec_type = REC_TYPE_MIDSTR; + s_len -= s_part_len; + s += s_part_len; + at_least_one = true; + } + } + + place_integer_be(tmp , 0 , 2 , at_least_one ? REC_TYPE_ENDSTR : REC_TYPE_FULLSTR); + place_integer_be(tmp , 2 , 2 , s_len); + if (dest.write(tmp , 4) != 4 || + dest.write(s , s_len) != s_len) { + return false; + } + if (s_len & 1) { + tmp[ 0 ] = 0; + if (dest.write(tmp , 1) != 1) { + return false; + } + } + return true; +} + +static imgtoolerr_t hp9845data_write_file(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts) +{ + imgtool::stream::ptr out_data; + + out_data = imgtool::stream::open_mem(NULL , 0); + if (!out_data) + return IMGTOOLERR_OUTOFMEMORY; + + while (1) { + char line[ 256 ]; + + // Read input file one line at time + if (sourcef.core_file()->gets(line , sizeof(line)) == nullptr) { + // EOF + break; + } + line[ sizeof(line) - 1 ] = '\0'; + + // Strip space and non-ASCII characters from the end of the line + size_t line_len = strlen(line); + char *p = &line[ line_len ]; + while (p != line) { + char c = *(--p); + if (isascii(c) && !isspace(c)) { + break; + } + *p = '\0'; + } + + // Ignore empty lines + if (p == line) { + continue; + } + + if (!split_string_n_dump(line, *out_data)) { + return IMGTOOLERR_WRITEERROR; + } + } + + // Fill free space of last record with EOFs + unsigned free_len = len_to_eor(*out_data); + uint8_t tmp[ 2 ]; + place_integer_be(tmp , 0 , 2 , REC_TYPE_EOF); + + while (free_len) { + if (out_data->write(tmp , 2 ) != 2) { + return IMGTOOLERR_WRITEERROR; + } + free_len -= 2; + } + + out_data->seek(0 , SEEK_SET); + + imgtoolerr_t res = hp9845_tape_write_file(partition, filename, fork, *out_data, opts); + + return res; +} + +void filter_hp9845data_getinfo(uint32_t state, union filterinfo *info) +{ + switch (state) { + case FILTINFO_PTR_READFILE: + info->read_file = hp9845data_read_file; + break; + + case FILTINFO_PTR_WRITEFILE: + info->write_file = hp9845data_write_file; + break; + + case FILTINFO_STR_NAME: + info->s = "9845data"; + break; + + case FILTINFO_STR_HUMANNAME: + info->s = "HP9845 text-only DATA files"; + break; + + case FILTINFO_STR_EXTENSION: + info->s = "txt"; + break; + } +} diff --git a/src/tools/imgtool/modules/mac.cpp b/src/tools/imgtool/modules/mac.cpp new file mode 100644 index 0000000..a6eadf4 --- /dev/null +++ b/src/tools/imgtool/modules/mac.cpp @@ -0,0 +1,6443 @@ +// license:BSD-3-Clause +// copyright-holders:Raphael Nabet +/**************************************************************************** + + mac.c + + Handlers for Classic MacOS images (MFS and HFS formats). + + Raphael Nabet, 2003 + + TODO: + * add support for HFS write + +***************************************************************************** + + terminology: + disk block: 512-byte logical block. With sectors of 512 bytes, one logical + block is equivalent to one sector; when the sector size is not 512 + bytes, sectors are split or grouped to make 512-byte disk blocks. + allocation block: The File Manager always allocates logical disk blocks to + a file in groups called allocation blocks; an allocation block is + simply a group of consecutive logical blocks. The size of a volume's + allocation blocks depends on the capacity of the volume; there can be + at most 4094 (MFS) or 65535 (HFS) allocation blocks on a volume. + MFS (Macintosh File System): File system used by the early Macintosh. This + File system does not support folders (you may create folders on a MFS + disk, but such folders are not implemented on File System level but in + the Desktop file, and they are just a hint of how programs should list + files, i.e. you can't have two files with the same name on a volume + even if they are in two different folders), and it is not adequate for + large volumes. + HFS (Hierarchical File System): File system introduced with the HD20 + harddisk, the Macintosh Plus ROMs, and system 3.2 (IIRC). Contrary to + MFS, it supports hierarchical folders. Also, it is suitable for larger + volumes. + HFS+ (HFS Plus): New file system introduced with MacOS 8.1. It has a lot + in common with HFS, but it supports more allocation blocks (up to 4 + billions IIRC), and many extra features, including longer file names + (up to 255 UTF-16 Unicode chars). + tag data: with the GCR encoding, each disk block is associated with a 12 + (3.5" floppies) or 20 (HD20) byte tag record. This tag record contains + information on the block allocation status (whether it is allocated + in a file or free, which file is it belongs to, what offset the block + has in the file). This enables to recover all files whose data blocks + are still on the disk surface even if the disk catalog has been trashed + completely (though most file properties, like the name, type and + logical EOF, are not saved in the tag record and cannot be recovered). + + Organization of an MFS volume: + + Logical Contents Allocation block + block + + 0 - 1: System startup information + 2 - m: Master directory block (MDB) + + allocation block link pointers + m+1 - n: Directory file + n+1 - p-2: Other files and free space 0 - ((p-2)-(n+1))/k + p-1: Alternate MDB + p: Not used + usually, k = 2, m = 3, n = 16, p = 799 (SSDD 3.5" floppy) + with DSDD 3.5" floppy, I assume that p = 1599, but I don't know the other + values + + + Master Directory Block: + + Offset Length Description + ------ ------ ----------- + 0 2 Volume Signature + 2 4 Creation Date + 6 4 Last Modification Date + 10 2 Volume Attributes + 12 2 Number of Files In Root Directory + 14 2 First Block of Volume Bitmap + ... + + Links: + http://developer.apple.com/documentation/mac/Files/Files-99.html + http://developer.apple.com/documentation/mac/Files/Files-100.html + http://developer.apple.com/documentation/mac/Files/Files-101.html + http://developer.apple.com/documentation/mac/Files/Files-102.html + http://developer.apple.com/documentation/mac/Files/Files-103.html + http://developer.apple.com/documentation/mac/Files/Files-104.html + http://developer.apple.com/documentation/mac/Files/Files-105.html + http://developer.apple.com/documentation/mac/Files/Files-106.html + http://developer.apple.com/documentation/mac/Devices/Devices-121.html#MARKER-2-169 + http://developer.apple.com/documentation/mac/MoreToolbox/MoreToolbox-99.html + +*****************************************************************************/ + +#include "imgtool.h" +#include "charconv.h" +#include "iflopimg.h" + +#include "macutil.h" + +#include "formats/ap_dsk35.h" +#include "formats/imageutl.h" +#include "opresolv.h" + +#include +#include +#include +#include +#include + +/* if 1, check consistency of B-Tree (most of the checks will eventually be + suppressed when the image is opened as read-only and only enabled when + the image is opened as read/write) */ +#define BTREE_CHECKS 1 +/* if 1, check consistency of tag data when we are at risk of corrupting the + disk (file write and allocation) */ +#define TAG_CHECKS 1 +/* if 1, check consistency of tag data when reading files (not recommended + IMHO) */ +#define TAG_EXTRA_CHECKS 0 + +#if 0 +#pragma mark MISCELLANEOUS UTILITIES +#endif + +struct UINT16BE +{ + uint8_t bytes[2]; +}; + +struct UINT24BE +{ + uint8_t bytes[3]; +}; + +struct UINT32BE +{ + uint8_t bytes[4]; +}; + +static inline uint16_t get_UINT16BE(UINT16BE word) +{ + return (word.bytes[0] << 8) | word.bytes[1]; +} + +static inline void set_UINT16BE(UINT16BE *word, uint16_t data) +{ + word->bytes[0] = (data >> 8) & 0xff; + word->bytes[1] = data & 0xff; +} + +#if 0 +static inline uint32_t get_UINT24BE(UINT24BE word) +{ + return (word.bytes[0] << 16) | (word.bytes[1] << 8) | word.bytes[2]; +} + +static inline void set_UINT24BE(UINT24BE *word, uint32_t data) +{ + word->bytes[0] = (data >> 16) & 0xff; + word->bytes[1] = (data >> 8) & 0xff; + word->bytes[2] = data & 0xff; +} +#endif + +static inline uint32_t get_UINT32BE(UINT32BE word) +{ + return (word.bytes[0] << 24) | (word.bytes[1] << 16) | (word.bytes[2] << 8) | word.bytes[3]; +} + +static inline void set_UINT32BE(UINT32BE *word, uint32_t data) +{ + word->bytes[0] = (data >> 24) & 0xff; + word->bytes[1] = (data >> 16) & 0xff; + word->bytes[2] = (data >> 8) & 0xff; + word->bytes[3] = data & 0xff; +} + +/* + Macintosh string: first byte is length +*/ +typedef uint8_t mac_str27[28]; +typedef uint8_t mac_str31[32]; +typedef uint8_t mac_str63[64]; +typedef uint8_t mac_str255[256]; + +/* + Macintosh type/creator code: 4 char value +*/ +typedef UINT32BE mac_type; + +/* + point record, with the y and x coordinates +*/ +struct mac_point +{ + UINT16BE v; /* actually signed */ + UINT16BE h; /* actually signed */ +}; + +/* + rect record, with the corner coordinates +*/ +struct mac_rect +{ + UINT16BE top; /* actually signed */ + UINT16BE left; /* actually signed */ + UINT16BE bottom;/* actually signed */ + UINT16BE right; /* actually signed */ +}; + +/* + FInfo (Finder file info) record +*/ +struct mac_FInfo +{ + mac_type type; /* file type */ + mac_type creator; /* file creator */ + UINT16BE flags; /* Finder flags */ + mac_point location; /* file's location in window */ + /* If set to {0, 0}, and the inited flag is + clear, the Finder will place the item + automatically */ + UINT16BE fldr; /* MFS: window that contains file */ + /* -3: trash */ + /* -2: desktop */ + /* -1: new folder template?????? */ + /* 0: disk window ("root") */ + /* > 0: specific folder, index of FOBJ resource??? */ + /* The FOBJ resource contains some folder info; + the name of the resource is the folder name. */ + /* HFS & HFS+: + System 7: The window in which the file???s icon appears + System 8: reserved (set to 0) */ +}; + +/* + FXInfo (Finder extended file info) record -- not found in MFS +*/ +struct mac_FXInfo +{ + UINT16BE iconID; /* System 7: An ID number for the file???s icon; the + numbers that identify icons are assigned by the + Finder */ + /* System 8: Reserved (set to 0) */ + UINT16BE reserved[3]; /* Reserved (set to 0) */ + uint8_t script; /* System 7: if high-bit is set, the script code + for displaying the file name; ignored otherwise */ + /* System 8: Extended flags MSB(?) */ + uint8_t XFlags; /* Extended flags */ + UINT16BE comment; /* System 7: Comment ID if high-bit is clear */ + /* System 8: Reserved (set to 0) */ + UINT32BE putAway; /* Put away folder ID (i.e. if the user moves the + file onto the desktop, the directory ID of the + folder from which the user moves the file is + saved here) */ +}; + +/* + DInfo (Finder folder info) record -- not found in MFS +*/ +struct mac_DInfo +{ + mac_rect rect; /* Folder's window bounds */ + UINT16BE flags; /* Finder flags, e.g. kIsInvisible, kNameLocked, etc */ + mac_point location; /* Location of the folder in parent window */ + /* If set to {0, 0}, and the initied flag is + clear, the Finder will place the item + automatically */ + UINT16BE view; /* System 7: The manner in which folders are + displayed */ + /* System 8: reserved (set to 0) */ +}; + +/* + DXInfo (Finder extended folder info) record -- not found in MFS +*/ +struct mac_DXInfo +{ + mac_point scroll; /* Scroll position */ + UINT32BE openChain; /* System 7: chain of directory IDs for open folders */ + /* System 8: reserved (set to 0) */ + uint8_t script; /* System 7: if high-bit is set, the script code + for displaying the folder name; ignored otherwise */ + /* System 8: Extended flags MSB(?) */ + uint8_t XFlags; /* Extended flags */ + UINT16BE comment; /* System 7: Comment ID if high-bit is clear */ + /* System 8: Reserved (set to 0) */ + UINT32BE putAway; /* Put away folder ID (i.e. if the user moves the + folder onto the desktop, the directory ID of + the folder from which the user moves it is + saved here) */ +}; + +/* + defines for FInfo & DInfo flags fields +*/ +enum +{ + fif_isOnDesk = 0x0001, /* System 6: set if item is located on desktop (files and folders) */ + /* System 7: Unused (set to 0) */ + fif_color = 0x000E, /* color coding (files and folders) */ + fif_colorReserved = 0x0010, /* System 6: reserved??? */ + /* System 7: Unused (set to 0) */ + fif_requiresSwitchLaunch= 0x0020, /* System 6: ??? */ + /* System 7: Unused (set to 0) */ + fif_isShared = 0x0040, /* Applications files: if set, the */ + /* application can be executed by */ + /* multiple users simultaneously. */ + /* Otherwise, set to 0. */ + fif_hasNoINITs = 0x0080, /* Extensions/Control Panels: if set(?), */ + /* this file contains no INIT */ + /* resource */ + /* Otherwise, set to 0. */ + fif_hasBeenInited = 0x0100, /* System 6: The Finder has recorded information from + the file???s bundle resource into the desktop + database and given the file or folder a + position on the desktop. */ + /* System 7? 8?: Clear if the file contains desktop database */ + /* resources ('BNDL', 'FREF', 'open', 'kind'...) */ + /* that have not been added yet. Set only by the Finder */ + /* Reserved for folders - make sure this bit is cleared for folders */ + + /* bit 0x0200 was at a point (AOCE for system 7.x?) the letter bit for + AOCE, but was reserved before and it is reserved again in recent MacOS + releases. */ + + fif_hasCustomIcon = 0x0400, /* (files and folders) */ + fif_isStationery = 0x0800, /* System 7: (files only) */ + fif_nameLocked = 0x1000, /* (files and folders) */ + fif_hasBundle = 0x2000, /* Files only */ + fif_isInvisible = 0x4000, /* (files and folders) */ + fif_isAlias = 0x8000 /* System 7: (files only) */ +}; + +/* + mac_to_c_strncpy() + + Encode a macintosh string as a C string. The NUL character is escaped, + using the "%00" sequence. To avoid any ambiguity, '%' is escaped with + '%25'. + + dst (O): C string + n (I): length of buffer pointed to by dst + src (I): macintosh string (first byte is length) +*/ +static void mac_to_c_strncpy(char *dst, int n, uint8_t *src) +{ + size_t len = src[0]; + int i, j; + + i = j = 0; + while ((i < len) && (j < n-1)) + { + switch (src[i+1]) + { + case '\0': + if (j >= n-3) + goto exit; + dst[j] = '%'; + dst[j+1] = '0'; + dst[j+2] = '0'; + j += 3; + break; + + case '%': + if (j >= n-3) + goto exit; + dst[j] = '%'; + dst[j+1] = '2'; + dst[j+2] = '5'; + j += 3; + break; + + default: + dst[j] = src[i+1]; + j++; + break; + } + i++; + } + +exit: + if (n > 0) + dst[j] = '\0'; +} + +/* + c_to_mac_strncpy() + + Decode a C string to a macintosh string. The NUL character is escaped, + using the "%00" sequence. To avoid any ambiguity, '%' is escaped with + '%25'. + + dst (O): macintosh string (first byte is length) + src (I): C string + n (I): length of string pointed to by src +*/ +static void c_to_mac_strncpy(mac_str255 dst, const char *src, int n) +{ + size_t len; + int i; + char buf[3]; + + + len = 0; + i = 0; + while ((i < n) && (len < 255)) + { + switch (src[i]) + { + case '%': + if (i >= n-2) + goto exit; + buf[0] = src[i+1]; + buf[1] = src[i+2]; + buf[2] = '\0'; + dst[len+1] = strtoul(buf, NULL, 16); + i += 3; + break; + + default: + dst[len+1] = src[i]; + i++; + break; + } + len++; + } + +exit: + dst[0] = len; +} + +/* + mac_strcmp() + + Compare two macintosh strings + + s1 (I): the string to compare + s2 (I): the comparison string + + Return a zero if s1 and s2 are equal, a negative value if s1 is less than + s2, and a positive value if s1 is greater than s2. +*/ +#ifdef UNUSED_FUNCTION +static int mac_strcmp(const uint8_t *s1, const uint8_t *s2) +{ + size_t common_len; + + common_len = (s1[0] <= s2[0]) ? s1[0] : s2[0]; + + return memcmp(s1+1, s2+1, common_len) || ((int)s1[0] - s2[0]); +} +#endif + +/* + mac_stricmp() + + Compare two macintosh strings in a manner compatible with the macintosh HFS + file system. + + This functions emulates the way HFS (and MFS???) sorts string: this is + equivalent to the RelString macintosh toolbox call with the caseSensitive + parameter as false and the diacSensitive parameter as true. + + s1 (I): the string to compare + s2 (I): the comparison string + + Return a zero if s1 and s2 are equal, a negative value if s1 is less than + s2, and a positive value if s1 is greater than s2. + +Known issues: + Using this function makes sense with the MacRoman encoding, as it means the + computer will regard "DeskTop File", "Desktop File", "Desktop file", etc, + as the same file. (UNIX users will probably regard this as an heresy, but + you must consider that, unlike UNIX, the Macintosh was not designed for + droids, but error-prone human beings that may forget about case.) + + (Also, letters with diatrical signs follow the corresponding letters + without diacritical signs in the HFS catalog file. This does not matter, + though, since the Finder will not display files in the HFS catalog order, + but it will instead sort files according to whatever order is currently + selected in the View menu.) + + However, with other text encodings, the behavior will be completely absurd. + For instance, with the Greek encoding, it will think that the degree symbol + is the same letter (with different case) as the upper-case Psi, so that if + a program asks for a file called "90??" on a greek HFS volume, and there is + a file called "90??" on this volume, file "90??" will be opened. + Results will probably be even weirder with 2-byte scripts like Japanese or + Chinese. Of course, we are not going to fix this issue, since doing so + would break the compatibility with the original Macintosh OS. +*/ + +static int mac_stricmp(const uint8_t *s1, const uint8_t *s2) +{ + static const unsigned char mac_char_sort_table[256] = + { + /* \x00 \x01 \x02 \x03 \x04 \x05 \x06 \x07 */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + /* \x08 \x09 \x0a \x0b \x0c \x0d \x0e \x0f */ + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + /* \x10 \x11 \x12 \x13 \x14 \x15 \x16 \x17 */ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + /* \x18 \x19 \x1a \x1b \x1c \x1d \x1e \x1f */ + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + /* \x20 \x21 \x22 \x23 \x24 \x25 \x26 \x27 */ + 0x20, 0x21, 0x22, 0x27, 0x28, 0x29, 0x2a, 0x2b, + /* \x28 \x29 \x2a \x2b \x2c \x2d \x2e \x2f */ + 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, + /* \x30 \x31 \x32 \x33 \x34 \x35 \x36 \x37 */ + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, + /* \x38 \x39 \x3a \x3b \x3c \x3d \x3e \x3f */ + 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, + /* \x40 \x41 \x42 \x43 \x44 \x45 \x46 \x47 */ + 0x46, 0x47, 0x51, 0x52, 0x54, 0x55, 0x5a, 0x5b, + /* \x48 \x49 \x4a \x4b \x4c \x4d \x4e \x4f */ + 0x5c, 0x5d, 0x62, 0x63, 0x64, 0x65, 0x66, 0x68, + /* \x50 \x51 \x52 \x53 \x54 \x55 \x56 \x57 */ + 0x71, 0x72, 0x73, 0x74, 0x76, 0x77, 0x7c, 0x7d, + /* \x58 \x59 \x5a \x5b \x5c \x5d \x5e \x5f */ + 0x7e, 0x7f, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, + /* \x60 \x61 \x62 \x63 \x64 \x65 \x66 \x67 */ + 0x4d, 0x47, 0x51, 0x52, 0x54, 0x55, 0x5a, 0x5b, + /* \x68 \x69 \x6a \x6b \x6c \x6d \x6e \x6f */ + 0x5c, 0x5d, 0x62, 0x63, 0x64, 0x65, 0x66, 0x68, + /* \x70 \x71 \x72 \x73 \x74 \x75 \x76 \x77 */ + 0x71, 0x72, 0x73, 0x74, 0x76, 0x77, 0x7c, 0x7d, + /* \x78 \x79 \x7a \x7b \x7c \x7d \x7e \x7f */ + 0x7e, 0x7f, 0x81, 0x87, 0x88, 0x89, 0x8a, 0x8b, + /* \x80 \x81 \x82 \x83 \x84 \x85 \x86 \x87 */ + 0x49, 0x4b, 0x53, 0x56, 0x67, 0x69, 0x78, 0x4e, + /* \x88 \x89 \x8a \x8b \x8c \x8d \x8e \x8f */ + 0x48, 0x4f, 0x49, 0x4a, 0x4b, 0x53, 0x56, 0x57, + /* \x90 \x91 \x92 \x93 \x94 \x95 \x96 \x97 */ + 0x58, 0x59, 0x5e, 0x5f, 0x60, 0x61, 0x67, 0x6d, + /* \x98 \x99 \x9a \x9b \x9c \x9d \x9e \x9f */ + 0x6e, 0x6f, 0x69, 0x6a, 0x79, 0x7a, 0x7b, 0x78, + /* \xa0 \xa1 \xa2 \xa3 \xa4 \xa5 \xa6 \xa7 */ + 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x75, + /* \xa8 \xa9 \xaa \xab \xac \xad \xae \xaf */ + 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x4c, 0x6b, + /* \xb0 \xb1 \xb2 \xb3 \xb4 \xb5 \xb6 \xb7 */ + 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, + /* \xb8 \xb9 \xba \xbb \xbc \xbd \xbe \xbf */ + 0xa1, 0xa2, 0xa3, 0x50, 0x70, 0xa4, 0x4c, 0x6b, + /* \xc0 \xc1 \xc2 \xc3 \xc4 \xc5 \xc6 \xc7 */ + 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0x25, + /* \xc8 \xc9 \xca \xcb \xcc \xcd \xce \xcf */ + 0x26, 0xac, 0x20, 0x48, 0x4a, 0x6a, 0x6c, 0x6c, + /* \xd0 \xd1 \xd2 \xd3 \xd4 \xd5 \xd6 \xd7 */ + 0xad, 0xae, 0x23, 0x24, 0x2c, 0x2d, 0xaf, 0xb0, + /* \xd8 \xd9 \xda \xdb \xdc \xdd \xde \xdf */ + 0x80, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + /* \xe0 \xe1 \xe2 \xe3 \xe4 \xe5 \xe6 \xe7 */ + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + /* \xe8 \xe9 \xea \xeb \xec \xed \xee \xef */ + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + /* \xf0 \xf1 \xf2 \xf3 \xf4 \xf5 \xf6 \xf7 */ + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + /* \xf8 \xf9 \xfa \xfb \xfc \xfd \xfe \xff */ + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7 + }; + + size_t common_len; + int i; + int c1, c2; + + common_len = (s1[0] <= s2[0]) ? s1[0] : s2[0]; + + for (i=0; i n) + len = n; + dest[0] = len; + memcpy(dest+1, src+1, len); +} +#endif + +/* + disk image reference +*/ +struct mac_l1_imgref +{ + imgtool::image *image; + uint32_t heads; +}; + + + +static imgtoolerr_t mac_find_block(mac_l1_imgref *image, int block, + uint32_t *track, uint32_t *head, uint32_t *sector) +{ + *track = 0; + while(block >= (apple35_sectors_per_track(imgtool_floppy(*image->image), *track) * image->heads)) + { + block -= (apple35_sectors_per_track(imgtool_floppy(*image->image), (*track)++) * image->heads); + if (*track >= 80) + return IMGTOOLERR_SEEKERROR; + } + + *head = block / apple35_sectors_per_track(imgtool_floppy(*image->image), *track); + *sector = block % apple35_sectors_per_track(imgtool_floppy(*image->image), *track); + return IMGTOOLERR_SUCCESS; +} + + + +/* + image_read_block() + + Read one 512-byte block of data from a macintosh disk image + + image (I/O): level-1 image reference + block (I): address of block to read + dest (O): buffer where block data should be stored + + Return imgtool error code +*/ +static imgtoolerr_t image_read_block(mac_l1_imgref *image, uint32_t block, void *dest) +{ + imgtoolerr_t err; + floperr_t ferr; + uint32_t track, head, sector; + + err = mac_find_block(image, block, &track, &head, §or); + if (err) + return err; + + ferr = floppy_read_sector(imgtool_floppy(*image->image), head, track, sector, 0, dest, 512); + if (ferr) + return imgtool_floppy_error(ferr); + + return IMGTOOLERR_SUCCESS; +} + +/* + image_write_block() + + Read one 512-byte block of data from a macintosh disk image + + image (I/O): level-1 image reference + block (I): address of block to write + src (I): buffer with the block data + + Return imgtool error code +*/ +static imgtoolerr_t image_write_block(mac_l1_imgref *image, uint32_t block, const void *src) +{ + imgtoolerr_t err; + floperr_t ferr; + uint32_t track, head, sector; + + err = mac_find_block(image, block, &track, &head, §or); + if (err) + return err; + + ferr = floppy_write_sector(imgtool_floppy(*image->image), head, track, sector, 0, src, 512, 0); /* TODO: pass ddam argument from imgtool */ + if (ferr) + return imgtool_floppy_error(ferr); + + return IMGTOOLERR_SUCCESS; +} + +/* + image_get_tag_len() + + Get length of tag data (12 for GCR floppies, 20 for HD20, 0 otherwise) + + image (I/O): level-1 image reference + + Return tag length +*/ +static inline int image_get_tag_len(mac_l1_imgref *image) +{ + return 0; +} + + + +/* + image_read_tag() + + Read a 12- or 20-byte tag record associated with a disk block + + image (I/O): level-1 image reference + block (I): address of block to read + dest (O): buffer where tag data should be stored + + Return imgtool error code +*/ +static imgtoolerr_t image_read_tag(mac_l1_imgref *image, uint32_t block, void *dest) +{ + return IMGTOOLERR_UNEXPECTED; +} + +/* + image_write_tag() + + Write a 12- or 20-byte tag record associated with a disk block + + image (I/O): level-1 image reference + block (I): address of block to write + src (I): buffer with the tag data + + Return imgtool error code +*/ +static imgtoolerr_t image_write_tag(mac_l1_imgref *image, uint32_t block, const void *src) +{ + return IMGTOOLERR_UNEXPECTED; +} + +#if 0 +#pragma mark - +#pragma mark MFS/HFS WRAPPERS +#endif + +enum mac_format +{ + L2I_MFS, + L2I_HFS +}; + +enum mac_forkID { data_fork = 0x00, rsrc_fork = 0xff }; + +/* + MFS image ref +*/ +struct mfs_l2_imgref +{ + uint16_t dir_num_files; + uint16_t dir_start; + uint16_t dir_blk_len; + + uint16_t ABStart; + + mac_str27 volname; + + unsigned char ABlink_dirty[13]; /* dirty flag for each disk block in the ABlink array */ + uint8_t ABlink[6141]; +}; + +/* + HFS extent descriptor +*/ +struct hfs_extent +{ + UINT16BE stABN; /* first allocation block */ + UINT16BE numABlks; /* number of allocation blocks */ +}; + +/* + HFS likes to group extents by 3 (it is 8 with HFS+), so we create a + specific type. +*/ +typedef hfs_extent hfs_extent_3[3]; + +/* + MFS open file ref +*/ +struct mfs_fileref +{ + uint16_t stBlk; /* first allocation block of file */ +}; + +/* + HFS open file ref +*/ +struct hfs_fileref +{ + hfs_extent_3 extents; /* first 3 file extents */ + + uint32_t parID; /* CNID of parent directory (undefined for extent & catalog files) */ + mac_str31 filename; /* file name (undefined for extent & catalog files) */ +}; + +struct mac_l2_imgref; + +/* + MFS/HFS open file ref +*/ +struct mac_fileref +{ + struct mac_l2_imgref *l2_img; /* image pointer */ + + uint32_t fileID; /* file ID (a.k.a. CNID in HFS/HFS+) */ + + mac_forkID forkType; /* 0x00 for data, 0xff for resource */ + + uint32_t eof; /* logical end-of-file */ + uint32_t pLen; /* physical end-of-file */ + + uint32_t crPs; /* current position in file */ + + uint8_t reload_buf; + uint8_t block_buffer[512]; /* buffer with current file block */ + + union + { + mfs_fileref mfs; + hfs_fileref hfs; + }; +}; + +/* + open BT ref +*/ +struct mac_BTref +{ + struct mac_fileref fileref; /* open B-tree file ref */ + + uint16_t nodeSize; /* size of a node, in bytes */ + uint32_t rootNode; /* node number of root node */ + uint32_t firstLeafNode; /* node number of first leaf node */ + uint32_t attributes; /* persistent attributes about the tree */ + uint16_t treeDepth; /* maximum height (usually leaf nodes) */ + uint16_t maxKeyLength; /* maximum key length */ + + /* function to compare keys during tree searches */ + int (*key_compare_func)(const void *key1, const void *key2); + + void *node_buf; /* current node buffer */ +}; + +/* + Constants for BTHeaderRec attributes field +*/ +enum +{ + btha_badCloseMask = 0x00000001, /* reserved */ + btha_bigKeysMask = 0x00000002, /* key length field is 16 bits */ + btha_variableIndexKeysMask = 0x00000004 /* keys in index nodes are variable length */ +}; + +/* + HFS image ref +*/ +struct hfs_l2_imgref +{ + uint16_t VBM_start; + + uint16_t ABStart; + + mac_str27 volname; + + mac_BTref extents_BT; + mac_BTref cat_BT; + + uint8_t VBM[8192]; +}; + +/* + MFS/HFS image ref +*/ +struct mac_l2_imgref +{ + mac_l1_imgref l1_img; + + uint16_t numABs; + uint16_t blocksperAB; + + uint16_t freeABs; + + uint32_t nxtCNID; /* nxtFNum in MFS, nxtCNID in HFS */ + + mac_format format; + union + { + mfs_l2_imgref mfs; + hfs_l2_imgref hfs; + } u; +}; + +/* + MFS Master Directory Block +*/ +struct mfs_mdb +{ + uint8_t sigWord[2]; /* volume signature - always $D2D7 */ + UINT32BE crDate; /* date and time of volume creation */ + UINT32BE lsMod/*lsBkUp???*/;/* date and time of last modification (backup???) */ + UINT16BE atrb; /* volume attributes (0x0000) */ + /* bit 15 is set if volume is locked by software */ + UINT16BE nmFls; /* number of files in directory */ + UINT16BE dirSt; /* first block of directory */ + + UINT16BE blLn; /* length of directory in blocks (0x000C) */ + UINT16BE nmAlBlks; /* number of allocation blocks in volume (0x0187) */ + UINT32BE alBlkSiz; /* size (in bytes) of allocation blocks (0x00000400) */ + UINT32BE clpSiz; /* default clump size - number of bytes to allocate + when a file grows (0x00002000) */ + UINT16BE alBlSt; /* first allocation block in volume (0x0010) */ + + UINT32BE nxtFNum; /* next unused file number */ + UINT16BE freeABs; /* number of unused allocation blocks */ + + mac_str27 VN; /* volume name */ + + uint8_t ABlink[512-64];/* Link array for file ABs. Array of nmAlBlks + 12-bit-long entries, indexed by AB address. If an + AB belongs to no file, the entry is 0; if an AB is + the last in any file, the entry is 1; if an AB + belongs to a file and is not the last one, the + entry is the AB address of the next file AB plus 1. + Note that the array extends on as many consecutive + disk blocks as needed (usually the MDB block plus + the next one). Incidentally, this array is not + saved in the secondary MDB: presumably, the idea + was that the disk utility could rely on the tag + data to rebuild the link array if it should ever + be corrupted. */ +}; + +/* + HFS Master Directory Block +*/ +struct hfs_mdb +{ +/* First fields are similar to MFS, though several fields have a different meaning */ + uint8_t sigWord[2]; /* volume signature - always $D2D7 */ + UINT32BE crDate; /* date and time of volume creation */ + UINT32BE lsMod; /* date and time of last modification */ + UINT16BE atrb; /* volume attributes (0x0000) */ + /* bit 15 is set if volume is locked by software */ + UINT16BE nmFls; /* number of files in root folder */ + UINT16BE VBMSt; /* first block of volume bitmap */ + UINT16BE allocPtr; /* start of next allocation search */ + + UINT16BE nmAlBlks; /* number of allocation blocks in volume */ + UINT32BE alBlkSiz; /* size (in bytes) of allocation blocks */ + UINT32BE clpSiz; /* default clump size - number of bytes to allocate + when a file grows */ + UINT16BE alBlSt; /* first allocation block in volume (0x0010) */ + UINT32BE nxtCNID; /* next unused catalog node ID */ + UINT16BE freeABs; /* number of unused allocation blocks */ + mac_str27 VN; /* volume name */ + +/* next fields are HFS-specific */ + + UINT32BE volBkUp; /* date and time of last backup */ + UINT16BE vSeqNum; /* volume backup sequence number */ + UINT32BE wrCnt; /* volume write count */ + UINT32BE xtClpSiz; /* clump size for extents overflow file */ + UINT32BE ctClpSiz; /* clump size for catalog file */ + UINT16BE nmRtDirs; /* number of directories in root folder */ + UINT32BE filCnt; /* number of files in volume */ + UINT32BE dirCnt; /* number of directories in volume */ + uint8_t fndrInfo[32]; /* information used by the Finder */ + + union + { + struct + { + UINT16BE VCSize; /* size (in blocks) of volume cache */ + UINT16BE VBMCSize; /* size (in blocks) of volume bitmap cache */ + UINT16BE ctlCSize; /* size (in blocks) of common volume cache */ + }; + struct + { + UINT16BE embedSigWord; /* embedded volume signature */ + hfs_extent embedExtent; /* embedded volume location and size */ + } v2; + } u; + + UINT32BE xtFlSize; /* size (in bytes) of extents overflow file */ + hfs_extent_3 xtExtRec; /* extent record for extents overflow file */ + UINT32BE ctFlSize; /* size (in bytes) of catalog file */ + hfs_extent_3 ctExtRec; /* extent record for catalog file */ +}; + +/* to save a little stack space, we use the same buffer for MDB and next blocks */ +union img_open_buf +{ + struct mfs_mdb mfs_mdb; + struct hfs_mdb hfs_mdb; + uint8_t raw[512]; +}; + +/* + Information extracted from catalog/directory +*/ +struct mac_dirent +{ + uint16_t dataRecType; /* type of data record */ + + mac_FInfo flFinderInfo; /* information used by the Finder */ + mac_FXInfo flXFinderInfo; /* information used by the Finder */ + + uint8_t flags; /* bit 0=1 if file locked */ + + uint32_t fileID; /* file ID in directory/catalog */ + + uint32_t dataLogicalSize; /* logical EOF of data fork */ + uint32_t dataPhysicalSize; /* physical EOF of data fork */ + uint32_t rsrcLogicalSize; /* logical EOF of resource fork */ + uint32_t rsrcPhysicalSize; /* physical EOF of resource fork */ + + uint32_t createDate; /* date and time of creation */ + uint32_t modifyDate; /* date and time of last modification */ +}; + +/* + Tag record for GCR floppies (12 bytes) + + And, no, I don't know the format of the 20-byte tag record of the HD20 +*/ +struct floppy_tag_record +{ + UINT32BE fileID; /* a.k.a. CNID */ + /* a value of 1 seems to be the default for non-AB blocks, but this is not consistent */ + uint8_t ftype; /* bit 1 = 1 if resource fork */ + /* bit 0 = 1 if block is allocated to user file (i.e. it is not + in HFS extent & catalog, and not in non-AB blocks such + as MDB and MFS directory)??? */ + /* bit 7 seems to be used, but I don't know what it means */ + /* a value of $FF seems to be the default for non-AB blocks, but this is not consistent */ + uint8_t fattr; /* bit 0 = 1 if locked(?) */ + /* a value of $FF seems to be the default for non-AB blocks, but this is not consistent */ + UINT16BE fblock; /* relative file block number (enough for any volume up to 32 MBytes in size) */ + UINT32BE wrCnt; /* MFS: date and time of last write */ + /* HFS: seems related to the wrCnt field in the mdb, i.e. + each time a volume is written to, the current value of + wrCnt is written in the tag field, then it is incremented */ + /* (DV17 says "disk block number", but it cannot be true) */ +}; + +#ifdef UNUSED_FUNCTION +static void hfs_image_close(struct mac_l2_imgref *l2_img); +#endif +static imgtoolerr_t mfs_file_get_nth_block_address(struct mac_fileref *fileref, uint32_t block_num, uint32_t *block_address); +static imgtoolerr_t hfs_file_get_nth_block_address(struct mac_fileref *fileref, uint32_t block_num, uint32_t *block_address); +static imgtoolerr_t mfs_lookup_path(struct mac_l2_imgref *l2_img, const char *fpath, mac_str255 filename, mac_dirent *cat_info, int create_it); +static imgtoolerr_t hfs_lookup_path(struct mac_l2_imgref *l2_img, const char *fpath, uint32_t *parID, mac_str255 filename, mac_dirent *cat_info); +static imgtoolerr_t mfs_file_open(struct mac_l2_imgref *l2_img, const mac_str255 filename, mac_forkID fork, struct mac_fileref *fileref); +static imgtoolerr_t hfs_file_open(struct mac_l2_imgref *l2_img, uint32_t parID, const mac_str255 filename, mac_forkID fork, struct mac_fileref *fileref); +static imgtoolerr_t mfs_file_setABeof(struct mac_fileref *fileref, uint32_t newABeof); +static imgtoolerr_t mfs_dir_update(struct mac_fileref *fileref); + +static struct mac_l2_imgref *get_imgref(imgtool::image &img) +{ + return (struct mac_l2_imgref *) imgtool_floppy_extrabytes(img); +} + + +#ifdef UNUSED_FUNCTION +/* + mac_image_close + + Close a macintosh image. + + l2_img (I/O): level-2 image reference +*/ +static void mac_image_close(struct mac_l2_imgref *l2_img) +{ + switch (l2_img->format) + { + case L2I_MFS: + break; + + case L2I_HFS: + hfs_image_close(l2_img); + break; + } +} +#endif + +/* + mac_lookup_path + + Resolve a file path, and translate it to a parID + filename pair that enables + to do an efficient file search on a HFS volume (and an inefficient one on + MFS, but it is not an issue as MFS volumes typically have a few dozens + files, vs. possibly thousands with HFS volumes). + + l2_img (I/O): level-2 image reference + fpath (I): file path (C string) + parID (O): set to the CNID of the parent directory if the volume is in HFS + format (reserved for MFS volumes) + filename (O): set to the actual name of the file, with capitalization matching + the one on the volume rather than the one in the fpath parameter (Mac + string) + cat_info (O): catalog info for this file extracted from the catalog file + (may be NULL) + + Return imgtool error code +*/ +static imgtoolerr_t mac_lookup_path(struct mac_l2_imgref *l2_img, const char *fpath, uint32_t *parID, mac_str255 filename, mac_dirent *cat_info, int create_it) +{ + imgtoolerr_t err = IMGTOOLERR_UNEXPECTED; + + switch (l2_img->format) + { + case L2I_MFS: + *parID = 0; + err = mfs_lookup_path(l2_img, fpath, filename, cat_info, create_it); + break; + + case L2I_HFS: + err = hfs_lookup_path(l2_img, fpath, parID, filename, cat_info); + break; + } + return err; +} + +/* + mac_file_open + + Open a file located on a macintosh image + + l2_img (I/O): level-2 image reference + parID (I): CNID of the parent directory if the volume is in HFS format + (reserved for MFS volumes) + filename (I): name of the file (Mac string) + mac_forkID (I): tells which fork should be opened + fileref (O): mac file reference to open + + Return imgtool error code +*/ +static imgtoolerr_t mac_file_open(struct mac_l2_imgref *l2_img, uint32_t parID, const mac_str255 filename, mac_forkID fork, struct mac_fileref *fileref) +{ + switch (l2_img->format) + { + case L2I_MFS: + return mfs_file_open(l2_img, filename, fork, fileref); + + case L2I_HFS: + return hfs_file_open(l2_img, parID, filename, fork, fileref); + } + + return IMGTOOLERR_UNEXPECTED; +} + +/* + mac_file_read + + Read data from an open mac file, starting at current position in file + + fileref (I/O): mac file reference + len (I): number of bytes to read + dest (O): destination buffer + + Return imgtool error code +*/ +static imgtoolerr_t mac_file_read(struct mac_fileref *fileref, uint32_t len, void *dest) +{ + uint32_t block = 0; + floppy_tag_record tag; + int run_len; + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + + if ((fileref->crPs + len) > fileref->eof) + /* EOF */ + return IMGTOOLERR_UNEXPECTED; + + while (len > 0) + { + if (fileref->reload_buf) + { + switch (fileref->l2_img->format) + { + case L2I_MFS: + err = mfs_file_get_nth_block_address(fileref, fileref->crPs/512, &block); + break; + + case L2I_HFS: + err = hfs_file_get_nth_block_address(fileref, fileref->crPs/512, &block); + break; + } + if (err) + return err; + err = image_read_block(&fileref->l2_img->l1_img, block, fileref->block_buffer); + if (err) + return err; + fileref->reload_buf = false; + + if (TAG_EXTRA_CHECKS) + { + /* optional check */ + if (image_get_tag_len(&fileref->l2_img->l1_img) == 12) + { + err = image_read_tag(&fileref->l2_img->l1_img, block, &tag); + if (err) + return err; + + if ((get_UINT32BE(tag.fileID) != fileref->fileID) + || (((tag.ftype & 2) != 0) != (fileref->forkType == rsrc_fork)) + || (get_UINT16BE(tag.fblock) != ((fileref->crPs/512) & 0xffff))) + { + return IMGTOOLERR_CORRUPTIMAGE; + } + } + } + } + run_len = 512 - (fileref->crPs % 512); + if (run_len > len) + run_len = len; + + memcpy(dest, fileref->block_buffer+(fileref->crPs % 512), run_len); + len -= run_len; + dest = (uint8_t *)dest + run_len; + fileref->crPs += run_len; + if ((fileref->crPs % 512) == 0) + fileref->reload_buf = true; + } + + return IMGTOOLERR_SUCCESS; +} + +/* + mac_file_write + + Write data to an open mac file, starting at current position in file + + fileref (I/O): mac file reference + len (I): number of bytes to read + dest (O): destination buffer + + Return imgtool error code +*/ +static imgtoolerr_t mac_file_write(struct mac_fileref *fileref, uint32_t len, const void *src) +{ + uint32_t block = 0; + floppy_tag_record tag; + int run_len; + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + + if ((fileref->crPs + len) > fileref->eof) + /* EOF */ + return IMGTOOLERR_UNEXPECTED; + + while (len > 0) + { + switch (fileref->l2_img->format) + { + case L2I_MFS: + err = mfs_file_get_nth_block_address(fileref, fileref->crPs/512, &block); + break; + + case L2I_HFS: + err = hfs_file_get_nth_block_address(fileref, fileref->crPs/512, &block); + break; + } + if (err) + return err; + + if (TAG_CHECKS) + { + /* optional check */ + if (image_get_tag_len(&fileref->l2_img->l1_img) == 12) + { + err = image_read_tag(&fileref->l2_img->l1_img, block, &tag); + if (err) + return err; + + if ((get_UINT32BE(tag.fileID) != fileref->fileID) + || (((tag.ftype & 2) != 0) != (fileref->forkType == rsrc_fork)) + || (get_UINT16BE(tag.fblock) != ((fileref->crPs/512) & 0xffff))) + { + return IMGTOOLERR_CORRUPTIMAGE; + } + } + } + + if (fileref->reload_buf) + { + err = image_read_block(&fileref->l2_img->l1_img, block, fileref->block_buffer); + if (err) + return err; + fileref->reload_buf = false; + } + run_len = 512 - (fileref->crPs % 512); + if (run_len > len) + run_len = len; + + memcpy(fileref->block_buffer+(fileref->crPs % 512), src, run_len); + err = image_write_block(&fileref->l2_img->l1_img, block, fileref->block_buffer); + if (err) + return err; + /* update tag data */ + if (image_get_tag_len(&fileref->l2_img->l1_img) == 12) + { + if (!TAG_CHECKS) + { + err = image_read_tag(&fileref->l2_img->l1_img, block, &tag); + if (err) + return err; + } + + switch (fileref->l2_img->format) + { + case L2I_MFS: + set_UINT32BE(&tag.wrCnt, mac_time_now()); + break; + + case L2I_HFS: + /*set_UINT32BE(&tag.wrCnt, ++fileref->l2_img.u.hfs.wrCnt);*/ /* ***TODO*** */ + break; + } + + err = image_write_tag(&fileref->l2_img->l1_img, block, &tag); + if (err) + return err; + } + len -= run_len; + src = (const uint8_t *)src + run_len; + fileref->crPs += run_len; + if ((fileref->crPs % 512) == 0) + fileref->reload_buf = true; + } + + return IMGTOOLERR_SUCCESS; +} + +#ifdef UNUSED_FUNCTION +/* + mac_file_tell + + Get current position in an open mac file + + fileref (I/O): mac file reference + filePos (O): current position in file + + Return imgtool error code +*/ +static imgtoolerr_t mac_file_tell(struct mac_fileref *fileref, uint32_t *filePos) +{ + *filePos = fileref->crPs; + + return IMGTOOLERR_SUCCESS; +} +#endif + +/* + mac_file_seek + + Set current position in an open mac file + + fileref (I/O): mac file reference + filePos (I): new position in file + + Return imgtool error code +*/ +static imgtoolerr_t mac_file_seek(struct mac_fileref *fileref, uint32_t filePos) +{ + if ((fileref->crPs / 512) != (filePos / 512)) + fileref->reload_buf = true; + + fileref->crPs = filePos; + + return IMGTOOLERR_SUCCESS; +} + +/* + mac_file_seteof + + Set the position of the EOF in an open mac file + + fileref (I/O): mac file reference + newEof (I): new position of EOF in file + + Return imgtool error code +*/ +static imgtoolerr_t mac_file_seteof(struct mac_fileref *fileref, uint32_t newEof) +{ + uint32_t newABEof; + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + + newABEof = (newEof + fileref->l2_img->blocksperAB * 512 - 1) / (fileref->l2_img->blocksperAB * 512); + +#if 0 + if (fileref->pLen % (fileref->l2_img->blocksperAB * 512)) + return IMGTOOLERR_CORRUPTIMAGE; +#endif + + if (newEof < fileref->eof) + fileref->eof = newEof; + + switch (fileref->l2_img->format) + { + case L2I_MFS: + err = mfs_file_setABeof(fileref, newABEof); + break; + + case L2I_HFS: + err = IMGTOOLERR_UNIMPLEMENTED; + break; + } + if (err) + return err; + + fileref->eof = newEof; + + err = mfs_dir_update(fileref); + if (err) + return err; + + /* update current pos if beyond new EOF */ +#if 0 + if (fileref->crPs > newEof) + { + if ((fileref->crPs / 512) != (newEof / 512)) + fileref->reload_buf = true; + + fileref->crPs = newEof; + } +#endif + + return IMGTOOLERR_SUCCESS; +} + +#if 0 +#pragma mark - +#pragma mark MFS IMPLEMENTATION +#endif + +/* + directory entry for use in the directory file + + Note the structure is variable length. It is always word-aligned, and + cannot cross block boundaries. + + Note that the directory does not seem to be sorted: the order in which + files appear does not match file names, and it does not always match file + IDs. +*/ +struct mfs_dir_entry +{ + uint8_t flags; /* bit 7=1 if entry used, bit 0=1 if file locked */ + /* 0x00 means end of block: if we are not done + with reading the directory, the remnants will + be read from next block */ + uint8_t flVersNum; /* version number (usually 0x00, but I don't + have the IM volume that describes it) */ + mac_FInfo flFinderInfo; /* information used by the Finder */ + + UINT32BE fileID; /* file ID */ + + UINT16BE dataStartBlock; /* first allocation block of data fork */ + UINT32BE dataLogicalSize; /* logical EOF of data fork */ + UINT32BE dataPhysicalSize; /* physical EOF of data fork */ + UINT16BE rsrcStartBlock; /* first allocation block of resource fork */ + UINT32BE rsrcLogicalSize; /* logical EOF of resource fork */ + UINT32BE rsrcPhysicalSize; /* physical EOF of resource fork */ + + UINT32BE createDate; /* date and time of creation */ + UINT32BE modifyDate; /* date and time of last modification */ + + uint8_t name[1]; /* first char is length of file name */ + /* next chars are file name - 255 chars at most */ + /* IIRC, Finder 7 only supports 31 chars, + wheareas earlier versions support 63 chars */ +}; + +/* + FOBJ desktop resource: describes a folder, or the location of the volume + icon. + + In typical Apple manner, this resource is not documented. However, I have + managed to reverse engineer some parts of it. +*/ +struct mfs_FOBJ +{ + uint8_t unknown0[2]; /* $00: $0004 for disk, $0008 for folder??? */ + mac_point location; /* $02: location in parent window */ + uint8_t unknown1[4]; /* $06: ??? */ + uint8_t view; /* $0A: manner in which folders are displayed??? */ + uint8_t unknown2; /* $0B: ??? */ + UINT16BE par_fldr; /* $0C: parent folder ID */ + uint8_t unknown3[10]; /* $0E: ??? */ + UINT16BE unknown4; /* $18: ??? */ + UINT32BE createDate; /* $1A: date and time of creation */ + UINT32BE modifyDate; /* $1E: date and time of last modification */ + UINT16BE unknown5; /* $22: put-away folder ID?????? */ + uint8_t unknown6[8]; /* $24: ??? */ + mac_rect bounds; /* $2C: window bounds */ + mac_point scroll; /* $34: current scroll offset??? */ + union + { /* I think there are two versions of the structure */ + struct + { + UINT16BE item_count; /* number of items (folders and files) in + this folder */ + UINT32BE item_descs[1]; /* this variable-length array has + item_count entries - meaning of entry is unknown */ + } v1; + struct + { + UINT16BE zerofill; /* always 0? */ + UINT16BE unknown0; /* always 0??? */ + UINT16BE item_count; /* number of items (folders and files) in + this folder */ + uint8_t unknown1[20]; /* ??? */ + uint8_t name[1]; /* variable-length macintosh string */ + } v2; + } u; +}; + +/* + MFS open dir ref +*/ +struct mfs_dirref +{ + struct mac_l2_imgref *l2_img; /* image pointer */ + uint16_t index; /* current file index in the disk directory */ + uint16_t cur_block; /* current block offset in directory file */ + uint16_t cur_offset; /* current byte offset in current block of directory file */ + uint8_t block_buffer[512]; /* buffer with current directory block */ +}; + + + +static imgtoolerr_t mfs_image_create(imgtool::image &image, imgtool::stream::ptr &&dummy, util::option_resolution *opts) +{ + imgtoolerr_t err; + uint8_t buffer[512]; + uint32_t heads, tracks, sector_bytes, i; + uint32_t total_disk_blocks, total_allocation_blocks, allocation_block_size; + uint32_t free_allocation_blocks; + + heads = opts->lookup_int('H'); + tracks = opts->lookup_int('T'); + sector_bytes = opts->lookup_int('L'); + + get_imgref(image)->l1_img.image = ℑ + get_imgref(image)->l1_img.heads = heads; + + if (sector_bytes != 512) + return IMGTOOLERR_UNEXPECTED; + + /* computation */ + allocation_block_size = 1024; + total_disk_blocks = 0; + for (i = 0; i < tracks; i++) + total_disk_blocks += heads * apple35_sectors_per_track(imgtool_floppy(image), i) * sector_bytes / 512; + total_allocation_blocks = total_disk_blocks / (allocation_block_size / 512); + free_allocation_blocks = total_allocation_blocks - 3; + + /* write master directory block */ + memset(buffer, 0, sizeof(buffer)); + place_integer_be(buffer, 0, 2, 0xd2d7); /* signature */ + place_integer_be(buffer, 2, 4, mac_time_now()); /* creation date */ + place_integer_be(buffer, 6, 4, mac_time_now()); /* last modified date */ + place_integer_be(buffer, 10, 2, 0); /* volume attributes */ + place_integer_be(buffer, 12, 2, 0); /* number of files in directory */ + place_integer_be(buffer, 14, 2, 4); /* first block of directory */ + place_integer_be(buffer, 16, 2, 12); /* length of directory in blocks */ + place_integer_be(buffer, 18, 2, total_allocation_blocks); /* allocation blocks on volume count */ + place_integer_be(buffer, 20, 4, allocation_block_size); /* allocation block size */ + place_integer_be(buffer, 24, 4, 8192); /* default clumping size */ + place_integer_be(buffer, 28, 2, 16); /* first allocation block on volume */ + place_integer_be(buffer, 30, 4, 2); /* next unused catalog node */ + place_integer_be(buffer, 34, 2, free_allocation_blocks); /* free allocation block count */ + pascal_from_c_string(&buffer[36], 28, "Untitled"); /* volume title */ + + err = image_write_block(&get_imgref(image)->l1_img, 2, buffer); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + + + +/* + mfs_image_open + + Open a MFS image. Image must already be open on level 1. This function + should not be called directly: call mac_image_open() instead. + + l2_img (I/O): level-2 image reference to open (l1_img and format fields + must be initialized) + img_open_buf (I): buffer with the MDB block + + Return imgtool error code +*/ +static imgtoolerr_t mfs_image_open(imgtool::image &image, imgtool::stream::ptr &&dummy) +{ + imgtoolerr_t err; + struct mac_l2_imgref *l2_img; + img_open_buf buf_local; + img_open_buf *buf; + + l2_img = get_imgref(image); + l2_img->l1_img.image = ℑ + l2_img->l1_img.heads = 1; + l2_img->format = L2I_MFS; + + /* read MDB */ + err = image_read_block(&l2_img->l1_img, 2, &buf_local.raw); + if (err) + return err; + buf = &buf_local; + + /* check signature word */ + if ((buf->mfs_mdb.sigWord[0] != 0xd2) || (buf->mfs_mdb.sigWord[1] != 0xd7) + || (buf->mfs_mdb.VN[0] > 27)) + return IMGTOOLERR_CORRUPTIMAGE; + + l2_img->u.mfs.dir_num_files = get_UINT16BE(buf->mfs_mdb.nmFls); + l2_img->u.mfs.dir_start = get_UINT16BE(buf->mfs_mdb.dirSt); + l2_img->u.mfs.dir_blk_len = get_UINT16BE(buf->mfs_mdb.blLn); + + l2_img->numABs = get_UINT16BE(buf->mfs_mdb.nmAlBlks); + if ((get_UINT32BE(buf->mfs_mdb.alBlkSiz) % 512) || (get_UINT32BE(buf->mfs_mdb.alBlkSiz) == 0)) + return IMGTOOLERR_CORRUPTIMAGE; + l2_img->blocksperAB = get_UINT32BE(buf->mfs_mdb.alBlkSiz) / 512; + l2_img->u.mfs.ABStart = get_UINT16BE(buf->mfs_mdb.alBlSt); + + l2_img->nxtCNID = get_UINT32BE(buf->mfs_mdb.nxtFNum); + + l2_img->freeABs = get_UINT16BE(buf->mfs_mdb.freeABs); + + mac_strcpy(l2_img->u.mfs.volname, buf->mfs_mdb.VN); + + if (l2_img->numABs > 4094) + return IMGTOOLERR_CORRUPTIMAGE; + + /* extract link array */ + { + int byte_len = l2_img->numABs + ((l2_img->numABs + 1) >> 1); + int cur_byte; + int cur_block; + int block_len = sizeof(buf->mfs_mdb.ABlink); + + /* clear dirty flags */ + for (cur_block=0; cur_block<13; cur_block++) + l2_img->u.mfs.ABlink_dirty[cur_block] = 0; + + /* append the chunk after MDB to link array */ + cur_byte = 0; + if (block_len > (byte_len - cur_byte)) + block_len = byte_len - cur_byte; + memcpy(l2_img->u.mfs.ABlink+cur_byte, buf->mfs_mdb.ABlink, block_len); + cur_byte += block_len; + cur_block = 2; + while (cur_byte < byte_len) + { + /* read next block */ + cur_block++; + err = image_read_block(&l2_img->l1_img, cur_block, buf->raw); + if (err) + return err; + block_len = 512; + + /* append this block to link array */ + if (block_len > (byte_len - cur_byte)) + block_len = byte_len - cur_byte; + memcpy(l2_img->u.mfs.ABlink+cur_byte, buf->raw, block_len); + cur_byte += block_len; + } + /* check that link array and directory don't overlap */ + if (cur_block >= l2_img->u.mfs.dir_start) + return IMGTOOLERR_CORRUPTIMAGE; + } + + return IMGTOOLERR_SUCCESS; +} + +/* + mfs_update_mdb + + Update MDB on disk + + l2_img (I/O): level-2 image reference + + Return imgtool error code +*/ +static imgtoolerr_t mfs_update_mdb(struct mac_l2_imgref *l2_img) +{ + imgtoolerr_t err; + union + { + struct mfs_mdb mfs_mdb; + uint8_t raw[512]; + } buf; + + assert(l2_img->format == L2I_MFS); + + /* read MDB */ + err = image_read_block(&l2_img->l1_img, 2, &buf.mfs_mdb); + if (err) + return err; + + set_UINT16BE(&buf.mfs_mdb.nmFls, l2_img->u.mfs.dir_num_files); +#if 0 /* these fields are never changed */ + set_UINT16BE(&buf.mfs_mdb.dirSt, l2_img->u.mfs.dir_start); + set_UINT16BE(&buf.mfs_mdb.blLn, l2_img->u.mfs.dir_blk_len); + + set_UINT16BE(&buf.mfs_mdb.nmAlBlks, l2_img->numABs); + set_UINT32BE(&buf.mfs_mdb.alBlkSiz, l2_img->blocksperAB*512); + set_UINT16BE(&buf.mfs_mdb.alBlSt, l2_img->u.mfs.ABStart); +#endif + + set_UINT32BE(&buf.mfs_mdb.nxtFNum, l2_img->nxtCNID); + + set_UINT16BE(&buf.mfs_mdb.freeABs, l2_img->freeABs); + +#if 0 /* these fields are never changed */ + mac_strcpy(buf.mfs_mdb.VN, l2_img->u.mfs.volname); +#endif + + /* save link array */ + { + int byte_len = l2_img->numABs + ((l2_img->numABs + 1) >> 1); + int cur_byte = 0; + int cur_block = 2; + int block_len = sizeof(buf.mfs_mdb.ABlink); + + /* update the chunk of link array after the MDB */ + if (block_len > (byte_len - cur_byte)) + block_len = byte_len - cur_byte; + memcpy(buf.mfs_mdb.ABlink, l2_img->u.mfs.ABlink+cur_byte, block_len); + cur_byte += block_len; + + if (block_len < sizeof(buf.mfs_mdb.ABlink)) + memset(buf.mfs_mdb.ABlink+block_len, 0, sizeof(buf.mfs_mdb.ABlink)-block_len); + + l2_img->u.mfs.ABlink_dirty[0] = 0; + + /* write back modified MDB+link */ + err = image_write_block(&l2_img->l1_img, 2, &buf.mfs_mdb); + if (err) + return err; + + while (cur_byte < byte_len) + { + /* advance to next block */ + cur_block++; + block_len = 512; + + /* extract the current chunk of link array */ + if (block_len > (byte_len - cur_byte)) + block_len = byte_len - cur_byte; + + if (l2_img->u.mfs.ABlink_dirty[cur_block-2]) + { + memcpy(buf.raw, l2_img->u.mfs.ABlink+cur_byte, block_len); + if (block_len < 512) + memset(buf.raw+block_len, 0, 512-block_len); + /* write back link array */ + err = image_write_block(&l2_img->l1_img, cur_block, buf.raw); + if (err) + return err; + l2_img->u.mfs.ABlink_dirty[cur_block-2] = 0; + } + + cur_byte += block_len; + } + } + + return IMGTOOLERR_SUCCESS; +} + +/* + mfs_dir_open + + Open the directory file + + l2_img (I/O): level-2 image reference + dirref (O): open directory file reference + + Return imgtool error code +*/ +static imgtoolerr_t mfs_dir_open(struct mac_l2_imgref *l2_img, const char *path, mfs_dirref *dirref) +{ + imgtoolerr_t err; + + assert(l2_img->format == L2I_MFS); + + if (path[0]) + return IMGTOOLERR_PATHNOTFOUND; + + dirref->l2_img = l2_img; + dirref->index = 0; + + dirref->cur_block = 0; + dirref->cur_offset = 0; + err = image_read_block(&l2_img->l1_img, l2_img->u.mfs.dir_start + dirref->cur_block, dirref->block_buffer); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + +/* + mfs_dir_read + + Read one entry of directory file + + dirref (I/O): open directory file reference + dir_entry (O): set to point to the entry read: set to NULL if EOF or error + + Return imgtool error code +*/ +static imgtoolerr_t mfs_dir_read(mfs_dirref *dirref, mfs_dir_entry **dir_entry) +{ + mfs_dir_entry *cur_dir_entry; + size_t cur_dir_entry_len; + imgtoolerr_t err; + + + if (dir_entry) + *dir_entry = NULL; + + if (dirref->index == dirref->l2_img->u.mfs.dir_num_files) + /* EOF */ + return IMGTOOLERR_SUCCESS; + + /* get cat entry pointer */ + cur_dir_entry = (mfs_dir_entry *) (dirref->block_buffer + dirref->cur_offset); + while ((dirref->cur_offset == 512) || ! (cur_dir_entry->flags & 0x80)) + { + dirref->cur_block++; + dirref->cur_offset = 0; + if (dirref->cur_block > dirref->l2_img->u.mfs.dir_blk_len) + /* aargh! */ + return IMGTOOLERR_CORRUPTIMAGE; + err = image_read_block(&dirref->l2_img->l1_img, dirref->l2_img->u.mfs.dir_start + dirref->cur_block, dirref->block_buffer); + if (err) + return err; + cur_dir_entry = (mfs_dir_entry *) (dirref->block_buffer + dirref->cur_offset); + } + + cur_dir_entry_len = offsetof(mfs_dir_entry, name) + cur_dir_entry->name[0] + 1; + + if ((dirref->cur_offset + cur_dir_entry_len) > 512) + /* aargh! */ + return IMGTOOLERR_CORRUPTIMAGE; + + /* entry looks valid: set pointer to entry */ + if (dir_entry) + *dir_entry = cur_dir_entry; + + /* update offset in block */ + dirref->cur_offset += cur_dir_entry_len; + /* align to word boundary */ + dirref->cur_offset = (dirref->cur_offset + 1) & ~1; + + /* update file count */ + dirref->index++; + + return IMGTOOLERR_SUCCESS; +} + +/* + mfs_dir_insert + + Add an entry in the directory file + + l2_img (I/O): level-2 image reference + dirref (I/O): open directory file reference + filename (I): name of the file for which an entry is created (Mac string) + dir_entry (O): set to point to the created entry: set to NULL if EOF or + error + + Return imgtool error code +*/ +static imgtoolerr_t mfs_dir_insert(struct mac_l2_imgref *l2_img, mfs_dirref *dirref, const uint8_t *new_fname, mfs_dir_entry **dir_entry) +{ + size_t new_dir_entry_len; + mfs_dir_entry *cur_dir_entry; + size_t cur_dir_entry_len; + uint32_t cur_date; + imgtoolerr_t err; + + dirref->l2_img = l2_img; + dirref->index = 0; + + new_dir_entry_len = offsetof(mfs_dir_entry, name) + new_fname[0] + 1; + + for (dirref->cur_block = 0; dirref->cur_block < dirref->l2_img->u.mfs.dir_blk_len; dirref->cur_block++) + { + /* read current block */ + err = image_read_block(&dirref->l2_img->l1_img, dirref->l2_img->u.mfs.dir_start + dirref->cur_block, dirref->block_buffer); + if (err) + return err; + + /* get free chunk in this block */ + dirref->cur_offset = 0; + cur_dir_entry = (mfs_dir_entry *) (dirref->block_buffer + dirref->cur_offset); + while ((dirref->cur_offset < 512) && (cur_dir_entry->flags & 0x80)) + { /* skip cur_dir_entry */ + cur_dir_entry_len = offsetof(mfs_dir_entry, name) + cur_dir_entry->name[0] + 1; + /* update offset in block */ + dirref->cur_offset += cur_dir_entry_len; + /* align to word boundary */ + dirref->cur_offset = (dirref->cur_offset + 1) & ~1; + /* update entry pointer */ + cur_dir_entry = (mfs_dir_entry *) (dirref->block_buffer + dirref->cur_offset); + /* update file index (useless, but can't harm) */ + dirref->index++; + } + + if (new_dir_entry_len <= (/*512*/510 - dirref->cur_offset)) + { + /*memcpy(cur_dir_entry, new_dir_entry, new_dir_entry_len);*/ + cur_dir_entry->flags = 0x80; + cur_dir_entry->flVersNum = 0x00; + memset(&cur_dir_entry->flFinderInfo, 0, sizeof(cur_dir_entry->flFinderInfo)); + set_UINT32BE(&cur_dir_entry->fileID, dirref->l2_img->nxtCNID++); + set_UINT16BE(&cur_dir_entry->dataStartBlock, 1); + set_UINT32BE(&cur_dir_entry->dataLogicalSize, 0); + set_UINT32BE(&cur_dir_entry->dataPhysicalSize, 0); + set_UINT16BE(&cur_dir_entry->rsrcStartBlock, 1); + set_UINT32BE(&cur_dir_entry->rsrcLogicalSize, 0); + set_UINT32BE(&cur_dir_entry->rsrcPhysicalSize, 0); + cur_date = mac_time_now(); + set_UINT32BE(&cur_dir_entry->createDate, cur_date); + set_UINT32BE(&cur_dir_entry->modifyDate, cur_date); + mac_strcpy(cur_dir_entry->name, new_fname); + + /* update offset in block */ + dirref->cur_offset += new_dir_entry_len; + /* align to word boundary */ + dirref->cur_offset = (dirref->cur_offset + 1) & ~1; + if (dirref->cur_offset < 512) + /* mark remaining space as free record */ + dirref->block_buffer[dirref->cur_offset] = 0; + /* write back directory */ + err = image_write_block(&dirref->l2_img->l1_img, dirref->l2_img->u.mfs.dir_start + dirref->cur_block, dirref->block_buffer); + if (err) + return err; + /* update file count */ + dirref->l2_img->u.mfs.dir_num_files++; + + /* update MDB (nxtCNID & dir_num_files fields) */ + err = mfs_update_mdb(dirref->l2_img); + if (err) + return err; + + if (dir_entry) + *dir_entry = cur_dir_entry; + return IMGTOOLERR_SUCCESS; + } + } + + return IMGTOOLERR_NOSPACE; +} + +/* + mfs_dir_update + + Update one entry of directory file + + fileref (I/O): open file reference + + Return imgtool error code +*/ +static imgtoolerr_t mfs_dir_update(struct mac_fileref *fileref) +{ + uint16_t cur_block; + uint16_t cur_offset; + uint8_t block_buffer[512]; + mfs_dir_entry *cur_dir_entry; + size_t cur_dir_entry_len; + imgtoolerr_t err; + + for (cur_block = 0; cur_block < fileref->l2_img->u.mfs.dir_blk_len; cur_block++) + { + /* read current block */ + err = image_read_block(&fileref->l2_img->l1_img, fileref->l2_img->u.mfs.dir_start + cur_block, block_buffer); + if (err) + return err; + + /* get free chunk in this block */ + cur_offset = 0; + cur_dir_entry = (mfs_dir_entry *) (block_buffer + cur_offset); + while ((cur_offset < 512) && (cur_dir_entry->flags & 0x80)) + { + if (get_UINT32BE(cur_dir_entry->fileID) == fileref->fileID) + { /* found it: update directory entry */ + switch (fileref->forkType) + { + case data_fork: + set_UINT16BE(&cur_dir_entry->dataStartBlock, fileref->mfs.stBlk); + set_UINT32BE(&cur_dir_entry->dataLogicalSize, fileref->eof); + set_UINT32BE(&cur_dir_entry->dataPhysicalSize, fileref->pLen); + break; + + case rsrc_fork: + set_UINT16BE(&cur_dir_entry->rsrcStartBlock, fileref->mfs.stBlk); + set_UINT32BE(&cur_dir_entry->rsrcLogicalSize, fileref->eof); + set_UINT32BE(&cur_dir_entry->rsrcPhysicalSize, fileref->pLen); + break; + } + /* write back directory */ + err = image_write_block(&fileref->l2_img->l1_img, fileref->l2_img->u.mfs.dir_start + cur_block, block_buffer); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; + } + /* skip cur_dir_entry */ + cur_dir_entry_len = offsetof(mfs_dir_entry, name) + cur_dir_entry->name[0] + 1; + /* update offset in block */ + cur_offset += cur_dir_entry_len; + /* align to word boundary */ + cur_offset = (cur_offset + 1) & ~1; + /* update entry pointer */ + cur_dir_entry = (mfs_dir_entry *) (block_buffer + cur_offset); + } + } + + return IMGTOOLERR_UNEXPECTED; +} + + +/* + mfs_find_dir_entry + + Find a file in an MFS directory + + dirref (I/O): open directory file reference + filename (I): file name (Mac string) + dir_entry (O): set to point to the entry read: set to NULL if EOF or error + + Return imgtool error code +*/ +static imgtoolerr_t mfs_find_dir_entry(mfs_dirref *dirref, const mac_str255 filename, mfs_dir_entry **dir_entry) +{ + mfs_dir_entry *cur_dir_entry; + imgtoolerr_t err; + + if (dir_entry) + *dir_entry = NULL; + + /* scan dir for file */ + while (1) + { + err = mfs_dir_read(dirref, &cur_dir_entry); + if (err) + return err; + if (!cur_dir_entry) + /* EOF */ + break; + if ((! mac_stricmp(filename, cur_dir_entry->name)) && (cur_dir_entry->flVersNum == 0)) + { /* file found */ + + if (dir_entry) + *dir_entry = cur_dir_entry; + + return IMGTOOLERR_SUCCESS; + } + } + + return IMGTOOLERR_FILENOTFOUND; +} + +/* + mfs_lookup_path + + Resolve a file path for MFS volumes. This function should not be called + directly: call mac_lookup_path instead. + + l2_img (I/O): level-2 image reference + fpath (I): file path (C string) + filename (O): set to the actual name of the file, with capitalization matching + the one on the volume rather than the one in the fpath parameter (Mac + string) + cat_info (I/O): on output, catalog info for this file extracted from the + catalog file (may be NULL) + If create_it is true, created info will first be set according to the + data from cat_info + create_it (I): true if entry should be created if not found + + Return imgtool error code +*/ +static imgtoolerr_t mfs_lookup_path(struct mac_l2_imgref *l2_img, const char *fpath, mac_str255 filename, mac_dirent *cat_info, int create_it) +{ + mfs_dirref dirref; + mfs_dir_entry *dir_entry; + imgtoolerr_t err; + + /* rapid check */ + if (strchr(fpath, ':')) + return IMGTOOLERR_BADFILENAME; + + /* extract file name */ + c_to_mac_strncpy(filename, fpath, strlen(fpath)); + + /* open dir */ + mfs_dir_open(l2_img, "", &dirref); + + /* find file */ + err = mfs_find_dir_entry(&dirref, filename, &dir_entry); + if ((err == IMGTOOLERR_FILENOTFOUND) && create_it) + err = mfs_dir_insert(l2_img, &dirref, filename, &dir_entry); + if (err) + return err; + + mac_strcpy(filename, dir_entry->name); + + if (create_it && cat_info) + { + dir_entry->flFinderInfo = cat_info->flFinderInfo; + dir_entry->flags = (dir_entry->flags & 0x80) | (cat_info->flags & 0x7f); + set_UINT32BE(&dir_entry->createDate, cat_info->createDate); + set_UINT32BE(&dir_entry->modifyDate, cat_info->modifyDate); + + /* write current directory block */ + err = image_write_block(&l2_img->l1_img, l2_img->u.mfs.dir_start + dirref.cur_block, dirref.block_buffer); + if (err) + return err; + } + + if (cat_info) + { + cat_info->flFinderInfo = dir_entry->flFinderInfo; + memset(&cat_info->flXFinderInfo, 0, sizeof(cat_info->flXFinderInfo)); + cat_info->flags = dir_entry->flags; + cat_info->fileID = get_UINT32BE(dir_entry->fileID); + cat_info->dataLogicalSize = get_UINT32BE(dir_entry->dataLogicalSize); + cat_info->dataPhysicalSize = get_UINT32BE(dir_entry->dataPhysicalSize); + cat_info->rsrcLogicalSize = get_UINT32BE(dir_entry->rsrcLogicalSize); + cat_info->rsrcPhysicalSize = get_UINT32BE(dir_entry->rsrcPhysicalSize); + cat_info->createDate = get_UINT32BE(dir_entry->createDate); + cat_info->modifyDate = get_UINT32BE(dir_entry->modifyDate); + cat_info->dataRecType = 0x200; /* hcrt_File */ + } + + return IMGTOOLERR_SUCCESS; +} + +/* + mfs_file_open_internal + + Open a file fork, given its directory entry. This function should not be + called directly: call mfs_file_open instead. + + l2_img (I/O): level-2 image reference + dir_entry (I): directory entry for the file to open + mac_forkID (I): tells which fork should be opened + fileref (O): mac file reference to open + + Return imgtool error code +*/ +static imgtoolerr_t mfs_file_open_internal(struct mac_l2_imgref *l2_img, const mfs_dir_entry *dir_entry, mac_forkID fork, struct mac_fileref *fileref) +{ + assert(l2_img->format == L2I_MFS); + + fileref->l2_img = l2_img; + + fileref->fileID = get_UINT32BE(dir_entry->fileID); + fileref->forkType = fork; + + switch (fork) + { + case data_fork: + fileref->mfs.stBlk = get_UINT16BE(dir_entry->dataStartBlock); + fileref->eof = get_UINT32BE(dir_entry->dataLogicalSize); + fileref->pLen = get_UINT32BE(dir_entry->dataPhysicalSize); + break; + + case rsrc_fork: + fileref->mfs.stBlk = get_UINT16BE(dir_entry->rsrcStartBlock); + fileref->eof = get_UINT32BE(dir_entry->rsrcLogicalSize); + fileref->pLen = get_UINT32BE(dir_entry->rsrcPhysicalSize); + break; + } + + fileref->crPs = 0; + + fileref->reload_buf = true; + + return IMGTOOLERR_SUCCESS; +} + +/* + mfs_file_open + + Open a file located on a MFS volume. This function should not be called + directly: call mac_file_open instead. + + l2_img (I/O): level-2 image reference + filename (I): name of the file (Mac string) + mac_forkID (I): tells which fork should be opened + fileref (O): mac file reference to open + + Return imgtool error code +*/ +static imgtoolerr_t mfs_file_open(struct mac_l2_imgref *l2_img, const mac_str255 filename, mac_forkID fork, struct mac_fileref *fileref) +{ + mfs_dirref dirref; + mfs_dir_entry *dir_entry; + imgtoolerr_t err; + + /* open dir */ + mfs_dir_open(l2_img, "", &dirref); + + /* find file */ + err = mfs_find_dir_entry(&dirref, filename, &dir_entry); + if (err) + return err; + + /* open it */ + return mfs_file_open_internal(l2_img, dir_entry, fork, fileref); +} + +/* + mfs_get_ABlink + + Read one entry of the Allocation Bitmap link array, on an MFS volume. + + l2_img (I/O): level-2 image reference + AB_address (I): index in the array, which is an AB address + + Returns the 12-bit value read in array. +*/ +static uint16_t mfs_get_ABlink(struct mac_l2_imgref *l2_img, uint16_t AB_address) +{ + uint16_t reply; + int base; + + assert(l2_img->format == L2I_MFS); + + base = (AB_address >> 1) * 3; + + if (! (AB_address & 1)) + reply = (l2_img->u.mfs.ABlink[base] << 4) | ((l2_img->u.mfs.ABlink[base+1] >> 4) & 0x0f); + else + reply = ((l2_img->u.mfs.ABlink[base+1] << 8) & 0xf00) | l2_img->u.mfs.ABlink[base+2]; + + return reply; +} + +/* + mfs_set_ABlink + + Set one entry of the Allocation Bitmap link array, on an MFS volume. + + l2_img (I/O): level-2 image reference + AB_address (I): index in the array, which is an AB address + data (I): 12-bit value to write in array +*/ +static void mfs_set_ABlink(struct mac_l2_imgref *l2_img, uint16_t AB_address, uint16_t data) +{ + int base; + + assert(l2_img->format == L2I_MFS); + + base = (AB_address >> 1) * 3; + + if (! (AB_address & 1)) + { + l2_img->u.mfs.ABlink[base] = (data >> 4) & 0xff; + l2_img->u.mfs.ABlink[base+1] = (l2_img->u.mfs.ABlink[base+1] & 0x0f) | ((data << 4) & 0xf0); + + l2_img->u.mfs.ABlink_dirty[(base+64)/512] = 1; + l2_img->u.mfs.ABlink_dirty[(base+1+64)/512] = 1; + } + else + { + l2_img->u.mfs.ABlink[base+1] = (l2_img->u.mfs.ABlink[base+1] & 0xf0) | ((data >> 8) & 0x0f); + l2_img->u.mfs.ABlink[base+2] = data & 0xff; + + l2_img->u.mfs.ABlink_dirty[(base+1+64)/512] = 1; + l2_img->u.mfs.ABlink_dirty[(base+2+64)/512] = 1; + } +} + +/* + mfs_file_get_nth_block_address + + Get the disk block address of a given block in an open file on a MFS image. + Called by macintosh file code. + + fileref (I/O): open mac file reference + block_num (I): file block index + block_address (O): disk block address for the file block + + Return imgtool error code +*/ +static imgtoolerr_t mfs_file_get_nth_block_address(struct mac_fileref *fileref, uint32_t block_num, uint32_t *block_address) +{ + uint32_t AB_num; + uint32_t i; + uint16_t AB_address; + + assert(fileref->l2_img->format == L2I_MFS); + + AB_num = block_num / fileref->l2_img->blocksperAB; + + AB_address = fileref->mfs.stBlk; + if ((AB_address == 0) || (AB_address >= fileref->l2_img->numABs+2)) + /* 0 -> ??? */ + return IMGTOOLERR_CORRUPTIMAGE; + if (AB_address == 1) + /* EOF */ + return IMGTOOLERR_UNEXPECTED; + AB_address -= 2; + for (i=0; il2_img, AB_address); + if ((AB_address == 0) || (AB_address >= fileref->l2_img->numABs+2)) + /* 0 -> empty block: there is no way an empty block could make it + into the link chain!!! */ + return IMGTOOLERR_CORRUPTIMAGE; + if (AB_address == 1) + /* EOF */ + return IMGTOOLERR_UNEXPECTED; + AB_address -= 2; + } + + *block_address = fileref->l2_img->u.mfs.ABStart + AB_address * fileref->l2_img->blocksperAB + + block_num % fileref->l2_img->blocksperAB; + + return IMGTOOLERR_SUCCESS; +} + +/* + mfs_file_allocABs + + Allocate a chunk of ABs + + fileref (I/O): open mac file reference + lastAB (I): AB address on disk of last file AB (only if + fileref->mfs.stBlk != 1) + allocABs (I): number of ABs to allocate in addition to the current file + allocation + fblock (I): first file block to allocate (used for tag data) + + Return imgtool error code +*/ +static imgtoolerr_t mfs_file_allocABs(struct mac_fileref *fileref, uint16_t lastAB, uint32_t allocABs, uint32_t fblock) +{ + int numABs = fileref->l2_img->numABs; + int free_ABs; + int i, j; + floppy_tag_record tag; + int extentBaseAB, extentABlen; + int firstBestExtentBaseAB = 0, firstBestExtentABlen; + int secondBestExtentBaseAB = 0, secondBestExtentABlen; + imgtoolerr_t err; + + /* return if done */ + if (! allocABs) + return IMGTOOLERR_SUCCESS; + + /* compute free space */ + free_ABs = 0; + for (i=0; il2_img, i) == 0) + { + if (TAG_CHECKS) + { + /* optional check */ + if (image_get_tag_len(&fileref->l2_img->l1_img) == 12) + { + for (j=0; jl2_img->blocksperAB; j++) + { + err = image_read_tag(&fileref->l2_img->l1_img, fileref->l2_img->u.mfs.ABStart + i * fileref->l2_img->blocksperAB + j, &tag); + if (err) + return err; + + if (get_UINT32BE(tag.fileID) != 0) + { + /*return IMGTOOLERR_CORRUPTIMAGE;*/ + goto corrupt_free_block; + } + } + } + } + + free_ABs++; + } +corrupt_free_block: + ; + } + + /* check we have enough free space */ + if (free_ABs < allocABs) + return IMGTOOLERR_NOSPACE; + + if (fileref->mfs.stBlk != 1) + { /* try to extend last file extent */ + /* append free ABs after last AB */ + for (i=lastAB+1; (mfs_get_ABlink(fileref->l2_img, i) == 0) && (allocABs > 0) && (i < numABs); i++) + { + if (TAG_CHECKS) + { + /* optional check */ + if (image_get_tag_len(&fileref->l2_img->l1_img) == 12) + { + for (j=0; jl2_img->blocksperAB; j++) + { + err = image_read_tag(&fileref->l2_img->l1_img, fileref->l2_img->u.mfs.ABStart + i * fileref->l2_img->blocksperAB + j, &tag); + if (err) + return err; + + if (get_UINT32BE(tag.fileID) != 0) + { + /*return IMGTOOLERR_CORRUPTIMAGE;*/ + goto corrupt_free_block2; + } + } + } + } + + mfs_set_ABlink(fileref->l2_img, lastAB, i+2); + lastAB = i; + allocABs--; + free_ABs--; + } +corrupt_free_block2: + /* return if done */ + if (! allocABs) + { + mfs_set_ABlink(fileref->l2_img, lastAB, 1); + fileref->l2_img->freeABs = free_ABs; + return IMGTOOLERR_SUCCESS; /* done */ + } + } + + while (allocABs) + { + /* find smallest data block at least nb_alloc_physrecs in length, and largest data block less than nb_alloc_physrecs in length */ + firstBestExtentABlen = INT_MAX; + secondBestExtentABlen = 0; + for (i=0; il2_img, i) == 0) + { /* found one free block */ + /* compute its length */ + extentBaseAB = i; + extentABlen = 0; + while ((il2_img, i) == 0)) + { + if (TAG_CHECKS) + { + /* optional check */ + if (image_get_tag_len(&fileref->l2_img->l1_img) == 12) + { + for (j=0; jl2_img->blocksperAB; j++) + { + err = image_read_tag(&fileref->l2_img->l1_img, fileref->l2_img->u.mfs.ABStart + i * fileref->l2_img->blocksperAB + j, &tag); + if (err) + return err; + + if (get_UINT32BE(tag.fileID) != 0) + { + /*return IMGTOOLERR_CORRUPTIMAGE;*/ + goto corrupt_free_block3; + } + } + } + } + + extentABlen++; + i++; + } +corrupt_free_block3: + /* compare to previous best and second-best blocks */ + if ((extentABlen < firstBestExtentABlen) && (extentABlen >= allocABs)) + { + firstBestExtentBaseAB = extentBaseAB; + firstBestExtentABlen = extentABlen; + if (extentABlen == allocABs) + /* no need to search further */ + break; + } + else if ((extentABlen > secondBestExtentABlen) && (extentABlen < allocABs)) + { + secondBestExtentBaseAB = extentBaseAB; + secondBestExtentABlen = extentABlen; + } + } + } + + if (firstBestExtentABlen != INT_MAX) + { /* found one contiguous block which can hold it all */ + extentABlen = allocABs; + for (i=0; imfs.stBlk != 1) + mfs_set_ABlink(fileref->l2_img, lastAB, firstBestExtentBaseAB+i+2); + else + fileref->mfs.stBlk = firstBestExtentBaseAB+i+2; + lastAB = firstBestExtentBaseAB+i; + free_ABs--; + /* set tag to allocated */ + if (image_get_tag_len(&fileref->l2_img->l1_img) == 12) + { + set_UINT32BE(&tag.fileID, fileref->fileID); + tag.ftype = 1; + if ((fileref->forkType) == rsrc_fork) + tag.ftype |= 2; + tag.fattr = /*fattr*/ 0; /* ***TODO*** */ + for (j=0; jl2_img->blocksperAB; j++) + { + set_UINT16BE(&tag.fblock, fblock & 0xffff); + set_UINT32BE(&tag.wrCnt, mac_time_now()); + err = image_write_tag(&fileref->l2_img->l1_img, fileref->l2_img->u.mfs.ABStart + lastAB * fileref->l2_img->blocksperAB + j, &tag); + if (err) + { + mfs_set_ABlink(fileref->l2_img, lastAB, 1); + fileref->l2_img->freeABs = free_ABs; + return err; + } + fblock++; + } + } + } + allocABs = 0; + mfs_set_ABlink(fileref->l2_img, lastAB, 1); + fileref->l2_img->freeABs = free_ABs; + /*return IMGTOOLERR_SUCCESS;*/ /* done */ + } + else if (secondBestExtentABlen != 0) + { /* jeez, we need to fragment it. We use the largest smaller block to limit fragmentation. */ + for (i=0; imfs.stBlk != 1) + mfs_set_ABlink(fileref->l2_img, lastAB, secondBestExtentBaseAB+i+2); + else + fileref->mfs.stBlk = secondBestExtentBaseAB+i+2; + lastAB = secondBestExtentBaseAB+i; + free_ABs--; + /* set tag to allocated */ + if (image_get_tag_len(&fileref->l2_img->l1_img) == 12) + { + set_UINT32BE(&tag.fileID, fileref->fileID); + tag.ftype = 1; + if ((fileref->forkType) == rsrc_fork) + tag.ftype |= 2; + tag.fattr = /*fattr*/ 0; /* ***TODO*** */ + for (j=0; jl2_img->blocksperAB; j++) + { + set_UINT16BE(&tag.fblock, fblock & 0xffff); + set_UINT32BE(&tag.wrCnt, mac_time_now()); + err = image_write_tag(&fileref->l2_img->l1_img, fileref->l2_img->u.mfs.ABStart + lastAB * fileref->l2_img->blocksperAB + j, &tag); + if (err) + { + mfs_set_ABlink(fileref->l2_img, lastAB, 1); + fileref->l2_img->freeABs = free_ABs; + return err; + } + fblock++; + } + } + } + allocABs -= secondBestExtentABlen; + } + else + { + mfs_set_ABlink(fileref->l2_img, lastAB, 1); + return IMGTOOLERR_NOSPACE; /* This should never happen, as we pre-check that there is enough free space */ + } + } + + return IMGTOOLERR_SUCCESS; +} + +/* + mfs_file_setABeof + + Set physical file EOF in ABs + + fileref (I/O): open mac file reference + newABeof (I): desired number of allocated ABs for this file + + Return imgtool error code +*/ +static imgtoolerr_t mfs_file_setABeof(struct mac_fileref *fileref, uint32_t newABeof) +{ + uint16_t AB_address = 0; + uint16_t AB_link; + int i, j; + floppy_tag_record tag; + int MDB_dirty = 0; + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + + + assert(fileref->l2_img->format == L2I_MFS); + + /* run through link chain until we reach the old or the new EOF */ + AB_link = fileref->mfs.stBlk; + if ((AB_link == 0) || (AB_link >= fileref->l2_img->numABs+2)) + /* 0 -> ??? */ + return IMGTOOLERR_CORRUPTIMAGE; + for (i=0; (il2_img, AB_address); + if ((AB_link == 0) || (AB_link >= fileref->l2_img->numABs+2)) + /* 0 -> empty block: there is no way an empty block could make it + into the link chain!!! */ + return IMGTOOLERR_CORRUPTIMAGE; + + if (TAG_CHECKS) + { + /* optional check */ + if (image_get_tag_len(&fileref->l2_img->l1_img) == 12) + { + for (j=0; jl2_img->blocksperAB; j++) + { + err = image_read_tag(&fileref->l2_img->l1_img, fileref->l2_img->u.mfs.ABStart + AB_address * fileref->l2_img->blocksperAB + j, &tag); + if (err) + return err; + + if ((get_UINT32BE(tag.fileID) != fileref->fileID) + || (((tag.ftype & 2) != 0) != (fileref->forkType == rsrc_fork)) + || (get_UINT16BE(tag.fblock) != ((i * fileref->l2_img->blocksperAB + j) & 0xffff))) + { + return IMGTOOLERR_CORRUPTIMAGE; + } + } + } + } + } + + if (i == newABeof) + { /* new EOF is shorter than old one */ + /* mark new eof */ + if (i==0) + fileref->mfs.stBlk = 1; + else + { + mfs_set_ABlink(fileref->l2_img, AB_address, 1); + MDB_dirty = 1; + } + + /* free all remaining blocks */ + while (AB_link != 1) + { + AB_address = AB_link - 2; + AB_link = mfs_get_ABlink(fileref->l2_img, AB_address); + if ((AB_link == 0) || (AB_link >= fileref->l2_img->numABs+2)) + { /* 0 -> empty block: there is no way an empty block could make + it into the link chain!!! */ + if (MDB_dirty) + { /* update MDB (freeABs field) and ABLink array */ + err = mfs_update_mdb(fileref->l2_img); + if (err) + return err; + } + return IMGTOOLERR_CORRUPTIMAGE; + } + + if (TAG_CHECKS) + { + /* optional check */ + if (image_get_tag_len(&fileref->l2_img->l1_img) == 12) + { + for (j=0; jl2_img->blocksperAB; j++) + { + err = image_read_tag(&fileref->l2_img->l1_img, fileref->l2_img->u.mfs.ABStart + AB_address * fileref->l2_img->blocksperAB + j, &tag); + if (err) + return err; + + if ((get_UINT32BE(tag.fileID) != fileref->fileID) + || (((tag.ftype & 2) != 0) != (fileref->forkType == rsrc_fork)) + || (get_UINT16BE(tag.fblock) != ((i * fileref->l2_img->blocksperAB + j) & 0xffff))) + { + return IMGTOOLERR_CORRUPTIMAGE; + } + } + } + } + + mfs_set_ABlink(fileref->l2_img, AB_address, 0); + fileref->l2_img->freeABs++; + MDB_dirty = 1; + /* set tag to free */ + if (image_get_tag_len(&fileref->l2_img->l1_img) == 12) + { + memset(&tag, 0, sizeof(tag)); + for (j=0; jl2_img->blocksperAB; j++) + { + err = image_write_tag(&fileref->l2_img->l1_img, fileref->l2_img->u.mfs.ABStart + AB_address * fileref->l2_img->blocksperAB + j, &tag); + if (err) + return err; + } + } + i++; + } + } + else + { /* new EOF is larger than old one */ + err = mfs_file_allocABs(fileref, AB_address, newABeof - i, i * fileref->l2_img->blocksperAB); + if (err) + return err; + MDB_dirty = 1; + } + + if (MDB_dirty) + { /* update MDB (freeABs field) and ABLink array */ + err = mfs_update_mdb(fileref->l2_img); + if (err) + return err; + } + + fileref->pLen = newABeof * (fileref->l2_img->blocksperAB * 512); + + return IMGTOOLERR_SUCCESS; +} + +#ifdef UNUSED_FUNCTION +/* + mfs_hashString + + Hash a string: under MFS, this provides the resource ID of the comment + resource associated with the file whose name is provided (FCMT resource + type). + + Ripped from Apple technote TB06 (converted from 68k ASM to C) + + string (I): string to hash + + Returns hash value +*/ +static int mfs_hashString(const mac_str255 string) +{ + int reply; + int len; + int i; + + len = string[0]; + + reply = 0; + for (i=0; i> 1) & 0x7fff) | ~0x7fff; + else + reply = ((reply >> 1) & 0x7fff); + if (! (reply & 0x8000)) + reply = - reply; + } + + return reply; +} +#endif + +#if 0 +#pragma mark - +#pragma mark HFS IMPLEMENTATION +#endif + +/* + HFS extents B-tree key +*/ +struct hfs_extentKey +{ + uint8_t keyLength; /* length of key, excluding this field */ + uint8_t forkType; /* 0 = data fork, FF = resource fork */ + UINT32BE fileID; /* file ID */ + UINT16BE startBlock; /* first file allocation block number in this extent */ +}; +enum +{ + keyLength_hfs_extentKey = sizeof(hfs_extentKey) - sizeof(uint8_t) +}; + +/* + HFS catalog B-tree key +*/ +struct hfs_catKey +{ + uint8_t keyLen; /* key length */ + uint8_t resrv1; /* reserved */ + UINT32BE parID; /* parent directory ID */ + mac_str31 cName; /* catalog node name */ + /* note that in index nodes, it is a mac_str31, but + in leaf keys it's a variable-length string */ +}; + +/* + HFS catalog data record for a folder - 70 bytes +*/ +struct hfs_catFolderData +{ + UINT16BE recordType; /* record type */ + UINT16BE flags; /* folder flags */ + UINT16BE valence; /* folder valence */ + UINT32BE folderID; /* folder ID */ + UINT32BE createDate; /* date and time of creation */ + UINT32BE modifyDate; /* date and time of last modification */ + UINT32BE backupDate; /* date and time of last backup */ + mac_DInfo userInfo; /* Finder information */ + mac_DXInfo finderInfo; /* additional Finder information */ + UINT32BE reserved[4]; /* reserved - set to zero */ +}; + +/* + HFS catalog data record for a file - 102 bytes +*/ +struct hfs_catFileData +{ + UINT16BE recordType; /* record type */ + uint8_t flags; /* file flags */ + uint8_t fileType; /* file type (reserved, always 0?) */ + mac_FInfo userInfo; /* Finder information */ + UINT32BE fileID; /* file ID */ + UINT16BE dataStartBlock; /* not used - set to zero */ + UINT32BE dataLogicalSize; /* logical EOF of data fork */ + UINT32BE dataPhysicalSize; /* physical EOF of data fork */ + UINT16BE rsrcStartBlock; /* not used - set to zero */ + UINT32BE rsrcLogicalSize; /* logical EOF of resource fork */ + UINT32BE rsrcPhysicalSize; /* physical EOF of resource fork */ + UINT32BE createDate; /* date and time of creation */ + UINT32BE modifyDate; /* date and time of last modification */ + UINT32BE backupDate; /* date and time of last backup */ + mac_FXInfo finderInfo; /* additional Finder information */ + UINT16BE clumpSize; /* file clump size (not used) */ + hfs_extent_3 dataExtents; /* first data fork extent record */ + hfs_extent_3 rsrcExtents; /* first resource fork extent record */ + UINT32BE reserved; /* reserved - set to zero */ +}; + +/* + HFS catalog data record for a thread - 46 bytes + + The key for a thread record features the CNID of the item and an empty + name, instead of the CNID of the parent and the item name. +*/ +struct hfs_catThreadData +{ + UINT16BE recordType; /* record type */ + UINT32BE reserved[2]; /* reserved - set to zero */ + UINT32BE parID; /* parent ID for this catalog node */ + mac_str31 nodeName; /* name of this catalog node */ +}; + +/* + union for all types at once +*/ +union hfs_catData +{ + UINT16BE dataType; + hfs_catFolderData folder; + hfs_catFileData file; + hfs_catThreadData thread; +}; + +/* + HFS catalog record types +*/ +enum +{ + hcrt_Folder = 0x0100, /* Folder record */ + hcrt_File = 0x0200, /* File record */ + hcrt_FolderThread = 0x0300, /* Folder thread record */ + hcrt_FileThread = 0x0400 /* File thread record */ +}; + +/* + Catalog file record flags + + This is similar to the MFS catalog flag field, but the "thread exists" flag + (0x02) is specific to HFS/HFS+, whereas the "Record in use" flag (0x80) is + only used by MFS. +*/ +enum +{ + cfrf_fileLocked = 0x01, /* file is locked and cannot be written to */ + cfrf_threadExists = 0x02 /* a file thread record exists for this file */ +}; + +/* + BT functions used by HFS functions +*/ +struct BT_leaf_rec_enumerator +{ + mac_BTref *BTref; + uint32_t cur_node; + int cur_rec; +}; + +static imgtoolerr_t BT_open(mac_BTref *BTref, int (*key_compare_func)(const void *key1, const void *key2), int is_extent); +static void BT_close(mac_BTref *BTref); +static imgtoolerr_t BT_search_leaf_rec(mac_BTref *BTref, const void *search_key, + uint32_t *node_ID, int *record_ID, + void **record_ptr, int *record_len, + int search_exact_match, int *match_found); +static imgtoolerr_t BT_get_keyed_record_data(mac_BTref *BTref, void *rec_ptr, int rec_len, void **data_ptr, int *data_len); +static imgtoolerr_t BT_leaf_rec_enumerator_open(mac_BTref *BTref, BT_leaf_rec_enumerator *enumerator); +static imgtoolerr_t BT_leaf_rec_enumerator_read(BT_leaf_rec_enumerator *enumerator, void **record_ptr, int *rec_len); + +struct hfs_cat_enumerator +{ + struct mac_l2_imgref *l2_img; + BT_leaf_rec_enumerator BT_enumerator; + uint32_t parID; +}; + +/* + hfs_open_extents_file + + Open the file extents B-tree file + + l2_img (I/O): level-2 image reference + mdb (I): copy of the MDB block + fileref (O): mac open file reference + + Return imgtool error code +*/ +static imgtoolerr_t hfs_open_extents_file(struct mac_l2_imgref *l2_img, const struct hfs_mdb *mdb, struct mac_fileref *fileref) +{ + assert(l2_img->format == L2I_HFS); + + fileref->l2_img = l2_img; + + fileref->fileID = 3; + fileref->forkType = (mac_forkID)0x00; + + fileref->eof = fileref->pLen = get_UINT32BE(mdb->xtFlSize); + memcpy(fileref->hfs.extents, mdb->xtExtRec, sizeof(hfs_extent_3)); + + fileref->crPs = 0; + + fileref->reload_buf = true; + + return IMGTOOLERR_SUCCESS; +} + +/* + hfs_open_cat_file + + Open the disk catalog B-tree file + + l2_img (I/O): level-2 image reference + mdb (I): copy of the MDB block + fileref (O): mac open file reference + + Return imgtool error code +*/ +static imgtoolerr_t hfs_open_cat_file(struct mac_l2_imgref *l2_img, const struct hfs_mdb *mdb, struct mac_fileref *fileref) +{ + assert(l2_img->format == L2I_HFS); + + fileref->l2_img = l2_img; + + fileref->fileID = 4; + fileref->forkType = (mac_forkID)0x00; + + fileref->eof = fileref->pLen = get_UINT32BE(mdb->ctFlSize); + memcpy(fileref->hfs.extents, mdb->ctExtRec, sizeof(hfs_extent_3)); + + fileref->crPs = 0; + + fileref->reload_buf = true; + + return IMGTOOLERR_SUCCESS; +} + +/* + hfs_extentKey_compare + + key compare function for file extents B-tree + + p1 (I): pointer to first key + p2 (I): pointer to second key + + Return a zero the two keys are equal, a negative value if the key pointed + to by p1 is less than the key pointed to by p2, and a positive value if the + key pointed to by p1 is greater than the key pointed to by p2. +*/ +static int hfs_extentKey_compare(const void *p1, const void *p2) +{ + const hfs_extentKey *key1 = (const hfs_extentKey*)p1; + const hfs_extentKey *key2 = (const hfs_extentKey*)p2; + + /* let's keep it simple for now */ + return memcmp(key1, key2, sizeof(hfs_extentKey)); +} + +/* + hfs_catKey_compare + + key compare function for disk catalog B-tree + + p1 (I): pointer to first key + p2 (I): pointer to second key + + Return a zero the two keys are equal, a negative value if the key pointed + to by p1 is less than the key pointed to by p2, and a positive value if the + key pointed to by p1 is greater than the key pointed to by p2. +*/ +static int hfs_catKey_compare(const void *p1, const void *p2) +{ + const hfs_catKey *key1 = (const hfs_catKey *)p1; + const hfs_catKey *key2 = (const hfs_catKey *)p2; + + if (get_UINT32BE(key1->parID) != get_UINT32BE(key2->parID)) + return (get_UINT32BE(key1->parID) < get_UINT32BE(key2->parID)) ? -1 : +1; + + return mac_stricmp(key1->cName, key2->cName); +} + +/* + hfs_image_open + + Open a HFS image. Image must already be open on level 1. + + l2_img (I/O): level-2 image reference to open (l1_img and format fields + must be initialized) + img_open_buf (I): buffer with the MDB block + + Return imgtool error code +*/ +static imgtoolerr_t hfs_image_open(imgtool::image &image, imgtool::stream::ptr &&stream) +{ + imgtoolerr_t err; + struct mac_l2_imgref *l2_img; + img_open_buf buf_local; + img_open_buf *buf; + + l2_img = get_imgref(image); + l2_img->l1_img.image = ℑ + l2_img->l1_img.heads = 2; + l2_img->format = L2I_HFS; + + /* read MDB */ + err = image_read_block(&l2_img->l1_img, 2, &buf_local.raw); + if (err) + return err; + buf = &buf_local; + + /* check signature word */ + if ((buf->hfs_mdb.sigWord[0] != 0x42) || (buf->hfs_mdb.sigWord[1] != 0x44) + || (buf->hfs_mdb.VN[0] > 27)) + return IMGTOOLERR_CORRUPTIMAGE; + + l2_img->u.hfs.VBM_start = get_UINT16BE(buf->hfs_mdb.VBMSt); + + l2_img->numABs = get_UINT16BE(buf->hfs_mdb.nmAlBlks); + if (get_UINT32BE(buf->hfs_mdb.alBlkSiz) % 512) + return IMGTOOLERR_CORRUPTIMAGE; + l2_img->blocksperAB = get_UINT32BE(buf->hfs_mdb.alBlkSiz) / 512; + l2_img->u.hfs.ABStart = get_UINT16BE(buf->hfs_mdb.alBlSt); + + l2_img->nxtCNID = get_UINT32BE(buf->hfs_mdb.nxtCNID); + + l2_img->freeABs = get_UINT16BE(buf->hfs_mdb.freeABs); + + mac_strcpy(l2_img->u.hfs.volname, buf->hfs_mdb.VN); + + /* open extents and catalog BT */ + err = hfs_open_extents_file(l2_img, &buf->hfs_mdb, &l2_img->u.hfs.extents_BT.fileref); + if (err) + return err; + err = BT_open(&l2_img->u.hfs.extents_BT, hfs_extentKey_compare, true); + if (err) + return err; + if ((l2_img->u.hfs.extents_BT.attributes & btha_bigKeysMask) + /*|| (l2_img->u.hfs.extents_BT.attributes & kBTVariableIndexKeysMask)*/ + || (l2_img->u.hfs.extents_BT.maxKeyLength != 7)) + { /* This is not supported by the HFS format */ + /* Variable Index keys are not supported either, but hopefully it will + not break this imgtool module if it set (though it would probably break + a real macintosh) */ + BT_close(&l2_img->u.hfs.extents_BT); + return IMGTOOLERR_CORRUPTIMAGE; + } + err = hfs_open_cat_file(l2_img, &buf->hfs_mdb, &l2_img->u.hfs.cat_BT.fileref); + if (err) + { + BT_close(&l2_img->u.hfs.extents_BT); + return err; + } + err = BT_open(&l2_img->u.hfs.cat_BT, hfs_catKey_compare, false); + if (err) + { + return err; + } + if ((l2_img->u.hfs.cat_BT.attributes & btha_bigKeysMask) + /*|| (l2_img->u.hfs.cat_BT.attributes & kBTVariableIndexKeysMask)*/ + || (l2_img->u.hfs.cat_BT.maxKeyLength != 37)) + { /* This is not supported by the HFS format */ + /* Variable Index keys are not supported either, but hopefully it will + not break this imgtool module if it set (though it would probably break + a real macintosh) */ + BT_close(&l2_img->u.hfs.extents_BT); + BT_close(&l2_img->u.hfs.cat_BT); + return IMGTOOLERR_CORRUPTIMAGE; + } + + /* extract volume bitmap */ + { + int byte_len = (l2_img->numABs + 7) / 8; + int cur_byte = 0; + int cur_block = l2_img->u.hfs.VBM_start; + + while (cur_byte < byte_len) + { + /* read next block */ + err = image_read_block(&l2_img->l1_img, cur_block, buf->raw); + if (err) + return err; + cur_block++; + + /* append this block to VBM */ + memcpy(l2_img->u.hfs.VBM+cur_byte, buf->raw, 512); + cur_byte += 512; + } + } + + return IMGTOOLERR_SUCCESS; +} + +#ifdef UNUSED_FUNCTION +/* + hfs_image_close + + Close a HFS image. + + l2_img (I/O): level-2 image reference +*/ +static void hfs_image_close(struct mac_l2_imgref *l2_img) +{ + assert(l2_img->format == L2I_HFS); + + BT_close(&l2_img->u.hfs.extents_BT); + BT_close(&l2_img->u.hfs.cat_BT); +} +#endif + +/* + hfs_get_cat_record_data + + extract data from a catalog B-tree leaf record + + l2_img (I/O): level-2 image reference + rec_raw (I): pointer to record key and data, as returned by + BT_node_get_keyed_record + rec_len (I): total length of record, as returned by + BT_node_get_keyed_record + rec_key (O): set to point to record key + rec_data (O): set to point to record data + + Return imgtool error code +*/ +static imgtoolerr_t hfs_get_cat_record_data(struct mac_l2_imgref *l2_img, void *rec_raw, int rec_len, hfs_catKey **rec_key, hfs_catData **rec_data) +{ + hfs_catKey *lrec_key; + void *rec_data_raw; + hfs_catData *lrec_data; + int rec_data_len; + int min_data_size; + imgtoolerr_t err; + + + assert(l2_img->format == L2I_HFS); + + lrec_key = (hfs_catKey*)rec_raw; + /* check that key is long enough to hold it all */ + if ((lrec_key->keyLen+1) < (offsetof(hfs_catKey, cName) + lrec_key->cName[0] + 1)) + return IMGTOOLERR_CORRUPTIMAGE; + + /* get pointer to record data */ + err = BT_get_keyed_record_data(&l2_img->u.hfs.cat_BT, rec_raw, rec_len, &rec_data_raw, &rec_data_len); + if (err) + return err; + lrec_data = (hfs_catData*)rec_data_raw; + + /* extract record type */ + if (rec_data_len < 2) + return IMGTOOLERR_CORRUPTIMAGE; + + /* check that the record is large enough for its type */ + switch (get_UINT16BE(lrec_data->dataType)) + { + case hcrt_Folder: + min_data_size = sizeof(hfs_catFolderData); + break; + + case hcrt_File: + min_data_size = sizeof(hfs_catFileData); + break; + + case hcrt_FolderThread: + case hcrt_FileThread: + min_data_size = sizeof(hfs_catThreadData); + break; + + default: + /* records of unknown type can be safely ignored */ + min_data_size = 0; + break; + } + + if (rec_data_len < min_data_size) + return IMGTOOLERR_CORRUPTIMAGE; + + if (rec_key) + *rec_key = lrec_key; + if (rec_data) + *rec_data = lrec_data; + + return IMGTOOLERR_SUCCESS; +} + +/* + hfs_cat_open + + Open an enumerator on the disk catalog + + l2_img (I/O): level-2 image reference + enumerator (O): open catalog enumerator reference + + Return imgtool error code +*/ +static imgtoolerr_t hfs_cat_open(struct mac_l2_imgref *l2_img, const char *path, hfs_cat_enumerator *enumerator) +{ + imgtoolerr_t err; + uint32_t parID; + mac_str255 filename; + mac_dirent cat_info; + + assert(l2_img->format == L2I_HFS); + + /* resolve path and fetch file info from directory/catalog */ + err = mac_lookup_path(l2_img, path, &parID, filename, &cat_info, false); + if (err) + return err; + if (cat_info.dataRecType != hcrt_Folder) + return IMGTOOLERR_FILENOTFOUND; + + enumerator->l2_img = l2_img; + enumerator->parID = parID; + + return BT_leaf_rec_enumerator_open(&l2_img->u.hfs.cat_BT, &enumerator->BT_enumerator); +} + +/* + hfs_cat_read + + Enumerate the disk catalog + + enumerator (I/O): open catalog enumerator reference + rec_key (O): set to point to record key + rec_data (O): set to point to record data + + Return imgtool error code +*/ +static imgtoolerr_t hfs_cat_read(hfs_cat_enumerator *enumerator, hfs_catKey **rec_key, hfs_catData **rec_data) +{ + void *rec; + int rec_len = 0; + imgtoolerr_t err; + + + *rec_key = NULL; + *rec_data = NULL; + + /* read next record */ + err = BT_leaf_rec_enumerator_read(&enumerator->BT_enumerator, &rec, &rec_len); + if (err) + return err; + + /* check EOList condition */ + if (rec == NULL) + return IMGTOOLERR_SUCCESS; + + /* extract record data */ + err = hfs_get_cat_record_data(enumerator->l2_img, rec, rec_len, rec_key, rec_data); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + +/* + hfs_cat_search + + Search the catalog for a given file + + l2_img (I/O): level-2 image reference + parID (I): CNID of file parent directory + cName (I): file name + rec_key (O): set to point to record key + rec_data (O): set to point to record data + + Return imgtool error code +*/ +static imgtoolerr_t hfs_cat_search(struct mac_l2_imgref *l2_img, uint32_t parID, const mac_str31 cName, hfs_catKey **rec_key, hfs_catData **rec_data) +{ + hfs_catKey search_key; + void *rec; + int rec_len; + imgtoolerr_t err; + + assert(l2_img->format == L2I_HFS); + + if (cName[0] > 31) + return IMGTOOLERR_UNEXPECTED; + + /* generate search key */ + search_key.keyLen = search_key.resrv1 = 0; /* these fields do not matter + to the compare function, so we + don't fill them */ + set_UINT32BE(&search_key.parID, parID); + mac_strcpy(search_key.cName, cName); + + /* search key */ + err = BT_search_leaf_rec(&l2_img->u.hfs.cat_BT, &search_key, NULL, NULL, &rec, &rec_len, true, NULL); + if (err) + return err; + + /* extract record data */ + err = hfs_get_cat_record_data(l2_img, rec, rec_len, rec_key, rec_data); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + +/* + hfs_lookup_path + + Resolve a file path + + l2_img (I/O): level-2 image reference + fpath (I): file path (C string) + parID (O): set to the CNID of the file parent directory + filename (O): set to the actual name of the file, with capitalization matching + the one on the volume rather than the one in the fpath parameter (Mac + string) + cat_info (O): catalog info for this file extracted from the catalog file + (may be NULL) + + Return imgtool error code +*/ +static imgtoolerr_t hfs_lookup_path(struct mac_l2_imgref *l2_img, const char *fpath, uint32_t *parID, mac_str255 filename, mac_dirent *cat_info) +{ + const char *element_start; + int element_len; + mac_str255 mac_element_name; + //int level; + imgtoolerr_t err; + hfs_catKey *catrec_key = NULL; + hfs_catData *catrec_data = NULL; + uint16_t dataRecType = hcrt_Folder; + + /* iterate each path element */ + element_start = fpath; + //level = 0; + *parID = 2; /* root parID is 2 */ + + while(*element_start) + { + /* find next path element */ + element_len = strlen(element_start); + /* decode path element name */ + c_to_mac_strncpy(mac_element_name, element_start, element_len); + + err = hfs_cat_search(l2_img, *parID, mac_element_name, &catrec_key, &catrec_data); + if (err) + return err; + + dataRecType = get_UINT16BE(catrec_data->dataType); + + /* regular folder/file name */ + if (dataRecType == hcrt_Folder) + *parID = get_UINT32BE(catrec_data->folder.folderID); + else if (element_start[element_len + 1]) + return IMGTOOLERR_BADFILENAME; + + /* iterate */ + element_start += element_len + 1; + } + + if (catrec_key && (dataRecType == hcrt_File)) + { + /* save ref */ + *parID = get_UINT32BE(catrec_key->parID); + mac_strcpy(filename, catrec_key->cName); + } + + if (cat_info) + { + if (catrec_data && (dataRecType == hcrt_File)) + { + cat_info->flFinderInfo = catrec_data->file.userInfo; + cat_info->flXFinderInfo = catrec_data->file.finderInfo; + cat_info->flags = catrec_data->file.flags; + cat_info->fileID = get_UINT32BE(catrec_data->file.fileID); + cat_info->dataLogicalSize = get_UINT32BE(catrec_data->file.dataLogicalSize); + cat_info->dataPhysicalSize = get_UINT32BE(catrec_data->file.dataPhysicalSize); + cat_info->rsrcLogicalSize = get_UINT32BE(catrec_data->file.rsrcLogicalSize); + cat_info->rsrcPhysicalSize = get_UINT32BE(catrec_data->file.rsrcPhysicalSize); + cat_info->createDate = get_UINT32BE(catrec_data->file.createDate); + cat_info->modifyDate = get_UINT32BE(catrec_data->file.modifyDate); + } + else + { + memset(cat_info, 0, sizeof(*cat_info)); + } + cat_info->dataRecType = dataRecType; + } + + return IMGTOOLERR_SUCCESS; +} + +/* + hfs_file_open_internal + + Open a file fork, given its catalog entry. This function should not be + called directly: call hfs_file_open instead. + + l2_img (I/O): level-2 image reference + file_rec (I): catalog entry for the file to open + mac_forkID (I): tells which fork should be opened + fileref (O): mac file reference to open + + Return imgtool error code +*/ +static imgtoolerr_t hfs_file_open_internal(struct mac_l2_imgref *l2_img, const hfs_catFileData *file_rec, mac_forkID fork, struct mac_fileref *fileref) +{ + assert(l2_img->format == L2I_HFS); + + fileref->l2_img = l2_img; + + fileref->fileID = get_UINT32BE(file_rec->fileID); + fileref->forkType = fork; + + switch (fork) + { + case data_fork: + fileref->eof = get_UINT32BE(file_rec->dataLogicalSize); + fileref->pLen = get_UINT32BE(file_rec->dataPhysicalSize); + memcpy(fileref->hfs.extents, file_rec->dataExtents, sizeof(hfs_extent_3)); + break; + + case rsrc_fork: + fileref->eof = get_UINT32BE(file_rec->rsrcLogicalSize); + fileref->pLen = get_UINT32BE(file_rec->rsrcPhysicalSize); + memcpy(fileref->hfs.extents, file_rec->rsrcExtents, sizeof(hfs_extent_3)); + break; + } + + fileref->crPs = 0; + + fileref->reload_buf = true; + + return IMGTOOLERR_SUCCESS; +} + +/* + hfs_file_open + + Open a file located on a HFS volume. This function should not be called + directly: call mac_file_open instead. + + l2_img (I/O): level-2 image reference + parID (I): CNID of file parent directory + filename (I): name of the file (Mac string) + mac_forkID (I): tells which fork should be opened + fileref (O): mac file reference to open + + Return imgtool error code +*/ +static imgtoolerr_t hfs_file_open(struct mac_l2_imgref *l2_img, uint32_t parID, const mac_str255 filename, mac_forkID fork, struct mac_fileref *fileref) +{ + hfs_catKey *catrec_key; + hfs_catData *catrec_data; + uint16_t dataRecType; + imgtoolerr_t err; + + /* lookup file in catalog */ + err = hfs_cat_search(l2_img, parID, filename, &catrec_key, &catrec_data); + if (err) + return err; + + dataRecType = get_UINT16BE(catrec_data->dataType); + + /* file expected */ + if (dataRecType != hcrt_File) + return IMGTOOLERR_BADFILENAME; + + fileref->hfs.parID = get_UINT32BE(catrec_key->parID); + mac_strcpy(fileref->hfs.filename, catrec_key->cName); + + /* open it */ + return hfs_file_open_internal(l2_img, &catrec_data->file, fork, fileref); +} + +/* + hfs_file_get_nth_block_address + + Get the disk block address of a given block in an open file on a MFS image. + Called by macintosh file code. + + fileref (I/O): open mac file reference + block_num (I): file block index + block_address (O): disk block address for the file block + + Return imgtool error code +*/ +static imgtoolerr_t hfs_file_get_nth_block_address(struct mac_fileref *fileref, uint32_t block_num, uint32_t *block_address) +{ + uint32_t AB_num; + uint32_t cur_AB; + uint32_t i; + void *cur_extents_raw; + hfs_extent *cur_extents; + int cur_extents_len; + void *extents_BT_rec; + int extents_BT_rec_len; + imgtoolerr_t err; + uint16_t AB_address; + + assert(fileref->l2_img->format == L2I_HFS); + + AB_num = block_num / fileref->l2_img->blocksperAB; + cur_AB = 0; + cur_extents = fileref->hfs.extents; + + /* first look in catalog tree extents */ + for (i=0; i<3; i++) + { + if (AB_num < cur_AB+get_UINT16BE(cur_extents[i].numABlks)) + break; + cur_AB += get_UINT16BE(cur_extents[i].numABlks); + } + if (i == 3) + { + /* extent not found: read extents record from extents BT */ + hfs_extentKey search_key; + hfs_extentKey *found_key; + + search_key.keyLength = keyLength_hfs_extentKey; + search_key.forkType = fileref->forkType; + set_UINT32BE(&search_key.fileID, fileref->fileID); + set_UINT16BE(&search_key.startBlock, AB_num); + + /* search for the record with the largest key lower than or equal to + search_key. The keys are constructed in such a way that, if a record + includes AB_num, it is that one. */ + err = BT_search_leaf_rec(&fileref->l2_img->u.hfs.extents_BT, &search_key, + NULL, NULL, &extents_BT_rec, &extents_BT_rec_len, + false, NULL); + if (err) + return err; + + if (extents_BT_rec == NULL) + return IMGTOOLERR_CORRUPTIMAGE; + + found_key = (hfs_extentKey*)extents_BT_rec; + /* check that this record concerns the correct file */ + if ((found_key->forkType != fileref->forkType) + || (get_UINT32BE(found_key->fileID) != fileref->fileID)) + return IMGTOOLERR_CORRUPTIMAGE; + + /* extract start AB */ + cur_AB = get_UINT16BE(found_key->startBlock); + /* get extents pointer */ + err = BT_get_keyed_record_data(&fileref->l2_img->u.hfs.extents_BT, extents_BT_rec, extents_BT_rec_len, &cur_extents_raw, &cur_extents_len); + if (err) + return err; + if (cur_extents_len < 3*sizeof(hfs_extent)) + return IMGTOOLERR_CORRUPTIMAGE; + cur_extents = (hfs_extent*)cur_extents_raw; + + /* pick correct extent in record */ + for (i=0; i<3; i++) + { + if (AB_num < cur_AB+get_UINT16BE(cur_extents[i].numABlks)) + break; + cur_AB += get_UINT16BE(cur_extents[i].numABlks); + } + if (i == 3) + /* extent not found */ + return IMGTOOLERR_CORRUPTIMAGE; + } + + AB_address = get_UINT16BE(cur_extents[i].stABN) + (AB_num-cur_AB); + + if (AB_address >= fileref->l2_img->numABs) + return IMGTOOLERR_CORRUPTIMAGE; + + *block_address = fileref->l2_img->u.hfs.ABStart + AB_address * fileref->l2_img->blocksperAB + + block_num % fileref->l2_img->blocksperAB; + + return IMGTOOLERR_SUCCESS; +} + +#if 0 +#pragma mark - +#pragma mark B-TREE IMPLEMENTATION +#endif + +/* + B-tree (Balanced search tree) files are used by the HFS and HFS+ file + systems: the Extents and Catalog files are both B-Tree. + + Note that these B-trees are B+-trees: data is only on the leaf level, and + nodes located on the same level are also linked sequentially, which allows + fast sequenctial access to the catalog file. + + These files are normal files, except for the fact that they are not + referenced from the catalog but the MDB. They are allocated in fixed-size + records of 512 bytes (HFS). (HFS+ supports any power of two from 512 + through 32768, and uses a default of 1024 for Extents, and 4096 for both + Catalog and Attributes.) + + Nodes can contain any number of records. The nodes can be of any of four + types: header node (unique node with b-tree information, pointer to root + node and start of the node allocation bitmap), map nodes (which are created + when the node allocation bitmap outgrows the header node), index nodes + (root and branch node that enable to efficiently search the leaf nodes for + a specific key value), and leaf nodes (which hold the actual user data + records with keys and data). The first node is always a header node. + Other nodes can be of any of the 3 other type, or they can be free. +*/ + +/* + BTNodeHeader + + Header of a node record +*/ +struct BTNodeHeader +{ + UINT32BE fLink; /* (index of) next node at this level */ + UINT32BE bLink; /* (index of) previous node at this level */ + uint8_t kind; /* kind of node (leaf, index, header, map) */ + uint8_t height; /* zero for header, map; 1 for leaf, 2 through + treeDepth for index (child is one LESS than + parent, whatever IM says) */ + UINT16BE numRecords; /* number of records in this node */ + UINT16BE reserved; /* reserved; set to zero */ +}; + +/* + Constants for BTNodeHeader kind field +*/ +enum +{ + btnk_leafNode = 0xff, /* leaf nodes hold the actual user data records + with keys and data */ + btnk_indexNode = 0, /* root and branch node that enable to efficiently + search the leaf nodes for a specific key value */ + btnk_headerNode = 1, /* unique node with b-tree information, pointer to + root node and start of the node allocation + bitmap */ + btnk_mapNode = 2 /* map nodes are created when the node allocation + bitmap outgrows the header node */ +}; + +/* + BTHeaderRecord: first record of a B-tree header node (second record is + unused, and third is node allocation bitmap). +*/ +struct BTHeaderRecord +{ + UINT16BE treeDepth; /* maximum height (usually leaf nodes) */ + UINT32BE rootNode; /* node number of root node */ + UINT32BE leafRecords; /* number of leaf records in all leaf nodes */ + UINT32BE firstLeafNode; /* node number of first leaf node */ + UINT32BE lastLeafNode; /* node number of last leaf node */ + UINT16BE nodeSize; /* size of a node, in bytes */ + UINT16BE maxKeyLength; /* maximum length of data (index + leaf) record keys; + length of all index record keys if + btha_variableIndexKeysMask attribute flag is not set */ + UINT32BE totalNodes; /* total number of nodes in tree */ + UINT32BE freeNodes; /* number of unused (free) nodes in tree */ + + UINT16BE reserved1; /* unused */ + UINT32BE clumpSize; /* used in some HFS implementations? (reserved in + early HFS implementations, and in HFS Plus) */ + uint8_t btreeType; /* reserved - set to 0 */ + uint8_t reserved2; /* reserved */ + UINT32BE attributes; /* persistent attributes about the tree */ + UINT32BE reserved3[16]; /* reserved */ +}; + +static imgtoolerr_t BT_check(mac_BTref *BTref, int is_extent); + +/* + BT_open + + Open a file as a B-tree. The file must be already open as a macintosh + file. + + BTref (I/O): B-tree file handle to open (BTref->fileref must have been + open previously) + key_compare_func (I): function that compares two keys + is_extent (I): true if we are opening the extent B-tree (we want to do + extra checks in this case because the extent B-Tree may include extent + records for the extent B-tree itself, and if an extent record for the + extent B-tree is located in an extent that has not been defined by + previous extent records, then we can never retreive this extent record) + + Return imgtool error code +*/ +static imgtoolerr_t BT_open(mac_BTref *BTref, int (*key_compare_func)(const void *key1, const void *key2), int is_extent) +{ + imgtoolerr_t err; + BTNodeHeader node_header; + BTHeaderRecord header_rec; + + /* seek to node 0 */ + err = mac_file_seek(&BTref->fileref, 0); + if (err) + return err; + + /* read node header */ + err = mac_file_read(&BTref->fileref, sizeof(node_header), &node_header); + if (err) + return err; + + if ((node_header.kind != btnk_headerNode) || (get_UINT16BE(node_header.numRecords) < 3) + || (node_header.height != 0)) + return IMGTOOLERR_CORRUPTIMAGE; /* right??? */ + + /* CHEESY HACK: we assume that the header record immediately follows the + node header. This is because we need to know the node length to know where + the record pointers are located, but we need to read the header record to + know the node length. */ + err = mac_file_read(&BTref->fileref, sizeof(header_rec), &header_rec); + if (err) + return err; + + BTref->nodeSize = get_UINT16BE(header_rec.nodeSize); + BTref->rootNode = get_UINT32BE(header_rec.rootNode); + BTref->firstLeafNode = get_UINT32BE(header_rec.firstLeafNode); + BTref->attributes = get_UINT32BE(header_rec.attributes); + BTref->treeDepth = get_UINT16BE(header_rec.treeDepth); + BTref->maxKeyLength = get_UINT16BE(header_rec.maxKeyLength); + + BTref->key_compare_func = key_compare_func; + + BTref->node_buf = malloc(BTref->nodeSize); + if (!BTref->node_buf) + return IMGTOOLERR_OUTOFMEMORY; + + if (BTREE_CHECKS) + { + /* optional: check integrity of B-tree */ + err = BT_check(BTref, is_extent); + if (err) + return err; + } + + return IMGTOOLERR_SUCCESS; +} + +/* + BT_close + + Close a B-tree + + BTref (I/O): open B-tree file handle +*/ +static void BT_close(mac_BTref *BTref) +{ + free(BTref->node_buf); +} + +/* + BT_read_node + + Read a node from a B-tree + + BTref (I/O): open B-tree file handle + node_ID (I): index of the node to read + expected_kind (I): kind of the node to read + expected_depth (I): depth of the node to read + dest (O): destination buffer + + Return imgtool error code +*/ +static imgtoolerr_t BT_read_node(mac_BTref *BTref, uint32_t node_ID, int expected_kind, int expected_depth, void *dest) +{ + imgtoolerr_t err; + + /* seek to node */ + err = mac_file_seek(&BTref->fileref, node_ID*BTref->nodeSize); + if (err) + return err; + + /* read it */ + err = mac_file_read(&BTref->fileref, BTref->nodeSize, dest); + if (err) + return err; + + /* check node kind and depth */ + if ((((BTNodeHeader *) dest)->kind != expected_kind) + || (((BTNodeHeader *) dest)->height != expected_depth)) + return IMGTOOLERR_CORRUPTIMAGE; + + return IMGTOOLERR_SUCCESS; +} + +/* + BT_node_get_record + + Extract a raw record from a B-tree node + + BTref (I/O): open B-tree file handle + node_buf (I): buffer with the node the record should be extracted from + recnum (I): index of record to read + rec_ptr (O): set to point to start of record (key + data) + rec_len (O): set to total length of record (key + data) + + Return imgtool error code +*/ +static imgtoolerr_t BT_node_get_record(mac_BTref *BTref, void *node_buf, unsigned recnum, void **rec_ptr, int *rec_len) +{ + uint16_t node_numRecords = get_UINT16BE(((BTNodeHeader *) node_buf)->numRecords); + uint16_t offset; + uint16_t next_offset; + + if (recnum >= node_numRecords) + return IMGTOOLERR_UNEXPECTED; + + int recnum_s = (int)recnum; + offset = get_UINT16BE(((UINT16BE *)((uint8_t *) node_buf + BTref->nodeSize))[-recnum_s - 1]); + next_offset = get_UINT16BE(((UINT16BE *)((uint8_t *) node_buf + BTref->nodeSize))[-recnum_s - 2]); + + if ((offset < sizeof(BTNodeHeader)) || (offset > BTref->nodeSize-2*node_numRecords) + || (next_offset < sizeof(BTNodeHeader)) || (next_offset > BTref->nodeSize-2*node_numRecords) + || (offset & 1) || (next_offset & 1) + || (offset > next_offset)) + return IMGTOOLERR_CORRUPTIMAGE; + + *rec_ptr = (uint8_t *)node_buf + offset; + *rec_len = next_offset - offset; + + return IMGTOOLERR_SUCCESS; +} + +/* + BT_node_get_keyed_record + + Extract a keyed record from a B-tree node. Equivalent to + BT_node_get_record, only we do extra checks. + + BTref (I/O): open B-tree file handle + node_buf (I): buffer with the node the record should be extracted from + node_is_index (I): true if node is index node + recnum (I): index of record to read + rec_ptr (O): set to point to start of record (key + data) + rec_len (O): set to total length of record (key + data) + + Return imgtool error code +*/ +static imgtoolerr_t BT_node_get_keyed_record(mac_BTref *BTref, void *node_buf, int node_is_index, unsigned recnum, void **rec_ptr, int *rec_len) +{ + imgtoolerr_t err; + void *lrec_ptr; + int lrec_len; + int key_len; + + /* extract record */ + err = BT_node_get_record(BTref, node_buf, recnum, &lrec_ptr, &lrec_len); + if (err) + return err; + + /* read key len */ + key_len = (BTref->attributes & btha_bigKeysMask) + ? get_UINT16BE(* (UINT16BE *)lrec_ptr) + : (* (uint8_t *)lrec_ptr); + + /* check that key fits in record */ + if ((key_len + ((BTref->attributes & btha_bigKeysMask) ? 2 : 1)) > lrec_len) + /* hurk! */ + return IMGTOOLERR_CORRUPTIMAGE; + + if (key_len > BTref->maxKeyLength) + return IMGTOOLERR_CORRUPTIMAGE; + + if (node_is_index && (! (BTref->attributes & btha_variableIndexKeysMask)) && (key_len != BTref->maxKeyLength)) + return IMGTOOLERR_CORRUPTIMAGE; + + if (rec_ptr) + *rec_ptr = lrec_ptr; + if (rec_len) + *rec_len = lrec_len; + + return IMGTOOLERR_SUCCESS; +} + +/* + BT_get_keyed_record_data + + extract data from a keyed record + + BTref (I/O): open B-tree file handle + rec_ptr (I): point to start of record (key + data) + rec_len (I): total length of record (key + data) + data_ptr (O): set to point to record data + data_len (O): set to length of record data + + Return imgtool error code +*/ +static imgtoolerr_t BT_get_keyed_record_data(mac_BTref *BTref, void *rec_ptr, int rec_len, void **data_ptr, int *data_len) +{ + int lkey_len; + int data_offset; + + /* read key len */ + lkey_len = (BTref->attributes & btha_bigKeysMask) + ? get_UINT16BE(* (UINT16BE *)rec_ptr) + : (* (uint8_t *)rec_ptr); + + /* compute offset to data record */ + data_offset = lkey_len + ((BTref->attributes & btha_bigKeysMask) ? 2 : 1); + if (data_offset > rec_len) + /* hurk! */ + return IMGTOOLERR_CORRUPTIMAGE; + /* fix alignment */ + if (data_offset & 1) + data_offset++; + + if (data_ptr) + *data_ptr = (uint8_t *)rec_ptr + data_offset; + if (data_len) + *data_len = (rec_len > data_offset) ? rec_len-data_offset : 0; + + return IMGTOOLERR_SUCCESS; +} + +/* + BT_check + + Check integrity of a complete B-tree + + BTref (I/O): open B-tree file handle + is_extent (I): true if we are opening the extent B-tree (we want to do + extra checks in this case because the extent B-Tree may include extent + records for the extent B-tree itself, and if an extent record for the + extent B-tree is located in an extent that has not been defined by + previous extent records, then we can never retreive this extent record) + + Return imgtool error code +*/ +struct data_nodes_t +{ + void *buf; + uint32_t node_num; + uint32_t cur_rec; + uint32_t num_recs; +}; +static imgtoolerr_t BT_check(mac_BTref *BTref, int is_extent) +{ + uint16_t node_numRecords; + BTHeaderRecord *header_rec; + uint8_t *bitmap; + + data_nodes_t *data_nodes; + int i, j; + uint32_t cur_node, prev_node; + void *rec1, *rec2; + int rec1_len, rec2_len; + void *rec1_data; + int rec1_data_len; + uint32_t totalNodes, lastLeafNode; + uint32_t freeNodes; + int compare_result; + uint32_t map_count, map_len; + uint32_t run_len; + uint32_t run_bit_len; + uint32_t actualFreeNodes; + imgtoolerr_t err; + uint32_t maxExtentAB = 0, maxExtentNode = 0, extentEOL = 0; /* if is_extent is true */ + + if (is_extent) + { + switch (BTref->fileref.l2_img->format) + { + case L2I_MFS: + /* MFS does not feature any extents B-tree! */ + return IMGTOOLERR_UNEXPECTED; + + case L2I_HFS: + maxExtentAB = 0; + for (j=0; j<3; j++) + maxExtentAB += get_UINT16BE(BTref->fileref.hfs.extents[j].numABlks); + maxExtentNode = (uint64_t)maxExtentAB * 512 * BTref->fileref.l2_img->blocksperAB + / BTref->nodeSize; + extentEOL = false; + break; + } + } + + /* read header node */ + if ((! is_extent) || (0 < maxExtentNode)) + err = BT_read_node(BTref, 0, btnk_headerNode, 0, BTref->node_buf); + else + err = IMGTOOLERR_CORRUPTIMAGE; + if (err) + return err; + + /* check we have enough records */ + node_numRecords = get_UINT16BE(((BTNodeHeader *) BTref->node_buf)->numRecords); + if (node_numRecords < 3) + return IMGTOOLERR_CORRUPTIMAGE; + + /* get header record */ + err = BT_node_get_record(BTref, BTref->node_buf, 0, &rec1, &rec1_len); + if (err) + return err; + header_rec = (BTHeaderRecord *)rec1; + + /* check length of header record */ + if (rec1_len < sizeof(BTHeaderRecord)) + return IMGTOOLERR_CORRUPTIMAGE; + + totalNodes = get_UINT32BE(header_rec->totalNodes); + if (totalNodes == 0) + /* we need at least one header node */ + return IMGTOOLERR_CORRUPTIMAGE; + lastLeafNode = get_UINT32BE(header_rec->lastLeafNode); + freeNodes = get_UINT32BE(header_rec->freeNodes); + + /* check file length */ + if ((BTref->nodeSize * totalNodes) > BTref->fileref.pLen) + return IMGTOOLERR_CORRUPTIMAGE; + + /* initialize for the function postlog ("bail:" tag) */ + err = IMGTOOLERR_SUCCESS; + bitmap = NULL; + data_nodes = NULL; + + /* alloc buffer for reconstructed bitmap */ + map_len = (totalNodes + 7) / 8; + bitmap = (uint8_t*)malloc(map_len); + if (! bitmap) + return IMGTOOLERR_OUTOFMEMORY; + memset(bitmap, 0, map_len); + + /* check B-tree data nodes (i.e. index and leaf nodes) */ + if (BTref->treeDepth == 0) + { + /* B-tree is empty */ + if (BTref->rootNode || BTref->firstLeafNode || lastLeafNode) + { + err = IMGTOOLERR_OUTOFMEMORY; + goto bail; + } + } + else + { + /* alloc array of buffers for catalog data nodes */ + data_nodes = (data_nodes_t *)malloc(sizeof(data_nodes_t) * BTref->treeDepth); + if (! data_nodes) + { + err = IMGTOOLERR_OUTOFMEMORY; + goto bail; + } + for (i=0; itreeDepth; i++) + data_nodes[i].buf = NULL; /* required for function postlog to work should next loop fail */ + for (i=0; itreeDepth; i++) + { + data_nodes[i].buf = malloc(BTref->nodeSize); + if (!data_nodes[i].buf) + { + err = IMGTOOLERR_OUTOFMEMORY; + goto bail; + } + } + + /* read first data nodes */ + cur_node = BTref->rootNode; + for (i=BTref->treeDepth-1; i>=0; i--) + { + /* check node index */ + if (cur_node >= totalNodes) + { + err = IMGTOOLERR_CORRUPTIMAGE; + goto bail; + } + /* check that node has not been used for another purpose */ + /* this check is unecessary because the current consistency checks + that forward and back linking match and that node height is correct + are enough to detect such errors */ +#if 0 + if (bitmap[cur_node >> 3] & (0x80 >> (cur_node & 7))) + { + err = IMGTOOLERR_CORRUPTIMAGE; + goto bail; + } +#endif + /* add node in bitmap */ + bitmap[cur_node >> 3] |= (0x80 >> (cur_node & 7)); + /* read node */ + if ((! is_extent) || (cur_node < maxExtentNode)) + err = BT_read_node(BTref, cur_node, i ? btnk_indexNode : btnk_leafNode, i+1, data_nodes[i].buf); + else + err = IMGTOOLERR_CORRUPTIMAGE; + if (err) + goto bail; + /* check that it is the first node at this level */ + if (get_UINT32BE(((BTNodeHeader *) data_nodes[i].buf)->bLink)) + { + err = IMGTOOLERR_CORRUPTIMAGE; + goto bail; + } + /* fill other fields */ + data_nodes[i].node_num = cur_node; + data_nodes[i].cur_rec = 0; + data_nodes[i].num_recs = get_UINT16BE(((BTNodeHeader *) data_nodes[i].buf)->numRecords); + /* check that there is at least one record */ + if (data_nodes[i].num_recs == 0) + { + err = IMGTOOLERR_CORRUPTIMAGE; + goto bail; + } + + /* iterate to next level if applicable */ + if (i != 0) + { + /* extract first record */ + err = BT_node_get_keyed_record(BTref, data_nodes[i].buf, true, 0, &rec1, &rec1_len); + if (err) + goto bail; + + /* extract record data ptr */ + err = BT_get_keyed_record_data(BTref, rec1, rec1_len, &rec1_data, &rec1_data_len); + if (err) + goto bail; + if (rec1_data_len < sizeof(UINT32BE)) + { + err = IMGTOOLERR_CORRUPTIMAGE; + goto bail; + } + + /* iterate to next level */ + cur_node = get_UINT32BE(* (UINT32BE *)rec1_data); + } + } + + /* check that a) the root node has no successor, and b) that we have really + read the first leaf node */ + if (get_UINT32BE(((BTNodeHeader *) data_nodes[BTref->treeDepth-1].buf)->fLink) + || (cur_node != BTref->firstLeafNode)) + { + err = IMGTOOLERR_CORRUPTIMAGE; + goto bail; + } + + /* check that keys are ordered correctly */ + while (1) + { + /* iterate through parent nodes */ + i = 0; + while ((itreeDepth) && ((data_nodes[i].cur_rec == 0) || (data_nodes[i].cur_rec == data_nodes[i].num_recs))) + { + /* read next node if necessary */ + if (data_nodes[i].cur_rec == data_nodes[i].num_recs) + { + /* get link to next node */ + cur_node = get_UINT32BE(((BTNodeHeader *) data_nodes[i].buf)->fLink); + if (cur_node == 0) + { + if (i == 0) + /* normal End of List */ + goto end_of_list; + else + { + /* error */ + err = IMGTOOLERR_CORRUPTIMAGE; + goto bail; + } + } + /* add node in bitmap */ + bitmap[cur_node >> 3] |= (0x80 >> (cur_node & 7)); + /* read node */ + if ((! is_extent) || (cur_node < maxExtentNode)) + err = BT_read_node(BTref, cur_node, i ? btnk_indexNode : btnk_leafNode, i+1, data_nodes[i].buf); + else + err = IMGTOOLERR_CORRUPTIMAGE; + if (err) + goto bail; + /* check that backward linking match forward linking */ + if (get_UINT32BE(((BTNodeHeader *) data_nodes[i].buf)->bLink) != data_nodes[i].node_num) + { + err = IMGTOOLERR_CORRUPTIMAGE; + goto bail; + } + /* fill other fields */ + data_nodes[i].node_num = cur_node; + data_nodes[i].cur_rec = 0; + data_nodes[i].num_recs = get_UINT16BE(((BTNodeHeader *) data_nodes[i].buf)->numRecords); + /* check that there is at least one record */ + if (data_nodes[i].num_recs == 0) + { + err = IMGTOOLERR_CORRUPTIMAGE; + goto bail; + } + /* next test is not necessary because we have checked that + the root node has no successor */ +#if 0 + if (i < BTref->treeDepth-1) + { +#endif + data_nodes[i+1].cur_rec++; +#if 0 + } + else + { + err = IMGTOOLERR_CORRUPTIMAGE; + goto bail; + } +#endif + } + i++; + } + + if (is_extent && !extentEOL) + { + /* extract current leaf record and update maxExtentAB and + maxExtentNode */ + hfs_extentKey *extentKey; + hfs_extent *extentData; + + /* extract current leaf record */ + err = BT_node_get_keyed_record(BTref, data_nodes[0].buf, false, data_nodes[0].cur_rec, &rec1, &rec1_len); + if (err) + goto bail; + + extentKey = (hfs_extentKey*)rec1; + if ((extentKey->keyLength < 7) || (extentKey->forkType != 0) || (get_UINT32BE(extentKey->fileID) != 3) + || (get_UINT16BE(extentKey->startBlock) != maxExtentAB)) + /* the key is corrupt or does not concern the extent + B-tree: set the extentEOL flag so that we stop looking for + further extent records for the extent B-tree */ + extentEOL = true; + else + { /* this key concerns the extent B-tree: update maxExtentAB + and maxExtentNode */ + /* extract record data ptr */ + err = BT_get_keyed_record_data(BTref, rec1, rec1_len, &rec1_data, &rec1_data_len); + if (err) + goto bail; + if (rec1_data_len < sizeof(hfs_extent)*3) + /* the record is corrupt: set the extentEOL flag so + that we stop looking for further extent records for the + extent B-tree */ + extentEOL = true; + else + { + extentData = (hfs_extent*)rec1_data; + + for (j=0; j<3; j++) + maxExtentAB += get_UINT16BE(extentData[j].numABlks); + maxExtentNode = (uint64_t)maxExtentAB * 512 * BTref->fileref.l2_img->blocksperAB + / BTref->nodeSize; + } + } + if (extentEOL) + { + /* check that the extent B-Tree has been defined entirely */ + if (maxExtentNode < totalNodes) + { /* no good */ + err = IMGTOOLERR_CORRUPTIMAGE; + goto bail; + } + } + } + + if (itreeDepth) + { + /* extract current record */ + err = BT_node_get_keyed_record(BTref, data_nodes[i].buf, i > 0, data_nodes[i].cur_rec, &rec1, &rec1_len); + if (err) + goto bail; + + /* extract previous record */ + err = BT_node_get_keyed_record(BTref, data_nodes[i].buf, i > 0, data_nodes[i].cur_rec-1, &rec2, &rec2_len); + if (err) + goto bail; + + /* check that it is sorted correctly */ + compare_result = (*BTref->key_compare_func)(rec1, rec2); + if (compare_result <= 0) + { + err = IMGTOOLERR_CORRUPTIMAGE; + goto bail; + } + + i--; + } + else + { + i--; + if (i>0) + { /* extract first record of root if it is an index node */ + err = BT_node_get_keyed_record(BTref, data_nodes[i].buf, true, data_nodes[i].cur_rec, &rec1, &rec1_len); + if (err) + goto bail; + } + i--; + } + + while (i>=0) + { + /* extract first record of current level */ + err = BT_node_get_keyed_record(BTref, data_nodes[i].buf, i > 0, data_nodes[i].cur_rec, &rec2, &rec2_len); + if (err) + goto bail; + + /* compare key with key of current record of upper level */ + compare_result = (*BTref->key_compare_func)(rec1, rec2); + if (compare_result != 0) + { + err = IMGTOOLERR_CORRUPTIMAGE; + goto bail; + } + + /* extract record data ptr */ + err = BT_get_keyed_record_data(BTref, rec1, rec1_len, &rec1_data, &rec1_data_len); + if (err) + goto bail; + if (rec1_data_len < sizeof(UINT32BE)) + { + err = IMGTOOLERR_CORRUPTIMAGE; + goto bail; + } + cur_node = get_UINT32BE(* (UINT32BE *)rec1_data); + + /* compare node index with data of current record of upper + level */ + if (cur_node != data_nodes[i].node_num) + { + err = IMGTOOLERR_CORRUPTIMAGE; + goto bail; + } + + /* iterate to next level */ + rec1 = rec2; + rec1_len = rec2_len; + i--; + } + + /* next leaf record */ + data_nodes[0].cur_rec++; + } + +end_of_list: + /* check that we are at the end of list for each index level */ + for (i=1; itreeDepth; i++) + { + if ((data_nodes[i].cur_rec != (data_nodes[i].num_recs-1)) + || get_UINT32BE(((BTNodeHeader *) data_nodes[i].buf)->fLink)) + { + err = IMGTOOLERR_CORRUPTIMAGE; + goto bail; + } + } + /* check that the last leaf node is what it is expected to be */ + if (data_nodes[0].node_num != lastLeafNode) + { + err = IMGTOOLERR_CORRUPTIMAGE; + goto bail; + } + } + + /* check map node chain */ + cur_node = 0; /* node 0 is the header node... */ + bitmap[0] |= 0x80; + /* check back linking */ + if (get_UINT32BE(((BTNodeHeader *) BTref->node_buf)->bLink)) + { + err = IMGTOOLERR_CORRUPTIMAGE; + goto bail; + } + /* get pointer to next node */ + cur_node = get_UINT32BE(((BTNodeHeader *) BTref->node_buf)->fLink); + while (cur_node != 0) + { + /* save node address */ + prev_node = cur_node; + /* check that node has not been used for another purpose */ + /* this check is unecessary because the current consistency checks that + forward and back linking match and that node height is correct are + enough to detect such errors */ +#if 0 + if (bitmap[cur_node >> 3] & (0x80 >> (cur_node & 7))) + { + err = IMGTOOLERR_CORRUPTIMAGE; + goto bail; + } +#endif + /* add node in bitmap */ + bitmap[cur_node >> 3] |= (0x80 >> (cur_node & 7)); + /* read map node */ + if ((! is_extent) || (cur_node < maxExtentNode)) + err = BT_read_node(BTref, cur_node, btnk_mapNode, 0, BTref->node_buf); + else + err = IMGTOOLERR_CORRUPTIMAGE; + if (err) + goto bail; + /* check back linking */ + if (get_UINT32BE(((BTNodeHeader *) BTref->node_buf)->bLink) != prev_node) + { + err = IMGTOOLERR_CORRUPTIMAGE; + goto bail; + } + /* get pointer to next node */ + cur_node = get_UINT32BE(((BTNodeHeader *) BTref->node_buf)->fLink); + } + + /* re-read header node */ + err = BT_read_node(BTref, 0, btnk_headerNode, 0, BTref->node_buf); + if (err) + goto bail; + + /* get header bitmap record */ + err = BT_node_get_record(BTref, BTref->node_buf, 2, &rec1, &rec1_len); + if (err) + goto bail; + + /* check bitmap, iterating map nodes */ + map_count = 0; + actualFreeNodes = 0; + while (map_count < map_len) + { + /* compute compare len */ + run_len = rec1_len; + if (run_len > (map_len-map_count)) + run_len = map_len-map_count; + /* check that all used nodes are marked as such in the B-tree bitmap */ + for (i=0; i (totalNodes-map_count*8)) + run_bit_len = totalNodes-map_count*8; + for (i=0; i>3] & (0x80 >> (i & 7)))) + actualFreeNodes++; + map_count += run_len; + /* read next map node if required */ + if (map_count < map_len) + { + /* get pointer to next node */ + cur_node = get_UINT32BE(((BTNodeHeader *) BTref->node_buf)->fLink); + if (cur_node == 0) + { + err = IMGTOOLERR_CORRUPTIMAGE; + goto bail; + } + /* read map node */ + err = BT_read_node(BTref, cur_node, btnk_mapNode, 0, BTref->node_buf); + if (err) + goto bail; + /* get map record */ + err = BT_node_get_record(BTref, BTref->node_buf, 0, &rec1, &rec1_len); + if (err) + goto bail; + header_rec = (BTHeaderRecord *)rec1; + } + } + + /* check free node count */ + if (freeNodes != actualFreeNodes) + { + err = IMGTOOLERR_CORRUPTIMAGE; + goto bail; + } + +bail: + /* free buffers */ + if (data_nodes) + { + for (i=0; itreeDepth; i++) + if (data_nodes[i].buf) + free(data_nodes[i].buf); + free(data_nodes); + } + if (bitmap) + free(bitmap); + + return err; +} + +/* + BT_search_leaf_rec + + Search for a given key in a B-Tree. If exact match found, returns + corresponding leaf record. Otherwise, may return the greatest record less + than the requested key (of course, this will fail if the key is lower than + all keys in the B-Tree). + + BTref (I/O): open B-tree file handle + search_key (I): key to search the B-Tree for + node_ID (O): set to the node ID of the node the record is located in (may + be NULL) + record_ID (O): set to the index of the record in the node (may be NULL) + record_ptr (O): set to point to record in node buffer (may be NULL) + record_len (O): set to total record len (may be NULL) + search_exact_match (I): if true, the function will search for a record + equal to search_key; if false, the function will search for the + greatest record less than or equal to search_key + match_found (O): set to true if an exact match for search_key has been + found (only makes sense if search_exact_match is false) (may be NULL) + + Return imgtool error code +*/ +static imgtoolerr_t BT_search_leaf_rec(mac_BTref *BTref, const void *search_key, + uint32_t *node_ID, int *record_ID, + void **record_ptr, int *record_len, + int search_exact_match, int *match_found) +{ + imgtoolerr_t err; + int i; + uint32_t cur_node; + void *cur_rec; + int cur_rec_len; + void *last_rec; + int last_rec_len = 0; + void *rec_data; + int rec_data_len; + int depth; + uint16_t node_numRecords; + int compare_result = 0; + + /* start with root node */ + if ((BTref->rootNode == 0) || (BTref->treeDepth == 0)) + /* tree is empty */ + return ((BTref->rootNode == 0) == (BTref->treeDepth == 0)) + ? IMGTOOLERR_FILENOTFOUND + : IMGTOOLERR_CORRUPTIMAGE; + + cur_node = BTref->rootNode; + depth = BTref->treeDepth; + + while (1) + { + /* read current node */ + err = BT_read_node(BTref, cur_node, (depth > 1) ? btnk_indexNode : btnk_leafNode, depth, BTref->node_buf); + if (err) + return err; + + /* search for key */ + node_numRecords = get_UINT16BE(((BTNodeHeader *) BTref->node_buf)->numRecords); + last_rec = cur_rec = NULL; + for (i=0; inode_buf, depth > 1, i, &cur_rec, &cur_rec_len); + if (err) + return err; + + compare_result = (*BTref->key_compare_func)(cur_rec, search_key); + if (compare_result > 0) + break; + last_rec = cur_rec; + last_rec_len = cur_rec_len; + if (compare_result == 0) + break; + } + + if (! last_rec) + { /* all keys are greater than the search key: the search key is + nowhere in the tree */ + if (search_exact_match) + return IMGTOOLERR_FILENOTFOUND; + + if (match_found) + *match_found = false; + + if (node_ID) + *node_ID = 0; + + if (record_ID) + *record_ID = -1; + + if (record_ptr) + *record_ptr = NULL; + + return IMGTOOLERR_SUCCESS; + } + + if (((BTNodeHeader *) BTref->node_buf)->kind == btnk_leafNode) + /* leaf node -> end of search */ + break; + + /* extract record data ptr */ + err = BT_get_keyed_record_data(BTref, last_rec, last_rec_len, &rec_data, &rec_data_len); + if (err) + return err; + if (rec_data_len < sizeof(UINT32BE)) + return IMGTOOLERR_CORRUPTIMAGE; + + /* iterate to next level */ + cur_node = get_UINT32BE(* (UINT32BE *)rec_data); + depth--; + } + + if (compare_result != 0) + /* key not found */ + if (search_exact_match) + return IMGTOOLERR_FILENOTFOUND; + + if (match_found) + *match_found = (compare_result == 0); + + if (node_ID) + *node_ID = cur_node; + + if (record_ID) + *record_ID = i; + + if (record_ptr) + *record_ptr = last_rec; + + if (record_len) + *record_len = last_rec_len; + + return IMGTOOLERR_SUCCESS; +} + +/* + BT_leaf_rec_enumerator_open + + Open enumerator for leaf records of a B-Tree + + BTref (I/O): open B-tree file handle + enumerator (O): B-Tree enumerator to open + + Return imgtool error code +*/ +static imgtoolerr_t BT_leaf_rec_enumerator_open(mac_BTref *BTref, BT_leaf_rec_enumerator *enumerator) +{ + enumerator->BTref = BTref; + enumerator->cur_node = BTref->firstLeafNode; + enumerator->cur_rec = 0; + + return IMGTOOLERR_SUCCESS; +} + +/* + BT_leaf_rec_enumerator_read + + Read next leaf record of a B-Tree + + enumerator (I/O): open B-Tree enumerator + + Return imgtool error code +*/ +static imgtoolerr_t BT_leaf_rec_enumerator_read(BT_leaf_rec_enumerator *enumerator, void **record_ptr, int *rec_len) +{ + uint16_t node_numRecords; + imgtoolerr_t err; + + + *record_ptr = NULL; + + /* check EOList condition */ + if (enumerator->cur_node == 0) + return IMGTOOLERR_SUCCESS; + + /* read current node */ + err = BT_read_node(enumerator->BTref, enumerator->cur_node, btnk_leafNode, 1, enumerator->BTref->node_buf); + if (err) + return err; + node_numRecords = get_UINT16BE(((BTNodeHeader *) enumerator->BTref->node_buf)->numRecords); + + /* skip nodes until we find a record */ + while ((enumerator->cur_rec >= node_numRecords) && (enumerator->cur_node != 0)) + { + enumerator->cur_node = get_UINT32BE(((BTNodeHeader *) enumerator->BTref->node_buf)->fLink); + enumerator->cur_rec = 0; + + /* read node */ + err = BT_read_node(enumerator->BTref, enumerator->cur_node, btnk_leafNode, 1, enumerator->BTref->node_buf); + if (err) + return err; + node_numRecords = get_UINT16BE(((BTNodeHeader *) enumerator->BTref->node_buf)->numRecords); + } + + /* check EOList condition */ + if (enumerator->cur_node == 0) + return IMGTOOLERR_SUCCESS; + + /* get current record */ + err = BT_node_get_keyed_record(enumerator->BTref, enumerator->BTref->node_buf, false, enumerator->cur_rec, record_ptr, rec_len); + if (err) + return err; + + /* iterate to next record */ + enumerator->cur_rec++; + if (enumerator->cur_rec >= node_numRecords) + { /* iterate to next node if last record (not required, but will improve + performance on next iteration) */ + enumerator->cur_node = get_UINT32BE(((BTNodeHeader *) enumerator->BTref->node_buf)->fLink); + enumerator->cur_rec = 0; + } + return IMGTOOLERR_SUCCESS; +} + +/* + B-Tree extend EOF algorithm: + * see if the bitmap will need to be extended + * extend EOF by min 1 (if bitmap is large engough) or 2 (if bitmap needs + to be extended) and max ClumpSiz (see extClpSiz and ctClpSiz in MDB) + ***If we are extending the extent B-Tree, we need to defer the possible + creation of an additional extent record, or we might enter an endless + recursion loop*** + + Empty node alloc algorithm: + + * see if there is any free node in B-tree bitmap + * optionally, try to compact the B-tree if file is full + * if file is still full, extend EOF and try again + * mark new block as used and return its index + + + Empty node delete algorithm: + + * remove node from link list + * mark node as free in the B-tree bitmap + * optionally, if more than N% of the B-tree is free, compact the B-tree and + free some disk space + * Count nodes on this level; if there is only one left, delete parent index + node and mark the relaining node as root; if it was the last leaf node, + update header node with an empty B-tree; in either case, decrement tree + depth + + + Record shifting algorithm: + + For a given node and its first non-empty successor node: + + * compute how much free room there is in the node + * see if the first record of the first non-empty successor can fit + * if so, move it (i.e. delete the first record of the later node, and add a + copy of it to the end of the former) + + + Node merging algorithm + + * Consider node and its predecessor. If there is room, shift all records + from later to former, then delete empty later node, and delete later + record from parent index node. + + + Node splitting algorithm (non-first) + + * Consider node and its predecessor. Create new middle node and split + records in 3 even sets. Update record for last node and insert record + for middle node in parent index node. + + + Node splitting algorithm (first node) + + * Create new successor node, and split records in 2 1/3 and 2/3 sets. + Insert record for later node in parent index node. + + + Record delete algorithm: + + * remove record from node + * if record was first in node, test if node is now empty + * if node is not empty, substitute key of deleted record with key of + new head record in index tree + * if node is empty, delete key of deleted record in index tree, then + delete empty node + * optionally, look the predecessor node. Merge the two nodes if possible. + + + Record insert algorithm: + + * if there room, just insert new record in node; if new record is in first + position, update record in parent index node + * else consider predecessor: see if we can make enough room by shifting + records. If so, do shift records, insert new record, update record in + parent index node + * else split the nodes and insert record +*/ +/* + Possible additions: + + Node compaction algorithm: + + This algorithm can be executed with a specific start point and max number + of nodes, or with all nodes on a level. + + * see how many nodes we can save by shifting records left + * if we will save at least one node, do shift as many records as possible + (try to leave free space split homogeneously???) +*/ + +/*static void*/ + +#if 0 +#pragma mark - +#pragma mark RESOURCE IMPLEMENTATION +#endif + +/* + Resource manager + + The resource manager stores arbitrary chunks of data (resource) identified + by a type/id pair. The resource type is a 4-char code, which generally + implies the format of the data (e.g. 'PICT' is for a quickdraw picture, + 'STR ' for a macintosh string, 'CODE' for 68k machine code, etc). The + resource id is a signed 16-bit number that uniquely identifies each + resource of a given type. Note that, with most resource types, resources + with id < 128 are system resources that are available to all applications, + whereas resources with id >= 128 are application resources visible only to + the application that defines them. + + Each resource can optionally have a resource name, which is a macintosh + string of 255 chars at most. + + Limits: + 16MBytes of data + 64kbytes of type+reference lists + 64kbytes of resource names + + The Macintosh toolbox can open several resource files simulteanously to + overcome these restrictions. + + Resources are used virtually everywhere in the Macintosh Toolbox, so it is + no surprise that file comments and MFS folders are stored in resource files. +*/ + +/* + Resource header +*/ +struct rsrc_header +{ + UINT32BE data_offs; /* Offset from beginning of resource fork to resource data */ + UINT32BE map_offs; /* Offset from beginning of resource fork to resource map */ + UINT32BE data_len; /* Length of resource data */ + UINT32BE map_len; /* Length of resource map */ +}; + +/* + Resource data: each data entry is preceded by its len (UINT32BE) + Offset to specific data fields are gotten from the resource map +*/ + +/* + Resource map: +*/ +struct rsrc_map_header +{ + rsrc_header reserved0; /* Reserved for copy of resource header */ + UINT32BE reserved1; /* Reserved for handle to next resource map */ + UINT16BE reserved2; /* Reserved for file reference number */ + + UINT16BE attr; /* Resource fork attributes */ + UINT16BE typelist_offs; /* Offset from beginning of map to resource type list */ + UINT16BE namelist_offs; /* Offset from beginning of map to resource name list */ + UINT16BE type_count; /* Number of types in the map minus 1 */ + /* This is actually part of the type list, which matters for offsets */ +}; + +/* + Resource type list entry +*/ +struct rsrc_type_entry +{ + UINT32BE type; /* Resource type */ + UINT16BE ref_count; /* Number of resources of this type in map minus 1 */ + UINT16BE ref_offs; /* Offset from beginning of resource type list to reference list for this type */ +}; + +/* + Resource reference list entry +*/ +struct rsrc_ref_entry +{ + UINT16BE id; /* Resource ID */ + UINT16BE name_offs; /* Offset from beginning of resource name list to resource name */ + /* (-1 if none) */ + uint8_t attr; /* Resource attributes */ + UINT24BE data_offs; /* Offset from beginning of resource data to data for this resource */ + UINT32BE reserved; /* Reserved for handle to resource */ +}; + +/* + Resource name list entry: this is just a standard macintosh string +*/ + +struct mac_resfileref +{ + mac_fileref fileref; /* open resource fork ref (you may open resources + files in data fork, too, if you ever need to, + but Classic MacOS never does such a thing + (MacOS X often does so, though)) */ + uint32_t data_offs; /* Offset from beginning of resource file to resource data */ + uint32_t map_offs; /* Offset from beginning of resource file to resource data */ + + uint16_t typelist_offs; /* Offset from beginning of map to resource type list */ + uint16_t namelist_offs; /* Offset from beginning of map to resource name list */ + uint16_t type_count; /* Number of types in the map minus 1 */ + /* This is actually part of the type list, which matters for offsets */ +}; + +#ifdef UNUSED_FUNCTION +/* + resfile_open + + Open a file as a resource file. The file must be already open as a + macintosh file. + + resfileref (I/O): resource file handle to open (resfileref->fileref must + have been opened previously) + + Return imgtool error code +*/ +static imgtoolerr_t resfile_open(mac_resfileref *resfileref) +{ + imgtoolerr_t err; + rsrc_header header; + rsrc_map_header map_header; + + /* seek to resource header */ + err = mac_file_seek(&resfileref->fileref, 0); + if (err) + return err; + + err = mac_file_read(&resfileref->fileref, sizeof(header), &header); + if (err) + return err; + + resfileref->data_offs = get_UINT32BE(header.data_offs); + resfileref->map_offs = get_UINT32BE(header.map_offs); + + /* seek to resource map header */ + err = mac_file_seek(&resfileref->fileref, resfileref->map_offs); + if (err) + return err; + + err = mac_file_read(&resfileref->fileref, sizeof(map_header), &map_header); + if (err) + return err; + + resfileref->typelist_offs = get_UINT16BE(map_header.typelist_offs); + resfileref->namelist_offs = get_UINT16BE(map_header.namelist_offs); + resfileref->type_count = get_UINT16BE(map_header.type_count); + + return IMGTOOLERR_SUCCESS; +} + +/* + resfile_get_entry + + Get the resource entry in the resource map associated with a given type/id + pair. + + resfileref (I/O): open resource file handle + type (I): type of the resource + id (I): id of the resource + entry (O): resource entry that has been read + + Return imgtool error code +*/ +static imgtoolerr_t resfile_get_entry(mac_resfileref *resfileref, uint32_t type, uint16_t id, rsrc_ref_entry *entry) +{ + imgtoolerr_t err; + rsrc_type_entry type_entry; + uint16_t ref_count; + int i; + + /* seek to resource type list in resource map */ + err = mac_file_seek(&resfileref->fileref, resfileref->map_offs+resfileref->typelist_offs+2); + if (err) + return err; + + if (resfileref->type_count == 0xffff) + /* type list is empty */ + return IMGTOOLERR_FILENOTFOUND; + + for (i=0; i<=resfileref->type_count; i++) + { + err = mac_file_read(&resfileref->fileref, sizeof(type_entry), &type_entry); + if (err) + return err; + if (type == get_UINT32BE(type_entry.type)) + break; + } + if (i > resfileref->type_count) + /* type not found in list */ + return IMGTOOLERR_FILENOTFOUND; + + ref_count = get_UINT16BE(type_entry.ref_count); + + /* seek to resource ref list for this type in resource map */ + err = mac_file_seek(&resfileref->fileref, resfileref->map_offs+resfileref->typelist_offs+get_UINT16BE(type_entry.ref_offs)); + if (err) + return err; + + if (ref_count == 0xffff) + /* ref list is empty */ + return IMGTOOLERR_FILENOTFOUND; + + for (i=0; i<=ref_count; i++) + { + err = mac_file_read(&resfileref->fileref, sizeof(*entry), entry); + if (err) + return err; + if (id == get_UINT16BE(entry->id)) + break; + } + if (i > ref_count) + /* id not found in list */ + return IMGTOOLERR_FILENOTFOUND; + + /* type+id have been found... */ + return IMGTOOLERR_SUCCESS; +} + +/* + resfile_get_resname + + Get the name of a resource. + + resfileref (I/O): open resource file handle + entry (I): resource entry in the resource map (returned by + resfile_get_entry) + string (O): resource name + + Return imgtool error code +*/ +static imgtoolerr_t resfile_get_resname(mac_resfileref *resfileref, const rsrc_ref_entry *entry, mac_str255 string) +{ + imgtoolerr_t err; + uint16_t name_offs; + uint8_t len; + + name_offs = get_UINT16BE(entry->name_offs); + + if (name_offs == 0xffff) + /* ref list is empty */ + return IMGTOOLERR_UNEXPECTED; + + /* seek to resource name in name list in resource map */ + err = mac_file_seek(&resfileref->fileref, resfileref->map_offs+name_offs); + if (err) + return err; + + /* get string length */ + err = mac_file_read(&resfileref->fileref, 1, &len); + if (err) + return err; + + string[0] = len; + + /* get string data */ + err = mac_file_read(&resfileref->fileref, len, string+1); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + +/* + resfile_get_reslen + + Get the data length for a given resource. + + resfileref (I/O): open resource file handle + entry (I): resource entry in the resource map (returned by + resfile_get_entry) + len (O): resource length + + Return imgtool error code +*/ +static imgtoolerr_t resfile_get_reslen(mac_resfileref *resfileref, const rsrc_ref_entry *entry, uint32_t *len) +{ + imgtoolerr_t err; + uint32_t data_offs; + UINT32BE llen; + + data_offs = get_UINT24BE(entry->data_offs); + + /* seek to resource data in resource data section */ + err = mac_file_seek(&resfileref->fileref, resfileref->data_offs+data_offs); + if (err) + return err; + + /* get data length */ + err = mac_file_read(&resfileref->fileref, sizeof(llen), &llen); + if (err) + return err; + + *len = get_UINT32BE(llen); + + return IMGTOOLERR_SUCCESS; +} + +/* + resfile_get_resdata + + Get the data for a given resource. + + resfileref (I/O): open resource file handle + entry (I): resource entry in the resource map (returned by + resfile_get_entry) + offset (I): offset the data should be read from, usually 0 + len (I): length of the data to read, usually the value returned by + resfile_get_reslen + dest (O): resource data + + Return imgtool error code +*/ +static imgtoolerr_t resfile_get_resdata(mac_resfileref *resfileref, const rsrc_ref_entry *entry, uint32_t offset, uint32_t len, void *dest) +{ + imgtoolerr_t err; + uint32_t data_offs; + UINT32BE llen; + + data_offs = get_UINT24BE(entry->data_offs); + + /* seek to resource data in resource data section */ + err = mac_file_seek(&resfileref->fileref, resfileref->data_offs+data_offs); + if (err) + return err; + + /* get data length */ + err = mac_file_read(&resfileref->fileref, sizeof(llen), &llen); + if (err) + return err; + + /* check that we do not ask to read more data than avalaible */ + if ((offset + len) > get_UINT32BE(llen)) + return IMGTOOLERR_UNEXPECTED; + + if (offset) + { /* seek to resource data offset in resource data section */ + err = mac_file_seek(&resfileref->fileref, resfileref->data_offs+data_offs+4+offset); + if (err) + return err; + } + + /* get data */ + err = mac_file_read(&resfileref->fileref, len, dest); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} +#endif + +#if 0 +#pragma mark - +#pragma mark DESKTOP FILE IMPLEMENTATION +#endif +/* + All macintosh volumes have information stored in the desktop file or the + desktop database. + + Such information include file comments, copy of BNDL and FREF resources + that describes supported file types for each application on the volume, + copy of icons for each file type registered by each application on the + volume, etc. On MFS volumes, the list of folders is stored in the desktop + file as well. + + + There have been two implementations of the desktop metadata: + + * The original desktop file. The database is stored in the resource fork + of a (usually invisible) file called "Desktop" (case may change + according to system versions), located at the root of the volume. The + desktop file is used by System 6 and earlier for all volumes (unless + Appleshare 2 is installed and the volume is shared IIRC), and by System + 7 and later for volumes smaller than 2MBytes (so that floppy disks + remain fully compatible with earlier versions of system). The desktop + file is incompletely documented by Apple technote TB06. + + * The desktop database. The database is stored in the resource fork is + stored in the data fork of two (usually invisible) files called + "Desktop DF" and "Desktop DF". The desktop database is used for + volumes shared by Appleshare 2, and for most volumes under System 7 and + later. The format of these file is not documented AFAIK. + + + The reasons for the introduction of the desktop database were: + * the macintosh resource manager cannot share resource files, which was + a problem for Appleshare + * the macintosh resource manager is pretty limited (+/-16MByte of data and + 2727 resources at most), which was a problem for large hard disks with + many programs/comments +*/ + +#ifdef UNUSED_FUNCTION +/* + get_comment + + Get a comment from the Desktop file + + l2_img (I): macintosh image the data should be read from + id (I): comment id (from mfs_hashString(), or HFS FXInfo/DXInfo records) + comment (O): comment that has been read + + Return imgtool error code +*/ +static imgtoolerr_t get_comment(struct mac_l2_imgref *l2_img, uint16_t id, mac_str255 comment) +{ + static const uint8_t desktop_fname[] = {'\7','D','e','s','k','t','o','p'}; + #define restype_FCMT (('F' << 24) | ('C' << 16) | ('M' << 8) | 'T') + mac_resfileref resfileref; + rsrc_ref_entry resentry; + uint32_t reslen; + imgtoolerr_t err; + + /* open rsrc fork of file Desktop in root directory */ + err = mac_file_open(l2_img, 2, desktop_fname, rsrc_fork, &resfileref.fileref); + if (err) + return err; + + /* open resource structures */ + err = resfile_open(&resfileref); + if (err) + return err; + + /* look for resource FCMT #id */ + err = resfile_get_entry(&resfileref, restype_FCMT, id, &resentry); + if (err) + return err; + + /* extract comment len */ + err = resfile_get_reslen(&resfileref, &resentry, &reslen); + if (err) + return err; + + /* check comment len */ + if (reslen > 256) + /* hurk */ + /*return IMGTOOLERR_CORRUPTIMAGE;*/ + /* people willing to extend the MFM comment field (you know, the kind + of masochists that try to support 20-year-old OSes) might append extra + fields, so we just truncate the resource */ + reslen = 256; + + /* extract comment data */ + err = resfile_get_resdata(&resfileref, &resentry, 0, reslen, comment); + if (err) + return err; + + /* phew, we are done! */ + return IMGTOOLERR_SUCCESS; +} +#endif + +#if 0 +#pragma mark - +#pragma mark IMGTOOL MODULE IMPLEMENTATION +#endif + +#ifdef UNUSED_FUNCTION +static void mac_image_exit(imgtool::image *img); +#endif +static void mac_image_info(imgtool::image &img, std::ostream &stream); +static imgtoolerr_t mac_image_beginenum(imgtool::directory &enumeration, const char *path); +static imgtoolerr_t mac_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent); +static imgtoolerr_t mac_image_freespace(imgtool::partition &partition, uint64_t *size); +static imgtoolerr_t mac_image_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf); +static imgtoolerr_t mac_image_writefile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *writeoptions); + +#ifdef UNUSED_FUNCTION +/* + close a mfs/hfs image +*/ +static void mac_image_exit(imgtool::image *img) +{ + struct mac_l2_imgref *image = get_imgref(img); + + mac_image_close(image); +} +#endif + +/* + get basic information on a mfs/hfs image + + Currently returns the volume name +*/ +static void mac_image_info(imgtool::image &img, std::ostream &stream) +{ + char buffer[256] = { 0, }; + struct mac_l2_imgref *image = get_imgref(img); + + switch (image->format) + { + case L2I_MFS: + mac_to_c_strncpy(buffer, std::size(buffer), image->u.mfs.volname); + break; + + case L2I_HFS: + mac_to_c_strncpy(buffer, std::size(buffer), image->u.hfs.volname); + break; + } + + stream << buffer; +} + +/* + MFS/HFS catalog iterator, used when imgtool reads the catalog +*/ +struct mac_iterator +{ + mac_format format; + struct mac_l2_imgref *l2_img; + union + { + struct + { + mfs_dirref dirref; /* open directory reference */ + } mfs; + struct + { + hfs_cat_enumerator catref; /* catalog file enumerator */ + } hfs; + } u; +}; + +/* + Open the disk catalog for enumeration +*/ +static imgtoolerr_t mac_image_beginenum(imgtool::directory &enumeration, const char *path) +{ + struct mac_l2_imgref *image = get_imgref(enumeration.image()); + mac_iterator *iter = (mac_iterator *) enumeration.extra_bytes(); + imgtoolerr_t err = IMGTOOLERR_UNEXPECTED; + + iter->format = image->format; + iter->l2_img = image; + + switch (iter->format) + { + case L2I_MFS: + err = mfs_dir_open(image, path, &iter->u.mfs.dirref); + break; + + case L2I_HFS: + err = hfs_cat_open(image, path, &iter->u.hfs.catref); + break; + } + + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + +/* + Enumerate disk catalog next entry (MFS) +*/ +static imgtoolerr_t mfs_image_nextenum(mac_iterator *iter, imgtool_dirent &ent) +{ + mfs_dir_entry *cur_dir_entry; + imgtoolerr_t err; + + + assert(iter->format == L2I_MFS); + + ent.corrupt = 0; + ent.eof = 0; + + err = mfs_dir_read(&iter->u.mfs.dirref, &cur_dir_entry); + if (err) + { + /* error */ + ent.corrupt = 1; + return err; + } + else if (!cur_dir_entry) + { + /* EOF */ + ent.eof = 1; + return IMGTOOLERR_SUCCESS; + } + + /* copy info */ + mac_to_c_strncpy(ent.filename, std::size(ent.filename), cur_dir_entry->name); + ent.filesize = get_UINT32BE(cur_dir_entry->dataPhysicalSize) + + get_UINT32BE(cur_dir_entry->rsrcPhysicalSize); + + return IMGTOOLERR_SUCCESS; +} + +#if 0 +/* + Concatenate path elements in the reverse order + + dest (O): destination buffer + dest_cur_pos (I/O): current position in destination buffer (buffer is + filled from end to start) + dest_max_len (I): length of destination buffer (use length minus one if you + want to preserve a trailing NUL character) + src (I): source C string +*/ +static void concat_fname(char *dest, int *dest_cur_pos, int dest_max_len, const char *src) +{ + static const char ellipsis[] = { '.', '.', '.' }; + int src_len = strlen(src); /* number of chars from src to insert */ + + if (src_len <= *dest_cur_pos) + { + *dest_cur_pos -= src_len; + memcpy(dest + *dest_cur_pos, src, src_len); + } + else + { + memcpy(dest, src + src_len - *dest_cur_pos, *dest_cur_pos); + *dest_cur_pos = 0; + memcpy(dest, ellipsis, (sizeof(ellipsis) <= dest_max_len) + ? sizeof(ellipsis) + : dest_max_len); + } +} +#endif + +/* + Enumerate disk catalog next entry (HFS) +*/ +static imgtoolerr_t hfs_image_nextenum(mac_iterator *iter, imgtool_dirent &ent) +{ + hfs_catKey *catrec_key; + hfs_catData *catrec_data; + uint16_t dataRecType; + imgtoolerr_t err; + /* currently, the mac->C conversion transcodes one mac char with at most 3 + C chars */ + int cur_name_head; + + assert(iter->format == L2I_HFS); + + ent.corrupt = 0; + ent.eof = 0; + + do + { + err = hfs_cat_read(&iter->u.hfs.catref, &catrec_key, &catrec_data); + if (err) + { + /* error */ + ent.corrupt = 1; + return err; + } + else if (!catrec_key) + { + /* EOF */ + ent.eof = 1; + return IMGTOOLERR_SUCCESS; + } + dataRecType = get_UINT16BE(catrec_data->dataType); + } while (((dataRecType != hcrt_Folder) && (dataRecType != hcrt_File)) + || (get_UINT32BE(catrec_key->parID) != iter->u.hfs.catref.parID)); + + /* copy info */ + switch (get_UINT16BE(catrec_data->dataType)) + { + case hcrt_Folder: + ent.directory = 1; + ent.filesize = 0; + break; + + case hcrt_File: + ent.directory = 0; + ent.filesize = get_UINT32BE(catrec_data->file.dataPhysicalSize) + + get_UINT32BE(catrec_data->file.rsrcPhysicalSize); + break; + } + + /* initialize file path buffer */ + cur_name_head = std::size(ent.filename); + if (cur_name_head > 0) + { + cur_name_head--; + ent.filename[cur_name_head] = '\0'; + } + + /* insert folder/file name in buffer */ + mac_to_c_strncpy(ent.filename, std::size(ent.filename), catrec_key->cName); +// concat_fname(ent.filename, &cur_name_head, std::size(ent.filename) - 1, buf); + +#if 0 + /* extract parent directory ID */ + parID = get_UINT32BE(catrec_key->parID); + + /* looping while (parID != 1) will display the volume name; looping while + (parID != 2) won't */ + while (parID != /*1*/2) + { + /* search catalog for folder thread */ + err = hfs_cat_search(iter->l2_img, parID, mac_empty_str, &catrec_key, &catrec_data); + if (err) + { + /* error */ + concat_fname(ent.filename, &cur_name_head, std::size(ent.filename) - 1, ":"); + concat_fname(ent.filename, &cur_name_head, std::size(ent.filename) - 1, "???"); + + memmove(ent.filename, ent.filename+cur_name_head, std::size(ent.filename) - cur_name_head); + ent.corrupt = 1; + return err; + } + + dataRecType = get_UINT16BE(catrec_data->dataType); + + if (dataRecType != hcrt_FolderThread) + { + /* error */ + concat_fname(ent.filename, &cur_name_head, std::size(ent.filename)-1, ":"); + concat_fname(ent.filename, &cur_name_head, std::size(ent.filename)-1, "???"); + + memmove(ent.filename, ent.filename+cur_name_head, std::size(ent.filename)-cur_name_head); + ent.corrupt = 1; + return IMGTOOLERR_CORRUPTIMAGE; + } + + /* got folder thread record: insert the folder name at the start of + file path, then iterate */ + mac_to_c_strncpy(buf, sizeof(buf), catrec_data->thread.nodeName); + concat_fname(ent.filename, &cur_name_head, std::size(ent.filename) - 1, ":"); + concat_fname(ent.filename, &cur_name_head, std::size(ent.filename) - 1, buf); + + /* extract parent directory ID */ + parID = get_UINT32BE(catrec_data->thread.parID); + } + memmove(ent.filename, ent.filename+cur_name_head, std::size(ent.filename) -cur_name_head); +#endif + return IMGTOOLERR_SUCCESS; +} + +/* + Enumerate disk catalog next entry +*/ +static imgtoolerr_t mac_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent) +{ + imgtoolerr_t err; + mac_iterator *iter = (mac_iterator *) enumeration.extra_bytes(); + + switch (iter->format) + { + case L2I_MFS: + err = mfs_image_nextenum(iter, ent); + break; + + case L2I_HFS: + err = hfs_image_nextenum(iter, ent); + break; + + default: + assert(1); + err = IMGTOOLERR_UNEXPECTED; + break; + } + return err; +} + +/* + Compute free space on disk image in bytes +*/ +static imgtoolerr_t mac_image_freespace(imgtool::partition &partition, uint64_t *size) +{ + imgtool::image &image(partition.image()); + *size = ((uint64_t) get_imgref(image)->freeABs) * 512; + return IMGTOOLERR_SUCCESS; +} + +#ifdef UNUSED_FUNCTION +static imgtoolerr_t mac_get_comment(struct mac_l2_imgref *image, mac_str255 filename, const mac_dirent *cat_info, mac_str255 comment) +{ + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + uint16_t commentID; + + comment[0] = '\0'; + + /* get comment from Desktop file */ + switch (image->format) + { + case L2I_MFS: + commentID = mfs_hashString(filename); + err = get_comment(image, commentID, comment); + break; + + case L2I_HFS: + /* This is the way to get Finder comments in system <= 7. Attached + comments use another method, and Finder 8 uses yet another one. */ + commentID = get_UINT16BE(cat_info->flXFinderInfo.comment); + if (commentID) + err = get_comment(image, commentID, comment); + break; + } + return err; +} +#endif + + +/* + Extract a file from a disk image. +*/ +static imgtoolerr_t mac_image_readfile(imgtool::partition &partition, const char *fpath, const char *fork, imgtool::stream &destf) +{ + imgtoolerr_t err; + imgtool::image &img(partition.image()); + struct mac_l2_imgref *image = get_imgref(img); + uint32_t parID; + mac_str255 filename; + mac_dirent cat_info; + mac_fileref fileref; + uint8_t buf[512]; + uint32_t i, run_len, data_len; + mac_fork_t fork_num; + + err = mac_identify_fork(fork, &fork_num); + if (err) + return err; + + /* resolve path and fetch file info from directory/catalog */ + err = mac_lookup_path(image, fpath, &parID, filename, &cat_info, false); + if (err) + return err; + if (cat_info.dataRecType != hcrt_File) + return IMGTOOLERR_FILENOTFOUND; + + /* open file */ + err = mac_file_open(image, parID, filename, fork_num ? rsrc_fork : data_fork, &fileref); + if (err) + return err; + + data_len = fork_num ? cat_info.rsrcLogicalSize : cat_info.dataLogicalSize; + + /* extract DF */ + i = 0; + while(i < data_len) + { + run_len = std::min(size_t(data_len - i), sizeof(buf)); + + err = mac_file_read(&fileref, run_len, buf); + if (err) + return err; + if (destf.write(buf, run_len) != run_len) + return IMGTOOLERR_WRITEERROR; + i += run_len; + } + + return IMGTOOLERR_SUCCESS; +} + +/* + Add a file to a disk image. +*/ +static imgtoolerr_t mac_image_writefile(imgtool::partition &partition, const char *fpath, const char *fork, imgtool::stream &sourcef, util::option_resolution *writeoptions) +{ + imgtool::image &img(partition.image()); + struct mac_l2_imgref *image = get_imgref(img); + uint32_t parID; + mac_str255 filename; + mac_dirent cat_info; + mac_fileref fileref; + uint32_t fork_len; + /*uint16_t commentID;*/ + /*mac_str255 comment;*/ + uint8_t buf[512]; + uint32_t i, run_len; + imgtoolerr_t err; + mac_fork_t fork_num; + + (void) writeoptions; + + if (image->format == L2I_HFS) + return IMGTOOLERR_UNIMPLEMENTED; + + err = mac_identify_fork(fork, &fork_num); + if (err) + return err; + +#if 0 + if (header.version_old != 0) + return IMGTOOLERR_UNIMPLEMENTED; +#endif + /*mac_strcpy(filename, header.filename);*/ + memset(&cat_info, 0, sizeof(cat_info)); + set_UINT32BE(&cat_info.flFinderInfo.type, 0x3F3F3F3F); + set_UINT32BE(&cat_info.flFinderInfo.creator, 0x3F3F3F3F); + fork_len = sourcef.size(); + /*comment[0] = get_UINT16BE(header.comment_len);*/ /* comment length */ + /* Next two fields are set to 0 with MFS volumes. IIRC, 0 normally + means system script: I don't think MFS stores the file name script code + anywhere on disk, so it should be a reasonable approximation. */ + + /* create file */ + /* clear inited flag and file location in window */ + set_UINT16BE(&cat_info.flFinderInfo.flags, get_UINT16BE(cat_info.flFinderInfo.flags) & ~fif_hasBeenInited); + set_UINT16BE(&cat_info.flFinderInfo.location.v, 0); + set_UINT16BE(&cat_info.flFinderInfo.location.h, 0); + + /* resolve path and create file */ + err = mac_lookup_path(image, fpath, &parID, filename, &cat_info, true); + if (err) + return err; + + /* open file fork */ + err = mac_file_open(image, parID, filename, (fork_num ? rsrc_fork : data_fork), &fileref); + if (err) + return err; + + err = mac_file_seteof(&fileref, fork_len); + if (err) + return err; + + /* extract fork */ + for (i=0; i 512) + run_len = 512; + if (sourcef.read(buf, run_len) != run_len) + return IMGTOOLERR_READERROR; + err = mac_file_write(&fileref, run_len, buf); + if (err) + return err; + i += run_len; + } + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t mac_image_listforks(imgtool::partition &partition, const char *path, std::vector &forks) +{ + imgtoolerr_t err; + uint32_t parID; + mac_str255 filename; + mac_dirent cat_info; + imgtool::image &img(partition.image()); + struct mac_l2_imgref *image = get_imgref(img); + + /* resolve path and fetch file info from directory/catalog */ + err = mac_lookup_path(image, path, &parID, filename, &cat_info, false); + if (err) + return err; + if (cat_info.dataRecType != hcrt_File) + return IMGTOOLERR_FILENOTFOUND; + + // specify data fork + forks.emplace_back(cat_info.dataLogicalSize, imgtool::fork_entry::type_t::DATA); + + if (cat_info.rsrcLogicalSize > 0) + { + // specify the resource fork + forks.emplace_back(cat_info.rsrcLogicalSize, imgtool::fork_entry::type_t::RESOURCE); + } + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t mac_image_getattrs(imgtool::partition &partition, const char *path, const uint32_t *attrs, imgtool_attribute *values) +{ + imgtoolerr_t err; + imgtool::image &img(partition.image()); + uint32_t parID; + mac_str255 filename; + mac_dirent cat_info; + struct mac_l2_imgref *image = get_imgref(img); + int i; + + /* resolve path and fetch file info from directory/catalog */ + err = mac_lookup_path(image, path, &parID, filename, &cat_info, false); + if (err) + return err; + if (cat_info.dataRecType != hcrt_File) + return IMGTOOLERR_FILENOTFOUND; + + for (i = 0; attrs[i]; i++) + { + switch(attrs[i]) + { + case IMGTOOLATTR_INT_MAC_TYPE: + values[i].i = get_UINT32BE(cat_info.flFinderInfo.type); + break; + case IMGTOOLATTR_INT_MAC_CREATOR: + values[i].i = get_UINT32BE(cat_info.flFinderInfo.creator); + break; + case IMGTOOLATTR_INT_MAC_FINDERFLAGS: + values[i].i = get_UINT16BE(cat_info.flFinderInfo.flags); + break; + case IMGTOOLATTR_INT_MAC_COORDX: + values[i].i = get_UINT16BE(cat_info.flFinderInfo.location.h); + break; + case IMGTOOLATTR_INT_MAC_COORDY: + values[i].i = get_UINT16BE(cat_info.flFinderInfo.location.v); + break; + case IMGTOOLATTR_INT_MAC_FINDERFOLDER: + values[i].i = get_UINT16BE(cat_info.flFinderInfo.fldr); + break; + case IMGTOOLATTR_INT_MAC_ICONID: + values[i].i = get_UINT16BE(cat_info.flXFinderInfo.iconID); + break; + case IMGTOOLATTR_INT_MAC_SCRIPTCODE: + values[i].i = cat_info.flXFinderInfo.script; + break; + case IMGTOOLATTR_INT_MAC_EXTENDEDFLAGS: + values[i].i = cat_info.flXFinderInfo.XFlags; + break; + case IMGTOOLATTR_INT_MAC_COMMENTID: + values[i].i = get_UINT16BE(cat_info.flXFinderInfo.comment); + break; + case IMGTOOLATTR_INT_MAC_PUTAWAYDIRECTORY: + values[i].i = get_UINT32BE(cat_info.flXFinderInfo.putAway); + break; + + case IMGTOOLATTR_TIME_CREATED: + values[i].t = mac_crack_time(cat_info.createDate).to_time_t(); + break; + case IMGTOOLATTR_TIME_LASTMODIFIED: + values[i].t = mac_crack_time(cat_info.modifyDate).to_time_t(); + break; + } + } + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t mac_image_setattrs(imgtool::partition &partition, const char *path, const uint32_t *attrs, const imgtool_attribute *values) +{ + imgtoolerr_t err; + uint32_t parID; + mac_str255 filename; + mac_dirent cat_info; + imgtool::image &img(partition.image()); + struct mac_l2_imgref *image = get_imgref(img); + int i; + + /* resolve path and fetch file info from directory/catalog */ + err = mac_lookup_path(image, path, &parID, filename, &cat_info, false); + if (err) + return err; + if (cat_info.dataRecType != hcrt_File) + return IMGTOOLERR_FILENOTFOUND; + + for (i = 0; attrs[i]; i++) + { + switch(attrs[i]) + { + case IMGTOOLATTR_INT_MAC_TYPE: + set_UINT32BE(&cat_info.flFinderInfo.type, values[i].i); + break; + case IMGTOOLATTR_INT_MAC_CREATOR: + set_UINT32BE(&cat_info.flFinderInfo.creator, values[i].i); + break; + case IMGTOOLATTR_INT_MAC_FINDERFLAGS: + set_UINT16BE(&cat_info.flFinderInfo.flags, values[i].i); + break; + case IMGTOOLATTR_INT_MAC_COORDX: + set_UINT16BE(&cat_info.flFinderInfo.location.h, values[i].i); + break; + case IMGTOOLATTR_INT_MAC_COORDY: + set_UINT16BE(&cat_info.flFinderInfo.location.v, values[i].i); + break; + case IMGTOOLATTR_INT_MAC_FINDERFOLDER: + set_UINT16BE(&cat_info.flFinderInfo.fldr, values[i].i); + break; + case IMGTOOLATTR_INT_MAC_ICONID: + set_UINT16BE(&cat_info.flXFinderInfo.iconID, values[i].i); + break; + case IMGTOOLATTR_INT_MAC_SCRIPTCODE: + cat_info.flXFinderInfo.script = values[i].i; + break; + case IMGTOOLATTR_INT_MAC_EXTENDEDFLAGS: + cat_info.flXFinderInfo.XFlags = values[i].i; + break; + case IMGTOOLATTR_INT_MAC_COMMENTID: + set_UINT16BE(&cat_info.flXFinderInfo.comment, values[i].i); + break; + case IMGTOOLATTR_INT_MAC_PUTAWAYDIRECTORY: + set_UINT32BE(&cat_info.flXFinderInfo.putAway, values[i].i); + break; + + case IMGTOOLATTR_TIME_CREATED: + cat_info.createDate = mac_setup_time(values[i].t); + break; + case IMGTOOLATTR_TIME_LASTMODIFIED: + cat_info.modifyDate = mac_setup_time(values[i].t); + break; + } + } + + /* resolve path and fetch file info from directory/catalog */ + err = mac_lookup_path(image, path, &parID, filename, &cat_info, true); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + + + +/************************************* + * + * Our very own resource manager + * + *************************************/ + +static const void *mac_walk_resources(const void *resource_fork, size_t resource_fork_length, + uint32_t resource_type, + int (*discriminator)(const void *resource, uint16_t id, uint32_t length, void *param_), + void *param, uint16_t *resource_id, uint32_t *resource_length) +{ + uint32_t resource_data_offset, resource_data_length; + uint32_t resource_map_offset, resource_map_length; + uint32_t resource_typelist_count, resource_typelist_offset; + uint32_t resource_entry_offset, resource_entry_count; + uint32_t i, this_resource_type; + uint32_t this_resource_data, this_resource_length; + uint16_t this_resource_id; + const void *this_resource_ptr; + + if (resource_fork_length < 16) + return NULL; + + /* read resource header; its ok if anything past this point fails */ + resource_data_offset = pick_integer_be(resource_fork, 0, 4); + resource_map_offset = pick_integer_be(resource_fork, 4, 4); + resource_data_length = pick_integer_be(resource_fork, 8, 4); + resource_map_length = pick_integer_be(resource_fork, 12, 4); + if ((resource_data_offset + resource_data_length) > resource_fork_length) + return NULL; + if ((resource_map_offset + resource_map_length) > resource_fork_length) + return NULL; + if (resource_map_length < 30) + return NULL; + + /* read resource map and locate the resource type list */ + resource_typelist_offset = pick_integer_be(resource_fork, + resource_map_offset + 24, 2) + resource_map_offset; + if ((resource_typelist_offset + 2) > resource_fork_length) + return NULL; + resource_typelist_count = pick_integer_be(resource_fork, + resource_typelist_offset, 2) + 1; + if ((resource_typelist_offset + resource_typelist_count * 16 + 2) > resource_fork_length) + return NULL; + + /* scan the resource type list and locate the entries for this type */ + resource_entry_count = 0; + resource_entry_offset = 0; + for (i = 0; i < resource_typelist_count; i++) + { + this_resource_type = pick_integer_be(resource_fork, resource_typelist_offset + + (i * 8) + 2 + 0, 4); + if (this_resource_type == resource_type) + { + resource_entry_count = pick_integer_be(resource_fork, resource_typelist_offset + + (i * 8) + 2 + 4, 2) + 1; + resource_entry_offset = pick_integer_be(resource_fork, resource_typelist_offset + + (i * 8) + 2 + 6, 2) + resource_typelist_offset; + break; + } + } + + /* scan the resource entries, and find the correct resource */ + for (i = 0; i < resource_entry_count; i++) + { + this_resource_id = pick_integer_be(resource_fork, resource_entry_offset + + (i * 12) + 0, 2); + this_resource_data = pick_integer_be(resource_fork, resource_entry_offset + + (i * 12) + 5, 3) + resource_data_offset; + if ((this_resource_data + 4) > resource_fork_length) + return NULL; + + /* gauge the length */ + this_resource_length = pick_integer_be(resource_fork, this_resource_data, 4); + this_resource_data += 4; + if ((this_resource_data + this_resource_length) > resource_fork_length) + return NULL; + + this_resource_ptr = ((const uint8_t *) resource_fork) + this_resource_data; + if (discriminator(this_resource_ptr, this_resource_id, + this_resource_length, param)) + { + if (resource_length) + *resource_length = this_resource_length; + if (resource_id) + *resource_id = this_resource_id; + return this_resource_ptr; + } + } + + return NULL; +} + + + +static int get_resource_discriminator(const void *resource, uint16_t id, uint32_t length, void *param) +{ + const uint16_t *id_ptr = (const uint16_t *) param; + return id == *id_ptr; +} + + + +static const void *mac_get_resource(const void *resource_fork, size_t resource_fork_length, + uint32_t resource_type, uint16_t resource_id, uint32_t *resource_length) +{ + return mac_walk_resources(resource_fork, resource_fork_length, + resource_type, get_resource_discriminator, &resource_id, NULL, resource_length); +} + + + +/************************************* + * + * Custom icons + * + *************************************/ + +static int bundle_discriminator(const void *resource, uint16_t id, uint32_t length, void *param) +{ + uint32_t this_creator_code = pick_integer_be(resource, 0, 4); + uint32_t desired_creator_code = *((uint32_t *) param); + return this_creator_code == desired_creator_code; +} + + + +static uint8_t get_pixel(const uint8_t *src, int width, int height, int bpp, + int x, int y) +{ + uint8_t byte, mask; + int bit_position; + + byte = src[(y * width + x) * bpp / 8]; + bit_position = 8 - ((x % (8 / bpp)) + 1) * bpp; + mask = (1 << bpp) - 1; + return (byte >> bit_position) & mask; +} + + + +static bool load_icon(uint32_t *dest, const void *resource_fork, uint64_t resource_fork_length, + uint32_t resource_type, uint16_t resource_id, int width, int height, int bpp, + const uint32_t *palette, bool has_mask) +{ + bool success = false; + uint32_t frame_length = width * height * bpp / 8; + uint32_t total_length = frame_length * (has_mask ? 2 : 1); + uint32_t resource_length; + + // attempt to fetch resource + const uint8_t *src = (const uint8_t*)mac_get_resource(resource_fork, resource_fork_length, resource_type, + resource_id, &resource_length); + if (src && (resource_length == total_length)) + { + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x ++) + { + // check the color + uint8_t color = get_pixel(src, width, height, bpp, x, y); + + // then check the mask + bool is_masked = has_mask + ? get_pixel(src + frame_length, width, height, bpp, x, y) != 0 + : dest[y * width + x] >= 0x80000000; + + // is this actually masked? (note there is a special case when bpp == 1; Mac B&W icons + // had a XOR effect, and this cannot be blocked by the mask) + uint32_t pixel; + if (is_masked || (color && bpp == 1)) + { + // mask is ok; check the actual icon + pixel = palette[color] | 0xFF000000; + } + else + { + // masked out; nothing + pixel = 0x00000000; + } + + dest[y * width + x] = pixel; + } + } + success = true; + } + return success; +} + + + +static imgtoolerr_t mac_image_geticoninfo(imgtool::partition &partition, const char *path, imgtool_iconinfo *iconinfo) +{ + static const uint32_t mac_palette_1bpp[2] = { 0xFFFFFF, 0x000000 }; + + static const uint32_t mac_palette_4bpp[16] = + { + 0xFFFFFF, 0xFCF305, 0xFF6402, 0xDD0806, 0xF20884, 0x4600A5, + 0x0000D4, 0x02ABEA, 0x1FB714, 0x006411, 0x562C05, 0x90713A, + 0xC0C0C0, 0x808080, 0x404040, 0x000000 + }; + + static const uint32_t mac_palette_8bpp[256] = + { + 0xFFFFFF, 0xFFFFCC, 0xFFFF99, 0xFFFF66, 0xFFFF33, 0xFFFF00, + 0xFFCCFF, 0xFFCCCC, 0xFFCC99, 0xFFCC66, 0xFFCC33, 0xFFCC00, + 0xFF99FF, 0xFF99CC, 0xFF9999, 0xFF9966, 0xFF9933, 0xFF9900, + 0xFF66FF, 0xFF66CC, 0xFF6699, 0xFF6666, 0xFF6633, 0xFF6600, + 0xFF33FF, 0xFF33CC, 0xFF3399, 0xFF3366, 0xFF3333, 0xFF3300, + 0xFF00FF, 0xFF00CC, 0xFF0099, 0xFF0066, 0xFF0033, 0xFF0000, + 0xCCFFFF, 0xCCFFCC, 0xCCFF99, 0xCCFF66, 0xCCFF33, 0xCCFF00, + 0xCCCCFF, 0xCCCCCC, 0xCCCC99, 0xCCCC66, 0xCCCC33, 0xCCCC00, + 0xCC99FF, 0xCC99CC, 0xCC9999, 0xCC9966, 0xCC9933, 0xCC9900, + 0xCC66FF, 0xCC66CC, 0xCC6699, 0xCC6666, 0xCC6633, 0xCC6600, + 0xCC33FF, 0xCC33CC, 0xCC3399, 0xCC3366, 0xCC3333, 0xCC3300, + 0xCC00FF, 0xCC00CC, 0xCC0099, 0xCC0066, 0xCC0033, 0xCC0000, + 0x99FFFF, 0x99FFCC, 0x99FF99, 0x99FF66, 0x99FF33, 0x99FF00, + 0x99CCFF, 0x99CCCC, 0x99CC99, 0x99CC66, 0x99CC33, 0x99CC00, + 0x9999FF, 0x9999CC, 0x999999, 0x999966, 0x999933, 0x999900, + 0x9966FF, 0x9966CC, 0x996699, 0x996666, 0x996633, 0x996600, + 0x9933FF, 0x9933CC, 0x993399, 0x993366, 0x993333, 0x993300, + 0x9900FF, 0x9900CC, 0x990099, 0x990066, 0x990033, 0x990000, + 0x66FFFF, 0x66FFCC, 0x66FF99, 0x66FF66, 0x66FF33, 0x66FF00, + 0x66CCFF, 0x66CCCC, 0x66CC99, 0x66CC66, 0x66CC33, 0x66CC00, + 0x6699FF, 0x6699CC, 0x669999, 0x669966, 0x669933, 0x669900, + 0x6666FF, 0x6666CC, 0x666699, 0x666666, 0x666633, 0x666600, + 0x6633FF, 0x6633CC, 0x663399, 0x663366, 0x663333, 0x663300, + 0x6600FF, 0x6600CC, 0x660099, 0x660066, 0x660033, 0x660000, + 0x33FFFF, 0x33FFCC, 0x33FF99, 0x33FF66, 0x33FF33, 0x33FF00, + 0x33CCFF, 0x33CCCC, 0x33CC99, 0x33CC66, 0x33CC33, 0x33CC00, + 0x3399FF, 0x3399CC, 0x339999, 0x339966, 0x339933, 0x339900, + 0x3366FF, 0x3366CC, 0x336699, 0x336666, 0x336633, 0x336600, + 0x3333FF, 0x3333CC, 0x333399, 0x333366, 0x333333, 0x333300, + 0x3300FF, 0x3300CC, 0x330099, 0x330066, 0x330033, 0x330000, + 0x00FFFF, 0x00FFCC, 0x00FF99, 0x00FF66, 0x00FF33, 0x00FF00, + 0x00CCFF, 0x00CCCC, 0x00CC99, 0x00CC66, 0x00CC33, 0x00CC00, + 0x0099FF, 0x0099CC, 0x009999, 0x009966, 0x009933, 0x009900, + 0x0066FF, 0x0066CC, 0x006699, 0x006666, 0x006633, 0x006600, + 0x0033FF, 0x0033CC, 0x003399, 0x003366, 0x003333, 0x003300, + 0x0000FF, 0x0000CC, 0x000099, 0x000066, 0x000033, 0xEE0000, + 0xDD0000, 0xBB0000, 0xAA0000, 0x880000, 0x770000, 0x550000, + 0x440000, 0x220000, 0x110000, 0x00EE00, 0x00DD00, 0x00BB00, + 0x00AA00, 0x008800, 0x007700, 0x005500, 0x004400, 0x002200, + 0x001100, 0x0000EE, 0x0000DD, 0x0000BB, 0x0000AA, 0x000088, + 0x000077, 0x000055, 0x000044, 0x000022, 0x000011, 0xEEEEEE, + 0xDDDDDD, 0xBBBBBB, 0xAAAAAA, 0x888888, 0x777777, 0x555555, + 0x444444, 0x222222, 0x111111, 0x000000 + }; + + static const uint32_t attrs[4] = + { + IMGTOOLATTR_INT_MAC_TYPE, + IMGTOOLATTR_INT_MAC_CREATOR, + IMGTOOLATTR_INT_MAC_FINDERFLAGS + }; + + imgtoolerr_t err; + imgtool_attribute attr_values[3]; + uint32_t type_code, creator_code, finder_flags; + imgtool::stream::ptr stream; + const void *resource_fork; + uint64_t resource_fork_length; + const void *bundle; + uint32_t bundle_length, pos, fref_pos, icn_pos, i; + uint16_t local_id = 0, resource_id; + uint32_t fref_bundleentry_length, icn_bundleentry_length; + const void *fref; + uint32_t resource_length; + + assert((std::size(attrs) - 1) + == std::size(attr_values)); + + /* first retrieve type and creator code */ + err = mac_image_getattrs(partition, path, attrs, attr_values); + if (err) + return err; + type_code = (uint32_t) attr_values[0].i; + creator_code = (uint32_t) attr_values[1].i; + finder_flags = (uint32_t) attr_values[2].i; + + /* check the bundle bit; if clear (and the type is not 'APPL'), use the + * desktop file */ + if (!(finder_flags & 0x2000) && (type_code != /* APPL */ 0x4150504C)) + path = "Desktop\0"; + + stream = imgtool::stream::open_mem(NULL, 0); + if (!stream) + return IMGTOOLERR_SUCCESS; + + /* read in the resource fork */ + err = mac_image_readfile(partition, path, "RESOURCE_FORK", *stream); + if (err) + return err; + resource_fork = stream->getptr(); + resource_fork_length = stream->size(); + + /* attempt to look up the bundle */ + bundle = mac_walk_resources(resource_fork, resource_fork_length, /* BNDL */ 0x424E444C, + bundle_discriminator, &creator_code, NULL, &bundle_length); + if (!bundle) + return err; + + /* find the FREF and the icon family */ + pos = 8; + fref_pos = icn_pos = 0; + fref_bundleentry_length = icn_bundleentry_length = 0; + while((pos + 10) <= bundle_length) + { + uint32_t this_bundleentry_type = pick_integer_be(bundle, pos + 0, 4); + uint32_t this_bundleentry_length = pick_integer_be(bundle, pos + 4, 2) + 1; + + if (this_bundleentry_type == /* FREF */ 0x46524546) + { + fref_pos = pos; + fref_bundleentry_length = this_bundleentry_length; + } + if (this_bundleentry_type == /* ICN# */ 0x49434E23) + { + icn_pos = pos; + icn_bundleentry_length = this_bundleentry_length; + } + pos += 6 + this_bundleentry_length * 4; + } + if (!fref_pos || !icn_pos) + return err; + + /* look up the FREF */ + for (i = 0; i < fref_bundleentry_length; i++) + { + local_id = pick_integer_be(bundle, fref_pos + (i * 4) + 6, 2); + resource_id = pick_integer_be(bundle, fref_pos + (i * 4) + 8, 2); + + fref = mac_get_resource(resource_fork, resource_fork_length, + /* FREF */ 0x46524546, resource_id, &resource_length); + if (fref && (resource_length >= 7)) + { + if (pick_integer_be(fref, 0, 4) == type_code) + break; + } + } + if (i >= fref_bundleentry_length) + return err; + + /* now look up the icon family */ + resource_id = 0; + for (i = 0; i < icn_bundleentry_length; i++) + { + if (pick_integer_be(bundle, icn_pos + (i * 4) + 6, 2) == local_id) + { + resource_id = pick_integer_be(bundle, icn_pos + (i * 4) + 8, 2); + break; + } + } + if (i >= icn_bundleentry_length) + return err; + + /* fetch 32x32 icons (ICN#, icl4, icl8) */ + if (load_icon((uint32_t *) iconinfo->icon32x32, resource_fork, resource_fork_length, + /* ICN# */ 0x49434E23, resource_id, 32, 32, 1, mac_palette_1bpp, true)) + { + iconinfo->icon32x32_specified = 1; + + load_icon((uint32_t *) iconinfo->icon32x32, resource_fork, resource_fork_length, + /* icl4 */ 0x69636C34, resource_id, 32, 32, 4, mac_palette_4bpp, false); + load_icon((uint32_t *) iconinfo->icon32x32, resource_fork, resource_fork_length, + /* icl8 */ 0x69636C38, resource_id, 32, 32, 8, mac_palette_8bpp, false); + } + + /* fetch 16x16 icons (ics#, ics4, ics8) */ + if (load_icon((uint32_t *) iconinfo->icon16x16, resource_fork, resource_fork_length, + /* ics# */ 0x69637323, resource_id, 16, 16, 1, mac_palette_1bpp, true)) + { + iconinfo->icon16x16_specified = 1; + + load_icon((uint32_t *) iconinfo->icon32x32, resource_fork, resource_fork_length, + /* ics4 */ 0x69637334, resource_id, 32, 32, 4, mac_palette_4bpp, false); + load_icon((uint32_t *) iconinfo->icon32x32, resource_fork, resource_fork_length, + /* ics8 */ 0x69637338, resource_id, 32, 32, 8, mac_palette_8bpp, false); + } + + return err; +} + + + +/************************************* + * + * File transfer suggestions + * + *************************************/ + +static imgtoolerr_t mac_image_suggesttransfer(imgtool::partition &partition, const char *path, imgtool_transfer_suggestion *suggestions, size_t suggestions_length) +{ + imgtoolerr_t err; + uint32_t parID; + mac_str255 filename; + mac_dirent cat_info; + imgtool::image &img(partition.image()); + struct mac_l2_imgref *image = get_imgref(img); + mac_filecategory_t file_category = MAC_FILECATEGORY_DATA; + + if (path) + { + /* resolve path and fetch file info from directory/catalog */ + err = mac_lookup_path(image, path, &parID, filename, &cat_info, false); + if (err) + return err; + if (cat_info.dataRecType != hcrt_File) + return IMGTOOLERR_FILENOTFOUND; + + file_category = (cat_info.rsrcLogicalSize > 0) ? MAC_FILECATEGORY_FORKED : MAC_FILECATEGORY_DATA; + } + + mac_suggest_transfer(file_category, suggestions, suggestions_length); + return IMGTOOLERR_SUCCESS; +} + + + +/************************************* +* +* MacOS Roman Conversion +* +*************************************/ + +static const char32_t macos_roman_code_page[128] = +{ + // 0x80 - 0x8F + 0x00C4, 0x00C5, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00E1, 0x00E0, 0x00E2, 0x00E4, 0x00E3, 0x00E5, 0x00E7, 0x00E9, 0x00E8, + // 0x90 - 0x9F + 0x00EA, 0x00EB, 0x00ED, 0x00EC, 0x00EE, 0x00EF, 0x00F1, 0x00F3, 0x00F2, 0x00F4, 0x00F6, 0x00F5, 0x00FA, 0x00F9, 0x00FB, 0x00FC, + // 0xA0 - 0xAF + 0x2020, 0x00B0, 0x00A2, 0x00A3, 0x00A7, 0x2022, 0x00B6, 0x00DF, 0x00AE, 0x00A9, 0x2122, 0x00B4, 0x00A8, 0x2260, 0x00C6, 0x00D8, + // 0xB0 - 0xBF + 0x221E, 0x00B1, 0x2264, 0x2265, 0x00A5, 0x00B5, 0x2202, 0x2211, 0x220F, 0x03C0, 0x222B, 0x00AA, 0x00BA, 0x03A9, 0x00E6, 0x00F8, + // 0xC0 - 0xCF + 0x00BF, 0x00A1, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB, 0x00BB, 0x2026, 0x00A0, 0x00C0, 0x00C3, 0x00D5, 0x0152, 0x0153, + // 0xD0 - 0xDF + 0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0x00F7, 0x25CA, 0x00FF, 0x0178, 0x2044, 0x20AC, 0x2039, 0x203A, 0xFB01, 0xFB02, + // 0xE0 - 0xEF + 0x2021, 0x00B7, 0x201A, 0x201E, 0x2030, 0x00C2, 0x00CA, 0x00C1, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00D3, 0x00D4, + // 0xF0 - 0xFF + 0xF8FF, 0x00D2, 0x00DA, 0x00DB, 0x00D9, 0x0131, 0x02C6, 0x02DC, 0x00AF, 0x02D8, 0x02D9, 0x02DA, 0x00B8, 0x02DD, 0x02DB, 0x02C7 +}; + +static imgtool::simple_charconverter charconverter_macos_roman(macos_roman_code_page); + + +/************************************* + * + * Module population + * + *************************************/ + +static void generic_mac_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case IMGTOOLINFO_INT_OPEN_IS_STRICT: info->i = 1; break; + case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(struct mac_l2_imgref); break; + case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(struct mac_iterator); break; + case IMGTOOLINFO_INT_PATH_SEPARATOR: info->i = ':'; break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break; + case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), "\r"); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case IMGTOOLINFO_PTR_MAKE_CLASS: info->make_class = imgtool_floppy_make_class; break; + case IMGTOOLINFO_PTR_CLOSE: /* info->close = mac_image_exit */; break; + case IMGTOOLINFO_PTR_INFO: info->info = mac_image_info; break; + case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = mac_image_beginenum; break; + case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = mac_image_nextenum; break; + case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = mac_image_freespace; break; + case IMGTOOLINFO_PTR_READ_FILE: info->read_file = mac_image_readfile; break; + case IMGTOOLINFO_PTR_LIST_FORKS: info->list_forks = mac_image_listforks; break; + case IMGTOOLINFO_PTR_GET_ATTRS: info->get_attrs = mac_image_getattrs; break; + case IMGTOOLINFO_PTR_SET_ATTRS: info->set_attrs = mac_image_setattrs; break; + case IMGTOOLINFO_PTR_GET_ICON_INFO: info->get_iconinfo = mac_image_geticoninfo; break; + case IMGTOOLINFO_PTR_SUGGEST_TRANSFER: info->suggest_transfer = mac_image_suggesttransfer; break; + case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_apple35_mac; break; + case IMGTOOLINFO_PTR_CHARCONVERTER: info->charconverter = &charconverter_macos_roman; break; + } +} + + + +void mac_mfs_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "mac_mfs"); break; + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "Mac MFS Floppy"); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case IMGTOOLINFO_PTR_FLOPPY_CREATE: info->create = mfs_image_create; break; + case IMGTOOLINFO_PTR_FLOPPY_OPEN: info->open = mfs_image_open; break; + case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = mac_image_writefile; break; + + default: generic_mac_get_info(imgclass, state, info); break; + } +} + + + +void mac_hfs_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "mac_hfs"); break; + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "Mac HFS Floppy"); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case IMGTOOLINFO_PTR_FLOPPY_OPEN: info->open = hfs_image_open; break; + + default: generic_mac_get_info(imgclass, state, info); break; + } +} diff --git a/src/tools/imgtool/modules/macbin.cpp b/src/tools/imgtool/modules/macbin.cpp new file mode 100644 index 0000000..b75ea3d --- /dev/null +++ b/src/tools/imgtool/modules/macbin.cpp @@ -0,0 +1,384 @@ +// license:BSD-3-Clause +// copyright-holders:Raphael Nabet +/**************************************************************************** + + macbin.cpp + + MacBinary filter for use with Mac and ProDOS drivers + +***************************************************************************** + + MacBinary file format + + Offset Length Description + ------ ------ ----------- + 0 1 [I] Magic byte (0x00) + 1 64 [I] File name (Pascal String) + 65 4 [I] File Type Code + 69 4 [I] File Creator Code + 73 1 [I] Finder Flags (bits 15-8) + 74 1 [I] Magic byte (0x00) + 75 2 [I] File Vertical Position + 77 2 [I] File Horizontal Position + 79 2 [I] Window/Folder ID + 81 1 [I] Protected (bit 0) + 82 1 [I] Magic byte (0x00) + 83 4 [I] Data Fork Length + 87 4 [I] Resource Fork Length + 91 4 [I] Creation Date + 95 4 [I] Last Modified Date + 99 2 [I] "Get Info" comment length + 101 1 [II] Finder Flags (bits 7-0) + 102 4 [III] MacBinary III Signature 'mBIN' + 106 1 [III] Script of Filename + 107 1 [III] Extended Finder Flags + 116 4 [II] Unpacked Length + 120 2 [II] Secondary Header Length + 122 1 [II] MacBinary II Version Number (II: 0x81, III: 0x82) + 123 1 [II] Minimum Compatible MacBinary II Version Number (0x81) + 124 2 [II] CRC of previous 124 bytes + + For more information, consult http://www.lazerware.com/formats/macbinary.html + + TODO: I believe that the script code is some sort of identifier identifying + the character set used for the filename. If this is true, we are not + handling that properly + +*****************************************************************************/ + +#include "imgtool.h" +#include "filter.h" + +#include "macutil.h" + +#include "formats/imageutl.h" + +#include + + + +static uint32_t pad128(uint32_t length) +{ + if (length % 128) + length += 128 - (length % 128); + return length; +} + + + +static imgtoolerr_t macbinary_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf) +{ + static const uint32_t attrs[] = + { + IMGTOOLATTR_TIME_CREATED, + IMGTOOLATTR_TIME_LASTMODIFIED, + IMGTOOLATTR_INT_MAC_TYPE, + IMGTOOLATTR_INT_MAC_CREATOR, + IMGTOOLATTR_INT_MAC_FINDERFLAGS, + IMGTOOLATTR_INT_MAC_COORDX, + IMGTOOLATTR_INT_MAC_COORDY, + IMGTOOLATTR_INT_MAC_FINDERFOLDER, + IMGTOOLATTR_INT_MAC_SCRIPTCODE, + IMGTOOLATTR_INT_MAC_EXTENDEDFLAGS, + 0 + }; + imgtoolerr_t err; + uint8_t header[128]; + const char *basename; + + uint32_t type_code = 0x3F3F3F3F; + uint32_t creator_code = 0x3F3F3F3F; + uint16_t finder_flags = 0; + uint16_t coord_x = 0; + uint16_t coord_y = 0; + uint16_t finder_folder = 0; + uint8_t script_code = 0; + uint8_t extended_flags = 0; + + uint32_t creation_time = 0; + uint32_t lastmodified_time = 0; + imgtool_attribute attr_values[10]; + + // get the forks + std::vector fork_entries; + err = partition.list_file_forks(filename, fork_entries); + if (err) + return err; + + const imgtool::fork_entry *data_fork = nullptr; + const imgtool::fork_entry *resource_fork = nullptr; + for (const auto &entry : fork_entries) + { + switch (entry.type()) + { + case imgtool::fork_entry::type_t::DATA: + data_fork = &entry; + break; + case imgtool::fork_entry::type_t::RESOURCE: + resource_fork = &entry; + break; + default: + // do nothing + break; + } + } + + /* get the attributes */ + err = partition.get_file_attributes(filename, attrs, attr_values); + if (err && (ERRORCODE(err) != IMGTOOLERR_UNIMPLEMENTED)) + return err; + if (err == IMGTOOLERR_SUCCESS) + { + creation_time = mac_setup_time(attr_values[0].t); + lastmodified_time = mac_setup_time(attr_values[1].t); + type_code = attr_values[2].i; + creator_code = attr_values[3].i; + finder_flags = attr_values[4].i; + coord_x = attr_values[5].i; + coord_y = attr_values[6].i; + finder_folder = attr_values[7].i; + script_code = attr_values[8].i; + extended_flags = attr_values[9].i; + } + + memset(header, 0, sizeof(header)); + + /* place filename */ + basename = filename; + while(basename[strlen(basename) + 1]) + basename += strlen(basename) + 1; + pascal_from_c_string((unsigned char *) &header[1], 64, basename); + + place_integer_be(header, 65, 4, type_code); + place_integer_be(header, 69, 4, creator_code); + place_integer_be(header, 73, 1, (finder_flags >> 8) & 0xFF); + place_integer_be(header, 75, 2, coord_x); + place_integer_be(header, 77, 2, coord_y); + place_integer_be(header, 79, 2, finder_folder); + place_integer_be(header, 83, 4, data_fork ? data_fork->size() : 0); + place_integer_be(header, 87, 4, resource_fork ? resource_fork->size() : 0); + place_integer_be(header, 91, 4, creation_time); + place_integer_be(header, 95, 4, lastmodified_time); + place_integer_be(header, 101, 1, (finder_flags >> 0) & 0xFF); + place_integer_be(header, 102, 4, 0x6D42494E); + place_integer_be(header, 106, 1, script_code); + place_integer_be(header, 107, 1, extended_flags); + place_integer_be(header, 122, 1, 0x82); + place_integer_be(header, 123, 1, 0x81); + place_integer_be(header, 124, 2, ccitt_crc16(0, header, 124)); + + destf.write(header, sizeof(header)); + + if (data_fork) + { + err = partition.read_file(filename, "", destf, NULL); + if (err) + return err; + + destf.fill(0, pad128(data_fork->size())); + } + + if (resource_fork) + { + err = partition.read_file(filename, "RESOURCE_FORK", destf, NULL); + if (err) + return err; + + destf.fill(0, pad128(resource_fork->size())); + } + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t write_fork(imgtool::partition &partition, const char *filename, const char *fork, + imgtool::stream &sourcef, uint64_t pos, uint64_t fork_len, util::option_resolution *opts) +{ + imgtoolerr_t err; + imgtool::stream::ptr mem_stream; + size_t len; + + if (fork_len > 0) + { + mem_stream = imgtool::stream::open_mem(nullptr, 0); + if (!mem_stream) + return IMGTOOLERR_OUTOFMEMORY; + + sourcef.seek(pos, SEEK_SET); + len = imgtool::stream::transfer(*mem_stream, sourcef, fork_len); + if (len < fork_len) + mem_stream->fill(0, fork_len); + + mem_stream->seek(0, SEEK_SET); + err = partition.write_file(filename, fork, *mem_stream, opts, NULL); + if (err) + return err; + } + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t macbinary_writefile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts) +{ + static const uint32_t attrs[] = + { + IMGTOOLATTR_TIME_CREATED, + IMGTOOLATTR_TIME_LASTMODIFIED, + IMGTOOLATTR_INT_MAC_TYPE, + IMGTOOLATTR_INT_MAC_CREATOR, + IMGTOOLATTR_INT_MAC_FINDERFLAGS, + IMGTOOLATTR_INT_MAC_COORDX, + IMGTOOLATTR_INT_MAC_COORDY, + IMGTOOLATTR_INT_MAC_FINDERFOLDER, + IMGTOOLATTR_INT_MAC_SCRIPTCODE, + IMGTOOLATTR_INT_MAC_EXTENDEDFLAGS, + 0 + }; + imgtoolerr_t err; + imgtool::image *image = &partition.image(); + uint8_t header[128]; + uint32_t datafork_size; + uint32_t resourcefork_size; + uint64_t total_size; + uint32_t creation_time; + uint32_t lastmodified_time; + //int version; + imgtool_attribute attr_values[10]; + + uint32_t type_code; + uint32_t creator_code; + uint16_t finder_flags; + uint16_t coord_x; + uint16_t coord_y; + uint16_t finder_folder; + uint8_t script_code = 0; + uint8_t extended_flags = 0; + + /* read in the header */ + memset(header, 0, sizeof(header)); + sourcef.read(header, sizeof(header)); + + /* check magic and zero fill bytes */ + if (header[0] != 0x00) + return IMGTOOLERR_CORRUPTFILE; + if (header[74] != 0x00) + return IMGTOOLERR_CORRUPTFILE; + if (header[82] != 0x00) + return IMGTOOLERR_CORRUPTFILE; + + datafork_size = pick_integer_be(header, 83, 4); + resourcefork_size = pick_integer_be(header, 87, 4); + total_size = sourcef.size(); + + /* size of a MacBinary header is always 128 bytes */ + if (total_size - pad128(datafork_size) - pad128(resourcefork_size) != 128) + return IMGTOOLERR_CORRUPTFILE; + + /* check filename length byte */ + if ((header[1] <= 0x00) || (header[1] > 0x3F)) + return IMGTOOLERR_CORRUPTFILE; + + /* check the CRC */ + if (pick_integer_be(header, 124, 2) != ccitt_crc16(0, header, 124)) + { + /* the CRC does not match; this file is MacBinary I */ + //version = 1; + } + else if (pick_integer_be(header, 102, 4) != 0x6D42494E) + { + /* did not see 'mBIN'; this file is MacBinary II */ + if (header[122] < 0x81) + return IMGTOOLERR_CORRUPTFILE; + if (header[123] < 0x81) + return IMGTOOLERR_CORRUPTFILE; + //version = 2; + } + else + { + /* we did see 'mBIN'; this file is MacBinary III */ + if (header[122] < 0x82) + return IMGTOOLERR_CORRUPTFILE; + if (header[123] < 0x81) + return IMGTOOLERR_CORRUPTFILE; + //version = 3; + } + + type_code = pick_integer_be(header, 65, 4); + creator_code = pick_integer_be(header, 69, 4); + finder_flags = pick_integer_be(header, 73, 1) << 8; + coord_x = pick_integer_be(header, 75, 2); + coord_y = pick_integer_be(header, 77, 2); + finder_folder = pick_integer_be(header, 79, 2); + creation_time = pick_integer_be(header, 91, 4); + lastmodified_time = pick_integer_be(header, 95, 4); + + if (image) + { + /* write out both forks */ + err = write_fork(partition, filename, "", sourcef, sizeof(header), datafork_size, opts); + if (err) + return err; + err = write_fork(partition, filename, "RESOURCE_FORK", sourcef, sizeof(header) + pad128(datafork_size), resourcefork_size, opts); + if (err) + return err; + + /* set up attributes */ + attr_values[0].t = mac_crack_time(creation_time).to_time_t(); + attr_values[1].t = mac_crack_time(lastmodified_time).to_time_t(); + attr_values[2].i = type_code; + attr_values[3].i = creator_code; + attr_values[4].i = finder_flags; + attr_values[5].i = coord_x; + attr_values[6].i = coord_y; + attr_values[7].i = finder_folder; + attr_values[8].i = script_code; + attr_values[9].i = extended_flags; + + err = partition.put_file_attributes(filename, attrs, attr_values); + if (err) + return err; + } + + return IMGTOOLERR_SUCCESS; +} + + +// this was completely broken - it was calling macbinary_writefile() with a nullptr partition +#if 0 +static imgtoolerr_t macbinary_checkstream(imgtool::stream &stream, imgtool_suggestion_viability_t *viability) +{ + imgtoolerr_t err; + + err = macbinary_writefile(NULL, NULL, NULL, stream, NULL); + if (err == IMGTOOLERR_CORRUPTFILE) + { + /* the filter returned corrupt; this is not a valid file */ + *viability = SUGGESTION_END; + err = IMGTOOLERR_SUCCESS; + } + else if (err == IMGTOOLERR_SUCCESS) + { + /* success; lets recommend this filter */ + *viability = SUGGESTION_RECOMMENDED; + } + return err; +} +#endif + + + +void filter_macbinary_getinfo(uint32_t state, union filterinfo *info) +{ + switch(state) + { + case FILTINFO_STR_NAME: info->s = "macbinary"; break; + case FILTINFO_STR_HUMANNAME: info->s = "MacBinary"; break; + case FILTINFO_STR_EXTENSION: info->s = "bin"; break; + case FILTINFO_PTR_READFILE: info->read_file = macbinary_readfile; break; + case FILTINFO_PTR_WRITEFILE: info->write_file = macbinary_writefile; break; + //case FILTINFO_PTR_CHECKSTREAM: info->check_stream = macbinary_checkstream; break; + } +} diff --git a/src/tools/imgtool/modules/macutil.cpp b/src/tools/imgtool/modules/macutil.cpp new file mode 100644 index 0000000..1838555 --- /dev/null +++ b/src/tools/imgtool/modules/macutil.cpp @@ -0,0 +1,118 @@ +// license:BSD-3-Clause +// copyright-holders:Raphael Nabet +/**************************************************************************** + + macutil.cpp + + Imgtool Utility code for manipulating certain Apple/Mac data structures + and conventions + +*****************************************************************************/ + +#include "macutil.h" +#include "filter.h" + +#include "timeconv.h" + + +typedef util::arbitrary_clock > classic_mac_clock; + +//------------------------------------------------- +// mac_crack_time +//------------------------------------------------- + +imgtool::datetime mac_crack_time(uint32_t t) +{ + classic_mac_clock::duration d(t); + std::chrono::time_point tp(d); + return imgtool::datetime(imgtool::datetime::datetime_type::LOCAL, tp); +} + + +//------------------------------------------------- +// mac_setup_time +//------------------------------------------------- + +uint32_t mac_setup_time(const imgtool::datetime &t) +{ + auto mac_time_point = classic_mac_clock::from_arbitrary_time_point(t.time_point()); + return mac_time_point.time_since_epoch().count(); +} + + +//------------------------------------------------- +// mac_setup_time +//------------------------------------------------- + +uint32_t mac_setup_time(time_t t) +{ + imgtool::datetime dt(imgtool::datetime::datetime_type::LOCAL, t); + return mac_setup_time(dt); +} + + +//------------------------------------------------- +// mac_time_now +//------------------------------------------------- + +uint32_t mac_time_now(void) +{ + imgtool::datetime dt = imgtool::datetime::now(imgtool::datetime::datetime_type::LOCAL); + return mac_setup_time(dt); +} + + +//------------------------------------------------- +// mac_identify_fork +//------------------------------------------------- + +imgtoolerr_t mac_identify_fork(const char *fork_string, mac_fork_t *fork_num) +{ + if (!strcmp(fork_string, "")) + *fork_num = MAC_FORK_DATA; + else if (!strcmp(fork_string, "RESOURCE_FORK")) + *fork_num = MAC_FORK_RESOURCE; + else + return IMGTOOLERR_FORKNOTFOUND; + return IMGTOOLERR_SUCCESS; +} + + + +void mac_suggest_transfer(mac_filecategory_t file_category, imgtool_transfer_suggestion *suggestions, size_t suggestions_length) +{ + suggestions[0].viability = (file_category == MAC_FILECATEGORY_FORKED) ? SUGGESTION_RECOMMENDED : SUGGESTION_POSSIBLE; + suggestions[0].filter = filter_macbinary_getinfo; + suggestions[0].fork = NULL; + suggestions[0].description = NULL; + + suggestions[1].viability = (file_category == MAC_FILECATEGORY_TEXT) ? SUGGESTION_RECOMMENDED : SUGGESTION_POSSIBLE; + suggestions[1].filter = filter_eoln_getinfo; + suggestions[1].fork = NULL; + suggestions[1].description = NULL; + + suggestions[2].viability = (file_category == MAC_FILECATEGORY_DATA) ? SUGGESTION_RECOMMENDED : SUGGESTION_POSSIBLE; + suggestions[2].filter = NULL; + suggestions[2].fork = ""; + suggestions[2].description = "Raw (data fork)"; + + suggestions[3].viability = SUGGESTION_POSSIBLE; + suggestions[3].filter = NULL; + suggestions[3].fork = "RESOURCE_FORK"; + suggestions[3].description = "Raw (resource fork)"; +} + + + +void pascal_from_c_string(unsigned char *pstring, size_t pstring_len, const char *cstring) +{ + size_t cstring_len, i; + + cstring_len = strlen(cstring); + pstring[0] = std::min(cstring_len, pstring_len - 1); + + for (i = 0; i < pstring[0]; i++) + pstring[1 + i] = cstring[i]; + while(i < pstring_len - 1) + pstring[1 + i++] = '\0'; +} diff --git a/src/tools/imgtool/modules/macutil.h b/src/tools/imgtool/modules/macutil.h new file mode 100644 index 0000000..ce364f6 --- /dev/null +++ b/src/tools/imgtool/modules/macutil.h @@ -0,0 +1,43 @@ +// license:BSD-3-Clause +// copyright-holders:Raphael Nabet +/**************************************************************************** + + macutil.h + + Imgtool Utility code for manipulating certain Apple/Mac data structures + and conventions + +*****************************************************************************/ + +#ifndef MACUTIL_H +#define MACUTIL_H + +#include "library.h" + +enum mac_fork_t +{ + MAC_FORK_DATA, + MAC_FORK_RESOURCE +}; + +enum mac_filecategory_t +{ + MAC_FILECATEGORY_DATA, + MAC_FILECATEGORY_TEXT, + MAC_FILECATEGORY_FORKED +}; + + +/* converting Classic Mac OS time <==> Imgtool time */ +imgtool::datetime mac_crack_time(uint32_t t); +uint32_t mac_setup_time(const imgtool::datetime &t); +uint32_t mac_setup_time(time_t t); +uint32_t mac_time_now(void); + +imgtoolerr_t mac_identify_fork(const char *fork_string, mac_fork_t *fork_num); + +void mac_suggest_transfer(mac_filecategory_t file_category, imgtool_transfer_suggestion *suggestions, size_t suggestions_length); + +void pascal_from_c_string(unsigned char *pstring, size_t pstring_len, const char *cstring); + +#endif /* MACUTIL_H */ diff --git a/src/tools/imgtool/modules/os9.cpp b/src/tools/imgtool/modules/os9.cpp new file mode 100644 index 0000000..104e48b --- /dev/null +++ b/src/tools/imgtool/modules/os9.cpp @@ -0,0 +1,1241 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/**************************************************************************** + + os9.cpp + + CoCo OS-9 disk images + +****************************************************************************/ + +#include "imgtool.h" +#include "iflopimg.h" + +#include "formats/imageutl.h" +#include "formats/coco_dsk.h" +#include "opresolv.h" + +#include +#include + +enum creation_policy_t +{ + CREATE_NONE, + CREATE_FILE, + CREATE_DIR +}; + +struct os9_diskinfo +{ + uint32_t total_sectors; + uint32_t sectors_per_track; + uint32_t allocation_bitmap_bytes; + uint32_t cluster_size; + uint32_t root_dir_lsn; + uint32_t owner_id; + uint32_t attributes; + uint32_t disk_id; + uint32_t format_flags; + uint32_t bootstrap_lsn; + uint32_t bootstrap_size; + uint32_t sector_size; + + unsigned int sides : 2; + unsigned int double_density : 1; + unsigned int double_track : 1; + unsigned int quad_track_density : 1; + unsigned int octal_track_density : 1; + + char name[33]; + uint8_t allocation_bitmap[65536]; +}; + + +struct os9_fileinfo +{ + unsigned int directory : 1; + unsigned int non_sharable : 1; + unsigned int public_execute : 1; + unsigned int public_write : 1; + unsigned int public_read : 1; + unsigned int user_execute : 1; + unsigned int user_write : 1; + unsigned int user_read : 1; + + uint32_t lsn; + uint32_t owner_id; + uint32_t link_count; + uint32_t file_size; + + struct + { + uint32_t lsn; + uint32_t count; + } sector_map[48]; +}; + + +struct os9_direnum +{ + struct os9_fileinfo dir_info; + uint32_t index; +}; + + + +static void pick_string(const void *ptr, size_t offset, size_t length, char *dest) +{ + uint8_t b; + + while(length--) + { + b = ((uint8_t *) ptr)[offset++]; + *(dest++) = b & 0x7F; + if (b & 0x80) + length = 0; + } + *dest = '\0'; +} + + + +static void place_string(void *ptr, size_t offset, size_t length, const char *s) +{ + size_t i; + uint8_t b; + uint8_t *bptr; + + bptr = (uint8_t *) ptr; + bptr += offset; + + bptr[0] = 0x80; + + for (i = 0; s[i] && (i < length); i++) + { + b = ((uint8_t) s[i]) & 0x7F; + if (s[i+1] == '\0') + b |= 0x80; + bptr[i] = b; + } +} + + + +static os9_diskinfo *os9_get_diskinfo(imgtool::image &image) +{ + return (os9_diskinfo *) imgtool_floppy_extrabytes(image); +} + + + +static struct os9_direnum *os9_get_dirinfo(imgtool::directory &directory) +{ + return (struct os9_direnum *) directory.extra_bytes(); +} + + + +static imgtoolerr_t os9_locate_lsn(imgtool::image &image, uint32_t lsn, uint32_t *head, uint32_t *track, uint32_t *sector) +{ + const os9_diskinfo *disk_info; + + disk_info = os9_get_diskinfo(image); + if (lsn > disk_info->total_sectors) + return IMGTOOLERR_CORRUPTIMAGE; + + *track = lsn / (disk_info->sectors_per_track * disk_info->sides); + *head = (lsn / disk_info->sectors_per_track) % disk_info->sides; + *sector = (lsn % disk_info->sectors_per_track) + 1; + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t os9_read_lsn(imgtool::image &img, uint32_t lsn, int offset, void *buffer, size_t buffer_len) +{ + imgtoolerr_t err; + floperr_t ferr; + uint32_t head, track, sector; + + err = os9_locate_lsn(img, lsn, &head, &track, §or); + if (err) + return err; + + ferr = floppy_read_sector(imgtool_floppy(img), head, track, sector, offset, buffer, buffer_len); + if (ferr) + return imgtool_floppy_error(ferr); + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t os9_write_lsn(imgtool::image &img, uint32_t lsn, int offset, const void *buffer, size_t buffer_len) +{ + imgtoolerr_t err; + floperr_t ferr; + uint32_t head, track, sector; + + err = os9_locate_lsn(img, lsn, &head, &track, §or); + if (err) + return err; + + ferr = floppy_write_sector(imgtool_floppy(img), head, track, sector, offset, buffer, buffer_len, 0); /* TODO: pass ddam argument from imgtool */ + if (ferr) + return imgtool_floppy_error(ferr); + + return IMGTOOLERR_SUCCESS; +} + + + +static uint32_t os9_lookup_lsn(imgtool::image &img, + const struct os9_fileinfo *file_info, uint32_t *index) +{ + int i; + uint32_t lsn; + const os9_diskinfo *disk_info; + + disk_info = os9_get_diskinfo(img); + lsn = *index / disk_info->sector_size; + + i = 0; + while(lsn >= file_info->sector_map[i].count) + lsn -= file_info->sector_map[i++].count; + + lsn = file_info->sector_map[i].lsn + lsn; + *index %= disk_info->sector_size; + return lsn; +} + + + +static int os9_interpret_dirent(void *entry, char **filename, uint32_t *lsn, int *corrupt) +{ + int i; + char *entry_b = (char *) entry; + + *filename = NULL; + *lsn = 0; + if (corrupt) + *corrupt = false; + + if (entry_b[28] != '\0') + { + if (corrupt) + *corrupt = true; + } + + for (i = 0; (i < 28) && !(entry_b[i] & 0x80); i++) + ; + entry_b[i] &= 0x7F; + entry_b[i+1] = '\0'; + + *lsn = pick_integer_be(entry, 29, 3); + if (strcmp(entry_b, ".") && strcmp(entry_b, "..")) + *filename = entry_b; + return *filename != NULL; +} + + + +/*------------------------------------------------- + os9_decode_file_header - reads a file/directory + entry from an LSN, and decodes it +-------------------------------------------------*/ + +static imgtoolerr_t os9_decode_file_header(imgtool::image &image, + int lsn, struct os9_fileinfo *info) +{ + imgtoolerr_t err; + uint32_t attributes, count; + int max_entries, i; + const os9_diskinfo *disk_info; + uint8_t header[256]; + + disk_info = os9_get_diskinfo(image); + + err = os9_read_lsn(image, lsn, 0, header, sizeof(header)); + if (err) + return err; + + info->lsn = lsn; + attributes = pick_integer_be(header, 0, 1); + info->owner_id = pick_integer_be(header, 1, 2); + info->link_count = pick_integer_be(header, 8, 1); + info->file_size = pick_integer_be(header, 9, 4); + info->directory = (attributes & 0x80) ? 1 : 0; + info->non_sharable = (attributes & 0x40) ? 1 : 0; + info->public_execute = (attributes & 0x20) ? 1 : 0; + info->public_write = (attributes & 0x10) ? 1 : 0; + info->public_read = (attributes & 0x08) ? 1 : 0; + info->user_execute = (attributes & 0x04) ? 1 : 0; + info->user_write = (attributes & 0x02) ? 1 : 0; + info->user_read = (attributes & 0x01) ? 1 : 0; + + if (info->directory && (info->file_size % 32 != 0)) + return IMGTOOLERR_CORRUPTIMAGE; + + /* read all sector map entries */ + max_entries = (disk_info->sector_size - 16) / 5; + max_entries = (std::min)(max_entries, std::size(info->sector_map) - 1); + for (i = 0; i < max_entries; i++) + { + lsn = pick_integer_be(header, 16 + (i * 5) + 0, 3); + count = pick_integer_be(header, 16 + (i * 5) + 3, 2); + if (count <= 0) + break; + + info->sector_map[i].lsn = lsn; + info->sector_map[i].count = count; + } + info->sector_map[i].lsn = 0; + info->sector_map[i].count = 0; + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t os9_allocate_lsn(imgtool::image &image, uint32_t *lsn) +{ + uint32_t i; + os9_diskinfo *disk_info; + uint8_t b, mask; + + disk_info = os9_get_diskinfo(image); + + for (i = 0; i < disk_info->total_sectors; i++) + { + b = disk_info->allocation_bitmap[i / 8]; + mask = 1 << (7 - (i % 8)); + + if ((b & mask) == 0) + { + disk_info->allocation_bitmap[i / 8] |= mask; + *lsn = i; + return os9_write_lsn(image, 1, 0, disk_info->allocation_bitmap, disk_info->allocation_bitmap_bytes); + } + } + return IMGTOOLERR_NOSPACE; +} + + + +static imgtoolerr_t os9_deallocate_lsn(imgtool::image &image, uint32_t lsn) +{ + uint8_t mask; + os9_diskinfo *disk_info; + + disk_info = os9_get_diskinfo(image); + mask = 1 << (7 - (lsn % 8)); + disk_info->allocation_bitmap[lsn / 8] &= ~mask; + return os9_write_lsn(image, 1, 0, disk_info->allocation_bitmap, disk_info->allocation_bitmap_bytes); +} + + + +static uint32_t os9_get_free_lsns(imgtool::image &image) +{ + const os9_diskinfo *disk_info; + uint32_t i, free_lsns; + uint8_t b; + + disk_info = os9_get_diskinfo(image); + free_lsns = 0; + + for (i = 0; i < disk_info->total_sectors; i++) + { + b = disk_info->allocation_bitmap[i / 8]; + b >>= (7 - (i % 8)); + free_lsns += ~b & 1; + } + + return free_lsns; +} + + + +static imgtoolerr_t os9_corrupt_file_error(const struct os9_fileinfo *file_info) +{ + imgtoolerr_t err; + if (file_info->directory) + err = IMGTOOLERR_CORRUPTDIR; + else + err = IMGTOOLERR_CORRUPTFILE; + return err; +} + + + +static imgtoolerr_t os9_set_file_size(imgtool::image &image, + struct os9_fileinfo *file_info, uint32_t new_size) +{ + imgtoolerr_t err; + const os9_diskinfo *disk_info; + uint32_t new_lsn_count, current_lsn_count; + uint32_t free_lsns, lsn, i; + int sector_map_length = -1; + uint8_t header[256]; + + /* easy way out? */ + if (file_info->file_size == new_size) + return IMGTOOLERR_SUCCESS; + + disk_info = os9_get_diskinfo(image); + + free_lsns = os9_get_free_lsns(image); + current_lsn_count = (file_info->file_size + disk_info->sector_size - 1) / disk_info->sector_size; + new_lsn_count = (new_size + disk_info->sector_size - 1) / disk_info->sector_size; + + /* check to see if the file is growing and we do not have enough space */ + if ((new_lsn_count > current_lsn_count) && (new_lsn_count - current_lsn_count > free_lsns)) + return IMGTOOLERR_NOSPACE; + + if (current_lsn_count != new_lsn_count) + { + /* first find out the size of our sector map */ + sector_map_length = 0; + lsn = 0; + while((lsn < current_lsn_count) && (sector_map_length < std::size(file_info->sector_map))) + { + if (file_info->sector_map[sector_map_length].count == 0) + return os9_corrupt_file_error(file_info); + + lsn += file_info->sector_map[sector_map_length].count; + sector_map_length++; + } + + /* keep in mind that the sector_map might not parallel our expected + * current LSN count; we should tolerate this abnormality */ + current_lsn_count = lsn; + + while(current_lsn_count > new_lsn_count) + { + /* shrink this file */ + lsn = file_info->sector_map[sector_map_length - 1].lsn + + file_info->sector_map[sector_map_length - 1].count - 1; + + err = os9_deallocate_lsn(image, lsn); + if (err) + return err; + + file_info->sector_map[sector_map_length - 1].count--; + while(sector_map_length > 0 && (file_info->sector_map[sector_map_length - 1].count == 0)) + sector_map_length--; + current_lsn_count--; + } + + while(current_lsn_count < new_lsn_count) + { + /* grow this file */ + err = os9_allocate_lsn(image, &lsn); + if (err) + return err; + + if ((sector_map_length > 0) && ((file_info->sector_map[sector_map_length - 1].lsn + + file_info->sector_map[sector_map_length - 1].count) == lsn)) + { + file_info->sector_map[sector_map_length - 1].count++; + } + else if (sector_map_length >= std::size(file_info->sector_map)) + { + return IMGTOOLERR_NOSPACE; + } + else + { + file_info->sector_map[sector_map_length].lsn = lsn; + file_info->sector_map[sector_map_length].count = 1; + sector_map_length++; + file_info->sector_map[sector_map_length].lsn = 0; + file_info->sector_map[sector_map_length].count = 0; + } + current_lsn_count++; + } + } + + /* now lets write back the sector */ + err = os9_read_lsn(image, file_info->lsn, 0, header, sizeof(header)); + if (err) + return err; + + file_info->file_size = new_size; + place_integer_be(header, 9, 4, file_info->file_size); + + /* do we have to write the sector map? */ + if (sector_map_length >= 0) + { + for (i = 0; i < (std::min)(sector_map_length + 1, std::size(file_info->sector_map)); i++) + { + place_integer_be(header, 16 + (i * 5) + 0, 3, file_info->sector_map[i].lsn); + place_integer_be(header, 16 + (i * 5) + 3, 2, file_info->sector_map[i].count); + } + } + + err = os9_write_lsn(image, file_info->lsn, 0, header, disk_info->sector_size); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + + + +/*------------------------------------------------- + os9_lookup_path - walks an OS-9 directory + structure to find a file, or optionally, create + a file +-------------------------------------------------*/ + +static imgtoolerr_t os9_lookup_path(imgtool::image &img, const char *path, + creation_policy_t create, struct os9_fileinfo *file_info, + uint32_t *parent_lsn, uint32_t *dirent_lsn, uint32_t *dirent_index) +{ + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + struct os9_fileinfo dir_info; + uint32_t index, current_lsn, dir_size; + uint32_t entry_index = 0; + uint32_t free_entry_index = 0xffffffff; + uint32_t entry_lsn = 0; + uint32_t allocated_lsn = 0; + uint8_t entry[32]; + uint8_t block[64]; + char *filename; + const os9_diskinfo *disk_info; + + disk_info = os9_get_diskinfo(img); + current_lsn = disk_info->root_dir_lsn; + + if (parent_lsn) + *parent_lsn = 0; + + /* we have to transverse each path element */ + while(*path) + { + if (parent_lsn) + *parent_lsn = current_lsn; + + /* decode the directory header of this directory */ + err = os9_decode_file_header(img, current_lsn, &dir_info); + if (err) + goto done; + dir_size = dir_info.file_size; + + /* sanity check directory */ + if (!dir_info.directory) + { + err = (current_lsn == disk_info->root_dir_lsn) ? IMGTOOLERR_CORRUPTIMAGE : IMGTOOLERR_INVALIDPATH; + goto done; + } + + /* walk the directory */ + for (index = 0; index < dir_size; index += 32) + { + entry_index = index; + entry_lsn = os9_lookup_lsn(img, &dir_info, &entry_index); + + err = os9_read_lsn(img, entry_lsn, entry_index, entry, sizeof(entry)); + if (err) + goto done; + + /* remember first free entry found */ + if( free_entry_index == 0xffffffff ) + { + if( entry[0] == 0 ) + free_entry_index = index; + } + + if (os9_interpret_dirent(entry, &filename, ¤t_lsn, NULL)) + { + if (!strcmp(path, filename)) + break; + } + + } + + /* at the end of the file? */ + if (index >= dir_size) + { + /* if we're not creating, or we are creating but we have not fully + * transversed the directory, error out */ + if (!create || path[strlen(path) + 1]) + { + err = IMGTOOLERR_PATHNOTFOUND; + goto done; + } + + /* allocate a new LSN */ + err = os9_allocate_lsn(img, &allocated_lsn); + if (err) + goto done; + + /* write the file */ + memset(block, 0, sizeof(block)); + place_integer_be(block, 0, 1, 0x1B | ((create == CREATE_DIR) ? 0x80 : 0x00)); + err = os9_write_lsn(img, allocated_lsn, 0, block, sizeof(block)); + if (err) + goto done; + + if( free_entry_index == 0xffffffff ) + { + /* expand the directory to hold the new entry */ + err = os9_set_file_size(img, &dir_info, dir_size + 32); + if (err) + goto done; + } + else + /* use first unused entry in directory */ + dir_size = free_entry_index; + + /* now prepare the directory entry */ + memset(entry, 0, sizeof(entry)); + place_string(entry, 0, 28, path); + place_integer_be(entry, 29, 3, allocated_lsn); + + /* now write the directory entry */ + entry_index = dir_size; + entry_lsn = os9_lookup_lsn(img, &dir_info, &entry_index); + err = os9_write_lsn(img, entry_lsn, entry_index, entry, 32); + if (err) + goto done; + + /* directory entry append complete; no need to hold this lsn */ + current_lsn = allocated_lsn; + allocated_lsn = 0; + } + path += strlen(path) + 1; + } + + if (file_info) + { + err = os9_decode_file_header(img, current_lsn, file_info); + if (err) + goto done; + } + + if (dirent_lsn) + *dirent_lsn = entry_lsn; + if (dirent_index) + *dirent_index = entry_index; + +done: + if (allocated_lsn != 0) + os9_deallocate_lsn(img, allocated_lsn); + return err; +} + + + +static imgtoolerr_t os9_diskimage_open(imgtool::image &image, imgtool::stream::ptr &&dummy) +{ + imgtoolerr_t err; + floperr_t ferr; + os9_diskinfo *info; + uint32_t track_size_in_sectors, i; //, attributes; + uint8_t header[256]; + uint32_t allocation_bitmap_lsns; + uint8_t b, mask; + + info = os9_get_diskinfo(image); + + ferr = floppy_read_sector(imgtool_floppy(image), 0, 0, 1, 0, header, sizeof(header)); + if (ferr) + return imgtool_floppy_error(ferr); + + info->total_sectors = pick_integer_be(header, 0, 3); + track_size_in_sectors = pick_integer_be(header, 3, 1); + info->allocation_bitmap_bytes = pick_integer_be(header, 4, 2); + info->cluster_size = pick_integer_be(header, 6, 2); + info->root_dir_lsn = pick_integer_be(header, 8, 3); + info->owner_id = pick_integer_be(header, 11, 2); +// attributes = + pick_integer_be(header, 13, 1); + info->disk_id = pick_integer_be(header, 14, 2); + info->format_flags = pick_integer_be(header, 16, 1); + info->sectors_per_track = pick_integer_be(header, 17, 2); + info->bootstrap_lsn = pick_integer_be(header, 21, 3); + info->bootstrap_size = pick_integer_be(header, 24, 2); + info->sector_size = pick_integer_be(header, 104, 2); + + info->sides = (info->format_flags & 0x01) ? 2 : 1; + info->double_density = (info->format_flags & 0x02) ? 1 : 0; + info->double_track = (info->format_flags & 0x04) ? 1 : 0; + info->quad_track_density = (info->format_flags & 0x08) ? 1 : 0; + info->octal_track_density = (info->format_flags & 0x10) ? 1 : 0; + + pick_string(header, 31, 32, info->name); + + if (info->sector_size == 0) + info->sector_size = 256; + + /* does the root directory and allocation bitmap collide? */ + allocation_bitmap_lsns = (info->allocation_bitmap_bytes + info->sector_size - 1) / info->sector_size; + if (1 + allocation_bitmap_lsns > info->root_dir_lsn) + return IMGTOOLERR_CORRUPTIMAGE; + + /* is the allocation bitmap too big? */ + memset(&info->allocation_bitmap[0], 0, info->allocation_bitmap_bytes); + + /* sectors per track and track size don't jive? */ + if (info->sectors_per_track != track_size_in_sectors) + return IMGTOOLERR_CORRUPTIMAGE; + + /* zero sectors per track? */ + if (info->sectors_per_track == 0) + return IMGTOOLERR_CORRUPTIMAGE; + + /* do we have an odd number of sectors? */ + if (info->total_sectors % info->sectors_per_track) + return IMGTOOLERR_CORRUPTIMAGE; + + /* read the allocation bitmap */ + for (i = 0; i < allocation_bitmap_lsns; i++) + { + err = os9_read_lsn(image, 1 + i, 0, &info->allocation_bitmap[i * info->sector_size], + std::min(info->allocation_bitmap_bytes - (i * info->sector_size), info->sector_size)); + if (err) + return err; + } + + /* check to make sure that the allocation bitmap and root sector are reserved */ + for (i = 0; i <= allocation_bitmap_lsns; i++) + { + b = info->allocation_bitmap[i / 8]; + mask = 1 << (7 - (i % 8)); + if ((b & mask) == 0) + return IMGTOOLERR_CORRUPTIMAGE; + } + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t os9_diskimage_create(imgtool::image &img, imgtool::stream::ptr &&stream, util::option_resolution *opts) +{ + imgtoolerr_t err; + std::vector header; + uint32_t heads, tracks, sectors, sector_bytes, first_sector_id; + uint32_t cluster_size, owner_id; + uint32_t allocation_bitmap_bits, allocation_bitmap_lsns; + uint32_t attributes, format_flags, disk_id; + uint32_t i; + int32_t total_allocated_sectors; + const char *title; + time_t t; + struct tm *ltime; + + time(&t); + ltime = localtime(&t); + + heads = opts->lookup_int('H'); + tracks = opts->lookup_int('T'); + sectors = opts->lookup_int('S'); + sector_bytes = opts->lookup_int('L'); + first_sector_id = opts->lookup_int('F'); + title = ""; + + header.resize(sector_bytes); + + if (sector_bytes > 256) + sector_bytes = 256; + cluster_size = 1; + owner_id = 1; + disk_id = 1; + attributes = 0; + allocation_bitmap_bits = heads * tracks * sectors / cluster_size; + allocation_bitmap_lsns = (allocation_bitmap_bits / 8 + sector_bytes - 1) / sector_bytes; + format_flags = ((heads > 1) ? 0x01 : 0x00) | ((tracks > 40) ? 0x02 : 0x00); + + memset(&header[0], 0, sector_bytes); + place_integer_be(&header[0], 0, 3, heads * tracks * sectors); + place_integer_be(&header[0], 3, 1, sectors); + place_integer_be(&header[0], 4, 2, (allocation_bitmap_bits + 7) / 8); + place_integer_be(&header[0], 6, 2, cluster_size); + place_integer_be(&header[0], 8, 3, 1 + allocation_bitmap_lsns); + place_integer_be(&header[0], 11, 2, owner_id); + place_integer_be(&header[0], 13, 1, attributes); + place_integer_be(&header[0], 14, 2, disk_id); + place_integer_be(&header[0], 16, 1, format_flags); + place_integer_be(&header[0], 17, 2, sectors); + place_string(&header[0], 31, 32, title); + place_integer_be(&header[0], 103, 2, sector_bytes / 256); + + /* path descriptor options */ + place_integer_be(&header[0], 0x3f+0x00, 1, 1); /* device class */ + place_integer_be(&header[0], 0x3f+0x01, 1, 1); /* drive number */ + place_integer_be(&header[0], 0x3f+0x03, 1, 0x20); /* device type */ + place_integer_be(&header[0], 0x3f+0x04, 1, 1); /* density capability */ + place_integer_be(&header[0], 0x3f+0x05, 2, tracks); /* number of tracks */ + place_integer_be(&header[0], 0x3f+0x07, 1, heads); /* number of sides */ + place_integer_be(&header[0], 0x3f+0x09, 2, sectors); /* sectors per track */ + place_integer_be(&header[0], 0x3f+0x0b, 2, sectors); /* sectors on track zero */ + place_integer_be(&header[0], 0x3f+0x0d, 1, 3); /* sector interleave factor */ + place_integer_be(&header[0], 0x3f+0x0e, 1, 8); /* default sectors per allocation */ + + err = (imgtoolerr_t)floppy_write_sector(imgtool_floppy(img), 0, 0, first_sector_id, 0, &header[0], sector_bytes, 0); /* TODO: pass ddam argument from imgtool */ + if (err) + goto done; + + total_allocated_sectors = 1 + allocation_bitmap_lsns + 1 + 7; + + for (i = 0; i < allocation_bitmap_lsns; i++) + { + memset(&header[0], 0x00, sector_bytes); + + if (total_allocated_sectors > (8 * 256)) + { + memset(&header[0], 0xff, sector_bytes); + total_allocated_sectors -= (8 * 256); + } + else if (total_allocated_sectors > 1 ) + { + int offset; + uint8_t mask; + + while( total_allocated_sectors >= 0 ) + { + offset = total_allocated_sectors / 8; + mask = 1 << (7 - ( total_allocated_sectors % 8 ) ); + + header[offset] |= mask; + total_allocated_sectors--; + } + } + + err = (imgtoolerr_t)floppy_write_sector(imgtool_floppy(img), 0, 0, first_sector_id + 1 + i, 0, &header[0], sector_bytes, 0); /* TODO: pass ddam argument from imgtool */ + if (err) + goto done; + } + + memset(&header[0], 0, sector_bytes); + header[0x00] = 0xBF; + header[0x01] = 0x00; + header[0x02] = 0x00; + header[0x03] = (uint8_t) ltime->tm_year; + header[0x04] = (uint8_t) ltime->tm_mon + 1; + header[0x05] = (uint8_t) ltime->tm_mday; + header[0x06] = (uint8_t) ltime->tm_hour; + header[0x07] = (uint8_t) ltime->tm_min; + header[0x08] = 0x02; + header[0x09] = 0x00; + header[0x0A] = 0x00; + header[0x0B] = 0x00; + header[0x0C] = 0x40; + header[0x0D] = (uint8_t) (ltime->tm_year % 100); + header[0x0E] = (uint8_t) ltime->tm_mon; + header[0x0F] = (uint8_t) ltime->tm_mday; + place_integer_be(&header[0], 0x10, 3, 1 + allocation_bitmap_lsns + 1); + place_integer_be(&header[0], 0x13, 2, 8); + + err = (imgtoolerr_t)floppy_write_sector(imgtool_floppy(img), 0, 0, first_sector_id + 1 + allocation_bitmap_lsns, 0, &header[0], sector_bytes, 0); /* TODO: pass ddam argument from imgtool */ + if (err) + goto done; + + memset(&header[0], 0, sector_bytes); + header[0x00] = 0x2E; + header[0x01] = 0xAE; + header[0x1F] = 1 + allocation_bitmap_lsns; + header[0x20] = 0xAE; + header[0x3F] = 1 + allocation_bitmap_lsns; + err = (imgtoolerr_t)floppy_write_sector(imgtool_floppy(img), 0, 0, first_sector_id + 2 + allocation_bitmap_lsns, 0, &header[0], sector_bytes, 0); /* TOOD: pass ddam argument from imgtool */ + if (err) + goto done; + +done: + return err; +} + + + +static imgtoolerr_t os9_diskimage_beginenum(imgtool::directory &enumeration, const char *path) +{ + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + struct os9_direnum *os9enum; + imgtool::image &image(enumeration.image()); + + os9enum = os9_get_dirinfo(enumeration); + + err = os9_lookup_path(image, path, CREATE_NONE, &os9enum->dir_info, NULL, NULL, NULL); + if (err) + goto done; + + /* this had better be a directory */ + if (!os9enum->dir_info.directory) + { + err = IMGTOOLERR_CORRUPTIMAGE; + goto done; + } + +done: + return err; +} + + + +static imgtoolerr_t os9_diskimage_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent) +{ + struct os9_direnum *os9enum; + uint32_t lsn, index; + imgtoolerr_t err; + uint8_t dir_entry[32]; + char filename[29]; + struct os9_fileinfo file_info; + imgtool::image &image(enumeration.image()); + + os9enum = os9_get_dirinfo(enumeration); + + do + { + /* check for EOF */ + if (os9enum->index >= os9enum->dir_info.file_size) + { + ent.eof = 1; + return IMGTOOLERR_SUCCESS; + } + + /* locate the LSN and offset for this directory entry */ + index = os9enum->index; + lsn = os9_lookup_lsn(image, &os9enum->dir_info, &index); + + /* read the directory entry out of the lSN */ + err = os9_read_lsn(image, lsn, index, dir_entry, sizeof(dir_entry)); + if (err) + return err; + + if (dir_entry[0]) + { + /* read the file or directory name */ + pick_string(dir_entry, 0, 28, filename); + + /* we have certain expectations of the directory contents; the + * first directory entry should be "..", the second ".", and + * subsequent entries should never be "." or ".." */ + switch(os9enum->index) + { + case 0: + if (strcmp(filename, "..")) + imgtool_warn("First entry in directory should be \"..\" and not \"%s\"", filename); + break; + + case 32: + if (strcmp(filename, ".")) + imgtool_warn("Second entry in directory should be \".\" and not \"%s\"", filename); + break; + + default: + if (!strcmp(filename, ".") || !strcmp(filename, "..")) + imgtool_warn("Directory entry %d should not be \"%s\"", index / 32, filename); + break; + } + + /* if the filename is ".", the file should point to the current directory */ + if (!strcmp(filename, ".") && (pick_integer_be(dir_entry, 29, 3) != os9enum->dir_info.lsn)) + { + imgtool_warn("Directory \".\" does not point back to same directory"); + } + } + else + { + /* no more directory entries */ + filename[0] = '\0'; + } + + /* move on to the next directory entry */ + os9enum->index += 32; + } + while(!filename[0] || !strcmp(filename, ".") || !strcmp(filename, "..")); + + /* read file attributes */ + lsn = pick_integer_be(dir_entry, 29, 3); + err = os9_decode_file_header(enumeration.image(), lsn, &file_info); + if (err) + return err; + + /* fill out imgtool_dirent structure */ + snprintf(ent.filename, std::size(ent.filename), "%s", filename); + snprintf(ent.attr, std::size(ent.attr), "%c%c%c%c%c%c%c%c", + file_info.directory ? 'd' : '-', + file_info.non_sharable ? 's' : '-', + file_info.public_execute ? 'x' : '-', + file_info.public_write ? 'w' : '-', + file_info.public_read ? 'r' : '-', + file_info.user_execute ? 'x' : '-', + file_info.user_write ? 'w' : '-', + file_info.user_read ? 'r' : '-'); + + ent.directory = file_info.directory; + ent.corrupt = (dir_entry[28] != 0); + ent.filesize = file_info.file_size; + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t os9_diskimage_freespace(imgtool::partition &partition, uint64_t *size) +{ + imgtool::image &image(partition.image()); + const os9_diskinfo *disk_info; + uint32_t free_lsns; + + disk_info = os9_get_diskinfo(image); + free_lsns = os9_get_free_lsns(image); + + *size = free_lsns * disk_info->sector_size; + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t os9_diskimage_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf) +{ + imgtoolerr_t err; + imgtool::image &img(partition.image()); + const os9_diskinfo *disk_info; + struct os9_fileinfo file_info; + uint8_t buffer[256]; + int i, j; + uint32_t file_size; + uint32_t used_size; + + disk_info = os9_get_diskinfo(img); + + err = os9_lookup_path(img, filename, CREATE_NONE, &file_info, NULL, NULL, NULL); + if (err) + return err; + if (file_info.directory) + return IMGTOOLERR_FILENOTFOUND; + file_size = file_info.file_size; + + for (i = 0; file_info.sector_map[i].count > 0; i++) + { + for (j = 0; j < file_info.sector_map[i].count; j++) + { + used_size = std::min(file_size, disk_info->sector_size); + err = os9_read_lsn(img, file_info.sector_map[i].lsn + j, 0, + buffer, used_size); + if (err) + return err; + destf.write(buffer, used_size); + file_size -= used_size; + } + } + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t os9_diskimage_writefile(imgtool::partition &partition, const char *path, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts) +{ + imgtoolerr_t err; + imgtool::image &image(partition.image()); + struct os9_fileinfo file_info; + size_t write_size; + std::vector buf; + int i = -1; + uint32_t lsn = 0; + uint32_t count = 0; + uint32_t sz; + const os9_diskinfo *disk_info; + + disk_info = os9_get_diskinfo(image); + + buf.resize(disk_info->sector_size); + + err = os9_lookup_path(image, path, CREATE_FILE, &file_info, NULL, NULL, NULL); + if (err) + goto done; + + sz = (uint32_t) sourcef.size(); + + err = os9_set_file_size(image, &file_info, sz); + if (err) + goto done; + + while(sz > 0) + { + write_size = (std::min)(sz, disk_info->sector_size); + + sourcef.read(&buf[0], write_size); + + while(count == 0) + { + i++; + lsn = file_info.sector_map[i].lsn; + count = file_info.sector_map[i].count; + } + + err = os9_write_lsn(image, lsn, 0, &buf[0], write_size); + if (err) + goto done; + + lsn++; + count--; + sz -= write_size; + } + +done: + return err; +} + + + +static imgtoolerr_t os9_diskimage_delete(imgtool::partition &partition, const char *path, + unsigned int delete_directory) +{ + imgtoolerr_t err; + imgtool::image &image(partition.image()); + struct os9_fileinfo file_info; + uint32_t dirent_lsn, dirent_index; + uint32_t entry_lsn, entry_index; + uint32_t i, j, lsn; + uint8_t b; + + //disk_info = os9_get_diskinfo(image); + + err = os9_lookup_path(image, path, CREATE_NONE, &file_info, NULL, &dirent_lsn, &dirent_index); + if (err) + return err; + if (file_info.directory != delete_directory) + return IMGTOOLERR_FILENOTFOUND; + + /* make sure that if we are deleting a directory, it is empty */ + if (delete_directory) + { + for (i = 64; i < file_info.file_size; i += 32) + { + entry_index = i; + entry_lsn = os9_lookup_lsn(image, &file_info, &entry_index); + + err = os9_read_lsn(image, entry_lsn, entry_index, &b, 1); + if (err) + return err; + + /* this had better be a deleted file, if not we can't delete */ + if (b != 0) + return IMGTOOLERR_DIRNOTEMPTY; + } + } + + /* zero out the file entry */ + b = '\0'; + err = os9_write_lsn(image, dirent_lsn, dirent_index, &b, 1); + if (err) + return err; + + /* get the link count */ + err = os9_read_lsn(image, file_info.lsn, 8, &b, 1); + if (err) + return err; + + if (b > 0) + b--; + if (b > 0) + { + /* link count is greater than zero */ + err = os9_write_lsn(image, file_info.lsn, 8, &b, 1); + if (err) + return err; + } + else + { + /* no more links; outright delete the file */ + err = os9_deallocate_lsn(image, file_info.lsn); + if (err) + return err; + + for (i = 0; (i < std::size(file_info.sector_map)) && file_info.sector_map[i].count; i++) + { + lsn = file_info.sector_map[i].lsn; + for (j = 0; j < file_info.sector_map[i].count; j++) + { + err = os9_deallocate_lsn(image, lsn + j); + if (err) + return err; + } + } + } + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t os9_diskimage_deletefile(imgtool::partition &partition, const char *path) +{ + return os9_diskimage_delete(partition, path, 0); +} + + + +static imgtoolerr_t os9_diskimage_createdir(imgtool::partition &partition, const char *path) +{ + imgtoolerr_t err; + imgtool::image &image(partition.image()); + struct os9_fileinfo file_info; + uint8_t dir_data[64]; + uint32_t parent_lsn; + + err = os9_lookup_path(image, path, CREATE_DIR, &file_info, &parent_lsn, NULL, NULL); + if (err) + goto done; + + err = os9_set_file_size(image, &file_info, 64); + if (err) + goto done; + + /* create intial directories */ + memset(dir_data, 0, sizeof(dir_data)); + place_string(dir_data, 0, 32, ".."); + place_integer_be(dir_data, 29, 3, parent_lsn); + place_string(dir_data, 32, 32, "."); + place_integer_be(dir_data, 61, 3, file_info.lsn); + + err = os9_write_lsn(image, file_info.sector_map[0].lsn, 0, dir_data, sizeof(dir_data)); + if (err) + goto done; + +done: + return err; +} + + + +static imgtoolerr_t os9_diskimage_deletedir(imgtool::partition &partition, const char *path) +{ + return os9_diskimage_delete(partition, path, 1); +} + + + +void os9_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case IMGTOOLINFO_INT_INITIAL_PATH_SEPARATOR: info->i = 1; break; + case IMGTOOLINFO_INT_OPEN_IS_STRICT: info->i = 1; break; + case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(os9_diskinfo); break; + case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(struct os9_direnum); break; + case IMGTOOLINFO_INT_PATH_SEPARATOR: info->i = '/'; break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "os9"); break; + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "OS-9 format"); break; + case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break; + case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), "\r"); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case IMGTOOLINFO_PTR_MAKE_CLASS: info->make_class = imgtool_floppy_make_class; break; + case IMGTOOLINFO_PTR_FLOPPY_CREATE: info->create = os9_diskimage_create; break; + case IMGTOOLINFO_PTR_FLOPPY_OPEN: info->open = os9_diskimage_open; break; + case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = os9_diskimage_beginenum; break; + case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = os9_diskimage_nextenum; break; + case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = os9_diskimage_freespace; break; + case IMGTOOLINFO_PTR_READ_FILE: info->read_file = os9_diskimage_readfile; break; + case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = os9_diskimage_writefile; break; + case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = os9_diskimage_deletefile; break; + case IMGTOOLINFO_PTR_CREATE_DIR: info->create_dir = os9_diskimage_createdir; break; + case IMGTOOLINFO_PTR_DELETE_DIR: info->delete_dir = os9_diskimage_deletedir; break; + case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_coco; break; + } +} diff --git a/src/tools/imgtool/modules/pc_flop.cpp b/src/tools/imgtool/modules/pc_flop.cpp new file mode 100644 index 0000000..fd08b8e --- /dev/null +++ b/src/tools/imgtool/modules/pc_flop.cpp @@ -0,0 +1,176 @@ +// license:BSD-3-Clause +// copyright-holders:Raphael Nabet +/**************************************************************************** + + pc_flop.c + + PC floppies + +****************************************************************************/ + +#include "imgtool.h" +#include "fat.h" +#include "iflopimg.h" + +#include "formats/imageutl.h" +#include "formats/pc_dsk_legacy.h" + +#include "opresolv.h" + +#define FAT_SECLEN 512 + + +static imgtoolerr_t fat_image_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *opts) +{ + imgtoolerr_t err; + uint32_t tracks, heads, sectors; + uint8_t buffer[FAT_SECLEN]; + imgtool_class imgclass = { fat_get_info }; + imgtoolerr_t (*fat_partition_create)(imgtool::image &image, uint64_t first_block, uint64_t block_count); + + tracks = opts->lookup_int('T'); + heads = opts->lookup_int('H'); + sectors = opts->lookup_int('S'); + + /* set up just enough of a boot sector to specify geometry */ + memset(buffer, 0, sizeof(buffer)); + place_integer_le(buffer, 24, 2, sectors); + place_integer_le(buffer, 26, 2, heads); + place_integer_le(buffer, 19, 2, (uint16_t) (((uint64_t) tracks * heads * sectors) >> 0)); + place_integer_le(buffer, 32, 4, (uint16_t) (((uint64_t) tracks * heads * sectors) >> 16)); + err = image.write_block(0, buffer); + if (err) + goto done; + + /* load fat_partition_create */ + fat_partition_create = (imgtoolerr_t (*)(imgtool::image &, uint64_t, uint64_t)) + imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_CREATE_PARTITION); + + /* actually create the partition */ + err = fat_partition_create(image, 0, ((uint64_t) tracks) * heads * sectors); + if (err) + goto done; + +done: + return err; +} + + + +static imgtoolerr_t fat_image_get_geometry(imgtool::image &image, uint32_t *tracks, uint32_t *heads, uint32_t *sectors) +{ + imgtoolerr_t err; + uint64_t total_sectors; + uint8_t buffer[FAT_SECLEN]; + + err = image.read_block(0, buffer); + if (err) + return err; + + total_sectors = pick_integer_le(buffer, 19, 2) + | (pick_integer_le(buffer, 32, 4) << 16); + + *sectors = pick_integer_le(buffer, 24, 2); + *heads = pick_integer_le(buffer, 26, 2); + *tracks = total_sectors / *heads / *sectors; + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t fat_get_sector_position(imgtool::image &image, uint32_t sector_index, + uint32_t *track, uint32_t *head, uint32_t *sector) +{ + imgtoolerr_t err; + uint32_t tracks, heads, sectors; + + if (sector_index == 0) + { + /* special case */ + *head = 0; + *track = 0; + *sector = 1; + } + else + { + err = image.get_geometry(&tracks, &heads, §ors); + if (err) + return err; + + *track = sector_index / sectors / heads; + *head = (sector_index / sectors) % heads; + *sector = 1 + (sector_index % sectors); + } + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t fat_image_readblock(imgtool::image &image, void *buffer, uint64_t block) +{ + imgtoolerr_t err; + floperr_t ferr; + uint32_t track, head, sector; + uint32_t block_size; + + err = image.get_block_size(block_size); + if (err) + return err; + + err = fat_get_sector_position(image, block, &track, &head, §or); + if (err) + return err; + + ferr = floppy_read_sector(imgtool_floppy(image), head, track, sector, 0, buffer, block_size); + if (ferr) + return imgtool_floppy_error(ferr); + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t fat_image_writeblock(imgtool::image &image, const void *buffer, uint64_t block) +{ + imgtoolerr_t err; + floperr_t ferr; + uint32_t track, head, sector; + uint32_t block_size; + + err = image.get_block_size(block_size); + if (err) + return err; + + err = fat_get_sector_position(image, block, &track, &head, §or); + if (err) + return err; + + ferr = floppy_write_sector(imgtool_floppy(image), head, track, sector, 0, buffer, block_size, 0); /* TODO: pass ddam argument from imgtool */ + if (ferr) + return imgtool_floppy_error(ferr); + return IMGTOOLERR_SUCCESS; +} + + + +void pc_floppy_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case IMGTOOLINFO_INT_BLOCK_SIZE: info->i = FAT_SECLEN; break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "fat"); break; + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "FAT format"); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case IMGTOOLINFO_PTR_MAKE_CLASS: info->make_class = imgtool_floppy_make_class; break; + case IMGTOOLINFO_PTR_FLOPPY_CREATE: info->create = fat_image_create; break; + case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_pc; break; + case IMGTOOLINFO_PTR_READ_BLOCK: info->read_block = fat_image_readblock; break; + case IMGTOOLINFO_PTR_WRITE_BLOCK: info->write_block = fat_image_writeblock; break; + case IMGTOOLINFO_PTR_GET_GEOMETRY: info->get_geometry = fat_image_get_geometry; break; + + default: fat_get_info(imgclass, state, info); break; + } +} diff --git a/src/tools/imgtool/modules/pc_hard.cpp b/src/tools/imgtool/modules/pc_hard.cpp new file mode 100644 index 0000000..c65db87 --- /dev/null +++ b/src/tools/imgtool/modules/pc_hard.cpp @@ -0,0 +1,470 @@ +// license:BSD-3-Clause +// copyright-holders:Raphael Nabet +/**************************************************************************** + + pc_hard.c + + PC hard drives + +***************************************************************************** + + Master boot record format: + + Offset Length Description + ------ ------ ----------- + 0 446 Boot machine code + 446 16 Partion #1 info + 462 16 Partion #2 info + 478 16 Partion #3 info + 494 16 Partion #4 info + 510 2 Magic bytes (0x55 0xAA) + + + Partition info format: + + Offset Length Description + ------ ------ ----------- + 0 1 Bootable (0x80=bootable 0x00=not bootable) + 1 1 Starting head + 2 1 Starting sector (bits 5-0) and high bits of starting track (bits 6-5) + 3 1 Low bits of starting track + 4 1 Partition type: + 0x00 Unused + 0x?1 FAT12 (0-15 MB) + 0x?2 XENIX + 0x?4 FAT16 (16-32 MB) + 0x?6 FAT16` (32 MB-2 GB) + 0x?7 HPFS or NTFS + 0x?A Boot Manager + 0x?B FAT32 (512 MB-2 TB) + 0x?C FAT32 (512 MB-2 TB LBA) + 0x1? OS/2 Boot manager/Win95 hidden + 0xC? DR-DOS secured partition + 0xD? Multiuser DOS secured partition + 0xE? SpeedStor extended partition + 5 1 Ending head + 6 1 Ending sector (bits 5-0) and high bits of ending track (bits 6-5) + 7 1 Low bits of ending track + 8 4 Sector index of beginning of partition + 12 4 Total sectors in partition + +****************************************************************************/ +#include "imgtool.h" +#include "formats/imageutl.h" +#include "imghd.h" + +#include "opresolv.h" + +#define FAT_SECLEN 512 + +OPTION_GUIDE_START( pc_chd_create_optionguide ) + OPTION_INT('T', "cylinders", "Cylinders" ) + OPTION_INT('H', "heads", "Heads" ) + OPTION_INT('S', "sectors", "Sectors" ) +OPTION_GUIDE_END + +static const char pc_chd_create_optionspec[] = "H1-[16];S1-[32]-63;T10/20/30/40/50/60/70/80/90/[100]/110/120/130/140/150/160/170/180/190/200"; + +static const char fat8_string[8] = { 'F', 'A', 'T', ' ', ' ', ' ', ' ', ' ' }; +static const char fat12_string[8] = { 'F', 'A', 'T', '1', '2', ' ', ' ', ' ' }; +static const char fat16_string[8] = { 'F', 'A', 'T', '1', '6', ' ', ' ', ' ' }; +//static const char fat32_string[8] = { 'F', 'A', 'T', '3', '2', ' ', ' ', ' ' }; + +/* imports from fat.c */ +extern void fat_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info); + + +struct pc_chd_image_info +{ + struct mess_hard_disk_file hard_disk; + + struct + { + unsigned int corrupt : 1; + uint8_t partition_type; + uint32_t fat_bits; + uint32_t starting_track; + uint32_t starting_head; + uint32_t starting_sector; + uint32_t ending_track; + uint32_t ending_head; + uint32_t ending_sector; + uint32_t sector_index; + uint32_t total_sectors; + } partitions[4]; +}; + + + +static pc_chd_image_info *pc_chd_get_image_info(imgtool::image &image) +{ + return (pc_chd_image_info *) image.extra_bytes(); +} + + + +static void pc_chd_locate_block(imgtool::image &image, uint64_t block, uint32_t *cylinder, uint32_t *head, uint32_t *sector) +{ + pc_chd_image_info *info; + const hard_disk_info *hd_info; + + info = pc_chd_get_image_info(image); + hd_info = imghd_get_header(&info->hard_disk); + + *sector = block % hd_info->sectors; + *head = (block / hd_info->sectors) % hd_info->heads; + *cylinder = block / hd_info->sectors / hd_info->heads; +} + + + +static imgtoolerr_t pc_chd_partition_create(imgtool::image &image, int partition_index, uint64_t first_block, uint64_t block_count) +{ + imgtoolerr_t err; + uint8_t header_block[FAT_SECLEN]; + uint8_t partition_block[FAT_SECLEN]; + uint8_t partition_type; + uint8_t *fat_type; + uint8_t *partition_entry; + uint32_t first_cylinder, first_head, first_sector; + uint32_t last_cylinder, last_head, last_sector; + imgtool_class imgclass = { fat_get_info }; + imgtoolerr_t (*fat_partition_create)(imgtool::image &image, uint64_t first_block, uint64_t block_count); + + /* sanity checks */ + assert((partition_index >= 0) && (partition_index <= 3)); + + /* compute geometry */ + pc_chd_locate_block(image, first_block, &first_cylinder, &first_head, &first_sector); + pc_chd_locate_block(image, first_block + block_count - 1, &last_cylinder, &last_head, &last_sector); + + /* load fat_partition_create */ + fat_partition_create = (imgtoolerr_t (*)(imgtool::image &, uint64_t, uint64_t)) + imgtool_get_info_fct(&imgclass, IMGTOOLINFO_PTR_CREATE_PARTITION); + + /* first create the actual partition */ + err = fat_partition_create(image, first_block, block_count); + if (err) + goto done; + + /* read the first block of the partition, to determine the type of FAT */ + err = image.read_block(first_block, partition_block); + if (err) + goto done; + fat_type = &partition_block[54]; + if (!memcmp(fat_type, fat8_string, sizeof(fat8_string))) + partition_type = 0x01; + else if (!memcmp(fat_type, fat12_string, sizeof(fat12_string))) + partition_type = 0x01; + else if ((!memcmp(fat_type, fat16_string, sizeof(fat16_string))) && (block_count < 32*1024*1024/FAT_SECLEN)) + partition_type = 0x04; + else if ((!memcmp(fat_type, fat16_string, sizeof(fat16_string))) && (block_count >= 32*1024*1024/FAT_SECLEN)) + partition_type = 0x06; + else + partition_type = 0x0B; + + /* read the partition header */ + err = image.read_block(0, header_block); + if (err) + goto done; + + /* fill out the partition entry */ + partition_entry = &header_block[446 + (partition_index * 16)]; + place_integer_le(partition_entry, 0, 1, 0x80); + place_integer_le(partition_entry, 1, 1, first_head); + place_integer_le(partition_entry, 2, 1, ((first_sector & 0x3F) | (first_cylinder >> 8 << 2))); + place_integer_le(partition_entry, 3, 1, first_cylinder); + place_integer_le(partition_entry, 4, 1, partition_type); + place_integer_le(partition_entry, 5, 1, last_head); + place_integer_le(partition_entry, 6, 1, ((last_sector & 0x3F) | (last_cylinder >> 8 << 2))); + place_integer_le(partition_entry, 7, 1, last_cylinder); + place_integer_le(partition_entry, 8, 4, first_block); + place_integer_le(partition_entry, 12, 4, block_count); + + /* write the partition header */ + err = image.write_block(0, header_block); + if (err) + goto done; + +done: + return err; +} + + + +static imgtoolerr_t pc_chd_read_partition_header(imgtool::image &image) +{ + imgtoolerr_t err; + const uint8_t *partition_info; + pc_chd_image_info *info; + uint8_t buffer[FAT_SECLEN]; + + info = pc_chd_get_image_info(image); + + /* read the initial block */ + err = image.read_block(0, buffer); + if (err) + return err; + + /* magic bytes present? */ + if ((buffer[510] != 0x55) || (buffer[511] != 0xAA)) + return IMGTOOLERR_CORRUPTIMAGE; + + for (int i = 0; i < std::size(info->partitions); i++) + { + partition_info = &buffer[446 + i * 16]; + + info->partitions[i].partition_type = partition_info[4]; + info->partitions[i].starting_head = partition_info[1]; + info->partitions[i].starting_track = ((partition_info[2] << 2) & 0xFF00) | partition_info[3]; + info->partitions[i].starting_sector = partition_info[2] & 0x3F; + info->partitions[i].ending_head = partition_info[5]; + info->partitions[i].ending_track = ((partition_info[6] << 2) & 0xFF00) | partition_info[7]; + info->partitions[i].ending_sector = partition_info[6] & 0x3F; + + info->partitions[i].sector_index = pick_integer_le(partition_info, 8, 4); + info->partitions[i].total_sectors = pick_integer_le(partition_info, 12, 4); + + if (info->partitions[i].starting_track > info->partitions[i].ending_track) + return IMGTOOLERR_CORRUPTIMAGE; + } + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t pc_chd_image_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *opts) +{ + imgtoolerr_t err; + uint32_t cylinders, heads, sectors; + pc_chd_image_info *info; + uint8_t header_block[FAT_SECLEN]; + + cylinders = opts->lookup_int('T'); + heads = opts->lookup_int('H'); + sectors = opts->lookup_int('S'); + + info = pc_chd_get_image_info(image); + + /* create the hard disk image */ + err = imghd_create(*stream, 0, cylinders, heads, sectors, FAT_SECLEN); + if (err) + goto done; + + err = imghd_open(*stream, &info->hard_disk); + if (err) + goto done; + + /* set up partition header block */ + memset(header_block, 0, sizeof(header_block)); + header_block[510] = 0x55; + header_block[511] = 0xAA; + err = image.write_block(0, header_block); + if (err) + goto done; + + err = pc_chd_partition_create(image, 0, 1, cylinders * heads * sectors - 1); + if (err) + goto done; + + err = pc_chd_read_partition_header(image); + if (err) + goto done; + +done: + if (err) + imghd_close(&info->hard_disk); + return err; +} + + + +static imgtoolerr_t pc_chd_image_open(imgtool::image &image, imgtool::stream::ptr &&stream) +{ + imgtoolerr_t err; + pc_chd_image_info *info; + + info = pc_chd_get_image_info(image); + + /* open the hard drive */ + err = imghd_open(*stream, &info->hard_disk); + if (err) + return err; + + err = pc_chd_read_partition_header(image); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + + + +static void pc_chd_image_close(imgtool::image &image) +{ + pc_chd_image_info *info; + info = pc_chd_get_image_info(image); + imghd_close(&info->hard_disk); +} + + + +static imgtoolerr_t pc_chd_image_get_geometry(imgtool::image &image, uint32_t *tracks, uint32_t *heads, uint32_t *sectors) +{ + pc_chd_image_info *info; + const hard_disk_info *hd_info; + + info = pc_chd_get_image_info(image); + hd_info = imghd_get_header(&info->hard_disk); + + *tracks = hd_info->cylinders; + *heads = hd_info->heads; + *sectors = hd_info->sectors; + return IMGTOOLERR_SUCCESS; +} + + + +static uint32_t pc_chd_calc_lbasector(pc_chd_image_info &info, uint32_t track, uint32_t head, uint32_t sector) +{ + uint32_t lbasector; + const hard_disk_info *hd_info; + + hd_info = imghd_get_header(&info.hard_disk); + lbasector = track; + lbasector *= hd_info->heads; + lbasector += head; + lbasector *= hd_info->sectors; + lbasector += sector; + return lbasector; +} + + + +static imgtoolerr_t pc_chd_image_readsector(imgtool::image &image, uint32_t track, uint32_t head, uint32_t sector, std::vector &buffer) +{ + pc_chd_image_info *info = pc_chd_get_image_info(image); + + // get the sector size and resize the buffer + uint32_t sector_size = imghd_get_header(&info->hard_disk)->sectorbytes; + try { buffer.resize(sector_size); } + catch (std::bad_alloc const &) { return IMGTOOLERR_OUTOFMEMORY; } + + // read the data + return imghd_read(&info->hard_disk, + pc_chd_calc_lbasector(*info, track, head, sector), + &buffer[0]); +} + + + +static imgtoolerr_t pc_chd_image_writesector(imgtool::image &image, uint32_t track, uint32_t head, uint32_t sector, const void *buffer, size_t len, int ddam) +{ + pc_chd_image_info *info; + info = pc_chd_get_image_info(image); + return imghd_write(&info->hard_disk, + pc_chd_calc_lbasector(*info, track, head, sector), + buffer); +} + + + +static imgtoolerr_t pc_chd_image_readblock(imgtool::image &image, void *buffer, uint64_t block) +{ + pc_chd_image_info *info; + info = pc_chd_get_image_info(image); + return imghd_read(&info->hard_disk, block, buffer); +} + + + +static imgtoolerr_t pc_chd_image_writeblock(imgtool::image &image, const void *buffer, uint64_t block) +{ + pc_chd_image_info *info; + info = pc_chd_get_image_info(image); + return imghd_write(&info->hard_disk, block, buffer); +} + + + +static imgtoolerr_t pc_chd_list_partitions(imgtool::image &image, std::vector &partitions) +{ + pc_chd_image_info *info; + size_t i; + + info = pc_chd_get_image_info(image); + + for (i = 0; i < 4; i++) + { + // what type of partition is this? + imgtool_get_info partition_get_info; + switch(info->partitions[i].partition_type) + { + case 0x00: /* Empty Partition */ + partition_get_info = nullptr; + break; + + case 0x01: /* FAT12 */ + case 0x04: /* FAT16 (-32 MB) */ + case 0x06: /* FAT16 (32+ MB) */ + case 0x0B: /* FAT32 */ + case 0x0C: /* FAT32 (LBA Mapped) */ + case 0x0E: /* FAT16 (LBA Mapped) */ + case 0x11: /* OS/2 FAT12 */ + case 0x14: /* OS/2 FAT16 (-32 MB) */ + case 0x16: /* OS/2 FAT16 (32+ MB) */ + case 0x1B: /* Hidden Win95 FAT32 */ + case 0x1C: /* Hidden Win95 FAT32 (LBA Mapped) */ + case 0x1D: /* Hidden Win95 FAT16 (LBA Mapped) */ + case 0xC1: /* DR-DOS FAT12 */ + case 0xC4: /* DR-DOS FAT16 (-32 MB) */ + case 0xC6: /* DR-DOS FAT16 (32+ MB) */ + case 0xD1: /* Old Multiuser DOS FAT12 */ + case 0xD4: /* Old Multiuser DOS FAT16 (-32 MB) */ + case 0xD6: /* Old Multiuser DOS FAT16 (32+ MB) */ + partition_get_info = fat_get_info; + break; + + default: + partition_get_info = unknown_partition_get_info; + break; + } + + partitions.emplace_back( + partition_get_info, + info->partitions[i].sector_index, + info->partitions[i].total_sectors); + } + return IMGTOOLERR_SUCCESS; +} + + + +void pc_chd_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case IMGTOOLINFO_INT_BLOCK_SIZE: info->i = FAT_SECLEN; break; + case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(pc_chd_image_info); break; + case IMGTOOLINFO_INT_TRACKS_ARE_CALLED_CYLINDERS: info->i = 1; break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "pc_chd"); break; + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "PC CHD disk image"); break; + case IMGTOOLINFO_STR_FILE_EXTENSIONS: strcpy(info->s = imgtool_temp_str(), "chd"); break; + case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC: strcpy(info->s = imgtool_temp_str(), pc_chd_create_optionspec); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case IMGTOOLINFO_PTR_CREATE: info->create = pc_chd_image_create; break; + case IMGTOOLINFO_PTR_OPEN: info->open = pc_chd_image_open; break; + case IMGTOOLINFO_PTR_CLOSE: info->close = pc_chd_image_close; break; + case IMGTOOLINFO_PTR_READ_SECTOR: info->read_sector = pc_chd_image_readsector; break; + case IMGTOOLINFO_PTR_WRITE_SECTOR: info->write_sector = pc_chd_image_writesector; break; + case IMGTOOLINFO_PTR_READ_BLOCK: info->read_block = pc_chd_image_readblock; break; + case IMGTOOLINFO_PTR_WRITE_BLOCK: info->write_block = pc_chd_image_writeblock; break; + case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE: info->createimage_optguide = &pc_chd_create_optionguide; break; + case IMGTOOLINFO_PTR_GET_GEOMETRY: info->get_geometry = pc_chd_image_get_geometry; break; + case IMGTOOLINFO_PTR_LIST_PARTITIONS: info->list_partitions = pc_chd_list_partitions; break; + } +} diff --git a/src/tools/imgtool/modules/prodos.cpp b/src/tools/imgtool/modules/prodos.cpp new file mode 100644 index 0000000..e196d53 --- /dev/null +++ b/src/tools/imgtool/modules/prodos.cpp @@ -0,0 +1,2270 @@ +// license:BSD-3-Clause +// copyright-holders:Raphael Nabet +/**************************************************************************** + + prodos.cpp + + Apple II ProDOS disk images + +***************************************************************************** + + Notes: + - ProDOS disks are split into 512 byte blocks. + + ProDOS directory structure: + + Offset Length Description + ------ ------ ----------- + 0 2 ??? + 2 2 Next block (0 if end) + 4 39 Directory Entry + 43 39 Directory Entry + ... + 472 39 Directory Entry + 511 1 ??? + + + ProDOS directory entry structure: + + Offset Length Description + ------ ------ ----------- + 0 1 Storage type (bits 7-4) + 10 - Seedling File (1 block) + 20 - Sapling File (2-256 blocks) + 30 - Tree File (257-32768 blocks) + 40 - Pascal Areas on ProFile HDs (???) + 50 - GS/OS Extended File (data and rsrc fork) + E0 - Subdirectory Header + F0 - Volume Header + 1 15 File name (NUL padded) + 16 1 File type + 17 2 Key pointer + 19 2 Blocks used + 21 3 File size + 24 4 Creation date + 28 1 ProDOS version that created the file + 29 1 Minimum ProDOS version needed to read the file + 30 1 Access byte + 31 2 Auxilary file type + 33 4 Last modified date + 37 2 Header pointer + + + In "seedling files", the key pointer points to a single block that is the + whole file. In "sapling files", the key pointer points to an index block + that contains 256 2-byte index pointers that point to the actual blocks of + the files. These 2-byte values are not contiguous; the low order byte is + in the first half of the block, and the high order byte is in the second + half of the block. In "tree files", the key pointer points to an index + block of index blocks. + + ProDOS dates are 32-bit little endian values + + bits 0- 4 Day + bits 5- 8 Month (0-11) + bits 9-15 Year (0-49 is 2000-2049, 50-99 is 1950-1999) + bits 16-21 Minute + bits 24-28 Hour + + + ProDOS directory and volume headers have this information: + + Offset Length Description + ------ ------ ----------- + 31 1 Length of the entry; generally is 39 + 32 1 Number of entries per block; generally is 13 + 33 2 Active entry count in directory + 35 2 Volume bitmap block number + 37 2 Total blocks on volume + + GS/OS Extended Files (storage type $5) point to an extended key block that + contains information about the two forks. The first half of the extended + key block contains info about the data form, and the second half the + resource fork. Both sides have the following format: + + Offset Length Description + ------ ------ ----------- + 0 1 Storage type (bits 3-0, unlike the directory entry) + 1 2 Key pointer + 3 2 Blocks used + 5 3 File size + 8 1 Size of secondary info #1 (must be 18 to be valid) + 9 1 Secondary info #1 type (1=FInfo 2=xFInfo) + 10 16 FInfo or xFInfo + 26 1 Size of secondary info #2 (must be 18 to be valid) + 27 1 Secondary info #2 type (1=FInfo 2=xFInfo) + 28 16 FInfo or xFInfo + + FInfo format: + + Offset Length Description + ------ ------ ----------- + 0 4 File type + 4 4 File creator + 8 2 Finder flags + 10 2 X Coordinate + 12 2 Y Coordinate + 14 2 Finder folder + + xFInfo format: + + Offset Length Description + ------ ------ ----------- + 0 2 Icon ID + 2 6 Reserved + 8 1 Script Code + 9 1 Extended flags + 10 2 Comment ID + 12 4 Put Away Directory + + + For more info, consult ProDOS technical note #25 + (http://web.pdx.edu/~heiss/technotes/pdos/tn.pdos.25.html) + +*****************************************************************************/ + +#include "imgtool.h" +#include "formats/imageutl.h" +#include "formats/ap2_dsk.h" +#include "formats/ap_dsk35.h" +#include "iflopimg.h" + +#include "macutil.h" + +#include "opresolv.h" + +#define ROOTDIR_BLOCK 2 +#define BLOCK_SIZE 512 + +struct prodos_diskinfo +{ + imgtoolerr_t (*load_block)(imgtool::image &image, int block, void *buffer); + imgtoolerr_t (*save_block)(imgtool::image &image, int block, const void *buffer); + uint8_t dirent_size; + uint8_t dirents_per_block; + uint16_t volume_bitmap_block; + uint16_t total_blocks; +}; + +struct prodos_direnum +{ + uint32_t block; + uint32_t index; + uint8_t block_data[BLOCK_SIZE]; +}; + +struct prodos_dirent +{ + char filename[16]; + uint8_t storage_type; + uint16_t extkey_pointer; + uint16_t key_pointer[2]; + uint32_t filesize[2]; + int depth[2]; + uint32_t lastmodified_time; + uint32_t creation_time; + + /* FInfo */ + uint32_t file_type; + uint32_t file_creator; + uint16_t finder_flags; + uint16_t coord_x; + uint16_t coord_y; + uint16_t finder_folder; + + /* xFInfo */ + uint16_t icon_id; + uint8_t script_code; + uint8_t extended_flags; + uint16_t comment_id; + uint32_t putaway_directory; +}; + +enum creation_policy_t +{ + CREATE_NONE, + CREATE_FILE, + CREATE_DIR +}; + + + +static imgtool::datetime prodos_crack_time(uint32_t prodos_time) +{ + util::arbitrary_datetime dt; + dt.second = 0; + dt.minute = ((prodos_time >> 16) & 0x3F); + dt.hour = ((prodos_time >> 24) & 0x1F); + dt.day_of_month = ((prodos_time >> 0) & 0x1F); + dt.month = ((prodos_time >> 5) & 0x0F) + 1; + dt.year = ((prodos_time >> 9) & 0x7F) + 1900; + if (dt.year <= 1949) + dt.year += 100; + + return imgtool::datetime(imgtool::datetime::datetime_type::LOCAL, dt); +} + + + +static uint32_t prodos_setup_time(time_t ansi_time) +{ + struct tm t; + uint32_t result = 0; + + t = *localtime(&ansi_time); + if ((t.tm_year >= 100) && (t.tm_year <= 149)) + t.tm_year -= 100; + + result |= (((uint32_t) t.tm_min) & 0x003F) << 16; + result |= (((uint32_t) t.tm_hour) & 0x001F) << 24; + result |= (((uint32_t) t.tm_mday) & 0x001F) << 0; + result |= (((uint32_t) t.tm_mon) & 0x000F) << 5; + result |= (((uint32_t) t.tm_year) & 0x007F) << 9; + return result; +} + + + +static uint32_t prodos_time_now(void) +{ + time_t now; + time(&now); + return prodos_setup_time(now); +} + + + +static int is_file_storagetype(uint8_t storage_type) +{ + return ((storage_type >= 0x10) && (storage_type <= 0x3F)) + || ((storage_type >= 0x50) && (storage_type <= 0x5F)); +} + + + +static int is_normalfile_storagetype(uint8_t storage_type) +{ + return ((storage_type >= 0x10) && (storage_type <= 0x3F)); +} + + + +static int is_extendedfile_storagetype(uint8_t storage_type) +{ + return ((storage_type >= 0x50) && (storage_type <= 0x5F)); +} + + + +static int is_dir_storagetype(uint8_t storage_type) +{ + return (storage_type >= 0xE0) && (storage_type <= 0xEF); +} + + + +static prodos_diskinfo *get_prodos_info(imgtool::image &image) +{ + prodos_diskinfo *info; + info = (prodos_diskinfo *) imgtool_floppy_extrabytes(image); + return info; +} + + + +/* ----------------------------------------------------------------------- */ + +static void prodos_find_block_525(imgtool::image &image, int block, + uint32_t *track, uint32_t *head, uint32_t *sector1, uint32_t *sector2) +{ + static const uint8_t skewing[] = + { + 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, + 0x01, 0x03, 0x05, 0x07, 0x09, 0x0B, 0x0D, 0x0F + }; + + block *= 2; + + *track = block / APPLE2_SECTOR_COUNT; + *head = 0; + *sector1 = skewing[block % APPLE2_SECTOR_COUNT + 0]; + *sector2 = skewing[block % APPLE2_SECTOR_COUNT + 1]; +} + + + +static imgtoolerr_t prodos_load_block_525(imgtool::image &image, + int block, void *buffer) +{ + floperr_t ferr; + uint32_t track, head, sector1, sector2; + + prodos_find_block_525(image, block, &track, &head, §or1, §or2); + + /* read first sector */ + ferr = floppy_read_sector(imgtool_floppy(image), head, track, + sector1, 0, ((uint8_t *) buffer) + 0, 256); + if (ferr) + return imgtool_floppy_error(ferr); + + /* read second sector */ + ferr = floppy_read_sector(imgtool_floppy(image), head, track, + sector2, 0, ((uint8_t *) buffer) + 256, 256); + if (ferr) + return imgtool_floppy_error(ferr); + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_save_block_525(imgtool::image &image, + int block, const void *buffer) +{ + floperr_t ferr; + uint32_t track, head, sector1, sector2; + + prodos_find_block_525(image, block, &track, &head, §or1, §or2); + + /* read first sector */ + ferr = floppy_write_sector(imgtool_floppy(image), head, track, + sector1, 0, ((const uint8_t *) buffer) + 0, 256, 0); /* TODO: pass ddam argument from imgtool */ + if (ferr) + return imgtool_floppy_error(ferr); + + /* read second sector */ + ferr = floppy_write_sector(imgtool_floppy(image), head, track, + sector2, 0, ((const uint8_t *) buffer) + 256, 256, 0); /* TODO: pass ddam argument from imgtool */ + if (ferr) + return imgtool_floppy_error(ferr); + + return IMGTOOLERR_SUCCESS; +} + + + +static void prodos_setprocs_525(imgtool::image &image) +{ + prodos_diskinfo *info; + info = get_prodos_info(image); + info->load_block = prodos_load_block_525; + info->save_block = prodos_save_block_525; +} + + + +/* ----------------------------------------------------------------------- */ + +static imgtoolerr_t prodos_find_block_35(imgtool::image &image, int block, + uint32_t *track, uint32_t *head, uint32_t *sector) +{ + int sides = 2; + + *track = 0; + while(block >= (apple35_sectors_per_track(imgtool_floppy(image), *track) * sides)) + { + block -= (apple35_sectors_per_track(imgtool_floppy(image), (*track)++) * sides); + if (*track >= 80) + return IMGTOOLERR_SEEKERROR; + } + + *head = block / apple35_sectors_per_track(imgtool_floppy(image), *track); + *sector = block % apple35_sectors_per_track(imgtool_floppy(image), *track); + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_load_block_35(imgtool::image &image, + int block, void *buffer) +{ + imgtoolerr_t err; + floperr_t ferr; + uint32_t track, head, sector; + + err = prodos_find_block_35(image, block, &track, &head, §or); + if (err) + return err; + + ferr = floppy_read_sector(imgtool_floppy(image), head, track, sector, 0, buffer, 512); + if (ferr) + return imgtool_floppy_error(ferr); + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_save_block_35(imgtool::image &image, + int block, const void *buffer) +{ + imgtoolerr_t err; + floperr_t ferr; + uint32_t track, head, sector; + + err = prodos_find_block_35(image, block, &track, &head, §or); + if (err) + return err; + + ferr = floppy_write_sector(imgtool_floppy(image), head, track, sector, 0, buffer, 512, 0); /* TODO: pass ddam argument from imgtool */ + if (ferr) + return imgtool_floppy_error(ferr); + + return IMGTOOLERR_SUCCESS; +} + + + +static void prodos_setprocs_35(imgtool::image &image) +{ + prodos_diskinfo *info; + info = get_prodos_info(image); + info->load_block = prodos_load_block_35; + info->save_block = prodos_save_block_35; +} + + + +/* ----------------------------------------------------------------------- */ + +static imgtoolerr_t prodos_load_block(imgtool::image &image, + int block, void *buffer) +{ + prodos_diskinfo *diskinfo; + diskinfo = get_prodos_info(image); + return diskinfo->load_block(image, block, buffer); +} + + + +static imgtoolerr_t prodos_save_block(imgtool::image &image, + int block, const void *buffer) +{ + prodos_diskinfo *diskinfo; + diskinfo = get_prodos_info(image); + return diskinfo->save_block(image, block, buffer); +} + + + +static imgtoolerr_t prodos_clear_block(imgtool::image &image, int block) +{ + uint8_t buffer[BLOCK_SIZE]; + memset(buffer, 0, sizeof(buffer)); + return prodos_save_block(image, block, buffer); +} + + + +/* ----------------------------------------------------------------------- */ + +static imgtoolerr_t prodos_diskimage_open(imgtool::image &image) +{ + imgtoolerr_t err; + uint8_t buffer[BLOCK_SIZE]; + prodos_diskinfo *di; + const uint8_t *ent; + + di = get_prodos_info(image); + + /* specify defaults */ + di->dirent_size = 39; + di->dirents_per_block = 13; + + /* load the first block, hoping that the volume header is first */ + err = prodos_load_block(image, ROOTDIR_BLOCK, buffer); + if (err) + return err; + + ent = &buffer[4]; + + /* did we find the volume header? */ + if ((ent[0] & 0xF0) == 0xF0) + { + di->dirent_size = pick_integer_le(ent, 31, 1); + di->dirents_per_block = pick_integer_le(ent, 32, 1); + di->volume_bitmap_block = pick_integer_le(ent, 35, 2); + di->total_blocks = pick_integer_le(ent, 37, 2); + } + + /* sanity check these values */ + if (di->dirent_size < 39) + return IMGTOOLERR_CORRUPTIMAGE; + if (di->dirents_per_block * di->dirent_size >= BLOCK_SIZE) + return IMGTOOLERR_CORRUPTIMAGE; + if (di->volume_bitmap_block >= di->total_blocks) + return IMGTOOLERR_CORRUPTIMAGE; + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_diskimage_open_525(imgtool::image &image, imgtool::stream::ptr &&dummy) +{ + prodos_setprocs_525(image); + return prodos_diskimage_open(image); +} + + + +static imgtoolerr_t prodos_diskimage_open_35(imgtool::image &image, imgtool::stream::ptr &&dummy) +{ + prodos_setprocs_35(image); + return prodos_diskimage_open(image); +} + + + +/* ----------------------------------------------------------------------- */ + +static imgtoolerr_t prodos_load_volume_bitmap(imgtool::image &image, uint8_t **bitmap) +{ + imgtoolerr_t err; + prodos_diskinfo *di; + uint8_t *alloc_bitmap; + uint32_t bitmap_blocks, i; + + di = get_prodos_info(image); + + bitmap_blocks = (di->total_blocks + (BLOCK_SIZE * 8) - 1) / (BLOCK_SIZE * 8); + alloc_bitmap = (uint8_t*)malloc(bitmap_blocks * BLOCK_SIZE); + if (!alloc_bitmap) + { + err = IMGTOOLERR_OUTOFMEMORY; + goto done; + } + + for (i = 0; i < bitmap_blocks; i++) + { + err = prodos_load_block(image, di->volume_bitmap_block + i, + &alloc_bitmap[i * BLOCK_SIZE]); + if (err) + goto done; + } + + err = IMGTOOLERR_SUCCESS; + +done: + if (err && alloc_bitmap) + { + free(alloc_bitmap); + alloc_bitmap = NULL; + } + *bitmap = alloc_bitmap; + return err; +} + + + +static imgtoolerr_t prodos_save_volume_bitmap(imgtool::image &image, const uint8_t *bitmap) +{ + imgtoolerr_t err; + prodos_diskinfo *di; + uint32_t bitmap_blocks, i; + + di = get_prodos_info(image); + + bitmap_blocks = (di->total_blocks + (BLOCK_SIZE * 8) - 1) / (BLOCK_SIZE * 8); + + for (i = 0; i < bitmap_blocks; i++) + { + err = prodos_save_block(image, di->volume_bitmap_block + i, + &bitmap[i * BLOCK_SIZE]); + if (err) + return err; + } + return IMGTOOLERR_SUCCESS; +} + + + +static void prodos_set_volume_bitmap_bit(uint8_t *buffer, uint16_t block, int value) +{ + uint8_t mask; + buffer += block / 8; + mask = 1 << (7 - (block % 8)); + if (value) + *buffer |= mask; + else + *buffer &= ~mask; +} + + + +static int prodos_get_volume_bitmap_bit(const uint8_t *buffer, uint16_t block) +{ + uint8_t mask; + buffer += block / 8; + mask = 1 << (7 - (block % 8)); + return (*buffer & mask) ? 1 : 0; +} + + + +static imgtoolerr_t prodos_alloc_block(imgtool::image &image, uint8_t *bitmap, + uint16_t *block) +{ + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + prodos_diskinfo *di; + uint16_t bitmap_blocks, i; + uint8_t *alloc_bitmap = NULL; + + di = get_prodos_info(image); + *block = 0; + bitmap_blocks = (di->total_blocks + (BLOCK_SIZE * 8) - 1) / (BLOCK_SIZE * 8); + + if (!bitmap) + { + err = prodos_load_volume_bitmap(image, &alloc_bitmap); + if (err) + goto done; + bitmap = alloc_bitmap; + } + + for (i = (di->volume_bitmap_block + bitmap_blocks); i < di->total_blocks; i++) + { + if (!prodos_get_volume_bitmap_bit(bitmap, i)) + { + prodos_set_volume_bitmap_bit(bitmap, i, 1); + *block = i; + break; + } + } + + if (*block > 0) + { + if (alloc_bitmap) + { + err = prodos_save_volume_bitmap(image, bitmap); + if (err) + goto done; + } + } + else + { + err = IMGTOOLERR_NOSPACE; + } + +done: + if (err) + *block = 0; + if (alloc_bitmap) + free(alloc_bitmap); + return err; +} + + + +/* ----------------------------------------------------------------------- */ + +static imgtoolerr_t prodos_diskimage_create(imgtool::image &image, util::option_resolution *opts) +{ + imgtoolerr_t err; + uint32_t heads, tracks, sectors, sector_bytes; + uint32_t dirent_size, volume_bitmap_block, i; + uint32_t volume_bitmap_block_count, total_blocks; + uint8_t buffer[BLOCK_SIZE]; + + heads = opts->lookup_int('H'); + tracks = opts->lookup_int('T'); + sectors = opts->lookup_int('S'); + sector_bytes = opts->lookup_int('L'); + + dirent_size = 39; + volume_bitmap_block = 6; + total_blocks = tracks * heads * sectors * sector_bytes / BLOCK_SIZE; + volume_bitmap_block_count = (total_blocks + (BLOCK_SIZE * 8) - 1) / (BLOCK_SIZE * 8); + + /* prepare initial dir block */ + memset(buffer, 0, sizeof(buffer)); + place_integer_le(buffer, 4 + 0, 1, 0xF0); + place_integer_le(buffer, 4 + 31, 1, dirent_size); + place_integer_le(buffer, 4 + 32, 1, BLOCK_SIZE / dirent_size); + place_integer_le(buffer, 4 + 35, 2, volume_bitmap_block); + place_integer_le(buffer, 4 + 37, 2, total_blocks); + + err = prodos_save_block(image, ROOTDIR_BLOCK, buffer); + if (err) + return err; + + /* setup volume bitmap */ + memset(buffer, 0, sizeof(buffer)); + for (i = 0; i < (volume_bitmap_block + volume_bitmap_block_count); i++) + prodos_set_volume_bitmap_bit(buffer, i, 1); + prodos_save_block(image, volume_bitmap_block, buffer); + + /* and finally open the image */ + return prodos_diskimage_open(image); +} + + + +static imgtoolerr_t prodos_diskimage_create_525(imgtool::image &image, imgtool::stream::ptr &&dummy, util::option_resolution *opts) +{ + prodos_setprocs_525(image); + return prodos_diskimage_create(image, opts); +} + + + +static imgtoolerr_t prodos_diskimage_create_35(imgtool::image &image, imgtool::stream::ptr &&dummy, util::option_resolution *opts) +{ + prodos_setprocs_35(image); + return prodos_diskimage_create(image, opts); +} + + + +/* ----------------------------------------------------------------------- */ + +static imgtoolerr_t prodos_enum_seek(imgtool::image &image, + prodos_direnum *appleenum, uint32_t block, uint32_t index) +{ + imgtoolerr_t err; + uint8_t buffer[BLOCK_SIZE]; + + if (appleenum->block != block) + { + if (block != 0) + { + err = prodos_load_block(image, block, buffer); + if (err) + return err; + memcpy(appleenum->block_data, buffer, sizeof(buffer)); + } + appleenum->block = block; + } + + appleenum->index = index; + return IMGTOOLERR_SUCCESS; +} + + + +static uint8_t *next_info_block(uint8_t *buffer, size_t *position) +{ + size_t side = *position & 0x100; + size_t subpos = *position & 0x0FF; + uint8_t *result; + + if (subpos < 8) + { + subpos = 8; + *position = side + subpos; + } + + while((buffer[side + subpos] == 0x00) || (subpos + buffer[side + subpos] > 0x100)) + { + if (side) + return NULL; + + side = 0x100; + subpos = 8; + } + + result = &buffer[side + subpos]; + subpos += *result; + *position = side + subpos; + return result; +} + + + +static uint8_t *alloc_info_block(uint8_t *buffer, size_t block_size, uint8_t block_type) +{ + size_t position = 0; + size_t side; + size_t subpos; + uint8_t *result; + + while(next_info_block(buffer, &position)) + ; + + side = position & 0x100; + subpos = position & 0x0FF; + + if ((subpos + block_size) > 0x100) + return NULL; + + result = &buffer[side + subpos]; + *(result++) = (uint8_t) block_size; + *(result++) = block_type; + memset(result, 0, block_size - 2); + return result; +} + + + +static imgtoolerr_t prodos_get_next_dirent(imgtool::image &image, + prodos_direnum *appleenum, prodos_dirent &ent) +{ + imgtoolerr_t err; + prodos_diskinfo *di; + size_t finfo_offset; + uint32_t next_block, next_index; + uint32_t offset; + uint8_t buffer[BLOCK_SIZE]; + const uint8_t *info_ptr; + int fork_num; + + di = get_prodos_info(image); + memset(&ent, 0, sizeof(ent)); + + /* have we hit the end of the file? */ + if (appleenum->block == 0) + return IMGTOOLERR_SUCCESS; + + /* populate the resulting dirent */ + offset = appleenum->index * di->dirent_size + 4; + ent.storage_type = appleenum->block_data[offset + 0]; + memcpy(ent.filename, &appleenum->block_data[offset + 1], 15); + ent.filename[15] = '\0'; + ent.creation_time = pick_integer_le(appleenum->block_data, offset + 24, 4); + ent.lastmodified_time = pick_integer_le(appleenum->block_data, offset + 33, 4); + ent.file_type = 0x3F3F3F3F; + ent.file_creator = 0x3F3F3F3F; + ent.finder_flags = 0; + ent.coord_x = 0; + ent.coord_y = 0; + ent.finder_folder = 0; + ent.icon_id = 0; + ent.script_code = 0; + ent.extended_flags = 0; + ent.comment_id = 0; + ent.putaway_directory = 0; + + if (is_extendedfile_storagetype(ent.storage_type)) + { + /* this is a ProDOS extended file; we need to get the extended info + * block */ + ent.extkey_pointer = pick_integer_le(appleenum->block_data, offset + 17, 2); + + err = prodos_load_block(image, ent.extkey_pointer, buffer); + if (err) + return err; + + for (fork_num = 0; fork_num <= 1; fork_num++) + { + ent.key_pointer[fork_num] = pick_integer_le(buffer, 1 + (fork_num * 256), 2); + ent.filesize[fork_num] = pick_integer_le(buffer, 5 + (fork_num * 256), 3); + ent.depth[fork_num] = buffer[fork_num * 256] & 0x0F; + } + + finfo_offset = 0; + while((info_ptr = next_info_block(buffer, &finfo_offset)) != NULL) + { + if (*(info_ptr++) == 18) + { + switch(*(info_ptr++)) + { + case 1: /* FInfo */ + ent.file_type = pick_integer_be(info_ptr, 0, 4); + ent.file_creator = pick_integer_be(info_ptr, 4, 4); + ent.finder_flags = pick_integer_be(info_ptr, 8, 2); + ent.coord_x = pick_integer_be(info_ptr, 10, 2); + ent.coord_y = pick_integer_be(info_ptr, 12, 2); + ent.finder_folder = pick_integer_be(info_ptr, 14, 4); + break; + + case 2: /* xFInfo */ + ent.icon_id = pick_integer_be(info_ptr, 0, 2); + ent.script_code = pick_integer_be(info_ptr, 8, 1); + ent.extended_flags = pick_integer_be(info_ptr, 9, 1); + ent.comment_id = pick_integer_be(info_ptr, 10, 2); + ent.putaway_directory = pick_integer_be(info_ptr, 12, 4); + break; + } + } + } + } + else + { + /* normal ProDOS files have all of the info right here */ + ent.key_pointer[0] = pick_integer_le(appleenum->block_data, offset + 17, 2); + ent.filesize[0] = pick_integer_le(appleenum->block_data, offset + 21, 3); + ent.depth[0] = ent.storage_type >> 4; + } + + /* identify next entry */ + next_block = appleenum->block; + next_index = appleenum->index + 1; + if (next_index >= di->dirents_per_block) + { + next_block = pick_integer_le(appleenum->block_data, 2, 2); + next_index = 0; + } + + /* seek next block */ + err = prodos_enum_seek(image, appleenum, next_block, next_index); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + + + +/* changes a normal file to a ProDOS extended file */ +static imgtoolerr_t prodos_promote_file(imgtool::image &image, uint8_t *bitmap, prodos_dirent *ent) +{ + imgtoolerr_t err; + uint16_t new_block; + uint8_t buffer[BLOCK_SIZE]; + + assert(is_normalfile_storagetype(ent->storage_type)); + + err = prodos_alloc_block(image, bitmap, &new_block); + if (err) + return err; + + /* create raw extended info block */ + memset(buffer, 0, sizeof(buffer)); + err = prodos_save_block(image, new_block, buffer); + if (err) + return err; + + ent->storage_type = (ent->storage_type & 0x0F) | 0x50; + ent->extkey_pointer = new_block; + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_put_dirent(imgtool::image &image, + prodos_direnum *appleenum, prodos_dirent *ent) +{ + imgtoolerr_t err; + prodos_diskinfo *di; + uint32_t offset; + size_t finfo_offset; + uint8_t buffer[BLOCK_SIZE]; + int fork_num; + int needs_finfo = false; + int needs_xfinfo = false; + uint8_t *info_ptr; + uint8_t *finfo; + uint8_t *xfinfo; + + di = get_prodos_info(image); + offset = appleenum->index * di->dirent_size + 4; + + /* determine whether we need FInfo and/or xFInfo */ + if (is_normalfile_storagetype(ent->storage_type)) + { + needs_finfo = (ent->file_type != 0x3F3F3F3F) || + (ent->file_creator != 0x3F3F3F3F) || + (ent->finder_flags != 0) || + (ent->coord_x != 0) || + (ent->coord_y != 0) || + (ent->finder_folder != 0); + + needs_xfinfo = (ent->icon_id != 0) || + (ent->script_code != 0) || + (ent->extended_flags != 0) || + (ent->comment_id != 0) || + (ent->putaway_directory != 0); + } + + /* do we need to promote this file to an extended file? */ + if (!is_extendedfile_storagetype(ent->storage_type) + && (needs_finfo || needs_xfinfo)) + { + err = prodos_promote_file(image, NULL, ent); + if (err) + return err; + } + + /* write out the storage type, filename, creation and lastmodified times */ + appleenum->block_data[offset + 0] = ent->storage_type; + memcpy(&appleenum->block_data[offset + 1], ent->filename, 15); + place_integer_le(appleenum->block_data, offset + 24, 4, ent->creation_time); + place_integer_le(appleenum->block_data, offset + 33, 4, ent->lastmodified_time); + + if (is_extendedfile_storagetype(ent->storage_type)) + { + /* ProDOS extended file */ + err = prodos_load_block(image, ent->extkey_pointer, buffer); + if (err) + return err; + + finfo = NULL; + xfinfo = NULL; + + for (fork_num = 0; fork_num <= 1; fork_num++) + { + place_integer_le(buffer, 1 + (fork_num * 256), 2, ent->key_pointer[fork_num]); + place_integer_le(buffer, 5 + (fork_num * 256), 3, ent->filesize[fork_num]); + buffer[fork_num * 256] = ent->depth[fork_num]; + } + + finfo_offset = 0; + while((info_ptr = next_info_block(buffer, &finfo_offset)) != NULL) + { + if (*(info_ptr++) == 18) + { + switch(*(info_ptr++)) + { + case 1: /* FInfo */ + finfo = info_ptr; + break; + + case 2: /* xFInfo */ + xfinfo = info_ptr; + break; + } + } + } + + /* allocate the finfo and/or xinfo blocks, if we need them */ + if (needs_finfo && !finfo) + finfo = alloc_info_block(buffer, 18, 1); + if (needs_xfinfo && !xfinfo) + xfinfo = alloc_info_block(buffer, 18, 2); + + if (finfo) + { + place_integer_be(finfo, 0, 4, ent->file_type); + place_integer_be(finfo, 4, 4, ent->file_creator); + place_integer_be(finfo, 8, 2, ent->finder_flags); + place_integer_be(finfo, 10, 2, ent->coord_x); + place_integer_be(finfo, 12, 2, ent->coord_y); + place_integer_be(finfo, 14, 4, ent->finder_folder); + } + + if (xfinfo) + { + place_integer_be(xfinfo, 0, 2, ent->icon_id); + place_integer_be(xfinfo, 8, 1, ent->script_code); + place_integer_be(xfinfo, 9, 1, ent->extended_flags); + place_integer_be(xfinfo, 10, 2, ent->comment_id); + place_integer_be(xfinfo, 12, 4, ent->putaway_directory); + } + + err = prodos_save_block(image, ent->extkey_pointer, buffer); + if (err) + return err; + + place_integer_le(appleenum->block_data, offset + 17, 2, ent->extkey_pointer); + place_integer_le(appleenum->block_data, offset + 21, 3, BLOCK_SIZE); + } + else + { + /* normal file */ + place_integer_le(appleenum->block_data, offset + 17, 2, ent->key_pointer[0]); + place_integer_le(appleenum->block_data, offset + 21, 3, ent->filesize[0]); + } + + err = prodos_save_block(image, appleenum->block, appleenum->block_data); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_lookup_path(imgtool::image &image, const char *path, + creation_policy_t create, prodos_direnum *direnum, prodos_dirent *ent) +{ + imgtoolerr_t err; + prodos_direnum my_direnum; + uint32_t block = ROOTDIR_BLOCK; + const char *old_path; + uint16_t this_block; + uint32_t this_index; + uint16_t free_block = 0; + uint32_t free_index = 0; + uint16_t new_file_block; + uint8_t buffer[BLOCK_SIZE]; + + if (!direnum) + direnum = &my_direnum; + + while(*path) + { + memset(direnum, 0, sizeof(*direnum)); + err = prodos_enum_seek(image, direnum, block, 0); + if (err) + goto done; + + do + { + this_block = direnum->block; + this_index = direnum->index; + + err = prodos_get_next_dirent(image, direnum, *ent); + if (err) + goto done; + + /* if we need to create a file entry and this is free, track it */ + if (create && this_block && !free_block && !ent->storage_type) + { + free_block = this_block; + free_index = this_index; + } + } + while(direnum->block && (strcmp(path, ent->filename) || ( + !is_file_storagetype(ent->storage_type) && + !is_dir_storagetype(ent->storage_type)))); + + old_path = path; + path += strlen(path) + 1; + if (*path) + { + /* we have found part of the path; we are not finished yet */ + if (!is_dir_storagetype(ent->storage_type)) + { + err = IMGTOOLERR_FILENOTFOUND; + goto done; + } + block = ent->key_pointer[0]; + } + else if (!direnum->block) + { + /* did not find file; maybe we need to create it */ + if (create == CREATE_NONE) + { + err = IMGTOOLERR_FILENOTFOUND; + goto done; + } + + /* do we need to expand the directory? */ + if (!free_block) + { + if (this_block == 0) + { + err = IMGTOOLERR_CORRUPTFILE; + goto done; + } + + err = prodos_load_block(image, this_block, buffer); + if (err) + goto done; + + /* allocate a block */ + err = prodos_alloc_block(image, NULL, &free_block); + if (err) + goto done; + + /* clear out this new block */ + err = prodos_clear_block(image, free_block); + if (err) + goto done; + + /* save this link */ + place_integer_le(buffer, 2, 2, free_block); + err = prodos_save_block(image, this_block, buffer); + if (err) + goto done; + + free_index = 0; + } + + /* seek back to the free space */ + err = prodos_enum_seek(image, direnum, free_block, free_index); + if (err) + goto done; + + new_file_block = 0; + if (create == CREATE_DIR) + { + /* if we are creating a directory, we need to create a new block */ + err = prodos_alloc_block(image, NULL, &new_file_block); + if (err) + goto done; + + err = prodos_clear_block(image, new_file_block); + if (err) + goto done; + } + + /* prepare the dirent */ + memset(ent, 0, sizeof(*ent)); + ent->storage_type = (create == CREATE_DIR) ? 0xe0 : 0x10; + ent->creation_time = ent->lastmodified_time = prodos_time_now(); + ent->key_pointer[0] = new_file_block; + ent->file_type = 0x3F3F3F3F; + ent->file_creator = 0x3F3F3F3F; + strncpy(ent->filename, old_path, std::size(ent->filename)); + + /* and place it */ + err = prodos_put_dirent(image, direnum, ent); + if (err) + goto done; + + this_block = free_block; + this_index = free_index; + } + else + { + /* we've found the file; seek that dirent */ + err = prodos_enum_seek(image, direnum, this_block, this_index); + if (err) + goto done; + } + } + + err = IMGTOOLERR_SUCCESS; +done: + return err; +} + + + +static imgtoolerr_t prodos_fill_file(imgtool::image &image, uint8_t *bitmap, + uint16_t key_block, int key_block_allocated, + int depth, uint32_t blockcount, uint32_t block_index) +{ + imgtoolerr_t err; + prodos_diskinfo *di; + int dirty; + int sub_block_allocated; + uint16_t i, sub_block, new_sub_block; + uint8_t buffer[BLOCK_SIZE]; + + di = get_prodos_info(image); + + if (key_block_allocated) + { + /* we are on a recently allocated key block; start fresh */ + memset(buffer, 0, sizeof(buffer)); + dirty = true; + } + else + { + /* this is a preexisting key block */ + err = prodos_load_block(image, key_block, buffer); + if (err) + return err; + dirty = false; + } + + for (i = 0; i < 256; i++) + { + sub_block_allocated = false; + + sub_block = buffer[i + 256]; + sub_block <<= 8; + sub_block += buffer[i + 0]; + + new_sub_block = sub_block; + if ((block_index < blockcount) && (sub_block == 0)) + { + err = prodos_alloc_block(image, bitmap, &new_sub_block); + if (err) + return err; + sub_block_allocated = true; + } + else if ((block_index >= blockcount) && (sub_block != 0)) + { + new_sub_block = 0; + if (sub_block < di->total_blocks) + prodos_set_volume_bitmap_bit(bitmap, sub_block, 0); + } + + /* did we change the block? */ + if (new_sub_block != sub_block) + { + dirty = true; + buffer[i + 0] = new_sub_block >> 0; + buffer[i + 256] = new_sub_block >> 8; + if (sub_block == 0) + sub_block = new_sub_block; + } + + /* call recursive function */ + if (depth > 2) + { + err = prodos_fill_file(image, bitmap, sub_block, sub_block_allocated, depth - 1, blockcount, block_index); + if (err) + return err; + } + + /* increment index */ + block_index += 1 << ((depth - 2) * 8); + } + + /* if we changed anything, then save the block */ + if (dirty) + { + err = prodos_save_block(image, key_block, buffer); + if (err) + return err; + } + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_set_file_block_count(imgtool::image &image, prodos_direnum *direnum, + prodos_dirent *ent, uint8_t *bitmap, int fork_num, uint32_t new_blockcount) +{ + imgtoolerr_t err; + int depth, new_depth, i; + uint16_t new_block, block; + uint8_t buffer[BLOCK_SIZE]; + uint16_t key_pointer; + + if (fork_num && (new_blockcount > 0) && !is_extendedfile_storagetype(ent->storage_type)) + { + /* need to change a normal file to an extended file */ + err = prodos_promote_file(image, bitmap, ent); + if (err) + return err; + } + + key_pointer = ent->key_pointer[fork_num]; + depth = ent->depth[fork_num]; + + /* determine the new tree depth */ + if (new_blockcount <= 1) + new_depth = 1; + else if (new_blockcount <= 256) + new_depth = 2; + else + new_depth = 3; + + /* are we zero length, and do we have to create a block? */ + if ((new_blockcount >= 1) && (key_pointer == 0)) + { + err = prodos_alloc_block(image, bitmap, &new_block); + if (err) + return err; + key_pointer = new_block; + } + + /* do we have to grow the tree? */ + while(new_depth > depth) + { + err = prodos_alloc_block(image, bitmap, &new_block); + if (err) + return err; + + /* create this new key block, with a link to the previous one */ + memset(buffer, 0, sizeof(buffer)); + buffer[0] = (uint8_t) (key_pointer >> 0); + buffer[256] = (uint8_t) (key_pointer >> 8); + err = prodos_save_block(image, new_block, buffer); + if (err) + return err; + + depth++; + key_pointer = new_block; + } + + /* do we have to shrink the tree? */ + while(new_depth < depth) + { + err = prodos_load_block(image, key_pointer, buffer); + if (err) + return err; + + for (i = 1; i < 256; i++) + { + block = buffer[i + 256]; + block <<= 8; + block |= buffer[i + 0]; + + if (block > 0) + { + if (depth > 2) + { + /* remove this block's children */ + err = prodos_fill_file(image, bitmap, block, false, depth - 1, 0, 0); + if (err) + return err; + } + + /* and remove this block */ + prodos_set_volume_bitmap_bit(bitmap, block, 0); + } + } + + /* remove this key block */ + prodos_set_volume_bitmap_bit(bitmap, key_pointer, 0); + + /* set the new key pointer */ + block = buffer[256]; + block <<= 8; + block |= buffer[0]; + key_pointer = block; + + depth--; + } + + if (new_blockcount > 0) + { + /* fill out the file tree */ + err = prodos_fill_file(image, bitmap, key_pointer, false, depth, new_blockcount, 0); + if (err) + return err; + } + else if (key_pointer != 0) + { + /* we are now zero length, and don't need a key pointer */ + prodos_set_volume_bitmap_bit(bitmap, key_pointer, 0); + key_pointer = 0; + } + + /* change the depth if we are not an extended file */ + if (is_normalfile_storagetype(ent->storage_type)) + { + ent->storage_type &= ~0xF0; + ent->storage_type |= depth * 0x10; + } + + ent->key_pointer[fork_num] = key_pointer; + ent->depth[fork_num] = depth; + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_set_file_size(imgtool::image &image, prodos_direnum *direnum, + prodos_dirent *ent, int fork_num, uint32_t new_size) +{ + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + uint32_t blockcount, new_blockcount; + uint8_t *bitmap = NULL; + + if (ent->filesize[fork_num] != new_size) + { + blockcount = (ent->filesize[fork_num] + BLOCK_SIZE - 1) / BLOCK_SIZE; + new_blockcount = (new_size + BLOCK_SIZE - 1) / BLOCK_SIZE; + + /* do we need to change the block chain? */ + if (new_blockcount != blockcount) + { + err = prodos_load_volume_bitmap(image, &bitmap); + if (err) + goto done; + + err = prodos_set_file_block_count(image, direnum, ent, bitmap, fork_num, new_blockcount); + if (err) + goto done; + + err = prodos_save_volume_bitmap(image, bitmap); + if (err) + goto done; + } + + ent->filesize[fork_num] = new_size; + err = prodos_put_dirent(image, direnum, ent); + if (err) + goto done; + } + +done: + if (bitmap) + free(bitmap); + return err; +} + + + +static uint32_t prodos_get_storagetype_maxfilesize(uint8_t storage_type) +{ + uint32_t max_filesize = 0; + switch(storage_type & 0xF0) + { + case 0x10: + max_filesize = BLOCK_SIZE * 1; + break; + case 0x20: + max_filesize = BLOCK_SIZE * 256; + break; + case 0x30: + case 0x50: + max_filesize = BLOCK_SIZE * 32768; + break; + } + return max_filesize; +} + + + +static imgtoolerr_t prodos_diskimage_beginenum(imgtool::directory &enumeration, const char *path) +{ + imgtoolerr_t err; + imgtool::image &image(enumeration.image()); + prodos_direnum *appleenum; + prodos_dirent ent; + uint16_t block = ROOTDIR_BLOCK; + + appleenum = (prodos_direnum *) enumeration.extra_bytes(); + + /* find subdirectory, if appropriate */ + if (*path) + { + err = prodos_lookup_path(image, path, CREATE_NONE, NULL, &ent); + if (err) + return err; + + /* only work on directories */ + if (!is_dir_storagetype(ent.storage_type)) + return IMGTOOLERR_FILENOTFOUND; + + block = ent.key_pointer[0]; + } + + /* seek initial block */ + err = prodos_enum_seek(image, appleenum, block, 0); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_diskimage_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent) +{ + imgtoolerr_t err; + imgtool::image &image(enumeration.image()); + prodos_direnum *appleenum; + prodos_dirent pd_ent; + uint32_t max_filesize; + + appleenum = (prodos_direnum *) enumeration.extra_bytes(); + + do + { + err = prodos_get_next_dirent(image, appleenum, pd_ent); + if (err) + return err; + } + while(appleenum->block + && !is_file_storagetype(pd_ent.storage_type) + && !is_dir_storagetype(pd_ent.storage_type)); + + /* end of file? */ + if (pd_ent.storage_type == 0x00) + { + ent.eof = 1; + return IMGTOOLERR_SUCCESS; + } + + strcpy(ent.filename, pd_ent.filename); + ent.directory = is_dir_storagetype(pd_ent.storage_type); + ent.creation_time = prodos_crack_time(pd_ent.creation_time); + ent.lastmodified_time = prodos_crack_time(pd_ent.lastmodified_time); + + if (!ent.directory) + { + ent.filesize = pd_ent.filesize[0]; + + max_filesize = prodos_get_storagetype_maxfilesize(pd_ent.storage_type); + if (ent.filesize > max_filesize) + { + ent.corrupt = 1; + ent.filesize = max_filesize; + } + } + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_read_file_tree(imgtool::image &image, uint32_t *filesize, + uint32_t block, int nest_level, imgtool::stream &destf) +{ + imgtoolerr_t err; + prodos_diskinfo *di; + uint8_t buffer[BLOCK_SIZE]; + uint16_t sub_block; + size_t bytes_to_write; + int i; + + /* check bounds */ + di = get_prodos_info(image); + if (block >= di->total_blocks) + return IMGTOOLERR_CORRUPTFILE; + + err = prodos_load_block(image, block, buffer); + if (err) + return err; + + if (nest_level > 0) + { + /* this is an index block */ + for (i = 0; i < 256; i++) + { + /* retrieve the block pointer; the two bytes are on either half + * of the block */ + sub_block = buffer[i + 256]; + sub_block <<= 8; + sub_block |= buffer[i + 0]; + + if (sub_block != 0) + { + err = prodos_read_file_tree(image, filesize, sub_block, nest_level - 1, destf); + if (err) + return err; + } + } + } + else + { + /* this is a leaf block */ + bytes_to_write = std::min(size_t(*filesize), sizeof(buffer)); + destf.write(buffer, bytes_to_write); + *filesize -= bytes_to_write; + } + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_write_file_tree(imgtool::image &image, uint32_t *filesize, + uint32_t block, int nest_level, imgtool::stream &sourcef) +{ + imgtoolerr_t err; + prodos_diskinfo *di; + uint8_t buffer[BLOCK_SIZE]; + uint16_t sub_block; + size_t bytes_to_read; + int i; + + /* nothing more to read? bail */ + if (*filesize == 0) + return IMGTOOLERR_SUCCESS; + + /* check bounds */ + di = get_prodos_info(image); + if (block >= di->total_blocks) + return IMGTOOLERR_CORRUPTFILE; + + err = prodos_load_block(image, block, buffer); + if (err) + return err; + + if (nest_level > 0) + { + for (i = 0; i < 256; i++) + { + sub_block = buffer[i + 256]; + sub_block <<= 8; + sub_block |= buffer[i + 0]; + + if (sub_block != 0) + { + err = prodos_write_file_tree(image, filesize, sub_block, nest_level - 1, sourcef); + if (err) + return err; + } + } + } + else + { + /* this is a leaf block */ + bytes_to_read = std::min(size_t(*filesize), sizeof(buffer)); + sourcef.read(buffer, bytes_to_read); + *filesize -= bytes_to_read; + + err = prodos_save_block(image, block, buffer); + if (err) + return err; + } + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_diskimage_freespace(imgtool::partition &partition, uint64_t *size) +{ + imgtoolerr_t err; + imgtool::image &image(partition.image()); + prodos_diskinfo *di; + uint8_t *bitmap = NULL; + uint16_t i; + + di = get_prodos_info(image); + *size = 0; + + err = prodos_load_volume_bitmap(image, &bitmap); + if (err) + goto done; + + for (i = 0; i < di->total_blocks; i++) + { + if (!prodos_get_volume_bitmap_bit(bitmap, i)) + *size += BLOCK_SIZE; + } + +done: + if (bitmap) + free(bitmap); + return err; +} + + + +static imgtoolerr_t prodos_diskimage_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf) +{ + imgtoolerr_t err; + imgtool::image &image(partition.image()); + prodos_dirent ent; + uint16_t key_pointer; + int nest_level; + mac_fork_t fork_num; + + err = prodos_lookup_path(image, filename, CREATE_NONE, NULL, &ent); + if (err) + return err; + + if (is_dir_storagetype(ent.storage_type)) + return IMGTOOLERR_FILENOTFOUND; + + err = mac_identify_fork(fork, &fork_num); + if (err) + return err; + + key_pointer = ent.key_pointer[fork_num]; + nest_level = ent.depth[fork_num] - 1; + + if (key_pointer != 0) + { + err = prodos_read_file_tree(image, &ent.filesize[fork_num], key_pointer, + nest_level, destf); + if (err) + return err; + } + + /* have we not actually received the correct amount of bytes? if not, fill in the rest */ + if (ent.filesize[fork_num] > 0) + destf.fill(0, ent.filesize[fork_num]); + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_diskimage_writefile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts) +{ + imgtoolerr_t err; + imgtool::image &image(partition.image()); + prodos_dirent ent; + prodos_direnum direnum; + uint64_t file_size; + mac_fork_t fork_num; + + file_size = sourcef.size(); + + err = prodos_lookup_path(image, filename, CREATE_FILE, &direnum, &ent); + if (err) + return err; + + /* only work on files */ + if (is_dir_storagetype(ent.storage_type)) + return IMGTOOLERR_FILENOTFOUND; + + err = mac_identify_fork(fork, &fork_num); + if (err) + return err; + + /* set the file size */ + err = prodos_set_file_size(image, &direnum, &ent, fork_num, file_size); + if (err) + return err; + + err = prodos_write_file_tree(image, &ent.filesize[fork_num], ent.key_pointer[fork_num], + ent.depth[fork_num] - 1, sourcef); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_diskimage_deletefile(imgtool::partition &partition, const char *path) +{ + imgtoolerr_t err; + imgtool::image &image(partition.image()); + prodos_dirent ent; + prodos_direnum direnum; + + err = prodos_lookup_path(image, path, CREATE_NONE, &direnum, &ent); + if (err) + return err; + + /* only work on files */ + if (is_dir_storagetype(ent.storage_type)) + return IMGTOOLERR_FILENOTFOUND; + + /* empty out both forks */ + err = prodos_set_file_size(image, &direnum, &ent, 0, 0); + if (err) + return err; + err = prodos_set_file_size(image, &direnum, &ent, 1, 0); + if (err) + return err; + + memset(&ent, 0, sizeof(ent)); + err = prodos_put_dirent(image, &direnum, &ent); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_diskimage_listforks(imgtool::partition &partition, const char *path, std::vector &forks) +{ + imgtoolerr_t err; + imgtool::image &image(partition.image()); + prodos_dirent ent; + prodos_direnum direnum; + + err = prodos_lookup_path(image, path, CREATE_NONE, &direnum, &ent); + if (err) + return err; + + if (is_dir_storagetype(ent.storage_type)) + return IMGTOOLERR_FILENOTFOUND; + + // specify data fork + forks.emplace_back(ent.filesize[0], imgtool::fork_entry::type_t::DATA); + + if (is_extendedfile_storagetype(ent.storage_type)) + { + // specify the resource fork + forks.emplace_back(ent.filesize[1], imgtool::fork_entry::type_t::RESOURCE); + } + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_diskimage_createdir(imgtool::partition &partition, const char *path) +{ + imgtoolerr_t err; + imgtool::image &image(partition.image()); + prodos_dirent ent; + prodos_direnum direnum; + + err = prodos_lookup_path(image, path, CREATE_DIR, &direnum, &ent); + if (err) + return err; + + /* only work on directories */ + if (!is_dir_storagetype(ent.storage_type)) + return IMGTOOLERR_FILENOTFOUND; + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_free_directory(imgtool::image &image, uint8_t *volume_bitmap, uint16_t key_pointer) +{ + imgtoolerr_t err; + prodos_diskinfo *di; + int i; + uint16_t next_block; + uint32_t offset; + uint8_t buffer[BLOCK_SIZE]; + + di = get_prodos_info(image); + + if (key_pointer != 0) + { + err = prodos_load_block(image, key_pointer, buffer); + if (err) + return err; + + for (i = 0; i < di->dirents_per_block; i++) + { + offset = i * di->dirent_size + 4; + + if (is_file_storagetype(buffer[offset]) || is_file_storagetype(buffer[offset])) + return IMGTOOLERR_DIRNOTEMPTY; + } + + next_block = pick_integer_le(buffer, 2, 2); + + err = prodos_free_directory(image, volume_bitmap, next_block); + if (err) + return err; + + prodos_set_volume_bitmap_bit(volume_bitmap, key_pointer, 0); + } + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_diskimage_deletedir(imgtool::partition &partition, const char *path) +{ + imgtoolerr_t err; + imgtool::image &image(partition.image()); + prodos_dirent ent; + prodos_direnum direnum; + uint8_t *volume_bitmap = NULL; + + err = prodos_lookup_path(image, path, CREATE_NONE, &direnum, &ent); + if (err) + goto done; + + /* only work on directories */ + if (!is_dir_storagetype(ent.storage_type)) + { + err = IMGTOOLERR_FILENOTFOUND; + goto done; + } + + err = prodos_load_volume_bitmap(image, &volume_bitmap); + if (err) + goto done; + + err = prodos_free_directory(image, volume_bitmap, ent.key_pointer[0]); + if (err) + goto done; + + err = prodos_save_volume_bitmap(image, volume_bitmap); + if (err) + goto done; + + memset(&ent, 0, sizeof(ent)); + err = prodos_put_dirent(image, &direnum, &ent); + if (err) + goto done; + +done: + if (volume_bitmap) + free(volume_bitmap); + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_get_file_tree(imgtool::image &image, imgtool_chainent *chain, size_t chain_size, + size_t *chain_pos, uint16_t block, uint8_t total_depth, uint8_t cur_depth) +{ + imgtoolerr_t err; + prodos_diskinfo *di; + int i; + uint16_t sub_block; + uint8_t buffer[BLOCK_SIZE]; + + if (block == 0) + return IMGTOOLERR_SUCCESS; + if (*chain_pos >= chain_size) + return IMGTOOLERR_SUCCESS; + + /* check bounds */ + di = get_prodos_info(image); + if (block >= di->total_blocks) + return IMGTOOLERR_CORRUPTFILE; + + chain[*chain_pos].level = cur_depth; + chain[*chain_pos].block = block; + (*chain_pos)++; + + /* must we recurse into the tree? */ + if (cur_depth < total_depth) + { + err = prodos_load_block(image, block, buffer); + if (err) + return err; + + for (i = 0; i < 256; i++) + { + sub_block = buffer[i + 256]; + sub_block <<= 8; + sub_block |= buffer[i + 0]; + + err = prodos_get_file_tree(image, chain, chain_size, chain_pos, sub_block, total_depth, cur_depth + 1); + if (err) + return err; + } + } + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_diskimage_getattrs(imgtool::partition &partition, const char *path, const uint32_t *attrs, imgtool_attribute *values) +{ + imgtoolerr_t err; + imgtool::image &image(partition.image()); + prodos_dirent ent; + int i; + + err = prodos_lookup_path(image, path, CREATE_NONE, NULL, &ent); + if (err) + return err; + + for (i = 0; attrs[i]; i++) + { + switch(attrs[i]) + { + case IMGTOOLATTR_INT_MAC_TYPE: + values[i].i = ent.file_type; + break; + case IMGTOOLATTR_INT_MAC_CREATOR: + values[i].i = ent.file_creator; + break; + case IMGTOOLATTR_INT_MAC_FINDERFLAGS: + values[i].i = ent.finder_flags; + break; + case IMGTOOLATTR_INT_MAC_COORDX: + values[i].i = ent.coord_x; + break; + case IMGTOOLATTR_INT_MAC_COORDY: + values[i].i = ent.coord_y; + break; + case IMGTOOLATTR_INT_MAC_FINDERFOLDER: + values[i].i = ent.finder_folder; + break; + case IMGTOOLATTR_INT_MAC_ICONID: + values[i].i = ent.icon_id; + break; + case IMGTOOLATTR_INT_MAC_SCRIPTCODE: + values[i].i = ent.script_code; + break; + case IMGTOOLATTR_INT_MAC_EXTENDEDFLAGS: + values[i].i = ent.extended_flags; + break; + case IMGTOOLATTR_INT_MAC_COMMENTID: + values[i].i = ent.comment_id; + break; + case IMGTOOLATTR_INT_MAC_PUTAWAYDIRECTORY: + values[i].i = ent.putaway_directory; + break; + + case IMGTOOLATTR_TIME_CREATED: + values[i].t = prodos_crack_time(ent.creation_time).to_time_t(); + break; + case IMGTOOLATTR_TIME_LASTMODIFIED: + values[i].t = prodos_crack_time(ent.lastmodified_time).to_time_t(); + break; + } + } + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_diskimage_setattrs(imgtool::partition &partition, const char *path, const uint32_t *attrs, const imgtool_attribute *values) +{ + imgtoolerr_t err; + imgtool::image &image(partition.image()); + prodos_dirent ent; + prodos_direnum direnum; + int i; + + err = prodos_lookup_path(image, path, CREATE_NONE, &direnum, &ent); + if (err) + return err; + + for (i = 0; attrs[i]; i++) + { + switch(attrs[i]) + { + case IMGTOOLATTR_INT_MAC_TYPE: + ent.file_type = values[i].i; + break; + case IMGTOOLATTR_INT_MAC_CREATOR: + ent.file_creator = values[i].i; + break; + case IMGTOOLATTR_INT_MAC_FINDERFLAGS: + ent.finder_flags = values[i].i; + break; + case IMGTOOLATTR_INT_MAC_COORDX: + ent.coord_x = values[i].i; + break; + case IMGTOOLATTR_INT_MAC_COORDY: + ent.coord_y = values[i].i; + break; + case IMGTOOLATTR_INT_MAC_FINDERFOLDER: + ent.finder_folder = values[i].i; + break; + case IMGTOOLATTR_INT_MAC_ICONID: + ent.icon_id = values[i].i; + break; + case IMGTOOLATTR_INT_MAC_SCRIPTCODE: + ent.script_code = values[i].i; + break; + case IMGTOOLATTR_INT_MAC_EXTENDEDFLAGS: + ent.extended_flags = values[i].i; + break; + case IMGTOOLATTR_INT_MAC_COMMENTID: + ent.comment_id = values[i].i; + break; + case IMGTOOLATTR_INT_MAC_PUTAWAYDIRECTORY: + ent.putaway_directory = values[i].i; + break; + + case IMGTOOLATTR_TIME_CREATED: + ent.creation_time = prodos_setup_time(values[i].t); + break; + case IMGTOOLATTR_TIME_LASTMODIFIED: + ent.lastmodified_time = prodos_setup_time(values[i].t); + break; + } + } + + err = prodos_put_dirent(image, &direnum, &ent); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_diskimage_suggesttransfer(imgtool::partition &partition, const char *path, imgtool_transfer_suggestion *suggestions, size_t suggestions_length) +{ + imgtoolerr_t err; + imgtool::image &image(partition.image()); + prodos_dirent ent; + mac_filecategory_t file_category = MAC_FILECATEGORY_DATA; + + if (path) + { + err = prodos_lookup_path(image, path, CREATE_NONE, NULL, &ent); + if (err) + return err; + + file_category = is_extendedfile_storagetype(ent.storage_type) + ? MAC_FILECATEGORY_FORKED : MAC_FILECATEGORY_DATA; + } + + mac_suggest_transfer(file_category, suggestions, suggestions_length); + return IMGTOOLERR_SUCCESS; +} + + + +static imgtoolerr_t prodos_diskimage_getchain(imgtool::partition &partition, const char *path, imgtool_chainent *chain, size_t chain_size) +{ + imgtoolerr_t err; + imgtool::image &image(partition.image()); + prodos_dirent ent; + size_t chain_pos = 0; + int fork_num; + + err = prodos_lookup_path(image, path, CREATE_NONE, NULL, &ent); + if (err) + return err; + + switch(ent.storage_type & 0xF0) + { + case 0x10: + case 0x20: + case 0x30: + /* normal ProDOS file */ + err = prodos_get_file_tree(image, chain, chain_size, &chain_pos, + ent.key_pointer[0], ent.depth[0] - 1, 0); + if (err) + return err; + break; + + case 0x50: + /* extended ProDOS file */ + chain[chain_pos].level = 0; + chain[chain_pos].block = ent.extkey_pointer; + chain_pos++; + + for (fork_num = 0; fork_num <= 1; fork_num++) + { + if (ent.key_pointer[fork_num]) + { + err = prodos_get_file_tree(image, chain, chain_size, &chain_pos, + ent.key_pointer[fork_num], ent.depth[fork_num] - 1, 1); + if (err) + return err; + } + } + break; + + case 0xE0: + /* directory */ + return IMGTOOLERR_UNIMPLEMENTED; + + default: + return IMGTOOLERR_UNEXPECTED; + } + + return IMGTOOLERR_SUCCESS; +} + + + +static void generic_prodos_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case IMGTOOLINFO_INT_INITIAL_PATH_SEPARATOR: info->i = 1; break; + case IMGTOOLINFO_INT_OPEN_IS_STRICT: info->i = 1; break; + case IMGTOOLINFO_INT_SUPPORTS_CREATION_TIME: info->i = 1; break; + case IMGTOOLINFO_INT_SUPPORTS_LASTMODIFIED_TIME: info->i = 1; break; + case IMGTOOLINFO_INT_WRITING_UNTESTED: info->i = 1; break; + case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(prodos_diskinfo); break; + case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(prodos_direnum); break; + case IMGTOOLINFO_INT_PATH_SEPARATOR: info->i = '/'; break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "ProDOS format"); break; + case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break; + case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), "\r"); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case IMGTOOLINFO_PTR_MAKE_CLASS: info->make_class = imgtool_floppy_make_class; break; + case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = prodos_diskimage_beginenum; break; + case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = prodos_diskimage_nextenum; break; + case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = prodos_diskimage_freespace; break; + case IMGTOOLINFO_PTR_READ_FILE: info->read_file = prodos_diskimage_readfile; break; + case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = prodos_diskimage_writefile; break; + case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = prodos_diskimage_deletefile; break; + case IMGTOOLINFO_PTR_LIST_FORKS: info->list_forks = prodos_diskimage_listforks; break; + case IMGTOOLINFO_PTR_CREATE_DIR: info->create_dir = prodos_diskimage_createdir; break; + case IMGTOOLINFO_PTR_DELETE_DIR: info->delete_dir = prodos_diskimage_deletedir; break; + case IMGTOOLINFO_PTR_GET_ATTRS: info->get_attrs = prodos_diskimage_getattrs; break; + case IMGTOOLINFO_PTR_SET_ATTRS: info->set_attrs = prodos_diskimage_setattrs; break; + case IMGTOOLINFO_PTR_SUGGEST_TRANSFER: info->suggest_transfer = prodos_diskimage_suggesttransfer; break; + case IMGTOOLINFO_PTR_GET_CHAIN: info->get_chain = prodos_diskimage_getchain; break; + } +} + + + +void prodos_525_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "prodos_525"); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case IMGTOOLINFO_PTR_FLOPPY_CREATE: info->create = prodos_diskimage_create_525; break; + case IMGTOOLINFO_PTR_FLOPPY_OPEN: info->open = prodos_diskimage_open_525; break; + case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_apple2; break; + + default: generic_prodos_get_info(imgclass, state, info); break; + } +} + + + +void prodos_35_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "prodos_35"); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case IMGTOOLINFO_PTR_FLOPPY_CREATE: info->create = prodos_diskimage_create_35; break; + case IMGTOOLINFO_PTR_FLOPPY_OPEN: info->open = prodos_diskimage_open_35; break; + case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_apple35_iigs; break; + + default: generic_prodos_get_info(imgclass, state, info); break; + } +} diff --git a/src/tools/imgtool/modules/psion.cpp b/src/tools/imgtool/modules/psion.cpp new file mode 100644 index 0000000..0076500 --- /dev/null +++ b/src/tools/imgtool/modules/psion.cpp @@ -0,0 +1,698 @@ +// license:BSD-3-Clause +// copyright-holders:Sandro Ronco +/*************************************************************************** + + Psion Organiser II Datapack + + 08/18/2010 Sandro Ronco + + Known file types: + 0x00 invalid record + 0x01 - 0x7e deleted record + 0x7f invalid deleted record + 0x80 long record + 0x81 file name + 0x82 diary + 0x83 OPL/OB3 procedure + 0x84 RS232 setup + 0x85 - 0x8f reserved + 0x90 MAIN Record + 0x91 - 0xfe data records from files + 0xff invalid record + +****************************************************************************/ + +#include "imgtool.h" + +#include "opresolv.h" + +#include + +#define MAXFILES 256 + +struct psion_file +{ + char filename[9]; + uint8_t type; + uint8_t id; + + uint16_t name_rec; + uint16_t data_rec; +}; + +struct psion_pack +{ + imgtool::stream *stream; + + uint16_t eop; + struct psion_file pack_index[MAXFILES]; +}; + +struct psion_iter +{ + uint16_t index; +}; + +static psion_pack *get_psion_pack(imgtool::image &image) +{ + return (psion_pack*)image.extra_bytes(); +} + +uint16_t head_checksum(uint8_t* data) +{ + uint16_t checksum = 0; + + for (int i=0; i<6; i+=2) + checksum += (data[i]<<8 | data[i+1]); + + return checksum; +} + +uint16_t get_long_rec_size(imgtool::stream &stream) +{ + uint8_t size_h, size_l; + + stream.read(&size_h, 1); + stream.read(&size_l, 1); + + return (size_h<<8) | size_l; +} + +uint32_t update_pack_index(psion_pack *pack) +{ + uint8_t data, type; + uint16_t size; + uint16_t index = 0; + + memset(pack->pack_index, 0, sizeof(psion_file) * MAXFILES); + + // start at the first record + pack->stream->seek(0x10, SEEK_SET); + + do + { + pack->stream->read(&data, 1); + + if(data == 0xff) + { + pack->eop = pack->stream->tell() - 1; + return true; + } + else if (data == 0x02) + { + // long record without name are ignored + pack->stream->read(&data, 1); + size = get_long_rec_size(*pack->stream); + pack->stream->seek(size, SEEK_CUR); + } + else + { + pack->stream->read(&type, 1); + + // deleted record are not listed + if (type < 0x90 && (type & 0x80)) + { + pack->pack_index[index].type = type; + pack->stream->read(&pack->pack_index[index].filename, 8); + pack->stream->read(&pack->pack_index[index].id, 1); + pack->pack_index[index].name_rec = pack->stream->tell() - 11; + + //check for data record + pack->stream->read(&data, 1); + if (data == 0x02) + pack->pack_index[index].data_rec = pack->stream->tell() - 1; + + pack->stream->seek(-1, SEEK_CUR); + + index++; + } + else + pack->stream->seek(data, SEEK_CUR); + } + + } while (pack->stream->size() > pack->stream->tell()); + + // corrupted image + return false; +} + +int seek_next_record(imgtool::stream &stream, uint8_t id) +{ + uint8_t data, rec_id; + uint16_t size; + + do + { + stream.read(&data, 1); + + if(data == 0xff) + break; + + if (data == 2) + { + stream.read(&rec_id, 1); + size = get_long_rec_size(stream); + + } + else + { + stream.read(&rec_id, 1); + if (id == rec_id) + { + stream.seek(-2, SEEK_CUR); + return true; + } + size = data; + } + + // next record + stream.seek(size, SEEK_CUR); + + } while (stream.size() > stream.tell()); + + return false; +} + +// if there are multiple files with the same name, only the first is found +int seek_file_name(psion_pack *pack, const char *filename) +{ + uint16_t index = 0; + + while (pack->pack_index[index].name_rec) + { + if (!strncmp(filename, pack->pack_index[index].filename, strlen(filename))) + return index; + index++; + } + + // filename not found + return -1; +} + +uint8_t get_free_file_id(psion_pack *pack) +{ + for (uint8_t file_id=0x91; file_id<0xff; file_id++) + { + int index = 0; + + while (pack->pack_index[index].id != file_id) + if (pack->pack_index[index++].name_rec == 0) + return file_id; + } + + return 0xff; +} + +static void put_name_record(imgtool::stream &stream, const char* filename, uint8_t record_type, uint8_t record_id) +{ + char data[0x10]; + int i = 0; + + data[i++] = 0x09; + data[i++] = record_type; + + // filename is 8 char long space filled + for (int j=0; j<8; j++) + if (j < strlen(filename)) + data[i++] = filename[j]; + else + data[i++] = 0x20; + + data[i++] = record_id; + + stream.write(data, i); +} + +static void update_opk_head(imgtool::stream &stream) +{ + uint16_t size = stream.size() - 6; + + stream.seek(4, SEEK_SET); + stream.putc((size>>8) & 0xff); + stream.putc(size & 0xff); +} + +char *stream_getline(imgtool::stream &source, uint16_t max_len) +{ + uint16_t pos = 0; + char data; + char *line = (char*)malloc(max_len); + memset(line, 0, max_len); + + while (pos < max_len && source.size() > source.tell()) + { + source.read(&data, 1); + + switch(data) + { + case '\r': + source.read(&data, 1); + if (data != '\n') + source.seek(-1, SEEK_CUR); + [[fallthrough]]; + case '\n': + return line; + default: + line[pos++] = data; + break; + } + } + + if (pos) + return line; + + free(line); + return NULL; +} + +uint16_t put_odb(imgtool::stream &instream, imgtool::stream &outstream, uint8_t file_id) +{ + char *line; + uint16_t out_size = 0; + + // reset stream + instream.seek(0, SEEK_SET); + + while ((line = stream_getline(instream, 256))) + { + uint16_t len = strlen(line); + + outstream.putc((uint8_t)len); + outstream.putc(file_id); + outstream.write(line, len); + + out_size += (len + 1); + + free(line); + } + + // end of pack + outstream.fill(0xff, 2); + + return out_size + 4; +} + +uint16_t put_ob3(imgtool::stream &instream, imgtool::stream &outstream) +{ + uint16_t size = instream.size() - 6; + std::vector buffer(size); + + instream.seek(6, SEEK_SET); + instream.read(&buffer[0], size); + + outstream.write(&buffer[0], size); + + // end of pack + outstream.fill(0xff, 2); + + return size; +} + +uint16_t put_opl(imgtool::stream &instream, imgtool::stream &outstream) +{ + uint16_t out_size = 0; + uint32_t rec_start = outstream.tell(); + char *line; + + // reset stream + instream.seek(0, SEEK_SET); + + outstream.fill(0x00, 4); + + // replace all eol with 0x00 + while ((line = stream_getline(instream, 256))) + { + // replace tab with space + for (int i=0; i>8) & 0xff); + outstream.putc(out_size & 0xff); + + return out_size + 4; +} + +uint16_t get_odb(imgtool::stream &instream, imgtool::stream &outstream, uint8_t type, uint8_t file_id) +{ + uint8_t data, *buffer; + uint16_t out_size = 0; + + if (file_id >= 0x90) + while (seek_next_record(instream, file_id)) + { + instream.read(&data, 1); + instream.seek(1, SEEK_CUR); + buffer = (uint8_t*)malloc(data); + instream.read(buffer, data); + outstream.write(buffer, data); + outstream.putc('\r'); + outstream.putc('\n'); + free (buffer); + out_size += data; + } + + return out_size; +} + +uint16_t get_ob3(imgtool::stream &instream, imgtool::stream &outstream, uint8_t type, uint8_t file_id) +{ + uint8_t data, *buffer = NULL; + uint16_t size = 0; + static const char ob3_magic[3] = {'O', 'R', 'G'}; + + instream.read(&data, 1); + + if (data == 0x02) + { + instream.seek(1, SEEK_CUR); + size = get_long_rec_size(instream); + buffer = (uint8_t*)malloc(size); + instream.read(buffer, size); + } + + outstream.write(ob3_magic, 3); + outstream.putc((size>>8) & 0xff); + outstream.putc(size & 0xff); + outstream.putc(type | 0x80); + + if (buffer) + { + outstream.write(buffer, size); + free (buffer); + } + + return size; +} + +static imgtoolerr_t datapack_open(imgtool::image &image, imgtool::stream::ptr &&stream) +{ + psion_pack *pack = get_psion_pack(image); + char opk_magic[4]; + + stream->read(opk_magic, 4); + + if(strcmp(opk_magic, "OPK\0")) + return IMGTOOLERR_UNEXPECTED; + + pack->stream = stream.get(); + + if (update_pack_index(pack)) + { + pack->stream = stream.release(); + return IMGTOOLERR_SUCCESS; + } + else + { + return IMGTOOLERR_CORRUPTIMAGE; + } +} + +static imgtoolerr_t datapack_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *opts) +{ + psion_pack *pack = get_psion_pack(image); + static const uint8_t opk_magic[4] = {'O', 'P', 'K', 0x00}; + uint8_t pack_head[8] = {0x40, 0x00, 0x59, 0x01, 0x01, 0x01, 0x00, 0x00}; + uint16_t checksum; + + pack_head[0] |= (opts->lookup_int('R')) ? 0x00 : 0x02; + pack_head[0] |= (opts->lookup_int('P')) ? 0x04 : 0x00; + pack_head[0] |= (opts->lookup_int('W')) ? 0x00 : 0x08; + pack_head[0] |= (opts->lookup_int('B')) ? 0x00 : 0x10; + pack_head[0] |= (opts->lookup_int('C')) ? 0x20 : 0x00; + pack_head[1] = opts->lookup_int('S'); + + checksum = head_checksum(pack_head); + + stream->write(opk_magic, 4); + stream->fill(0x00, 2); + stream->write(pack_head, 8); + + stream->putc((checksum>>8) & 0xff); + stream->putc(checksum & 0xff); + + put_name_record(*stream, "MAIN", 0x81, 0x90); + + stream->fill(0xff, 2); + + update_opk_head(*stream); + + pack->stream = stream.get(); + + if (update_pack_index(pack)) + { + pack->stream = stream.release(); + return IMGTOOLERR_SUCCESS; + } + else + { + return IMGTOOLERR_CORRUPTIMAGE; + } +} + +static void datapack_close(imgtool::image &image) +{ + psion_pack *pack = get_psion_pack(image); + + delete pack->stream; +} + +static imgtoolerr_t datapack_begin_enum(imgtool::directory &enumeration, const char *path) +{ + psion_iter *iter = (psion_iter*)enumeration.extra_bytes(); + iter->index = 0; + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t datapack_next_enum(imgtool::directory &enumeration, imgtool_dirent &ent) +{ + imgtool::image &image(enumeration.image()); + psion_pack *pack = get_psion_pack(image); + psion_iter *iter = (psion_iter*)enumeration.extra_bytes(); + uint8_t data = 0; + + if (!pack->pack_index[iter->index].name_rec) + { + ent.eof = 1; + return IMGTOOLERR_SUCCESS; + } + memcpy(ent.filename, pack->pack_index[iter->index].filename, 8); + sprintf(ent.attr, "Type: %02x ID: %02x", pack->pack_index[iter->index].type, pack->pack_index[iter->index].id); + + if (pack->pack_index[iter->index].data_rec) + { + pack->stream->seek(pack->pack_index[iter->index].data_rec + 2, SEEK_SET); + ent.filesize = get_long_rec_size(*pack->stream); + } + + // seek all file's records + if (pack->pack_index[iter->index].id >= 0x90) + { + pack->stream->seek(0x10, SEEK_SET); + while (seek_next_record(*pack->stream, pack->pack_index[iter->index].id)) + { + pack->stream->read(&data, 1); + pack->stream->seek(data + 1, SEEK_CUR); + ent.filesize +=data; + } + } + + iter->index++; + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t datapack_free_space(imgtool::partition &partition, uint64_t *size) +{ + imgtool::image &image(partition.image()); + psion_pack *pack = get_psion_pack(image); + uint32_t pack_size = 0; + + pack->stream->seek(0x07, SEEK_SET); + pack->stream->read(&pack_size, 1); + + if (size) + *size = (pack_size * 0x2000) - pack->eop; + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t datapack_read_file(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf) +{ + imgtool::image &image(partition.image()); + psion_pack *pack = get_psion_pack(image); + int index = seek_file_name(pack, filename); + + if (index >= 0) + { + if ((pack->pack_index[index].type & 0x7f) == 0x01) + { + // ODB files + pack->stream->seek(0x10, SEEK_SET); + get_odb(*pack->stream, destf, pack->pack_index[index].type, pack->pack_index[index].id); + } + else if ((pack->pack_index[index].type & 0x7f) == 0x03) + { + // OB3/OPL files + pack->stream->seek(pack->pack_index[index].data_rec, SEEK_SET); + get_ob3(*pack->stream, destf, pack->pack_index[index].type, pack->pack_index[index].id); + } + else + { + // Other files + return IMGTOOLERR_UNIMPLEMENTED; + } + + return IMGTOOLERR_SUCCESS; + } + else + return IMGTOOLERR_FILENOTFOUND; +} + +static imgtoolerr_t datapack_write_file(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts) +{ + imgtool::image &image(partition.image()); + psion_pack *pack = get_psion_pack(image); + static const uint8_t data_head[4] = {0x02, 0x80, 0x00, 0x00}; + uint8_t head[3]; + uint16_t size = 0; + uint8_t type = opts->lookup_int('T'); + uint8_t file_id = opts->lookup_int('I'); + + if (!pack->eop) + return IMGTOOLERR_CORRUPTIMAGE; + + // if not file_id is specified get the first free (for ODB only) + if (file_id == 0 && type == 3) + { + file_id = get_free_file_id(pack); + + if (file_id == 0xff) + return IMGTOOLERR_NOSPACE; + } + + sourcef.read(head, 3); + pack->stream->seek(pack->eop, SEEK_SET); + + if (type == 0) + type = (!strncmp((char*)head, "ORG", 3)) ? 1 : 2; + + switch (type) + { + case 1: //OB3 file + put_name_record(*pack->stream, filename, 0x83, file_id); + pack->stream->write(data_head, 4); + size = put_ob3(sourcef, *pack->stream); + break; + case 2: //OPL file + put_name_record(*pack->stream, filename, 0x83, file_id); + pack->stream->write(data_head, 4); + size = put_opl(sourcef, *pack->stream); + break; + case 3: //ODB file + put_name_record(*pack->stream, filename, 0x81, file_id); + size = put_odb(sourcef, *pack->stream, file_id); + break; + } + + if (type != 3) + { + // update the OB3/OPL long record size + pack->stream->seek(pack->eop + 13, SEEK_SET); + pack->stream->putc((size>>8) & 0xff); + pack->stream->putc(size & 0xff); + } + + update_opk_head(*pack->stream); + + if (update_pack_index(pack)) + return IMGTOOLERR_SUCCESS; + else + return IMGTOOLERR_CORRUPTIMAGE; +} + +static imgtoolerr_t datapack_delete_file(imgtool::partition &partition, const char *filename) +{ + imgtool::image &image(partition.image()); + psion_pack *pack = get_psion_pack(image); + int index = seek_file_name(pack, filename); + + if (index >= 0) + { + // clear the bit 7 of the file type to mark the file as deleted + pack->stream->seek(pack->pack_index[index].name_rec + 1, SEEK_SET); + pack->stream->putc(pack->pack_index[index].type & 0x7f); + + if (update_pack_index(pack)) + return IMGTOOLERR_SUCCESS; + else + return IMGTOOLERR_CORRUPTIMAGE; + } + else + return IMGTOOLERR_FILENOTFOUND; +} + +OPTION_GUIDE_START( psion_create_optguide ) + OPTION_ENUM_START( 'S', "size", "datapack size" ) + OPTION_ENUM( 1, "8k", "8 kbyte" ) + OPTION_ENUM( 2, "16k", "16 kbyts" ) + OPTION_ENUM( 4, "32k", "32 kbyte" ) + OPTION_ENUM( 8, "64k", "64 kbyte" ) + OPTION_ENUM( 16, "128k", "128 kbyte" ) // only paged datapacks can have this size + OPTION_ENUM_END + OPTION_INT('R', "ram", "EPROM/RAM datapack" ) + OPTION_INT('P', "paged", "linear/paged datapack" ) + OPTION_INT('W', "protect", "write-protected datapack" ) + OPTION_INT('B', "boot", "bootable datapack" ) + OPTION_INT('C', "copy", "copyable datapack" ) +OPTION_GUIDE_END + +OPTION_GUIDE_START( psion_write_optguide ) + OPTION_ENUM_START( 'T', "type", "file type" ) + OPTION_ENUM( 1, "OB3", "OB3 files" ) + OPTION_ENUM( 2, "OPL", "OPL files" ) + OPTION_ENUM( 3, "ODB", "ODB or text files" ) + OPTION_ENUM_END + OPTION_INT( 'I', "id", "File ID" ) +OPTION_GUIDE_END + +void psion_get_info( const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch (state) + { + // --- the following bits of info are returned as 64-bit signed integers --- + case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES : info->i = sizeof(psion_pack); break; + case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES : info->i = sizeof(psion_iter); break; + + // --- the following bits of info are returned as pointers to data or functions --- + case IMGTOOLINFO_PTR_OPEN : info->open = datapack_open; break; + case IMGTOOLINFO_PTR_CREATE : info->create = datapack_create; break; + case IMGTOOLINFO_PTR_CLOSE : info->close = datapack_close; break; + case IMGTOOLINFO_PTR_BEGIN_ENUM : info->begin_enum = datapack_begin_enum; break; + case IMGTOOLINFO_PTR_NEXT_ENUM : info->next_enum = datapack_next_enum; break; + case IMGTOOLINFO_PTR_FREE_SPACE : info->free_space = datapack_free_space; break; + case IMGTOOLINFO_PTR_READ_FILE : info->read_file = datapack_read_file; break; + case IMGTOOLINFO_PTR_WRITE_FILE : info->write_file = datapack_write_file; break; + case IMGTOOLINFO_PTR_DELETE_FILE : info->delete_file = datapack_delete_file; break; + case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE : info->createimage_optguide = &psion_create_optguide; break; + case IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE : info->createimage_optguide = &psion_write_optguide; break; + + // --- the following bits of info are returned as NULL-terminated strings --- + case IMGTOOLINFO_STR_NAME : strcpy( info->s = imgtool_temp_str(), "psionpack"); break; + case IMGTOOLINFO_STR_DESCRIPTION : strcpy( info->s = imgtool_temp_str(), "Psion Organiser II Datapack"); break; + case IMGTOOLINFO_STR_FILE : strcpy( info->s = imgtool_temp_str(), __FILE__); break; + case IMGTOOLINFO_STR_FILE_EXTENSIONS : strcpy( info->s = imgtool_temp_str(), "opk"); break; + case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC : strcpy( info->s = imgtool_temp_str(), "S1/2/[4]/8/16;R[0]/1;P[0]/1;W0/[1];B[0]/1;C0/[1]"); break; + case IMGTOOLINFO_STR_WRITEFILE_OPTSPEC : strcpy( info->s = imgtool_temp_str(), "T[1]/2/3;I[0]/145-255"); break; + } +} diff --git a/src/tools/imgtool/modules/rsdos.cpp b/src/tools/imgtool/modules/rsdos.cpp new file mode 100644 index 0000000..09b8131 --- /dev/null +++ b/src/tools/imgtool/modules/rsdos.cpp @@ -0,0 +1,666 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/**************************************************************************** + + rsdos.cpp + + CoCo RS-DOS disk images + +****************************************************************************/ + +#include "imgtool.h" +#include "filter.h" +#include "iflopimg.h" + +#include "formats/coco_dsk.h" +#include "corestr.h" +#include "opresolv.h" + +#include +#include +#include + +/* this structure mirrors the structure of an RS-DOS directory entry on disk */ +struct rsdos_dirent +{ + char filename[11]; + char ftype; + char asciiflag; + unsigned char first_granule; + unsigned char lastsectorbytes_msb; + unsigned char lastsectorbytes_lsb; + unsigned char unused[16]; +}; + +struct rsdos_direnum +{ + int index; + bool eof; +}; + +#define RSDOS_OPTIONS_FTYPE 'T' +#define RSDOS_OPTIONS_ASCII 'M' + + + +/********************************************************************* + Imgtool module code +*********************************************************************/ + +#define MAX_DIRENTS ((18-2)*(256/32)) +#define MAX_GRANULEMAP_SIZE 256 + +//------------------------------------------------- +// get_rsdos_dirent +//------------------------------------------------- + +static floperr_t get_rsdos_dirent(imgtool::image &f, int index_loc, rsdos_dirent &ent) +{ + return floppy_read_sector(imgtool_floppy(f), 0, 17, 3, index_loc * 32, (void *) &ent, sizeof(ent)); +} + + +//------------------------------------------------- +// put_rsdos_dirent +//------------------------------------------------- + +static floperr_t put_rsdos_dirent(imgtool::image &f, int index_loc, const rsdos_dirent &ent) +{ + if (index_loc >= MAX_DIRENTS) + return (floperr_t)IMGTOOLERR_FILENOTFOUND; + return floppy_write_sector(imgtool_floppy(f), 0, 17, 3, index_loc * 32, (void *) &ent, sizeof(ent), 0); /* TODO: pass ddam argument from imgtool */ +} + + +//------------------------------------------------- +// get_dirent_fname +//------------------------------------------------- + +static std::string get_dirent_fname(const rsdos_dirent &ent) +{ + return extract_padded_filename(ent.filename, 8, 3); +} + + +//------------------------------------------------- +// lookup_rsdos_file +//------------------------------------------------- + +static imgtoolerr_t lookup_rsdos_file(imgtool::image &f, const char *fname, rsdos_dirent &ent, int *position = nullptr) +{ + int i; + floperr_t ferr; + std::string fnamebuf; + + i = 0; + + do + { + do + { + ferr = get_rsdos_dirent(f, i++, ent); + if (ferr) + return imgtool_floppy_error(ferr); + } + while(ent.filename[0] == '\0'); + + + if (ent.filename[0] != -1) + fnamebuf = get_dirent_fname(ent); + } + while((ent.filename[0] != -1) && core_stricmp(fnamebuf.c_str(), fname)); + + if (ent.filename[0] == -1) + return IMGTOOLERR_FILENOTFOUND; + + if (position) + *position = i - 1; + return (imgtoolerr_t)0; +} + + +//------------------------------------------------- +// get_granule_count +//------------------------------------------------- + +static uint8_t get_granule_count(imgtool::image &img) +{ + uint16_t tracks; + uint16_t granules; + + tracks = floppy_get_tracks_per_disk(imgtool_floppy(img)); + granules = (tracks - 1) * 2; + return (granules > 255) ? 255 : (uint8_t) granules; +} + + +//------------------------------------------------- +// get_granule_map +//------------------------------------------------- + +static floperr_t get_granule_map(imgtool::image &img, uint8_t *granule_map, uint8_t granule_count[MAX_GRANULEMAP_SIZE]) +{ + uint8_t count; + + count = get_granule_count(img); + if (granule_count) + *granule_count = count; + + return floppy_read_sector(imgtool_floppy(img), 0, 17, 2, 0, granule_map, count); +} + + +//------------------------------------------------- +// put_granule_map +//------------------------------------------------- + +static floperr_t put_granule_map(imgtool::image &img, const uint8_t *granule_map, uint8_t granule_count) +{ + return floppy_write_sector(imgtool_floppy(img), 0, 17, 2, 0, granule_map, granule_count, 0); /* TODO: pass ddam argument from imgtool */ +} + + +//------------------------------------------------- +// transfer_granule +//------------------------------------------------- + +static imgtoolerr_t transfer_granule(imgtool::image &img, uint8_t granule, int length, imgtool::stream &f, imgtoolerr_t (*proc)(imgtool::image &, int, int, int, int, size_t, imgtool::stream &)) +{ + imgtoolerr_t err = IMGTOOLERR_SUCCESS; + uint8_t track, sector; + + track = granule / 2; + if (track >= 17) + track++; + + sector = (granule % 2) ? 10 : 1; + + if (length > 0) + err = proc(img, 0, track, sector, 0, length, f); + return err; +} + + +//------------------------------------------------- +// transfer_from_granule +//------------------------------------------------- + +static imgtoolerr_t transfer_from_granule(imgtool::image &img, uint8_t granule, int length, imgtool::stream &destf) +{ + return transfer_granule(img, granule, length, destf, imgtool_floppy_read_sector_to_stream); +} + + +//------------------------------------------------- +// transfer_to_granule +//------------------------------------------------- + +static imgtoolerr_t transfer_to_granule(imgtool::image &img, uint8_t granule, int length, imgtool::stream &sourcef) +{ + return transfer_granule(img, granule, length, sourcef, imgtool_floppy_write_sector_from_stream); +} + + +//------------------------------------------------- +// process_rsdos_file +//------------------------------------------------- + +static imgtoolerr_t process_rsdos_file(struct rsdos_dirent *ent, imgtool::image &img, imgtool::stream *destf, size_t &size) +{ + floperr_t ferr; + size_t s, lastgransize; + uint8_t granule_count; + unsigned char i = 0, granule; + uint8_t usedmap[MAX_GRANULEMAP_SIZE]; // used to detect infinite loops + uint8_t granule_map[MAX_GRANULEMAP_SIZE]; + + ferr = get_granule_map(img, granule_map, &granule_count); + if (ferr) + return imgtool_floppy_error(ferr); + + memset(usedmap, 0, granule_count); + + lastgransize = ent->lastsectorbytes_lsb + (((int) ent->lastsectorbytes_msb) << 8); + s = 0; + granule = ent->first_granule; + + while(!usedmap[granule] && ((i = granule_map[granule]) < granule_count)) + { + usedmap[granule] = 1; + if (destf) + transfer_from_granule(img, granule, 9*256, *destf); + + /* i is the next granule */ + s += (256 * 9); + granule = i; + } + + if ((i < 0xc0) || (i > 0xc9)) + return IMGTOOLERR_CORRUPTIMAGE; + + if (lastgransize) + i--; + lastgransize += (256 * (i - 0xc0)); + + if (destf) + transfer_from_granule(img, granule, lastgransize, *destf); + + size = s + lastgransize; + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// prepare_dirent - create a new directory entry +// with a specified name +//------------------------------------------------- + +static imgtoolerr_t prepare_dirent(rsdos_dirent &ent, const char *fname) +{ + const char *fname_end; + const char *fname_ext; + int fname_ext_len; + + memset(&ent, '\0', sizeof(ent)); + memset(ent.filename, ' ', sizeof(ent.filename)); + + fname_end = strchr(fname, '.'); + if (fname_end) + fname_ext = fname_end + 1; + else + fname_end = fname_ext = fname + strlen(fname); + + fname_ext_len = strlen(fname_ext); + + // we had better be an 8.3 filename + if (((fname_end - fname) > 8) || (fname_ext_len > 3)) + return IMGTOOLERR_BADFILENAME; + + memcpy(&ent.filename[0], fname, fname_end - fname); + memcpy(&ent.filename[8], fname_ext, fname_ext_len); + + // for now, all files are type 2 binary files + ent.ftype = 2; + ent.asciiflag = 0; + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// rsdos_diskimage_nextenum +//------------------------------------------------- + +static imgtoolerr_t rsdos_diskimage_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent) +{ + floperr_t ferr; + imgtoolerr_t err; + size_t filesize; + rsdos_direnum *rsenum; + rsdos_dirent rsent; + + imgtool::image &image(enumeration.image()); + rsenum = (rsdos_direnum *) enumeration.extra_bytes(); + + /* Did we hit the end of file before? */ + if (rsenum->eof) + goto eof; + + do + { + if (rsenum->index >= MAX_DIRENTS) + goto eof; + + ferr = get_rsdos_dirent(image, rsenum->index++, rsent); + if (ferr) + return imgtool_floppy_error(ferr); + } + while(rsent.filename[0] == '\0'); + + // now are we at the eof point? + if (rsent.filename[0] == -1) + { + rsenum->eof = 1; +eof: + ent.eof = 1; + } + else + { + /* Note the end of file */ + err = process_rsdos_file(&rsent, image, nullptr, filesize); + if (err) + return err; + + if (filesize == ((size_t) -1)) + { + /* corrupt! */ + ent.filesize = 0; + ent.corrupt = 1; + } + else + { + ent.filesize = filesize; + ent.corrupt = 0; + } + ent.eof = 0; + + std::string fname = get_dirent_fname(rsent); + + snprintf(ent.filename, std::size(ent.filename), "%s", fname.c_str()); + snprintf(ent.attr, std::size(ent.attr), "%d %c", (int) rsent.ftype, (char) (rsent.asciiflag + 'B')); + } + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// rsdos_diskimage_freespace +//------------------------------------------------- + +static imgtoolerr_t rsdos_diskimage_freespace(imgtool::partition &partition, uint64_t *size) +{ + floperr_t ferr; + uint8_t i; + size_t s = 0; + uint8_t granule_count; + uint8_t granule_map[MAX_GRANULEMAP_SIZE]; + imgtool::image &image(partition.image()); + + ferr = get_granule_map(image, granule_map, &granule_count); + if (ferr) + return imgtool_floppy_error(ferr); + + for (i = 0; i < granule_count; i++) + { + if (granule_map[i] == 0xff) + s += (9 * 256); + } + *size = s; + return (imgtoolerr_t)FLOPPY_ERROR_SUCCESS; +} + + +//------------------------------------------------- +// delete_entry +//------------------------------------------------- + +static imgtoolerr_t delete_entry(imgtool::image &img, rsdos_dirent &ent, int pos) +{ + floperr_t ferr; + unsigned char g, i; + uint8_t granule_count; + uint8_t granule_map[MAX_GRANULEMAP_SIZE]; + + // write a NUL in the filename, marking it deleted + ent.filename[0] = 0; + ferr = put_rsdos_dirent(img, pos, ent); + if (ferr) + return imgtool_floppy_error(ferr); + + ferr = get_granule_map(img, granule_map, &granule_count); + if (ferr) + return imgtool_floppy_error(ferr); + + // now free up the granules + g = ent.first_granule; + while (g < granule_count) + { + i = granule_map[g]; + granule_map[g] = 0xff; + g = i; + } + + ferr = put_granule_map(img, granule_map, granule_count); + if (ferr) + return imgtool_floppy_error(ferr); + + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// rsdos_diskimage_readfile +//------------------------------------------------- + +static imgtoolerr_t rsdos_diskimage_readfile(imgtool::partition &partition, const char *fname, const char *fork, imgtool::stream &destf) +{ + imgtoolerr_t err; + struct rsdos_dirent ent; + size_t size; + imgtool::image &img(partition.image()); + + err = lookup_rsdos_file(img, fname, ent); + if (err) + return err; + + err = process_rsdos_file(&ent, img, &destf, size); + if (err) + return err; + + if (size == (size_t) -1) + return IMGTOOLERR_CORRUPTIMAGE; + + return (imgtoolerr_t)0; +} + + +//------------------------------------------------- +// rsdos_diskimage_writefile +//------------------------------------------------- + +static imgtoolerr_t rsdos_diskimage_writefile(imgtool::partition &partition, const char *fname, const char *fork, imgtool::stream &sourcef, util::option_resolution *writeoptions) +{ + floperr_t ferr; + imgtoolerr_t err; + imgtool::image &img(partition.image()); + struct rsdos_dirent ent, ent2; + size_t i; + uint64_t sz; + uint64_t freespace = 0; + unsigned char g; + unsigned char *gptr; + uint8_t granule_count; + uint8_t granule_map[MAX_GRANULEMAP_SIZE]; + + // can we write to this image? + if (floppy_is_read_only(imgtool_floppy(img))) + return IMGTOOLERR_READONLY; + + err = rsdos_diskimage_freespace(partition, &freespace); + if (err) + return err; + + // is there enough space? + sz = sourcef.size(); + if (sz > freespace) + return IMGTOOLERR_NOSPACE; + + // setup our directory entry + err = prepare_dirent(ent, fname); + if (err) + return err; + + ent.ftype = writeoptions->lookup_int(RSDOS_OPTIONS_FTYPE); + ent.asciiflag = uint8_t(writeoptions->lookup_int(RSDOS_OPTIONS_ASCII)) - 1; + ent.lastsectorbytes_lsb = sz % 256; + ent.lastsectorbytes_msb = (((sz % 256) == 0) && (sz > 0)) ? 1 : 0; + gptr = &ent.first_granule; + + ferr = get_granule_map(img, granule_map, &granule_count); + if (ferr) + return imgtool_floppy_error(ferr); + + g = 0x00; + + do + { + while (granule_map[g] != 0xff) + { + g++; + if ((g >= granule_count) || (g == 0)) + return IMGTOOLERR_UNEXPECTED; // we should have already verified that there is enough space + } + *gptr = g; + gptr = &granule_map[g]; + + + i = std::min(sz, uint64_t(9*256)); + err = transfer_to_granule(img, g, i, sourcef); + if (err) + return err; + + sz -= i; + + // go to next granule + g++; + } + while(sz > 0); + + // now that we are done with the file, we need to specify the final entry + // in the file allocation table + *gptr = 0xc0 + ((i + 255) / 256); + + // now we need to find an empty directory entry + i = -1; + do + { + ferr = get_rsdos_dirent(img, ++i, ent2); + if (ferr) + return imgtool_floppy_error(ferr); + } + while((ent2.filename[0] != '\0') && strcmp(ent.filename, ent2.filename) && (ent2.filename[0] != -1)); + + // delete file if it already exists + if (ent2.filename[0] && (ent2.filename[0] != -1)) + { + err = delete_entry(img, ent2, i); + if (err) + return err; + } + + ferr = put_rsdos_dirent(img, i, ent); + if (ferr) + return imgtool_floppy_error(ferr); + + // write the granule map back out + ferr = put_granule_map(img, granule_map, granule_count); + if (ferr) + return imgtool_floppy_error(ferr); + + return IMGTOOLERR_SUCCESS; +} + + +//------------------------------------------------- +// rsdos_diskimage_deletefile +//------------------------------------------------- + +static imgtoolerr_t rsdos_diskimage_deletefile(imgtool::partition &partition, const char *fname) +{ + imgtoolerr_t err; + imgtool::image &image(partition.image()); + int pos; + struct rsdos_dirent ent; + + err = lookup_rsdos_file(image, fname, ent, &pos); + if (err) + return err; + + return delete_entry(image, ent, pos); +} + + +//------------------------------------------------- +// rsdos_diskimage_suggesttransfer +//------------------------------------------------- + +static imgtoolerr_t rsdos_diskimage_suggesttransfer(imgtool::partition &partition, const char *fname, imgtool_transfer_suggestion *suggestions, size_t suggestions_length) +{ + imgtoolerr_t err; + imgtool::image &image(partition.image()); + struct rsdos_dirent ent; + int pos; + + if (fname) + { + err = lookup_rsdos_file(image, fname, ent, &pos); + if (err) + return err; + + if (ent.asciiflag == (char) 0xFF) + { + /* ASCII file */ + suggestions[0].viability = SUGGESTION_RECOMMENDED; + suggestions[0].filter = filter_eoln_getinfo; + suggestions[1].viability = SUGGESTION_POSSIBLE; + suggestions[1].filter = NULL; + } + else if (ent.ftype == 0) + { + /* tokenized BASIC file */ + suggestions[0].viability = SUGGESTION_RECOMMENDED; + suggestions[0].filter = NULL; + suggestions[1].viability = SUGGESTION_POSSIBLE; + suggestions[1].filter = filter_cocobas_getinfo; + } + } + else + { + suggestions[0].viability = SUGGESTION_RECOMMENDED; + suggestions[0].filter = NULL; + suggestions[1].viability = SUGGESTION_POSSIBLE; + suggestions[1].filter = filter_eoln_getinfo; + suggestions[2].viability = SUGGESTION_POSSIBLE; + suggestions[2].filter = filter_cocobas_getinfo; + } + + return IMGTOOLERR_SUCCESS; +} + + + +/********************************************************************* + Imgtool module declaration +*********************************************************************/ + +OPTION_GUIDE_START( coco_rsdos_writefile_optionguide ) + OPTION_ENUM_START( RSDOS_OPTIONS_FTYPE, "ftype", "File type" ) + OPTION_ENUM( 0, "basic", "Basic" ) + OPTION_ENUM( 1, "data", "Data" ) + OPTION_ENUM( 2, "binary", "Binary" ) + OPTION_ENUM( 3, "assembler", "Assembler Source" ) + OPTION_ENUM_END + OPTION_ENUM_START( RSDOS_OPTIONS_ASCII, "ascii", "Ascii flag" ) + OPTION_ENUM( 0, "ascii", "Ascii" ) + OPTION_ENUM( 1, "binary", "Binary" ) + OPTION_ENUM_END +OPTION_GUIDE_END + + + +void rsdos_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case IMGTOOLINFO_INT_PREFER_UCASE: info->i = 1; break; + case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(struct rsdos_direnum); break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "rsdos"); break; + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "RS-DOS format"); break; + case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break; + case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), "\r"); break; + case IMGTOOLINFO_STR_WRITEFILE_OPTSPEC: strcpy(info->s = imgtool_temp_str(), "T0-[2]-3;M0-[1]"); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case IMGTOOLINFO_PTR_MAKE_CLASS: info->make_class = imgtool_floppy_make_class; break; + case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = rsdos_diskimage_nextenum; break; + case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = rsdos_diskimage_freespace; break; + case IMGTOOLINFO_PTR_READ_FILE: info->read_file = rsdos_diskimage_readfile; break; + case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = rsdos_diskimage_writefile; break; + case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = rsdos_diskimage_deletefile; break; + case IMGTOOLINFO_PTR_SUGGEST_TRANSFER: info->suggest_transfer = rsdos_diskimage_suggesttransfer; break; + case IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE: info->writefile_optguide = &coco_rsdos_writefile_optionguide; break; + case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_coco; break; + } +} diff --git a/src/tools/imgtool/modules/rt11.cpp b/src/tools/imgtool/modules/rt11.cpp new file mode 100644 index 0000000..be65355 --- /dev/null +++ b/src/tools/imgtool/modules/rt11.cpp @@ -0,0 +1,690 @@ +// license:BSD-3-Clause +// copyright-holders:Sergey Svishchev +/**************************************************************************** + + rt11.cpp + + DEC RT-11 disk images + + References: + + VaFFM -- bitsavers://pdf/dec/pdp11/rt11/v5.6_Aug91/AA-PD6PA-TC_RT-11_Volume_and_File_Formats_Manual_Aug91.pdf + DHM -- bitsavers://pdf/dec/pdp11/rt11/v5.6_Aug91/AA-PE7VA-TC_RT-11_Device_Handlers_Manual_Aug91.pdf + SSM -- bitsavers://pdf/dec/pdp11/rt11/v5.0_Mar83/AA-H379B-TC_5.0_SWsuppMar83.pdf + TSX+ -- bitsavers://pdf/dec/pdp11/tsxPlus/manuals_6.31/TSX-Plus_UsersRef_Jan88.pdf + PUTR -- http://www.dbit.com/pub/putr/putr.asm + + To do: + - filter for text files + - read-write support + - report empty 'last modified' time if date field is all zeros + - report free space + - arbitrary sized images + - don't crash when strings in home block have non-ascii chars (charconverter does not apply) + - do something about bootblock bug in imgtool (commit aca90520) + + LBN Contents + --- -------- + 0 Reserved (primary bootstrap) + 1 Reserved (home block) + 2-5 Reserved (secondary bootstrap) + 6-7 Directory segment 1 + ... Directory segment 2-n + ... Data + + Home block + ---------- + 000-201 Bad block replacement table + 202-203 ? + 204-251 INITIALIZE/RESTORE data area + 252-273 BUP information area + 274-677 ? + 700-701 (Reserved for Digital, must be zero) + 702-703 (Reserved for Digital, must be zero) + 704-721 ? + 722-723 Pack cluster size (= 1) + 724-725 Block number of first directory segment + 726-727 System version (RAD50) + 730-742 Volume Identification + 744-757 Owner name + 760-773 System Identification + 776-777 Checksum + + Directory segment header + ------------------------ + 0 The total number of segments in this directory. + 1 The segment number of the next logical directory segment. If this word is 0, there are no more segments in the list. + 2 The number of the highest segment currently in use. Valid only in the first directory segment. + 3 The number of extra bytes per directory entry, always an unsigned, even octal number. + 4 The block number on the volume where the actual stored data identified by this segment begins. + + Directory entry + --------------- + 0 Status word + 1 File name 1-3 (RAD50) + 2 File name 4-6 (RAD50) + 3 File type 1-3 (RAD50) + 4 Total file length (blocks) + 5 Job#, Channel# (RT-11 uses this information only for tentative files) + 6 Creation date + 7- Optional extra words + +****************************************************************************/ + +#include "imgtool.h" +#include "formats/imageutl.h" +#include "iflopimg.h" + +#include +#include +#include + + +namespace +{ + struct rt11_diskinfo + { + uint16_t directory_start; + uint16_t total_segments; + uint16_t last_segment; + uint16_t dirent_size; + int dirents_per_block; + // autodetected + int tracks; + int heads; + int sectors; + uint32_t sector_size; + // cache + int map[16]; + int cached_track; + int cached_head; + }; + + enum misc_t + { + HOME_BLOCK = 1, + BLOCK_SIZE = 512 + }; + + struct rt11_direnum + { + uint8_t segment_data[2 * BLOCK_SIZE]; + uint16_t segment; + uint16_t index; + uint16_t data; + }; + + struct rt11_dirent + { + uint16_t status; + uint16_t filename[3]; + uint16_t time; + // synthetic + uint64_t filesize; + uint16_t data; + }; + + enum rt11_status + { + E_PRE = 0000020, + E_TENT = 0000400, + E_MPTY = 0001000, + E_PERM = 0002000, + E_EOS = 0004000, + E_READ = 0040000, + E_PROT = 0100000 + }; + + enum creation_policy_t + { + CREATE_NONE, + CREATE_FILE, + }; + + + util::arbitrary_datetime _rt11_crack_time(uint16_t rt11_time) + { + util::arbitrary_datetime dt; + + dt.second = 0; + dt.minute = 0; + dt.hour = 0; + dt.day_of_month = (rt11_time >> 5) & 31; + dt.month = (rt11_time >> 10) & 15; + dt.year = 1972 + (rt11_time & 31) + 32 * ((rt11_time >> 14) & 3); + + return dt; + } + + imgtool::datetime rt11_crack_time(uint16_t rt11_time) + { + util::arbitrary_datetime dt; + imgtool::datetime it; + + if (rt11_time == 0) + { + return imgtool::datetime(imgtool::datetime::datetime_type::NONE, dt); + } + + dt = _rt11_crack_time(rt11_time); + + it = imgtool::datetime(imgtool::datetime::datetime_type::LOCAL, dt); + + return it; + } + + void rt11_from_rad50(char *ascii, uint16_t *rad50, int num) + { + const char rad[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$.%0123456789:"; + + for (int i = 0, j = 0; i < num; i++) + { + ascii[j++] = rad[ rad50[i] / (050 * 050)]; + ascii[j++] = rad[(rad50[i] / 050) % 050]; + ascii[j++] = rad[ rad50[i] % 050]; + } + } + + void rt11_filename_from_rad50(char *ascii, uint16_t *rad50) + { + int i, j; + + rt11_from_rad50(&ascii[0], &rad50[0], 2); + for (i = 0; i < 6; i++) + { + if (ascii[i] == ' ') break; + } + ascii[i++] = '.'; + rt11_from_rad50(&ascii[i], &rad50[2], 1); + for (j = i; j < i + 3; j++) + { + if (ascii[j] == ' ') break; + } + ascii[j] = '\0'; + } + + int is_file_storagetype(uint16_t status) + { + return !(status & (E_MPTY | E_EOS | E_TENT)); + } + + rt11_diskinfo *get_rt11_info(imgtool::image &image) + { + return (rt11_diskinfo *)imgtool_floppy_extrabytes(image); + } +} + + +static imgtoolerr_t rt11_image_get_geometry(imgtool::image &image, uint32_t *tracks, uint32_t *heads, uint32_t *sectors) +{ + const rt11_diskinfo *di = get_rt11_info(image); + + *sectors = di->sectors; + *heads = di->heads; + *tracks = di->tracks; + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t rt11_get_sector_position(imgtool::image &image, uint32_t sector_index, + uint32_t &head, uint32_t &track, uint32_t §or) +{ + imgtoolerr_t err; + rt11_diskinfo *di = get_rt11_info(image); + uint32_t tracks, heads, sectors; + + err = image.get_geometry(&tracks, &heads, §ors); + if (err) + return err; + + track = sector_index / sectors / heads; + head = (sector_index / sectors) % heads; + + // map 1-based sector numbers, possibly interleaved, to sector indexes + if (track != di->cached_track || head != di->cached_head) + { + memset(di->map, -1, sizeof(di->map)); + for (int i = 0; i < 256; i++) + { + int sector_id; + + if (floppy_get_indexed_sector_info(imgtool_floppy(image), head, track, i, NULL, NULL, §or_id, NULL, NULL)) + continue; + + if (sector_id > 0 && sector_id <= sectors) + di->map[sector_id - 1] = i; + } + di->cached_track = track; + di->cached_head = head; + } + + if (di->map[(sector_index % sectors)] < 0) + { + return IMGTOOLERR_SEEKERROR; + } + else + { + sector = di->map[(sector_index % sectors)]; + return IMGTOOLERR_SUCCESS; + } +} + + +static imgtoolerr_t rt11_image_readblock(imgtool::image &image, void *buffer, uint64_t block) +{ + imgtoolerr_t err; + floperr_t ferr; + const rt11_diskinfo *di = get_rt11_info(image); + uint32_t track, head, sector; + unsigned long flags; + + if (di->sector_size == 256) + { + err = rt11_get_sector_position(image, block * 2, head, track, sector); + if (err) + return err; + + ferr = floppy_read_indexed_sector(imgtool_floppy(image), head, track, sector, 0, buffer, 256); + if (ferr) + return imgtool_floppy_error(ferr); + + ferr = floppy_get_indexed_sector_info(imgtool_floppy(image), head, track, sector, NULL, NULL, NULL, NULL, &flags); + if (ferr) + return imgtool_floppy_error(ferr); + + if (flags) + return IMGTOOLERR_READERROR; + + err = rt11_get_sector_position(image, (block * 2) + 1, head, track, sector); + if (err) + return err; + + ferr = floppy_read_indexed_sector(imgtool_floppy(image), head, track, sector, 0, ((uint8_t *) buffer) + 256, 256); + if (ferr) + return imgtool_floppy_error(ferr); + + ferr = floppy_get_indexed_sector_info(imgtool_floppy(image), head, track, sector, NULL, NULL, NULL, NULL, &flags); + if (ferr) + return imgtool_floppy_error(ferr); + + if (flags) + return IMGTOOLERR_READERROR; + } + else + { + err = rt11_get_sector_position(image, block, head, track, sector); + if (err) + return err; + + ferr = floppy_read_indexed_sector(imgtool_floppy(image), head, track, sector, 0, buffer, BLOCK_SIZE); + if (ferr) + return imgtool_floppy_error(ferr); + + ferr = floppy_get_indexed_sector_info(imgtool_floppy(image), head, track, sector, NULL, NULL, NULL, NULL, &flags); + if (ferr) + return imgtool_floppy_error(ferr); + + if (flags) + return IMGTOOLERR_READERROR; + } + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t rt11_probe_geometry(imgtool::image &image) +{ + floperr_t ferr; + rt11_diskinfo *di = get_rt11_info(image); + + // MX (11x256 byte sectors) or MY (10x512 byte sectors)? + ferr = floppy_get_indexed_sector_info(imgtool_floppy(image), 0, 0, 0, NULL, NULL, NULL, &di->sector_size, NULL); + if (ferr) + return imgtool_floppy_error(ferr); + + if (di->sector_size == 256) + di->sectors = 11; + else + di->sectors = 10; + + // double- or single-sided? + ferr = floppy_get_indexed_sector_info(imgtool_floppy(image), 1, 0, 0, NULL, NULL, NULL, NULL, NULL); + if (ferr) + di->heads = 1; + else + di->heads = 2; + + // 80 or 40 tracks? + ferr = floppy_get_indexed_sector_info(imgtool_floppy(image), 0, 50, 0, NULL, NULL, NULL, NULL, NULL); + if (ferr) + di->tracks = 40; + else + di->tracks = 80; + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t rt11_image_open(imgtool::image &image, imgtool::stream::ptr &&dummy) +{ + imgtoolerr_t err; + uint8_t buffer[BLOCK_SIZE]; + rt11_diskinfo *di = get_rt11_info(image); + + di->cached_head = -1; + di->cached_track = -1; + memset(di->map, -1, sizeof(di->map)); + // dummy values for rt11_image_readblock + di->tracks = 40; + di->heads = 1; + + err = rt11_probe_geometry(image); + if (err) + return err; + + /* load home block */ + err = rt11_image_readblock(image, buffer, HOME_BLOCK); + if (err) + return err; + + di->directory_start = pick_integer_le(buffer, 0724, 2); + + // real-world images seem to never have a valid checksum, but directory_start is always 6 +#if 0 + uint16_t tmp, cksum; + + tmp = 0; + cksum = pick_integer_le(buffer, 0776, 2); + for (int i = 0; i < 510; i+=2) + { + tmp += pick_integer_le(buffer, i, 2); + } + + /* sanity check these values */ + if (cksum != tmp) + { + fprintf(stderr, "cksum stored:computed %04x:%04x\n", cksum, tmp); + return IMGTOOLERR_CORRUPTIMAGE; + } +#endif + if (di->directory_start != 6) + { + return IMGTOOLERR_CORRUPTIMAGE; + } + + /* load first directory segment */ + err = rt11_image_readblock(image, buffer, di->directory_start); + if (err) + return err; + + di->total_segments = pick_integer_le(buffer, 0, 2); + di->last_segment = pick_integer_le(buffer, 4, 2); + di->dirent_size = (pick_integer_le(buffer, 6, 2) + 7) * 2; + di->dirents_per_block = (2 * BLOCK_SIZE - 10) / di->dirent_size; + + return IMGTOOLERR_SUCCESS; +} + +static void rt11_image_info(imgtool::image &image, std::ostream &stream) +{ + uint8_t buffer[BLOCK_SIZE]; + char system[4]; + char vid[13], oid[13], sid[13]; + + rt11_image_readblock(image, buffer, HOME_BLOCK); + rt11_from_rad50(system, (uint16_t *)&buffer[0726], 1); + system[3] = '\0'; + + memcpy(vid, &buffer[0730], 12); + memcpy(oid, &buffer[0744], 12); + memcpy(sid, &buffer[0760], 12); + vid[12] = '\0'; + oid[12] = '\0'; + sid[12] = '\0'; + + stream << "System version: '" << system << "', System ID: '" << sid << "', Volume ID: '" << vid << "', Owner: '" << oid << "'"; +} + +// directory operations + +static imgtoolerr_t rt11_enum_seek(imgtool::image &image, + rt11_direnum *rt11enum, uint16_t segment, uint16_t index) +{ + const rt11_diskinfo *di = get_rt11_info(image); + imgtoolerr_t err; + uint8_t buffer[BLOCK_SIZE]; + + if (rt11enum->segment != segment) + { + if (segment != 0) + { + err = rt11_image_readblock(image, buffer, di->directory_start + (segment - 1) * 2); + if (err) + return err; + memcpy(rt11enum->segment_data, buffer, sizeof(buffer)); + + err = rt11_image_readblock(image, buffer, di->directory_start + (segment - 1) * 2 + 1); + if (err) + return err; + memcpy(&rt11enum->segment_data[BLOCK_SIZE], buffer, sizeof(buffer)); + + rt11enum->data = pick_integer_le(rt11enum->segment_data, 8, 2); + } + rt11enum->segment = segment; + } + + rt11enum->index = index; + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t rt11_get_next_dirent(imgtool::image &image, + rt11_direnum *rt11enum, rt11_dirent &rt_ent) +{ + imgtoolerr_t err; + const rt11_diskinfo *di = get_rt11_info(image); + uint32_t next_segment, next_index; + uint32_t offset; + + memset(&rt_ent, 0, sizeof(rt_ent)); + + /* have we hit the end of the file? */ + if (rt11enum->segment == 0) + return IMGTOOLERR_SUCCESS; + + /* populate the resulting dirent */ + offset = (rt11enum->index * di->dirent_size) + 10; + rt_ent.status = pick_integer_le(rt11enum->segment_data, offset, 2); + memcpy(rt_ent.filename, &rt11enum->segment_data[offset + 2], 6); + rt_ent.filesize = pick_integer_le(rt11enum->segment_data, offset + 8, 2) * BLOCK_SIZE; + rt_ent.data = rt11enum->data; + rt_ent.time = pick_integer_le(rt11enum->segment_data, offset + 12, 2); + rt11enum->data += pick_integer_le(rt11enum->segment_data, offset + 8, 2); + + /* identify next entry */ + next_segment = rt11enum->segment; + next_index = rt11enum->index + 1; + if (next_index >= di->dirents_per_block || (rt_ent.status & E_EOS)) + { + next_segment = pick_integer_le(rt11enum->segment_data, 2, 2); + next_index = 0; + } + + if (next_segment > di->total_segments || next_segment > di->last_segment) + return IMGTOOLERR_CORRUPTIMAGE; + + /* seek next segment */ + err = rt11_enum_seek(image, rt11enum, next_segment, next_index); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t rt11_image_beginenum(imgtool::directory &enumeration, const char *path) +{ + imgtoolerr_t err; + imgtool::image &image(enumeration.image()); + + /* seek initial block */ + err = rt11_enum_seek(image, (rt11_direnum *) enumeration.extra_bytes(), 1, 0); + if (err) + return err; + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t rt11_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent) +{ + imgtoolerr_t err; + imgtool::image &image(enumeration.image()); + rt11_direnum *rt11enum = (rt11_direnum *)enumeration.extra_bytes(); + rt11_dirent rt_ent; + + do + { + err = rt11_get_next_dirent(image, rt11enum, rt_ent); + if (err) + return err; + } + while (rt11enum->segment && !is_file_storagetype(rt_ent.status)); + + /* end of file? */ + if (rt11enum->segment == 0) + { + ent.eof = 1; + return IMGTOOLERR_SUCCESS; + } + + ent.directory = 0; + ent.lastmodified_time = rt11_crack_time(rt_ent.time); + ent.filesize = rt_ent.filesize; + + rt11_filename_from_rad50(ent.filename, rt_ent.filename); + + snprintf(ent.attr, sizeof(ent.attr), "%c%c%c %4d %06o", + rt_ent.status & E_PROT ? 'P' : '.', + rt_ent.time == 0 ? 'B' : '.', + rt_ent.status & E_TENT ? 'T' : '.', + rt_ent.data, rt_ent.status); + + return IMGTOOLERR_SUCCESS; +} + +// file operations + +static imgtoolerr_t rt11_lookup_path(imgtool::image &image, const char *path, + creation_policy_t create, rt11_direnum *direnum, rt11_dirent *rt_ent) +{ + imgtoolerr_t err; + + rt11_direnum my_direnum; + if (!direnum) + direnum = &my_direnum; + + memset(direnum, 0, sizeof(*direnum)); + err = rt11_enum_seek(image, direnum, 1, 0); + if (err) + return err; + + uint16_t this_segment; + uint32_t this_index; + char filename[16]; + do + { + this_segment = direnum->segment; + this_index = direnum->index; + + err = rt11_get_next_dirent(image, direnum, *rt_ent); + if (err) + return err; + rt11_filename_from_rad50(filename, rt_ent->filename); + } + while(direnum->segment && (strcmp(path, filename) || + !is_file_storagetype(rt_ent->status))); + + if (!direnum->segment) + { + /* did not find file; maybe we need to create it */ + if (create == CREATE_NONE) + return IMGTOOLERR_FILENOTFOUND; + } + else + { + /* we've found the file; seek that dirent */ + err = rt11_enum_seek(image, direnum, this_segment, this_index); + if (err) + return err; + } + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t rt11_read_bootblock(imgtool::partition &partition, imgtool::stream &stream) +{ + imgtoolerr_t err; + uint8_t block[BLOCK_SIZE]; + + err = rt11_image_readblock(partition.image(), block, 0); + if (err) + return err; + + stream.write(block, sizeof(block)); + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t rt11_image_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf) +{ + imgtoolerr_t err; + imgtool::image &image(partition.image()); + rt11_dirent rt_ent; + uint8_t buffer[BLOCK_SIZE]; + + if (filename == FILENAME_BOOTBLOCK) + return rt11_read_bootblock(partition, destf); + + err = rt11_lookup_path(image, filename, CREATE_NONE, NULL, &rt_ent); + if (err) + return err; + + for (uint16_t i = rt_ent.data; i < rt_ent.data + (rt_ent.filesize / BLOCK_SIZE); i++) + { + err = rt11_image_readblock(image, buffer, i); + if (err) + return err; + + destf.write(buffer, BLOCK_SIZE); + } + + return IMGTOOLERR_SUCCESS; +} + + +void rt11_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch (state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case IMGTOOLINFO_INT_PREFER_UCASE: info->i = 1; break; + case IMGTOOLINFO_INT_OPEN_IS_STRICT: info->i = 1; break; + case IMGTOOLINFO_INT_SUPPORTS_LASTMODIFIED_TIME: info->i = 1; break; + case IMGTOOLINFO_INT_SUPPORTS_BOOTBLOCK: info->i = 1; break; + case IMGTOOLINFO_INT_BLOCK_SIZE: info->i = BLOCK_SIZE; break; + case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(rt11_diskinfo); break; + case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(rt11_direnum); break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "rt11"); break; + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "RT11 format"); break; + case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break; + case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), EOLN_CRLF); break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case IMGTOOLINFO_PTR_INFO: info->info = rt11_image_info; break; + case IMGTOOLINFO_PTR_MAKE_CLASS: info->make_class = imgtool_floppy_make_class; break; + case IMGTOOLINFO_PTR_GET_GEOMETRY: info->get_geometry = rt11_image_get_geometry; break; + case IMGTOOLINFO_PTR_READ_BLOCK: info->read_block = rt11_image_readblock; break; + case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = rt11_image_beginenum; break; + case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = rt11_image_nextenum; break; + case IMGTOOLINFO_PTR_READ_FILE: info->read_file = rt11_image_readfile; break; + + case IMGTOOLINFO_PTR_FLOPPY_OPEN: info->open = rt11_image_open; break; + case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_default; break; + } +} diff --git a/src/tools/imgtool/modules/thomson.cpp b/src/tools/imgtool/modules/thomson.cpp new file mode 100644 index 0000000..be584bd --- /dev/null +++ b/src/tools/imgtool/modules/thomson.cpp @@ -0,0 +1,2073 @@ +// license:BSD-3-Clause +// copyright-holders:Antoine Mine +/**************************************************************************** + + Copyright (C) Antoine Mine' 2006 + + Thomson 8-bit micro-computers. + + Handles SAP, QD and FD floppy formats with a BASIC-DOS filesystem + (most floppies except some games and demo). + +*****************************************************************************/ + +/* TODO: + - improve two-sided floppy support + - check & correct BASIC read filter + - implement BASIC write filter + - handle accented letters + */ + + +/* Thomson floppy geometry are: + - 5"1/4 single density: 40 tracks, 16 sectors/track, 128 bytes/sector + - 5"1/4 double density: 40 tracks, 16 sectors/track, 256 bytes/sector + - 3"1/2 double density: 80 tracks, 16 sectors/track, 256 bytes/sector + - 2"8: 25 logical tracks, 16 logical sectors/track, 128 bytes/sector + + Notes: + + - 2"8 QDD actually contain a single spiraling track, with 400 128-byte + sectors; however, the filesystem considers it has 25 virtual tracks of + 16 sectors => we do the same in imgtool (but thomflop.c uses a 400-sector + addressing to emulate the device hardware) + + - tracks always have 16 sectors, this is requred by the filesystem + + - two-sided floppies are handled as two one-sided floppies in two + logical floppy drives on the original Thomsons; each has its own + independent FAT and directory. + In imgtool, we handle two-sided images as two partitions. + For now, only 3"1/2 .fd and QDD .qd files can be two-sided. + + - it seems possible to create 3"1/2 single density floppies on the TO9, + I am not sure this is standard, and we do not support it... + +*/ + + +/* Microsoft BASIC Filesystem. + + The floppy is divided into blocks, with always two blocks / track. + Also, in double density, only the first 255 bytes of each 256-byte + sector is used. Thus, blocks are 1024 bytes in single density, and + 2040 bytes in double density. + Depending on the number of tracks, there can be 50, 80 or 160 blocks. + + Filesystem data always occupy track 20, whatever the floppy geometry. + + * sector 1: + - bytes 0-7: floppy name + - remaining bytes: undocumented, seem unused + + * sector 2: FAT, 1 byte info per block + - byte 0: always 0 + - byte 1: block 0 + ... + - byte i: block i-1 + ... + - byte 160: block 159 + - remaining bytes: undocumented, seem unused + + The byte has the following meaning: + - ff: block is free + - fb: block is reserved + - 00-bf: block allocated, points to then next file block + - c1-c8: last block in file, (value & 15) is the number of sectors + actually allocated to the file + + Note: in single density, we can reference only 127 blocks. This is not + a problem because single density floppies can only have 50 or 80 blocks, + never 160. + + * sector 3-16: directory + + no subdirectories + + each file entry occupies 32 bytes: + - bytes 00-07: filename, padded with whitespaces + - bytes 08-0A: file extension, padedd with whitespaces + - byte 0B: file type + . 0 = BASIC, ASCII or binary program (B) + . 1 = BASIC or ASCII data (D) + . 2 = machine code (M) + . 3 = ASCII assembler file (A) + - byte 0C: format flag + . 00 = binary (tokenized) + . ff = ASCII + - byte 0D: first block + - bytes 0E-0F: bytes used in the last sector + - bytes 10-17: comment + - byte 18: day (of month) + - byte 19: month + - byte 1A: year (two last digits) + - bytes 1B-1f: unknown + + Note that byte 0 has a special significance: + - 00: free directory entry + - 20-7F: actual directory entry, value is the first character ini filename + - ff: end of directory + + There can be 56 files in single density, 112 in double density. + */ + +#include "imgtool.h" +#include "filter.h" +#include "iflopimg.h" + +#include "formats/imageutl.h" +#include "corestr.h" +#include "opresolv.h" + +#include +#include + + +#define MAXSIZE 80*16*256*2 /* room for two faces, double-density, 80 tracks */ + +struct thom_floppy { + imgtool::stream *stream; + + uint16_t sector_size; /* 128 or 256 */ + uint16_t sectuse_size; /* bytes used in sector: 128 or 255 */ + uint8_t tracks; /* 25, 40, or 80 */ + uint8_t heads; /* 1 or 2 */ + uint8_t data[MAXSIZE]; /* image data */ + + int modified; /* data need to be copied back to image file */ + +}; + + +enum thom_dirent_type { + THOM_DIRENT_END, + THOM_DIRENT_FREE, + THOM_DIRENT_FILE, + THOM_DIRENT_INVALID, + +}; + + +struct thom_dirent { + thom_dirent_type type; + int index; + + char name[9]; + char ext[4]; + char comment[9]; + uint8_t ftype; + uint8_t format; + uint8_t firstblock; + uint16_t lastsectsize; + uint8_t day; + uint8_t month; + uint8_t year; + +}; + + +static void thom_basic_get_info(const imgtool_class *clas, uint32_t param, + union imgtoolinfo *info); + +static uint8_t* thom_get_sector(thom_floppy* f, unsigned head, + unsigned track, unsigned sector); + + +static thom_floppy *get_thom_floppy(imgtool::image &image) +{ + return (thom_floppy*)image.extra_bytes(); +} + +/*********************** .fd / .qd format ************************/ + +/* .fd and .qd formats are very simple: the sectors are simply put one after + another, starting at sector 1 of track 0 to sector 16 track 0, then + starting again at sector 1 of track 1, and so on. + There is no image or sector header, and no gap between sectors. + The number of tracks and sector size are determined by the extension + (.fd have 40 or 80 tracks, .qd have 25 tracks) and the file size. +*/ + +static imgtoolerr_t thom_open_fd_qd(imgtool::image &img, imgtool::stream::ptr &&stream) +{ + thom_floppy* f = get_thom_floppy(img); + int size = stream->size(); + + f->stream = stream.get(); + f->modified = 0; + + /* guess format */ + switch ( size ) { + case 81920: + f->tracks = 40; + f->sector_size = 128; + f->sectuse_size = 128; + f->heads = 1; + break; + + case 163840: + f->tracks = 40; + f->sector_size = 256; + f->sectuse_size = 255; + f->heads = 1; + /* could also be: sector_size=128, heads=2 */ + /* maight even be: tracks=80, sector_size=128 */ + break; + + case 327680: + f->tracks = 80; + f->sector_size = 256; + f->sectuse_size = 255; + f->heads = 1; + /* could also be: tracks=40, heads=2 */ + break; + + case 655360: + f->tracks = 80; + f->sector_size = 256; + f->sectuse_size = 255; + f->heads = 2; + break; + + case 51200: + f->tracks = 25; + f->sector_size = 128; + f->sectuse_size = 128; + f->heads = 1; + break; + + case 62400: + f->tracks = 25; + f->sector_size = 128; + f->sectuse_size = 128; + f->heads = 2; + break; + + default: + return IMGTOOLERR_CORRUPTIMAGE; + } + + assert( size == f->heads * f->tracks * 16 * f->sector_size ); + + f->stream->seek(0, SEEK_SET); + if (f->stream->read(f->data, size ) < size) + return IMGTOOLERR_READERROR; + + f->stream = stream.release(); + return IMGTOOLERR_SUCCESS; +} + +static void thom_close_fd_qd(imgtool::image &img) +{ + thom_floppy* f = get_thom_floppy(img); + + /* save image */ + if ( f->modified ) { + int size = f->heads * f->tracks * 16 * f->sector_size; + f->stream->seek(0, SEEK_SET); + if (f->stream->write(f->data, size) < size) + { + /* logerror( "thom_diskimage_close_fd_qd: write error\n" ); */ + } + } + + delete f->stream; +} + + +/*********************** .sap format ************************/ + +/* SAP format, Copyright 1998 by Alexandre Pukall + + image is composed of: + - byte 0: version (0 or 1) + - bytes 1..65: signature + - bytes 66..end: sectors + + each sector is composed of + - a 4-byte header: + . byte 0: format + 0 3"1/2 double density + 1 5"1/4 low density + . byte 1: protection (?) + . byte 2: track index, in 0..79 + . byte 3: sector index, in 1..16 + - sector data, XOR 0xB3 + - 2-byte CRC of header + data, high byte first + + there is no provision for two-sided images in sap + */ + +static const int sap_magic_num = 0xB3; /* simple XOR crypt */ + +static const char sap_header[]= + "\001SYSTEME D'ARCHIVAGE PUKALL S.A.P. " + "(c) Alexandre PUKALL Avril 1998"; + +static const uint16_t sap_crc[] = { + 0x0000, 0x1081, 0x2102, 0x3183, 0x4204, 0x5285, 0x6306, 0x7387, + 0x8408, 0x9489, 0xa50a, 0xb58b, 0xc60c, 0xd68d, 0xe70e, 0xf78f, +}; + +static uint16_t thom_sap_crc( uint8_t* data, int size ) +{ + int i; + uint16_t crc = 0xffff, crc2; + for ( i = 0; i < size; i++ ) { + crc2 = ( crc >> 4 ) ^ sap_crc[ ( crc ^ data[i] ) & 15 ]; + crc = ( crc2 >> 4 ) ^ sap_crc[ ( crc2 ^ (data[i] >> 4) ) & 15 ]; + } + return crc; +} + +static imgtoolerr_t thom_open_sap(imgtool::image &img, imgtool::stream::ptr &&stream) +{ + thom_floppy* f = get_thom_floppy(img); + uint8_t buf[262]; + + f->stream = stream.get(); + f->modified = 0; + + // check image header + f->stream->seek(0, SEEK_SET); + f->stream->read(buf, 66 ); + if ( memcmp( buf+1, sap_header+1, 65 ) ) return IMGTOOLERR_CORRUPTIMAGE; + + /* guess format */ + f->stream->read(buf, 1 ); + switch ( buf[0] ) + { + case 1: + case 3: + f->heads = 1; + f->tracks = 40; + f->sector_size = 128; + f->sectuse_size = 128; + break; + case 0: + case 2: + case 4: + f->heads = 1; + f->tracks = 80; + f->sector_size = 256; + f->sectuse_size = 255; + break; + default: + return IMGTOOLERR_CORRUPTIMAGE; + } + + f->stream->seek(66, SEEK_SET); + while ( 1) { + int i, sector, track; + uint16_t crc; + + /* load sector */ + if (f->stream->read(buf, 6 + f->sector_size ) < 6 + f->sector_size ) + break; + + /* parse sector header */ + track = buf[2]; + sector = buf[3]; + if ( sector < 1 || sector > 16 || track >= f->tracks ) + return IMGTOOLERR_CORRUPTIMAGE; + + /* decrypt */ + for ( i = 0; i < f->sector_size; i++ ) buf[ i + 4 ] ^= sap_magic_num; + memcpy( thom_get_sector( f, 0, track, sector ), buf + 4, f->sector_size ); + + /* check CRC */ + crc = thom_sap_crc( buf, f->sector_size + 4 ); + if ( ( (crc >> 8) != buf[ f->sector_size + 4 ] ) || + ( (crc & 0xff) != buf[ f->sector_size + 5 ] ) ) + return IMGTOOLERR_CORRUPTIMAGE; + } + + f->stream = stream.release(); + return IMGTOOLERR_SUCCESS; +} + +static void thom_close_sap(imgtool::image &img) +{ + thom_floppy* f = get_thom_floppy(img); + + if ( f->modified ) { + int i, sector, track; + uint8_t buf[262]; + uint16_t crc; + + /* rewind */ + f->stream->seek(0, SEEK_SET); + + /* image header */ + if ( f->stream->write(sap_header, 66) < 66) { + /* logerror( "thom_diskimage_close_sap: write error\n" ); */ + return; + } + + for ( track = 0; track < f->tracks; track++ ) + for ( sector = 1; sector <= 16; sector++ ) { + /* sector header & data */ + buf[0] = ( f->tracks == 80 ) ? 2 : 1; + buf[1] = 0; + buf[2] = track; + buf[3] = sector; + memcpy( buf + 4, + thom_get_sector( f, 0, track, sector), f->sector_size ); + + /* compute crc */ + crc = thom_sap_crc( buf, f->sector_size + 4 ); + buf[ f->sector_size + 4 ] = crc >> 8; + buf[ f->sector_size + 5 ] = crc & 0xff; + + /* crypt */ + for ( i = 0; i < f->sector_size; i++ ) buf[ i + 4 ] ^= sap_magic_num; + + /* save */ + if (f->stream->write(buf, f->sector_size + 6) < + f->sector_size + 6) { + /* logerror( "thom_diskimage_close_sap: write error\n" ); */ + return; + } + } + } + + delete f->stream; +} + + +/*********************** low-level functions *********************/ + +static uint8_t* thom_get_sector(thom_floppy* f, unsigned head, + unsigned track, unsigned sector) +{ + assert( head < f->heads); + assert( track < f->tracks ); + assert( sector > 0 && sector <= 16 ); + return & f->data[ ( (head * f->tracks + track) * 16 + (sector-1) ) * + f->sector_size ]; +} + +static int thom_nb_blocks(thom_floppy* f) +{ + int n1 = f->tracks * 2; /* 2 blocks per track */ + int n2 = f->sector_size - 1; /* FAT entries */ + return (n1 < n2) ? n1 : n2; +} + +static int thom_max_dirent(thom_floppy* f) +{ + return 14 * f->sector_size / 32; +} + +/* remove trailing spaces */ +static void thom_stringify(char* str) +{ + char* s = str + strlen(str) - 1; + while ( s >= str && *s == ' ' ) { *s = 0; s--; } + for ( s = str; *s; s++ ) + if ( (uint8_t)*s == 0xff ) *s = 0; + else if ( *s < ' ' || *s >= 127 ) *s = '?'; +} + +/* pad with spaces until the length is nb */ +static void thom_unstringity(char* str, int nb) +{ + char* s = str; + while ( s < str + nb && *s ) s++; + while ( s < str + nb ) { *s = ' '; s++; } +} + +/* get filename and extension & truncate them to 8 + 3 */ +static void thom_conv_filename(const char* s, char name[9], char ext[4]) +{ + int i; + memset( name, 0, 9 ); + memset( ext, 0, 4 ); + for ( i = 0; i < 8 && *s && *s != '.'; i++, s++ ) name[i] = *s; + while ( *s && *s != '.' ) s++; + if ( *s == '.' ) { + s++; + for ( i = 0; i < 3 && *s; i++, s++ ) ext[i] = *s; + } +} + +static imgtool::datetime thom_crack_time(thom_dirent* d) +{ + /* check */ + if ( d->day < 1 || d->day > 31 || d->month < 1 || d->month > 12 ) return imgtool::datetime(); + + /* converts */ + util::arbitrary_datetime dt; + dt.second = 0; + dt.minute = 0; + dt.hour = 0; + dt.day_of_month = d->day; + dt.month = d->month; + dt.year = d->year + (d->year < 65 ? 2000 : 1900); + return imgtool::datetime(imgtool::datetime::datetime_type::LOCAL, dt); +} + +static void thom_make_time(thom_dirent* d, time_t time) +{ + if ( ! time ) { + d->day = d->month = d->year = 0; + } + else { + struct tm t = *localtime( &time ); + d->day = t.tm_mday; + d->month = t.tm_mon + 1; + d->year = t.tm_year % 100; + } +} + +static void thom_make_time_now(thom_dirent* d) +{ + time_t now; + time( &now ); + thom_make_time( d, now ); +} + +static void thom_get_dirent(thom_floppy* f, unsigned head, + unsigned n, thom_dirent* d) +{ + uint8_t* base = thom_get_sector( f, head, 20, 3 ) + n * 32; + memset( d, 0, sizeof(*d) ); + d->index = n; + if ( n >= thom_max_dirent( f ) ) + d->type = THOM_DIRENT_END; + else if ( *base == 0xff ) + d->type = THOM_DIRENT_END; + else if ( *base == 0 ) + d->type = THOM_DIRENT_FREE; + else { + int i; + /* fill regular entry */ + d->type = THOM_DIRENT_FILE; + memcpy( d->name, base, 8 ); + memcpy( d->ext, base + 8, 3 ); + memcpy( d->comment, base + 16, 8 ); + thom_stringify( d->name ); + thom_stringify( d->ext ); + thom_stringify( d->comment ); + d->ftype = base[11]; + d->format = base[12]; + d->firstblock = base[13]; + d->lastsectsize = ((int)base[14] << 8) + base[15]; + d->day = base[24]; + d->month = base[25]; + d->year = base[26]; + /* sanity check */ + for ( i = 0; i < 11; i++ ) + if ( base[i] < ' ' || base[i] > 127 ) d->type = THOM_DIRENT_INVALID; + } +} + +static void thom_set_dirent(thom_floppy* f, unsigned head, + unsigned n, thom_dirent* d) +{ + uint8_t* base = thom_get_sector( f, head, 20, 3 ) + n * 32; + if ( n >= thom_max_dirent( f ) ) return; + memset( base, 0, 32 ); + if ( d->type == THOM_DIRENT_END ) base[ 0 ] = 0xff; + else if ( d->type == THOM_DIRENT_FREE ) base[ 0 ] = 0; + else { + memcpy( base, d->name, 8 ); + memcpy( base + 8, d->ext, 3 ); + memcpy( base + 16, d->comment, 8 ); + thom_unstringity( (char*)base, 8 ); + thom_unstringity( (char*)base + 8, 3 ); + if ( base[16] ) thom_unstringity( (char*)base + 16, 8 ); + base[11] = d->ftype; + base[12] = d->format; + base[13] = d->firstblock; + base[14] = d->lastsectsize >> 8; + base[15] = d->lastsectsize & 0xff; + base[24] = d->day; + base[25] = d->month; + base[26] = d->year; + } + f->modified = 1; +} + +/* returns 1 if file found, 0 if not */ +static int thom_find_dirent(thom_floppy* f, unsigned head, + const char* name, thom_dirent* d) +{ + int n = 0; + while ( 1 ) { + thom_get_dirent( f, head, n, d ); + if ( d->type == THOM_DIRENT_END ) return 0; + if ( d->type == THOM_DIRENT_FILE ) { + char buf[13]; + sprintf( buf, "%s.%s", d->name, d->ext ); + if ( ! strcmp( buf, name ) ) return 1; + } + n++; + } +} + +/* returns 1 if free entry found, 0 if none available */ +static int thom_find_free_dirent(thom_floppy* f, unsigned head, thom_dirent* d) +{ + int n = 0; + while ( 1 ) { + thom_get_dirent( f, head, n, d ); + if ( d->type == THOM_DIRENT_FREE ) return 1; + if ( d->type == THOM_DIRENT_END ) break; + n++; + } + if ( n + 1 >= thom_max_dirent( f ) ) return 0; + thom_set_dirent( f, head, n+1, d ); + d->type = THOM_DIRENT_FREE; + return 1; +} + +/* returns the file size in bytes, or -1 if error */ +static int thom_get_file_size(thom_floppy* f, unsigned head, thom_dirent* d) +{ + uint8_t* fat = thom_get_sector( f, head, 20, 2 ); + int nbblocks = thom_nb_blocks(f); + int block = d->firstblock; + int size = 0; + int timeout = nbblocks; + if ( d->type != THOM_DIRENT_FILE ) return -1; + if ( block >= nbblocks ) return -1; + block = fat[ block + 1 ]; + while ( 1 ) { + if ( block < nbblocks ) { + /* full block */ + size += 8 * f->sectuse_size; + block = fat[ block + 1 ]; + } + else if ( block >= 0xc1 && block <= 0xc8 ) { + /* last block in file */ + size += (block-0xc1) * f->sectuse_size; + size += d->lastsectsize; + return size; + } + else return -1; + timeout--; + if ( timeout < 0 ) return -1; + } +} + +/* number of blocks used by file */ +static int thom_get_file_blocks(thom_floppy* f, unsigned head, thom_dirent* d) +{ + uint8_t* fat = thom_get_sector( f, head, 20, 2 ); + int nbblocks = thom_nb_blocks(f); + int block = d->firstblock; + int nb = 0; + if ( d->type != THOM_DIRENT_FILE ) return 0; + if ( block >= nbblocks ) return 0; + block = fat[ block + 1 ]; + while ( 1 ) { + if ( block < nbblocks ) { + /* full block */ + nb++; + block = fat[ block + 1 ]; + } + else if ( block >= 0xc1 && block <= 0xc8 ) { + /* last block in file */ + nb++; + return nb; + } + else return nb; + } +} + +/* number of free blocks */ +static int thom_get_free_blocks(thom_floppy* f, unsigned head) +{ + uint8_t* fat = thom_get_sector( f, head, 20, 2 ); + int nbblocks = thom_nb_blocks(f); + int i, nb = 0; + for ( i = 1; i <= nbblocks; i++ ) + if ( fat[i] == 0xff ) nb++; + return nb; +} + +/* dump file contents into dst */ +static void thom_get_file(thom_floppy* f, unsigned head, + thom_dirent* d, imgtool::stream &dst) +{ + uint8_t* fat = thom_get_sector( f, head, 20, 2 ); + int nbblocks = thom_nb_blocks(f); + int block = d->firstblock; + if ( block >= nbblocks ) return; + while ( 1 ) + { + int nextblock = fat[ block + 1 ]; + int track = block / 2; + int firstsect = (block % 2) ? 9 : 1; + if ( nextblock < nbblocks ) + { + /* full block */ + int i; + for ( i = 0; i < 8; i++ ) + { + uint8_t* data = thom_get_sector( f, head, track, firstsect + i ); + dst.write(data, f->sectuse_size); + } + block = fat[ block + 1 ]; + } + else if ( nextblock >= 0xc1 && nextblock <= 0xc8 ) + { + /* last block in file */ + int i; + uint8_t* data; + for ( i = 0; i < nextblock - 0xc1; i++ ) + { + data = thom_get_sector( f, head, track, firstsect + i ); + dst.write(data, f->sectuse_size); + } + data = thom_get_sector( f, head, track, firstsect + i ); + dst.write(data, d->lastsectsize); + return; + } + else + { + /* invalid, assume last block */ + uint8_t* data = thom_get_sector( f, head, track, firstsect ); + dst.write(data, d->lastsectsize); + return; + } + block = nextblock; + } +} + +static void thom_del_file(thom_floppy* f, unsigned head, thom_dirent* d) +{ + uint8_t* fat = thom_get_sector( f, head, 20, 2 ); + int nbblocks = thom_nb_blocks(f); + int block = d->firstblock; + if ( d->type != THOM_DIRENT_FILE ) return; + if ( block >= nbblocks ) return; + while ( 1 ) { + int nextblock = fat[ block + 1 ]; + fat[ block ] = 0xff; + if ( nextblock < nbblocks ) block = fat[ block + 1 ]; + else break; + } + d->type = THOM_DIRENT_FREE; + thom_set_dirent( f, head, d->index, d ); + f->modified = 1; +} + +/* create a new file or overwrite an old one, with the contents of src */ +static void thom_put_file(thom_floppy* f, unsigned head, + thom_dirent* d, imgtool::stream &src) +{ + int size = src.size(); + uint8_t* fat = thom_get_sector( f, head, 20, 2 ); + int nbblocks = thom_nb_blocks(f); + int block; + + /* find first free block */ + for ( block = 0; block < nbblocks && fat[ block + 1 ] != 0xff; block++ ); + if ( block >= nbblocks ) return; + d->firstblock = block; + + /* store file */ + while (1) { + int track = block / 2; + int firstsect = (block % 2) ? 9 : 1; + int i; + + /* store data, full sectors */ + for ( i = 0; i < 8 && size > f->sectuse_size; i++ ) { + uint8_t* dst = thom_get_sector( f, head, track, firstsect + i ); + src.read(dst, f->sectuse_size); + size -= f->sectuse_size; + } + + /* store data, last sector */ + if ( i < 8 ) { + uint8_t* dst = thom_get_sector( f, head, track, firstsect + i ); + src.read(dst, size); + fat[ block + 1 ] = 0xc1 + i; + d->lastsectsize = size; + break; + } + + /* find next free block & update fat */ + i = block; + for ( block++; block < nbblocks && fat[ block + 1 ] != 0xff; block++ ); + if ( block >= nbblocks ) { + /* out of memory! */ + fat[ i + 1 ] = 0; + d->lastsectsize = 0; + break; + } + fat[ i + 1 ] = block; + } + + d->type = THOM_DIRENT_FILE; + thom_set_dirent( f, head, d->index, d ); + f->modified = 1; +} + + +/********************** module functions ***********************/ + +static imgtoolerr_t thom_get_geometry(imgtool::image &img, uint32_t* tracks, + uint32_t* heads, uint32_t* sectors) +{ + thom_floppy* f = get_thom_floppy(img); + if ( tracks ) *tracks = f->tracks; + if ( heads ) *heads = f->heads; + if ( sectors ) *sectors = 16; + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t thom_read_sector(imgtool::image &img, uint32_t track, + uint32_t head, uint32_t sector, std::vector &buffer) +{ + thom_floppy* f = get_thom_floppy(img); + if ( head >= f->heads || sector < 1 || sector > 16 || track >= f->tracks ) + return IMGTOOLERR_SEEKERROR; + + // resize the buffer + try { buffer.resize(f->sector_size); } + catch (std::bad_alloc const &) { return IMGTOOLERR_OUTOFMEMORY; } + + memcpy( &buffer[0], thom_get_sector( f, head, track, sector ), f->sector_size); + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t thom_write_sector(imgtool::image &img, uint32_t track, + uint32_t head, uint32_t sector, + const void *buf, size_t len, int ddam) +{ + thom_floppy* f = get_thom_floppy(img); + if ( f->stream->is_read_only() ) return IMGTOOLERR_WRITEERROR; + if ( head >= f->heads || sector < 1 || sector > 16 || track >= f->tracks ) + return IMGTOOLERR_SEEKERROR; + if ( len > f->sector_size) return IMGTOOLERR_WRITEERROR; + f->modified = 1; + memcpy( thom_get_sector( f, head, track, sector ), buf, len ); + return IMGTOOLERR_SUCCESS; +} + +/* returns floopy name */ +/* actually, each side has its own name, but we only return the one on side 0. + */ +static void thom_info(imgtool::image &img, std::ostream &stream) +{ + thom_floppy* f = get_thom_floppy(img); + uint8_t* base = thom_get_sector( f, 0, 20, 1 ); + char buf[9]; + memcpy( buf, base, 8 ); + buf[8] = 0; + thom_stringify( buf ); + stream << buf; +} + +/* each side of a floppy has its own filesystem, we treat them as'partitions' + */ +static imgtoolerr_t thom_list_partitions(imgtool::image &img, std::vector &partitions) +{ + thom_floppy* f = get_thom_floppy(img); + + partitions.emplace_back(thom_basic_get_info, 0, 1); + if (f->heads >= 2) + partitions.emplace_back(thom_basic_get_info, 1, 1); + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t thom_open_partition(imgtool::partition &part, + uint64_t first_block, uint64_t block_count) +{ + imgtool::image &img(part.image()); + thom_floppy* f = get_thom_floppy(img); + if ( first_block >= f->heads ) + return IMGTOOLERR_INVALIDPARTITION; + * ( (int*) part.extra_bytes() ) = first_block; + return IMGTOOLERR_SUCCESS; +} + + +static imgtoolerr_t thom_begin_enum(imgtool::directory &enumeration, + const char *path) +{ + int* n = (int*) enumeration.extra_bytes(); + *n = 0; + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t thom_next_enum(imgtool::directory &enumeration, imgtool_dirent &ent) +{ + imgtool::partition &part(enumeration.partition()); + int head = *( (int*) part.extra_bytes() ); + imgtool::image &img(part.image()); + thom_floppy* f = get_thom_floppy(img); + int* n = (int*) enumeration.extra_bytes(); + thom_dirent d; + + do { + thom_get_dirent( f, head, *n, &d ); + (*n) ++; + } + while ( d.type == THOM_DIRENT_FREE ); + if ( d.type == THOM_DIRENT_END ) ent.eof = 1; + else if ( d.type == THOM_DIRENT_INVALID ) { + ent.corrupt = 1; + } + else { + int size; + snprintf( ent.filename, sizeof(ent.filename), "%s.%s", d.name, d.ext ); + snprintf( ent.attr, sizeof(ent.attr), "%c %c %s", + (d.ftype == 0) ? 'B' : (d.ftype == 1) ? 'D' : + (d.ftype == 2) ? 'M' : (d.ftype == 3) ? 'A' : '?', + (d.format == 0) ? 'B' : (d.format == 0xff) ? 'A' : '?', + d.comment ); + ent.creation_time = thom_crack_time( &d ); + size = thom_get_file_size( f, head, &d ); + if ( size >= 0 ) ent.filesize = size; + else { + ent.filesize = 0; + ent.corrupt = 1; + } + } + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t thom_free_space(imgtool::partition &part, uint64_t *size) +{ + int head = *( (int*) part.extra_bytes() ); + imgtool::image &img(part.image()); + thom_floppy* f = get_thom_floppy(img); + int nb = thom_get_free_blocks( f, head ); + (*size) = nb * f->sectuse_size * 8; + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t thom_read_file(imgtool::partition &part, + const char *filename, + const char *fork, + imgtool::stream &destf) +{ + int head = *( (int*) part.extra_bytes() ); + imgtool::image &img(part.image()); + thom_floppy* f = get_thom_floppy(img); + thom_dirent d; + char name[9], ext[4], fname[14]; + int size; + + /* convert filename */ + thom_conv_filename( filename, name, ext ); + sprintf( fname, "%s.%s", name, ext ); + + if ( ! thom_find_dirent( f, head, fname, &d ) ) + return IMGTOOLERR_FILENOTFOUND; + size = thom_get_file_size( f, head, &d ); + if ( size < 0 ) return IMGTOOLERR_CORRUPTFILE; + thom_get_file( f, head, &d, destf ); + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t thom_delete_file(imgtool::partition &part, + const char *filename) +{ + int head = *( (int*) part.extra_bytes() ); + imgtool::image &img(part.image()); + thom_floppy* f = get_thom_floppy(img); + thom_dirent d; + char name[9], ext[4], fname[14]; + + /* convert filename */ + thom_conv_filename( filename, name, ext ); + sprintf( fname, "%s.%s", name, ext ); + + if ( ! thom_find_dirent( f, head, fname, &d ) ) + return IMGTOOLERR_FILENOTFOUND; + /*if ( thom_get_file_size( f, head, &d ) < 0 ) return IMGTOOLERR_CORRUPTFILE;*/ + if ( f->stream->is_read_only() ) return IMGTOOLERR_WRITEERROR; + thom_del_file( f, head, &d ); + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t thom_write_file(imgtool::partition &part, + const char *filename, + const char *fork, + imgtool::stream &sourcef, + util::option_resolution *opts) +{ + int head = *( (int*) part.extra_bytes() ); + imgtool::image &img(part.image()); + thom_floppy* f = get_thom_floppy(img); + thom_dirent d; + int size = sourcef.size(); + int blocks = thom_get_free_blocks( f, head ); + char name[9], ext[4], fname[14]; + int is_new = 1; + + if ( f->stream->is_read_only() ) return IMGTOOLERR_WRITEERROR; + + /* convert filename */ + thom_conv_filename( filename, name, ext ); + sprintf( fname, "%s.%s", name, ext ); + + /* check available space & find dir entry */ + if ( thom_find_dirent( f, head, fname, &d ) ) { + /* file already exists: delete it */ + if ( thom_get_file_size( f, head, &d ) < 0 ) return IMGTOOLERR_CORRUPTFILE; + blocks += thom_get_file_blocks( f, head, &d ); + if ( blocks * 8 * f->sectuse_size < size ) return IMGTOOLERR_NOSPACE; + thom_del_file( f, head, &d ); + is_new = 0; + } + else { + /* new file, need new dir entry */ + if ( blocks * 8 * f->sectuse_size < size ) return IMGTOOLERR_NOSPACE; + if ( ! thom_find_free_dirent( f, head, &d ) ) return IMGTOOLERR_NOSPACE; + thom_make_time_now( &d ); + } + + /* fill-in dir entry */ + memcpy( d.name, name, 9 ); + memcpy( d.ext, ext, 4 ); + + /* file type */ + switch ( opts->lookup_int('T') ) { + case 0: + if ( ! is_new ) break; + if ( ! core_stricmp( ext, "BAS" ) ) d.ftype = 0; + else if ( ! core_stricmp( ext, "BAT" ) ) d.ftype = 0; + else if ( ! core_stricmp( ext, "DAT" ) ) d.ftype = 1; + else if ( ! core_stricmp( ext, "ASC" ) ) d.ftype = 1; + else if ( ! core_stricmp( ext, "BIN" ) ) d.ftype = 2; + else if ( ! core_stricmp( ext, "MAP" ) ) d.ftype = 2; + else if ( ! core_stricmp( ext, "CFG" ) ) d.ftype = 2; + else if ( ! core_stricmp( ext, "ASM" ) ) d.ftype = 3; + else d.ftype = 2; + break; + case 1: d.ftype = 0; break; + case 2: d.ftype = 1; break; + case 3: d.ftype = 2; break; + case 4: d.ftype = 3; break; + } + + /* format flag */ + switch ( opts->lookup_int('F') ) { + case 0: + if ( ! is_new ) break; + if ( ! core_stricmp( ext, "DAT" ) ) d.format = 0xff; + else if ( ! core_stricmp( ext, "ASC" ) ) d.format = 0xff; + else if ( ! core_stricmp( ext, "ASM" ) ) d.format = 0xff; + else d.format = 0; + break; + case 1: d.format = 0; break; + case 2: d.format = 0xff; break; + } + + /* comment */ + char const *const comment = opts->lookup_string('C').c_str(); + if (comment && *comment) + strncpy(d.comment, comment, 8); + + /* write file */ + thom_put_file( f, head, &d, sourcef ); + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t thom_suggest_transfer(imgtool::partition &part, + const char *fname, + imgtool_transfer_suggestion *suggestions, + size_t suggestions_length) +{ + int head = *( (int*) part.extra_bytes() ); + imgtool::image &img(part.image()); + thom_floppy* f = get_thom_floppy(img); + thom_dirent d; + int is_basic = 0; + + if ( suggestions_length < 1 ) return IMGTOOLERR_SUCCESS; + + if ( fname ) { + if ( ! thom_find_dirent( f, head, fname, &d ) ) + return IMGTOOLERR_FILENOTFOUND; + if ( d.ftype == 0 && d.format == 0 ) is_basic = 1; + } + + if ( is_basic ) { + suggestions[0].viability = SUGGESTION_RECOMMENDED; + suggestions[0].filter = filter_thombas128_getinfo; + if ( suggestions_length >= 2 ) { + suggestions[1].viability = SUGGESTION_POSSIBLE; + suggestions[1].filter = filter_thombas7_getinfo; + } + if ( suggestions_length >= 3 ) { + suggestions[2].viability = SUGGESTION_POSSIBLE; + suggestions[2].filter = filter_thombas5_getinfo; + } + if ( suggestions_length >= 4 ) { + suggestions[3].viability = SUGGESTION_POSSIBLE; + suggestions[3].filter = NULL; + } + } + else { + suggestions[0].viability = SUGGESTION_RECOMMENDED; + suggestions[0].filter = NULL; + } + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t thom_create(imgtool::image &img, + imgtool::stream::ptr &&stream, + util::option_resolution *opts) +{ + thom_floppy* f = get_thom_floppy(img); + int i; + uint8_t* buf; + const char* name; + + f->stream = stream.get(); + f->modified = 0; + + /* get parameters */ + f->heads = opts->lookup_int('H'); + f->tracks = opts->lookup_int('T'); + name = opts->lookup_string('N').c_str(); + switch ( opts->lookup_int('D') ) { + case 0: f->sector_size = 128; f->sectuse_size = 128; break; + case 1: f->sector_size = 256; f->sectuse_size = 255; break; + default: return IMGTOOLERR_PARAMCORRUPT; + } + + /* sanity checks */ + switch ( f->tracks ) { + case 25: if ( f->sector_size != 128 ) return IMGTOOLERR_PARAMCORRUPT; break; + case 40: break; + case 80: if ( f->sector_size != 256 ) return IMGTOOLERR_PARAMCORRUPT; break; + default: return IMGTOOLERR_PARAMCORRUPT; + } + + memset( f->data, 0xe5, sizeof( f->data ) ); + + for ( i = 0; i < f->heads; i++ ) { + /* disk info */ + buf = thom_get_sector( f, i, 20, 1 ); + memset( buf, 0xff, f->sector_size ); + if ( name ) { + strncpy( (char*)buf, name, 8 ); + thom_unstringity( (char*)buf, 8 ); + } + else memset( buf, ' ', 8 ); + + /* FAT */ + buf = thom_get_sector( f, i, 20, 2 ); + memset( buf, 0, f->sector_size ); + memset( buf + 1, 0xff, thom_nb_blocks( f ) ); + buf[ 41 ] = buf[ 42 ] = 0xfe; /* reserved for FAT */ + + /* (empty) directory */ + buf = thom_get_sector( f, i, 20, 3 ); + memset( buf, 0xff, 14 * f->sector_size ); + } + + f->modified = 1; + + f->stream = stream.release(); + return IMGTOOLERR_SUCCESS; +} + + +/****************** BASIC tokenization *********************/ + +/* character codes >= 128 are reserved for BASIC keywords */ + +static const char *const thombas7[2][128] = { + { /* statements */ + "END", "FOR", "NEXT", "DATA", "DIM", "READ", "LET", "GO", "RUN", "IF", + "RESTORE", "RETURN", "REM", "'", "STOP", "ELSE", "TRON", "TROFF", "DEFSTR", + "DEFINT", "DEFSNG", "DEFDBL", "ON", "WAIT", "ERROR", "RESUME", "AUTO", + "DELETE", "LOCATE", "CLS", "CONSOLE", "PSET", "MOTOR", "SKIP", "EXEC", + "BEEP", "COLOR", "LINE", "BOX", "UNMASK", "ATTRB", "DEF", "POKE", "PRINT", + "CONT", "LIST", "CLEAR", "WHILE", "WHEN", "NEW", "SAVE", "LOAD", "MERGE", + "OPEN", "CLOSE", "INPEN", "PEN", "PLAY", "TAB(", "TO", "SUB", "FN", + "SPC(", "USING", "USR", "ERL", "ERR", "OFF", "THEN", "NOT", "STEP", + "+", "-", "*", "/", "^", "AND", "OR", "XOR", "EQV", "IMP", "MOD", "@", + ">", "=", "<", + /* DOS specific */ + "DSKINI", "DSKO$", "KILL", "NAME", "FIELD", "LSET", "RSET", + "PUT", "GET", "VERIFY", "DEVICE", "DIR", "FILES", "WRITE", "UNLOAD", + "BACKUP", "COPY", "CIRCLE", "PAINT", "DRAW", "RENUM", "SWAP", "DENSITY", + }, + { /* functions: 0xff prefix */ + "SGN", "INT", "ABS", "FRE", "SQR", "LOG", "EXP", "COS", "SIN", + "TAN", "PEEK", "LEN", "STR$", "VAL", "ASC", "CHR$", "EOF", "CINT", "CSNG", + "CDBL", "FIX", "HEX$", "OCT$", "STICK", "STRIG", "GR$", "LEFT$", "RIGHT$", + "MID$", "INSTR", "VARPTR", "RND", "INKEY$", "INPUT", "CSRLIN", "POINT", + "SCREEN", "POS", "PTRIG", + /* DOS specific */ + "DSKL", "CVI", "CVS", "CVD", "MKI$", "MKS$", "MKD$", "LOC", "LOF", + "SPACE$", "STRING$", "DSKI$", + } +}; + +/* MO5: some keywords ar missing; DOS and TUNE are added */ +static const char *const thombas5[2][128] = { + { /* statements */ + "END", "FOR", "NEXT", "DATA", "DIM", "READ", NULL, "GO", "RUN", "IF", + "RESTORE", "RETURN", "REM", "'", "STOP", "ELSE", "TRON", "TROFF", "DEFSTR", + "DEFINT", "DEFSNG", NULL, "ON", "TUNE", "ERROR", "RESUME", "AUTO", + "DELETE", "LOCATE", "CLS", "CONSOLE", "PSET", "MOTOR", "SKIP", "EXEC", + "BEEP", "COLOR", "LINE", "BOX", NULL, "ATTRB", "DEF", "POKE", "PRINT", + "CONT", "LIST", "CLEAR", "DOS", NULL, "NEW", "SAVE", "LOAD", "MERGE", + "OPEN", "CLOSE", "INPEN", "PEN", "PLAY", "TAB(", "TO", "SUB", "FN", + "SPC(", "USING", "USR", "ERL", "ERR", "OFF", "THEN", "NOT", "STEP", + "+", "-", "*", "/", "^", "AND", "OR", "XOR", "EQV", "IMP", "MOD", "@", + ">", "=", "<", + /* DOS specific */ + "DSKINI", "DSKO$", "KILL", "NAME", "FIELD", "LSET", "RSET", + "PUT", "GET", "VERIFY", "DEVICE", "DIR", "FILES", "WRITE", "UNLOAD", + "BACKUP","COPY", "CIRCLE", "PAINT", "DRAW", "RENUM", "SWAP", "SEARCH", + "DENSITY", + }, + { /* functions: 0xff prefix */ + "SGN", "INT", "ABS", "FRE", "SQR", "LOG", "EXP", "COS", "SIN", + "TAN", "PEEK", "LEN", "STR$", "VAL", "ASC", "CHR$", "EOF", "CINT", NULL, + NULL, "FIX", "HEX$", NULL, "STICK", "STRIG", "GR$", "LEFT$", "RIGHT$", + "MID$", "INSTR", "VARPTR", "RND", "INKEY$", "INPUT", "CSRLIN", "POINT", + "SCREEN", "POS", "PTRIG", + /* DOS specific */ + "DSKL", "CVI", "CVS", "CVD", "MKI$", "MKS$", "MKD$", "LOC", "LOF", + "SPACE$", "STRING$", "DSKI$", + } +}; + +/* BASIC 128 has many new keywords */ +/* DENSITY is missing on BASIC 512 & BASIC 128 for MO6 but, otherwise, they + have the same tokens +*/ +static const char *const thombas128[2][128] = { + { /* statements */ + "END", "FOR", "NEXT", "DATA", "DIM", "READ", "LET", "GO", "RUN", "IF", + "RESTORE", "RETURN", "REM", "'", "STOP", "ELSE", "TRON", "TROFF", "DEFSTR", + "DEFINT", "DEFSNG", "DEFDBL", "ON", "WAIT", "ERROR", "RESUME", "AUTO", + "DELETE", "LOCATE", "CLS", "CONSOLE", "PSET", "MOTOR", "SKIP", "EXEC", + "BEEP", "COLOR", "LINE", "BOX", "UNMASK", "ATTRB", "DEF", "POKE", "PRINT", + "CONT", "LIST", "CLEAR", "INTERVAL", "KEY", "NEW", "SAVE", "LOAD", "MERGE", + "OPEN", "CLOSE", "INPEN", "PEN", "PLAY", "TAB(", "TO", "SUB", "FN", + "SPC(", "USING", "USR", "ERL", "ERR", "OFF", "THEN", "NOT", "STEP", + "+", "-", "*", "/", "^", "AND", "OR", "XOR", "EQV", "IMP", "MOD", "@", + ">", "=", "<", + "DSKINI", "DSKO$", "KILL", "NAME", "FIELD", "LSET", "RSET", + "PUT", "GET", "VERIFY", "DEVICE", "DIR", "FILES", "WRITE", "UNLOAD", + "BACKUP", "COPY", "CIRCLE", "PAINT", "RESET", "RENUM", "SWAP", "DENSITY", + "WINDOW", "PATTERN", "DO", "LOOP", "EXIT", "INMOUSE", "MOUSE", "CHAINE", + "COMMON", "SEARCH", "FWD", "TURTLE", + }, + { /* functions: 0xff prefix */ + "SGN", "INT", "ABS", "FRE", "SQR", "LOG", "EXP", "COS", "SIN", + "TAN", "PEEK", "LEN", "STR$", "VAL", "ASC", "CHR$", "EOF", "CINT", "CSNG", + "CDBL", "FIX", "HEX$", "OCT$", "STICK", "STRIG", "GR$", "LEFT$", "RIGHT$", + "MID$", "INSTR", "VARPTR", "RND", "INKEY$", "INPUT", "CSRLIN", "POINT", + "SCREEN", "POS", "PTRIG", + "DSKL", "CVI", "CVS", "CVD", "MKI$", "MKS$", "MKD$", "LOC", "LOF", + "SPACE$", "STRING$", "DSKI$", + "FKEY$", "MIN(", "MAX(", "ATN", "CRUNCH$", "MTRIG", "EVAL", "PALETTE", + "BANK", "HEAD", "ROT", "SHOW", "ZOOM", "TRACE", + } +}; + +/* tables for simple XOR encryption sieve */ +static const uint8_t crypt1[11] = { + 0x80, 0x19, 0x56, 0xAA, 0x80, 0x76, 0x22, 0xF1, 0x82, 0x38, 0xAA +}; + +static const uint8_t crypt2[13] = { + 0x86, 0x1E, 0xD7, 0xBA, 0x87, 0x99, 0x26, 0x64, 0x87, 0x23, 0x34, 0x58, 0x86 +}; + +/* decrypt BASIC protected files */ +static void thom_decrypt(imgtool::stream &out, imgtool::stream &in) +{ + int i1 = 11, i2 = 13; + while ( 1 ) + { + uint8_t b; + if ( in.read(&b, 1) < 1 ) break; + b = ( (uint8_t)(b - i2) ^ crypt2[i2-1] ^ crypt1[i1-1] ) + i1; + out.putc(b ); + i1--; i2--; + if ( !i1 ) i1 = 11; + if ( !i2 ) i2 = 13; + } +} + +/* encrypt BASIC protected files */ +static void thom_encrypt(imgtool::stream &out, imgtool::stream &in) +{ + int i1 = 11, i2 = 13; + while ( 1 ) + { + uint8_t b; + if ( in.read(&b, 1) < 1 ) break; + b = ( (uint8_t)(b - i1) ^ crypt2[i2-1] ^ crypt1[i1-1] ) + i2; + out.putc(b ); + i1--; i2--; + if ( !i1 ) i1 = 11; + if ( !i2 ) i2 = 13; + } +} + +static imgtoolerr_t thomcrypt_read_file(imgtool::partition &part, + const char *name, + const char *fork, imgtool::stream &dst) +{ + uint8_t buf[3]; + imgtool::stream::ptr org = imgtool::stream::open_mem(nullptr, 0); + imgtoolerr_t err; + if ( !org ) return IMGTOOLERR_OUTOFMEMORY; + + /* read file */ + err = thom_read_file( part, name, fork, *org ); + if (err) + return err; + + org->seek(0, SEEK_SET); + if ( org->read(buf, 3 ) < 3 || buf[0] != 0xfe ) + { + /* regular file */ + org->seek(0, SEEK_SET); + imgtool::stream::transfer_all( dst, *org ); + } + else + { + /* encrypted file */ + dst.putc( '\xff' ); + dst.write(buf+1, 2); + thom_decrypt( dst, *org ); + } + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t thomcrypt_write_file(imgtool::partition &part, + const char *name, + const char *fork, imgtool::stream &src, + util::option_resolution *opts) +{ + uint8_t buf[3]; + + if ( src.read(buf, 3 ) < 3 || buf[0] == 0xfe ) + { + /* too short or already encrypted file */ + src.seek(0, SEEK_SET); + return thom_write_file( part, name, fork, src, opts ); + } + else + { + /* regular file */ + imgtool::stream::ptr dst = imgtool::stream::open_mem(nullptr, 0); + if ( !dst ) return IMGTOOLERR_OUTOFMEMORY; + dst->putc( '\xfe' ); + dst->write(buf+1, 2); + thom_encrypt( *dst, src ); + dst->seek(0, SEEK_SET); + return thom_write_file( part, name, fork, *dst, opts ); + } +} + +void filter_thomcrypt_getinfo(uint32_t state, union filterinfo *info) +{ + switch(state) { + case FILTINFO_STR_NAME: + info->s = "thomcrypt"; + break; + case FILTINFO_STR_HUMANNAME: + info->s = "Thomson BASIC, Protected file encryption (no tokenization)"; + break; + case FILTINFO_PTR_READFILE: + info->read_file = thomcrypt_read_file; + break; + case FILTINFO_PTR_WRITEFILE: + info->write_file = thomcrypt_write_file; + break; + } +} + +static void +thom_accent_grave(imgtool::stream &dst, + uint8_t c) +{ + switch ( c ) { + case 'a': dst.printf("\xc3\xa0"); break; + case 'e': dst.printf("\xc3\xa8"); break; + case 'i': dst.printf("\xc3\xac"); break; + case 'o': dst.printf("\xc3\xb2"); break; + case 'u': dst.printf("\xc3\xb9"); break; + case 'A': dst.printf("\xc3\x80"); break; + case 'E': dst.printf("\xc3\x88"); break; + case 'I': dst.printf("\xc3\x8c"); break; + case 'O': dst.printf("\xc3\x92"); break; + case 'U': dst.printf("\xc3\x99"); break; + default: break; /* invalid data */ + } +} + +static void +thom_accent_acute(imgtool::stream &dst, + uint8_t c) +{ + switch ( c ) { + case 'a': dst.printf("\xc3\xa1"); break; + case 'e': dst.printf("\xc3\xa9"); break; + case 'i': dst.printf("\xc3\xad"); break; + case 'o': dst.printf("\xc3\xb3"); break; + case 'u': dst.printf("\xc3\xba"); break; + case 'A': dst.printf("\xc3\x81"); break; + case 'E': dst.printf("\xc3\x89"); break; + case 'I': dst.printf("\xc3\x8d"); break; + case 'O': dst.printf("\xc3\x93"); break; + case 'U': dst.printf("\xc3\x9a"); break; + default: break; + } +} + +static void +thom_accent_circ(imgtool::stream &dst, + uint8_t c) +{ + switch ( c ) { + case 'a': dst.printf("\xc3\xa2"); break; + case 'e': dst.printf("\xc3\xaa"); break; + case 'i': dst.printf("\xc3\xae"); break; + case 'o': dst.printf("\xc3\xb4"); break; + case 'u': dst.printf("\xc3\xbb"); break; + case 'A': dst.printf("\xc3\x82"); break; + case 'E': dst.printf("\xc3\x8a"); break; + case 'I': dst.printf("\xc3\x8e"); break; + case 'O': dst.printf("\xc3\x94"); break; + case 'U': dst.printf("\xc3\x9b"); break; + default: break; /* invalid data */ + } +} + +static void +thom_accent_uml(imgtool::stream &dst, + uint8_t c) +{ + switch ( c ) { + case 'a': dst.printf("\xc3\xa4"); break; + case 'e': dst.printf("\xc3\xab"); break; + case 'i': dst.printf("\xc3\xaf"); break; + case 'o': dst.printf("\xc3\xb6"); break; + case 'u': dst.printf("\xc3\xbc"); break; + case 'A': dst.printf("\xc3\x84"); break; + case 'E': dst.printf("\xc3\x8b"); break; + case 'I': dst.printf("\xc3\x8f"); break; + case 'O': dst.printf("\xc3\x96"); break; + case 'U': dst.printf("\xc3\x9c"); break; + default: break; /* invalid data */ + } +} + +static void +thom_accent_cedil(imgtool::stream &dst, + uint8_t c) +{ + switch ( c ) { + case 'c': dst.printf("\xc3\xa7"); break; + case 'C': dst.printf("\xc3\x87"); break; + default: break; /* invalid data */ + } +} + +static void +thom_basic_specialchar(imgtool::stream::ptr &src, + imgtool::stream &dst, + uint8_t c) +{ + uint8_t l; + + /* special characters */ + switch ( c ) { + case '#': dst.printf("\xc2\xa3"); return; /* pound */ + case '$': dst.printf("$"); return; /* dollar */ + case '&': dst.printf("#"); return; /* diesis */ + case ',': dst.printf("\xe2\x86\x90"); return; /* arrow left */ + case '-': dst.printf("\xe2\x86\x91"); return; /* arrow up */ + case '.': dst.printf("\xe2\x86\x92"); return; /* arrow right */ + case '/': dst.printf("\xe2\x86\x93"); return; /* arrow down */ + case '0': dst.printf("\xc2\xb0"); return; /* ° */ + case '1': dst.printf("\xc2\xb1"); return; /* ± */ + case '8': dst.printf("\xc3\xb7"); return; /* ÷ */ + case '<': dst.printf("\xc2\xbc"); return; /* ¼ */ + case '=': dst.printf("\xc2\xbd"); return; /* ½ */ + case '>': dst.printf("\xc2\xbe"); return; /* ¾ */ + case 'j': dst.printf("\xc5\x92"); return; /* Å’ */ + case 'z': dst.printf("\xc5\x93"); return; /* Å“ */ + case '{': dst.printf("\xc3\x9f"); return; /* ß */ + case '\'': dst.printf("\xc2\xa7"); return; /* § */ + case 'A': + case 'B': + case 'C': + case 'H': + case 'K': break; /* accents */ + default: return; /* invalid data */ + } + + if ( src->read(&l, 1 ) < 1 ) return; + + /* accents */ + switch ( c ) { + case 'A': thom_accent_grave(dst, l); return; + case 'B': thom_accent_acute(dst, l); return; + case 'C': thom_accent_circ(dst, l); return; + case 'H': thom_accent_uml(dst, l); return; + case 'K': thom_accent_cedil(dst, l); return; + default: break; + } + + /* invalid data */ +} + +struct { + const char *utf8; + const char *thom; +} special_chars[] = { + { "\xc2\xa3", "\x16" "#" }, /* £ */ + { "$", "\x16" "$" }, + { "#", "\x16" "&" }, + { "\xe2\x86\x90", "\x16" "," }, /* arrow left */ + { "\xe2\x86\x91", "\x16" "-" }, /* arrow up */ + { "\xe2\x86\x92", "\x16" "." }, /* arrow right */ + { "\xe2\x86\x93", "\x16" "/" }, /* arrow down */ + { "\xc2\xb0", "\x16" "0" }, /* ° */ + { "\xc2\xb1", "\x16" "1" }, /* ± */ + { "\xc3\xb7", "\x16" "8" }, /* ÷ */ + { "\xc2\xbc", "\x16" "<" }, /* ¼ */ + { "\xc2\xbd", "\x16" "=" }, /* ½ */ + { "\xc2\xbe", "\x16" ">" }, /* ¾ */ + { "\xc5\x92", "\x16" "j" }, /* Å’ */ + { "\xc5\x93", "\x16" "z" }, /* Å“ */ + { "\xc3\x9f", "\x16" "{" }, /* ß */ + { "\xc2\xa7", "\x16" "'" }, /* § */ + + /* accent grave */ + { "\xc3\xa0", "\x16" "Aa" }, + { "\xc3\xa8", "\x16" "Ae" }, + { "\xc3\xac", "\x16" "Ai" }, + { "\xc3\xb2", "\x16" "Ao" }, + { "\xc3\xb9", "\x16" "Au" }, + { "\xc3\x80", "\x16" "AA" }, + { "\xc3\x88", "\x16" "AE" }, + { "\xc3\x8c", "\x16" "AI" }, + { "\xc3\x92", "\x16" "AO" }, + { "\xc3\x99", "\x16" "AU" }, + + /* accent acute */ + { "\xc3\xa1", "\x16" "Ba" }, + { "\xc3\xa9", "\x16" "Be" }, + { "\xc3\xad", "\x16" "Bi" }, + { "\xc3\xb3", "\x16" "Bo" }, + { "\xc3\xba", "\x16" "Bu" }, + { "\xc3\x81", "\x16" "BA" }, + { "\xc3\x89", "\x16" "BE" }, + { "\xc3\x8d", "\x16" "BI" }, + { "\xc3\x93", "\x16" "BO" }, + { "\xc3\x9a", "\x16" "BU" }, + + /* circumflex */ + { "\xc3\xa2", "\x16" "Ca" }, + { "\xc3\xaa", "\x16" "Ce" }, + { "\xc3\xae", "\x16" "Ci" }, + { "\xc3\xb4", "\x16" "Co" }, + { "\xc3\xbb", "\x16" "Cu" }, + { "\xc3\x82", "\x16" "CA" }, + { "\xc3\x8a", "\x16" "CE" }, + { "\xc3\x8e", "\x16" "CI" }, + { "\xc3\x94", "\x16" "CO" }, + { "\xc3\x9b", "\x16" "CU" }, + + /* umlaut */ + { "\xc3\xa4", "\x16" "Ha" }, + { "\xc3\xab", "\x16" "He" }, + { "\xc3\xaf", "\x16" "Hi" }, + { "\xc3\xb6", "\x16" "Ho" }, + { "\xc3\xbc", "\x16" "Hu" }, + { "\xc3\x84", "\x16" "HA" }, + { "\xc3\x8b", "\x16" "HE" }, + { "\xc3\x8f", "\x16" "HI" }, + { "\xc3\x96", "\x16" "HO" }, + { "\xc3\x9c", "\x16" "HU" }, + + /* cedilla */ + { "\xc3\xa7", "\x16" "Kc" }, + { "\xc3\x87", "\x16" "KC" }, + + { NULL, NULL } +}; + +static bool thom_basic_is_specialchar(const char *buf, + int *pos, + const char **thom) +{ + int i; + + for (i = 0; special_chars[i].utf8 != NULL; i++) + { + if (!strncmp(&buf[*pos], special_chars[i].utf8, strlen(special_chars[i].utf8))) + { + *pos += strlen(special_chars[i].utf8); + *thom = special_chars[i].thom; + return true; + } + } + + return false; +} + +/* untokenization automatically decrypt protected files */ +static imgtoolerr_t thom_basic_read_file(imgtool::partition &part, + const char *name, + const char *fork, + imgtool::stream &dst, + const char *const table[2][128]) +{ + imgtool::stream::ptr org = imgtool::stream::open_mem(nullptr, 0); + imgtoolerr_t err; + uint8_t buf[4]; + int i; + + if ( !org ) return IMGTOOLERR_OUTOFMEMORY; + + err = thomcrypt_read_file( part, name, fork, *org ); + if (err) + return err; + + org->seek(3, SEEK_SET); /* skip header */ + + while ( 1 ) + { + int in_str = 0, in_fun = 0; + int linelength, linenum; + + /* line header: line length and line number */ + /* I am not sure this is 100% correct but it works in many cases */ + if ( org->read(buf, 2 ) < 2 ) goto end; + linelength = ((int)buf[0] << 8) + (int)buf[1] - 4; + if ( linelength <= 0 ) goto end; + if ( org->read(buf, 2 ) < 2 ) goto end; + linenum = ((int)buf[0] << 8) + buf[1]; + dst.printf( "%u ", linenum ); + + /* process line */ + for ( i = 0; i < linelength; i++ ) + { + uint8_t c; + if ( org->read(&c, 1 ) < 1 ) break; + if ( c == 0 ) + { + /* Sometimes, linelength seems wrong and we must rely on the fact that + BASIC lines are 0-terminated. + At other times, there are 0 embedded within lines or extra stuff + between the 0 and the following line, and so, we must rely + on linelength to cut the line (!) + */ + if ( linelength > 256 ) break; + } + else if ( c == 0xff && ! in_str ) in_fun = 1; /* function prefix */ + else + { + if ( c >= 0x80 && ! in_str ) + { + /* token */ + const char* token = table[ in_fun ][ c - 0x80 ]; + if ( token ) dst.puts(token ); + else dst.puts("???" ); + } + else if ( c == '\x16' ) + { + uint64_t l; + l = org->tell(); + if ( org->read(&c, 1 ) < 1 ) break; + thom_basic_specialchar(org, dst, c); + i += org->tell() - l; /* update i */ + } + else + { + /* regular character */ + if ( c == '"' ) in_str = 1 - in_str; + dst.putc( c ); /* normal letter */ + } + in_fun = 0; + } + } + dst.putc( '\n' ); + } + end: + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t thom_basic_write_file(imgtool::partition &partition, + const char *filename, + const char *fork, + imgtool::stream &sourcef, + util::option_resolution *opts, + const char *const table[2][128]) +{ + imgtool::stream::ptr mem_stream; + char buf[1024]; + int eof = false; + uint32_t len; + char c; + int i, j, pos, in_quotes; + uint16_t line_number; + uint8_t line_header[4]; + uint8_t file_header[3]; + const char *token; + uint8_t token_shift, token_value; + uint64_t line_header_loc, end_line_loc; + + /* open a memory stream */ + mem_stream = imgtool::stream::open_mem(nullptr, 0); + if (!mem_stream) + return IMGTOOLERR_OUTOFMEMORY; + + /* skip past 3-byte header */ + mem_stream->fill(0x00, 3); + + /* loop until the file is complete */ + while(!eof) + { + /* read a line */ + pos = 0; + while((len = sourcef.read(&c, 1)) > 0) + { + /* break if at end of line */ + if ((c == '\r') || (c == '\n')) + break; + + if (pos <= std::size(buf) - 1) + { + buf[pos++] = c; + } + } + eof = (len == 0); + buf[pos] = '\0'; + + /* ignore lines that don't start with digits */ + if (isdigit(buf[0])) + { + /* start at beginning of line */ + pos = 0; + + /* read line number */ + line_number = 0; + while(isdigit(buf[pos])) + { + line_number *= 10; + line_number += (buf[pos++] - '0'); + } + + /* set up line header */ + memset(&line_header, 0, sizeof(line_header)); + /* linelength or offset (2-bytes) will be rewritten */ + place_integer_be(line_header, 0, 2, 0xffff); + place_integer_be(line_header, 2, 2, line_number); + + /* emit line header */ + line_header_loc = mem_stream->tell(); + mem_stream->write(line_header, sizeof(line_header)); + + /* skip spaces */ + while(isspace(buf[pos])) + pos++; + + /* when we start out, we are not within quotation marks */ + in_quotes = false; + + /* read until end of line */ + while(buf[pos] != '\0') + { + token = nullptr; + token_shift = 0; + token_value = 0; + + if (buf[pos] == '\"') + { + /* flip quotation status */ + in_quotes = !in_quotes; + } + else if (!in_quotes) + { + for (i = 0; i < 2; i++) + { + bool found = false; + for (j = 0; j < 128; j++) + { + if (table[i][j] == nullptr) + continue; + if (!strncmp(&buf[pos], table[i][j], strlen(table[i][j]))) + { + token = table[i][j]; + token_shift = (i == 0) ? 0x00 : 0xff; + token_value = 0x80 + j; + pos += strlen(token); + found = true; + break; + } + } + if (found) + break; + } + } + + /* did we find a token? */ + if (token != nullptr) + { + /* emit the token */ + if (token_shift != 0) + mem_stream->write(&token_shift, 1); + mem_stream->write(&token_value, 1); + } + else if (thom_basic_is_specialchar(buf, &pos, &token)) + { + /* emit the escaped accent */ + mem_stream->write(token, strlen(token)); + } + else + { + /* no token; emit the byte */ + mem_stream->write(&buf[pos++], 1); + } + } + + /* emit line terminator */ + mem_stream->fill(0x00, 1); + + /* rewrite the line length */ + end_line_loc = mem_stream->tell(); + mem_stream->seek(line_header_loc, SEEK_SET); + place_integer_be(line_header, 0, 2, end_line_loc - line_header_loc); + mem_stream->write(line_header, sizeof(line_header)); + mem_stream->seek(end_line_loc, SEEK_SET); + } + } + + /* emit program terminator */ + mem_stream->fill(0x00, 2); + + /* reset stream */ + mem_stream->seek(0, SEEK_SET); + + /* Write file header */ + place_integer_be(file_header, 0, 1, 0xFF); + place_integer_be(file_header, 1, 2, mem_stream->size()); + mem_stream->write(file_header, 3); + mem_stream->seek(0, SEEK_SET); + + /* write actual file */ + return partition.write_file(filename, fork, *mem_stream, opts, nullptr); +} + + +#define FILTER(short,long) \ + static imgtoolerr_t short##_read_file(imgtool::partition &part, \ + const char *name, \ + const char *fork, \ + imgtool::stream &dst) \ + { \ + return thom_basic_read_file( part, name, fork, dst, short ); \ + } \ + static imgtoolerr_t short##_write_file(imgtool::partition &part, \ + const char *name, \ + const char *fork, \ + imgtool::stream &src, \ + util::option_resolution *opts) \ + { \ + return thom_basic_write_file( part, name, fork, src, opts, short ); \ + } \ + void filter_##short##_getinfo(uint32_t state, union filterinfo *info) \ + { \ + switch(state) \ + { \ + case FILTINFO_STR_NAME: \ + info->s = #short; \ + break; \ + case FILTINFO_STR_HUMANNAME: \ + info->s = long; \ + break; \ + case FILTINFO_PTR_READFILE: \ + info->read_file = short##_read_file; \ + break; \ + case FILTINFO_PTR_WRITEFILE: \ + info->write_file = short##_write_file; \ + break; \ + } \ + } + +FILTER( thombas5, + "Thomson MO5 w/ BASIC 1.0, Tokenized Files (auto-decrypt)" ) +FILTER( thombas7, + "Thomson TO7 w/ BASIC 1.0, Tokenized Files (auto-decrypt)" ) +FILTER( thombas128, + "Thomson w/ BASIC 128/512, Tokenized Files (auto-decrypt)" ) + + +/************************* driver ***************************/ + +OPTION_GUIDE_START( thom_createimage_optguide ) + OPTION_INT( 'H', "heads", "Heads" ) + OPTION_INT( 'T', "tracks", "Tracks" ) + OPTION_ENUM_START( 'D', "density", "Density" ) + OPTION_ENUM( 0, "SD", "Single density (128 bytes)" ) + OPTION_ENUM( 1, "DD", "Double density (256 bytes)" ) + OPTION_ENUM_END + OPTION_STRING( 'N', "name", "Floppy name" ) +OPTION_GUIDE_END + +OPTION_GUIDE_START( thom_writefile_optguide ) + OPTION_ENUM_START( 'T', "ftype", "File type" ) + OPTION_ENUM( 0, "auto", "Automatic (by extension)" ) + OPTION_ENUM( 1, "B", "Program" ) + OPTION_ENUM( 2, "D", "Data" ) + OPTION_ENUM( 3, "M", "Machine Code" ) + OPTION_ENUM( 4, "A", "Assembler Source" ) + OPTION_ENUM_END + OPTION_ENUM_START( 'F', "format", "Format flag" ) + OPTION_ENUM( 0, "auto", "Automatic (by extension)" ) + OPTION_ENUM( 1, "B", "Binary, Tokenized BASIC" ) + OPTION_ENUM( 2, "A", "ASCII" ) + OPTION_ENUM_END + OPTION_STRING( 'C', "comment", "Comment" ) +OPTION_GUIDE_END + +static void thom_basic_get_info(const imgtool_class *clas, + uint32_t param, + union imgtoolinfo *info) +{ + switch ( param ) { + case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: + info->i = sizeof(thom_floppy); break; + case IMGTOOLINFO_INT_PARTITION_EXTRA_BYTES: + info->i = sizeof(int); break; + case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: + info->i = sizeof(int); break; + case IMGTOOLINFO_INT_SUPPORTS_CREATION_TIME: + case IMGTOOLINFO_INT_PREFER_UCASE: + info->i = 1; break; + case IMGTOOLINFO_INT_PATH_SEPARATOR: + info->i = 0; break; + case IMGTOOLINFO_STR_FILE: + strcpy( info->s = imgtool_temp_str(), __FILE__ ); break; + case IMGTOOLINFO_STR_NAME: + strcpy( info->s = imgtool_temp_str(), "thom" ); break; + case IMGTOOLINFO_STR_DESCRIPTION: + strcpy( info->s = imgtool_temp_str(), "Thomson BASIC filesystem" ); + break; + case IMGTOOLINFO_PTR_CREATE: + info->create = thom_create; break; + case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE: + info->createimage_optguide = &thom_createimage_optguide; break; + case IMGTOOLINFO_PTR_BEGIN_ENUM: + info->begin_enum = thom_begin_enum; break; + case IMGTOOLINFO_PTR_NEXT_ENUM: + info->next_enum = thom_next_enum; break; + case IMGTOOLINFO_PTR_READ_FILE: + info->read_file = thom_read_file; break; + case IMGTOOLINFO_PTR_WRITE_FILE: + info->write_file = thom_write_file; break; + case IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE: + info->writefile_optguide = &thom_writefile_optguide; break; + case IMGTOOLINFO_STR_WRITEFILE_OPTSPEC: + strcpy( info->s = imgtool_temp_str(), "T[0]-4;F[0]-2;C" ); break; + case IMGTOOLINFO_PTR_SUGGEST_TRANSFER: + info->suggest_transfer = thom_suggest_transfer; break; + case IMGTOOLINFO_PTR_DELETE_FILE: + info->delete_file = thom_delete_file; break; + case IMGTOOLINFO_PTR_FREE_SPACE: + info->free_space = thom_free_space; break; + case IMGTOOLINFO_PTR_GET_GEOMETRY: + info->get_geometry = thom_get_geometry; break; + case IMGTOOLINFO_PTR_INFO: + info->info = thom_info; break; + case IMGTOOLINFO_PTR_READ_SECTOR: + info->read_sector = thom_read_sector; break; + case IMGTOOLINFO_PTR_WRITE_SECTOR: + info->write_sector = thom_write_sector; break; + case IMGTOOLINFO_PTR_LIST_PARTITIONS: + info->list_partitions = thom_list_partitions; break; + case IMGTOOLINFO_PTR_OPEN_PARTITION: + info->open_partition = thom_open_partition; break; + } +} + +void thom_fd_basic_get_info(const imgtool_class *clas, + uint32_t param, + union imgtoolinfo *info) +{ + switch ( param ) { + case IMGTOOLINFO_STR_NAME: + strcpy( info->s = imgtool_temp_str(), "thom_fd" ); break; + case IMGTOOLINFO_STR_DESCRIPTION: + strcpy( info->s = imgtool_temp_str(), + "Thomson .fd disk image, BASIC format" ); + break; + case IMGTOOLINFO_STR_FILE_EXTENSIONS: + strcpy( info->s = imgtool_temp_str(), "fd" ); break; + case IMGTOOLINFO_PTR_OPEN: + info->open = thom_open_fd_qd; break; + case IMGTOOLINFO_PTR_CLOSE: + info->close = thom_close_fd_qd; break; + case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC: + strcpy( info->s = imgtool_temp_str(), "H[1]-2;T40/[80];D0-[1];N" ); break; + default: + thom_basic_get_info( clas, param, info ); + } +} + +void thom_qd_basic_get_info(const imgtool_class *clas, + uint32_t param, + union imgtoolinfo *info) +{ + switch ( param ) { + case IMGTOOLINFO_STR_NAME: + strcpy( info->s = imgtool_temp_str(), "thom_qd" ); break; + case IMGTOOLINFO_STR_DESCRIPTION: + strcpy( info->s = imgtool_temp_str(), + "Thomson .qd disk image, BASIC format" ); + break; + case IMGTOOLINFO_STR_FILE_EXTENSIONS: + strcpy( info->s = imgtool_temp_str(), "qd" ); break; + case IMGTOOLINFO_PTR_OPEN: + info->open = thom_open_fd_qd; break; + case IMGTOOLINFO_PTR_CLOSE: + info->close = thom_close_fd_qd; break; + case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC: + strcpy( info->s = imgtool_temp_str(), "H[1]-2;T[25];D[0];N" ); break; + default: + thom_basic_get_info( clas, param, info ); + } +} + +void thom_sap_basic_get_info(const imgtool_class *clas, + uint32_t param, + union imgtoolinfo *info) +{ + switch ( param ) { + case IMGTOOLINFO_STR_NAME: + strcpy( info->s = imgtool_temp_str(), "thom_sap" ); break; + case IMGTOOLINFO_STR_DESCRIPTION: + strcpy( info->s = imgtool_temp_str(), + "Thomson .sap disk image, BASIC format" ); + break; + case IMGTOOLINFO_STR_FILE_EXTENSIONS: + strcpy( info->s = imgtool_temp_str(), "sap" ); break; + case IMGTOOLINFO_PTR_OPEN: + info->open = thom_open_sap; break; + case IMGTOOLINFO_PTR_CLOSE: + info->close = thom_close_sap; break; + case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC: + strcpy( info->s = imgtool_temp_str(), "H[1];T40/[80];D0-[1];N" ); break; + default: + thom_basic_get_info( clas, param, info ); + } +} diff --git a/src/tools/imgtool/modules/ti99.cpp b/src/tools/imgtool/modules/ti99.cpp new file mode 100644 index 0000000..37367a8 --- /dev/null +++ b/src/tools/imgtool/modules/ti99.cpp @@ -0,0 +1,5368 @@ +// license:BSD-3-Clause +// copyright-holders:Raphael Nabet +/* + Handlers for ti99 disk images + + We support the following types of image: + * floppy disks ("DSK format") in v9t9, pc99 and old mess image format + * hard disks ("WIN format") in MAME harddisk format (256-byte sectors only, + i.e. no SCSI) + Files are extracted in TIFILES format. There is a compile-time option to + extract text files in flat format instead, but I need to re-implement it + properly (using filters comes to mind). + + Raphael Nabet, 2002-2004 + + TODO: + - finish and test hd support ***test sibling FDR support*** + - check allocation bitmap against corruption when an image is opened +*/ + +#include "imgtool.h" +#include "harddisk.h" +#include "imghd.h" + +#include "opresolv.h" + +#include +#include +#include +#include +#include + +/* + Concepts shared by both disk structures: + ---------------------------------------- + + In both formats, the entire disk surface is split into physical records, + each 256-byte-long. For best efficiency, the sector size is usually 256 + bytes so that one sector is equivalent to a physical record (this is true + with floppies and HFDC hard disks). If the sector size is not 256 bytes + (e.g. 512 bytes with SCSI hard disks, possibly 128 bytes on a TI-99 + simulator running on TI-990), sectors are grouped or split to form 256-byte + physical records. Physical records are numbered from 0, with the first + starting on sector 0 track 0. To avoid confusion with physical records in + files, such physical records will be called "absolute physical records", + abbreviated as "absolute physrecs" or "aphysrecs". + + Disk space is allocated in units referred to as AUs. Each AU represents an + integral number of 256-byte physical records. The number of physrecs per + AU varies depending on the disk format and size. AUs are numbered from 0, + with the first starting with absolute physrec 0. + + Absolute physrec 0 contains a 256-byte record with volume information, + which is called "Volume Information Block", abbreviated as "vib". + + To keep track of which areas on the disk are allocated and which are free, + disk managers maintain a bit map of allocated AUs (called "allocation + bitmap", abbreviated as "abm"). Each bit in the allocation bitmap + represents an AU: if a bit is 0, the AU is free; if a bit is 1, the AU is + allocated (or possibly bad if the disk manager can track bad sectors). + Where on the disk the abm is located depends on the disk structure (see + structure-specific description for details). + + Files are implemented as a succession of 256-byte physical records. To + avoid confusion with absolute physical records, these physical records will + be called "file physical records", abbreviated as "file physrecs" or + "fphysrecs". + + Programs do normally not access file physical records directly. They may + call high-level file routines that enable to create either fixed-length + logical records of any size from 1 through 255, or variable-length records + of any size from 0 through 254: logical records are grouped by file + managers into 256-byte physical records. Some disk managers (HFDC and + SCSI) allow programs to create fixed-length records larger than 255 bytes, + too, but few programs use this possibility. Additionally, program files + can be created: program files do not implement any logical record, and can + be seen as a flat byte stream, not unlike files under MSDOS, UNIX and the C + standard library. (Unfortunately, the API for program files lacks + flexibility, and most programs that need to process a flat byte stream + will use fixed-size records of 128 bytes.) + + Fixed-length records (reclen < 256) are grouped like this: + fphysrec 0 fphysrec 1 + _______________ _______________ + |R0 |R1 |R2 |X| |R3 |R4 |R5 |X| + --------------- --------------- + \_/ \_/ + unused unused + (size < reclen) (size < reclen) + + Fixed-length records (reclen > 256) are grouped like this: + fphysrec 0 fphysrec 1 fphysrec 2 fphysrec 3 ... + _________________ _________________ _________________ _________________ + | R0 (#0-255) | | R0 (#256-511) | |R0 (#512-end)|X| | R1 (#0-255) | ... + ----------------- ----------------- ----------------- ----------------- + \_/ + unused + + Variable length records are grouped like this: + fphysrec 0: + byte: + ------------------------------ + 0 |l0 = length of record 0 data| + |----------------------------| + 1 | | + | | + : | record 0 data | + | | + l0 | | + |----------------------------| + l0+1 |l1 = length of record 1 data| + |----------------------------| + l0+2 | | + | | + : | record 1 data | + | | + l0+l1+1 | | + |----------------------------| + : : : + : : : + |----------------------------| + n |lm = length of record m data| + |----------------------------| + n+1 | | + | | + : | record m data | + | | + n+lm | | + |----------------------------| + n+lm+1 |>ff: EOR mark | + |----------------------------| + n+lm+2 | | + | | + : | unused | + : |(we should have size < lm+1)| + | | + 255 | | + ------------------------------ + + fphysrec 1: + byte: + ------------------------------ + 0 |lm+1=length of record 0 data| + |----------------------------| + 1 | | + | | + : | record m+1 data | + | | + lm+1 | | + |----------------------------| + : : : + : : : + + I think the EOR mark is not required if all 256 bytes of a physical + record are used by logical records. + + + All files are associated with a "file descriptor record" ("fdr") that holds + file information (name, format, length) and points to the data AUs. The WIN + disk structure also supports sibling FDRs, in case a file is so fragmented + that all the data pointers cannot fit in one FDR; the DSK disk structure + does not implement any such feature, and you may be unable to append data + to an existing file if it is too fragmented, even though the disk is not + full. + + + DSK disk structure: + ------------------- + + The DSK disk structure is used for floppy disks, and a few RAMDISKs as + well. It supports 1600 AUs and 16 MBytes at most (the 16 MByte value is a + theoretical maximum, as I doubt anyone has ever created so big a DSK volume). + + The disk sector size is always 256 bytes (the only potential exceptions are + the SCSI floppy disk units and the TI-99 simulator on TI-990, but I don't + know anything about either). + + For some reason, the number of physrecs per AU is always a power of 2. + Originally, AUs always were one physical record each. However, since the + allocation bitmap cannot support more than 1600 AUs, disks larger than + 400kbytes need to have several physical records per AU. Note that + relatively few disk controllers implement AUs that span multiple physical + records (IIRC, these are Myarc's HFDC, SCSI DSR with floppy disk interface, + and some upgraded Myarc and Corcomp DD floppy controllers). + + The allocation bitmap is located in the VIB. It is 200 bytes long, and + supports 1600 AUs at most. + + Directories are implemented with a "File Descriptor Index Record" (FDIR) + for each directory. The FDIR is array of 0 through 128 words, containing + the aphysrec address of each fdr in the directory, sorted by ascending file + name, terminated with a 0. Note that, while we should be prepared to read + images images with 128 entries, I think (not 100% sure) that we should + write no more than 127 for compatibility with some existing disk managers. + + Originally, the DSK structure only supported one directory level (i.e. the + root directory level). The FDIR record for the root directory is always + located in aphysrec 1. Moreover, Myarc extended the DSK structure to + support up to 3 subdirectories in the root directory (note that there is no + support for more subdirs, or for subdirs located in subdirs). To do so, + they used an unused field of the VIB to hold the 10-char name of each + directory and the aphysrec address of the associated FDIR record. + + aphysrec 0: Volume Information Block (VIB): see below + aphysrec 1: FDIR for root directory + Remaining AUs are used for fdr and data (and subdirectory FDIR if + applicable). There is one FDIR record per directory; the FDIR points to + the FDR for each file in the directory. The FDR (File Descriptor Record) + contains the file information (name, format, length, pointers to data + physrecs/AUs, etc). + + + WIN disk structure: + ------------------- + + The WIN disk structure is used for hard disks. It supports 65535 AUs and + 256 MBytes at most. + + The disk sector size is 256 bytes on HFDC, 512 bytes on SCSI. 512-byte + sectors are split into 256-byte physical records. + + We may have from 1 through 16 physrecs per AUs. Since we cannot have more + than 65535 AUs, we need to have several physical records per AU in disks + larger than 16 Mbytes. + + Contrary to the DSK disk structure, the WIN disk structure supports + hierarchic subdirectories, with a limit of 114 subdirectories and 127 files + per directory. Each directory is associated with both a "Directory + Descriptor Record" (DDR) and a "File Descriptor Index Record" (FDIR). The + only exception is the root directory, which uses the VIB instead of a DDR + (the VIB includes all the important fields of the DDR). The DDR contains + some directory info (name, number of files and subdirectories directly + enclosed in the directory, AU address of the FDIR of the parent directory, + etc), the AU address of the associated FDIR, and the AU address of the DDR + of up to 114 subdirectories, sorted by ascending directory name. The WIN + FDIR is similar to, yet different from, the DSK FDIR: it contains up to 127 + (vs. 128) AU address (vs. aphysrec address) of each fdr in the directory, + sorted by ascending file name. Additionally, it includes the AU address of + the associated DDR. + + When a file has more than 54 data fragments, the disk manager creates + sibling FDRs that hold additional fragments. (Sibling FDRs are called + offspring FDRs in most existing documentation, but I prefer "sibling FDRs" + as "offspring FDRs" wrongly suggests a directory-like hierarchy. + Incidentally, I cannot really figure out why they chose to duplicate the + FDR rather than create a file extension record, but there must be a + reason.) Note that sibling FDRs usually fill unused physrecs in the AU + allocated for the eldest FDR, and a new AU is allocated for new sibling + FDRs only when the first AU is full. + + aphysrec 0: Volume Information Block (VIB): see below + aphysrec 1-n (where n = 1+SUP(number_of_AUs/2048)): Volume bitmap + Remaining AUs are used for ddr, fdir, fdr and data. +*/ + +/* Since string length is encoded with a byte, the maximum length of a string +is 255. Since we need to add a device name (1 char minimum, typically 4 chars) +and a '.' separator, we chose a practical value of 250 (though few applications +will accept paths of such length). */ +#define MAX_PATH_LEN 253 +#define MAX_SAFE_PATH_LEN 250 + +#define MAX_DIR_LEVEL 125 /* We need to put a recursion limit to avoid endless recursion hazard */ + + +#if 0 +#pragma mark MISCELLANEOUS UTILITIES +#endif + +/* + Miscellaneous utilities that are used to handle TI data types +*/ + +struct UINT16BE +{ + uint8_t bytes[2]; +}; + +struct UINT16LE +{ + uint8_t bytes[2]; +}; + +static inline uint16_t get_UINT16BE(UINT16BE word) +{ + return (word.bytes[0] << 8) | word.bytes[1]; +} + +static inline void set_UINT16BE(UINT16BE *word, uint16_t data) +{ + word->bytes[0] = (data >> 8) & 0xff; + word->bytes[1] = data & 0xff; +} + +static inline uint16_t get_UINT16LE(UINT16LE word) +{ + return word.bytes[0] | (word.bytes[1] << 8); +} + +static inline void set_UINT16LE(UINT16LE *word, uint16_t data) +{ + word->bytes[0] = data & 0xff; + word->bytes[1] = (data >> 8) & 0xff; +} + +/* + Convert a C string to a 10-character file name (padded with spaces if necessary) + + dst (O): destination 10-character array + src (I): source C string +*/ +static void str_to_fname(char dst[10], const char *src) +{ + int i; + + + i = 0; + + /* copy 10 characters at most */ + if (src) + while ((i<10) && (src[i]!='\0')) + { + dst[i] = src[i]; + i++; + } + + /* pad with spaces */ + while (i<10) + { + dst[i] = ' '; + i++; + } +} + +/* + Convert a 10-character file name to a C string (removing trailing spaces if necessary) + + dst (O): destination C string + src (I): source 10-character array + n (I): length of dst buffer (string may be truncated if less than 11) +*/ +static void fname_to_str(char *dst, const char src[10], int n) +{ + int i; + int last_nonspace; + + + /* copy 10 characters at most */ + if (--n > 10) + n = 10; + + /* copy filename */ + i = 0; + last_nonspace = -1; + + while (i MAX_PATH_LEN) || strchr(fpath, ' ')) + return 1; + + /* check that each path element has an acceptable length */ + element_start = fpath; + do + { + /* find next path element */ + element_end = strchr(element_start, '.'); + element_len = element_end ? (element_end - element_start) : strlen(element_start); + if ((element_len > 10) || (element_len == 0)) + return 1; + + /* iterate */ + if (element_end) + element_start = element_end+1; + else + element_start = NULL; + } + while (element_start); + + return 0; +} + +#if 0 +#pragma mark - +#pragma mark LEVEL 1 DISK ROUTINES +#endif + +/* + Level 1 deals with accessing the disk hardware (in our case, the raw disk + image). + + Higher level routines will only know the disk as a succession of + 256-byte-long physical records addressed by a linear address. +*/ + +/* + Disk geometry +*/ +struct ti99_geometry +{ + int secspertrack; + int cylinders; + int heads; +}; + +/* + Physical sector address +*/ +struct ti99_sector_address +{ + int sector; + int cylinder; + int side; +}; + +/* + Time stamp (used in fdr, and WIN VIB/DDR) +*/ +struct ti99_date_time +{ + uint8_t time_MSB, time_LSB;/* 0-4: hour, 5-10: minutes, 11-15: seconds/2 */ + uint8_t date_MSB, date_LSB;/* 0-6: year, 7-10: month, 11-15: day */ +}; + +/* + Subdirectory descriptor (HFDC only) + + The HFDC supports up to 3 subdirectories. +*/ +struct dsk_subdir +{ + char name[10]; /* subdirectory name (10 characters, pad with spaces) */ + UINT16BE fdir_aphysrec; /* aphysrec address of fdir record for this subdirectory */ +}; + +/* + DSK VIB record + + Most fields in this record are only relevant to level 2 routines, but level + 1 routines need the disk geometry information extracted from the VIB. +*/ +struct dsk_vib +{ + char name[10]; /* disk volume name (10 characters, pad with spaces) */ + UINT16BE totphysrecs; /* total number of physrecs on disk (usually 360, */ + /* 720, 640, 1280 or 1440). (TI originally */ + /* said it should be the total number of AUs, */ + /* but, in practice, DSRs that supports disks */ + /* over 400kbytes (e.g. HFDC) write the total */ + /* number of physrecs.) */ + uint8_t secspertrack; /* sectors per track (usually 9 (FM SD), */ + /* 16 or 18 (MFM DD), or 36 (MFM HD) */ + uint8_t id[3]; /* 'DSK' */ + uint8_t protection; /* 'P' if disk is protected, ' ' otherwise. */ + uint8_t cylinders; /* tracks per side (usually 40) */ + uint8_t heads; /* sides (1 or 2) */ + uint8_t density; /* density: 1 (FM SD), 2 (MFM DD), or 3 (MFM HD) */ + dsk_subdir subdir[3]; /* descriptor for up to 3 subdirectories (HFDC only) */ + /* reserved by TI */ + uint8_t abm[200]; /* allocation bitmap: there is one bit per AU. */ + /* A binary 1 in a bit position indicates that */ + /* the allocation unit associated with that */ + /* bit has been allocated. */ + /* Bits corresponding to system reserved AUs, */ + /* non-existent AUs, and bad AUs, are set to one, too. */ + /* (AU 0 is associated to LSBit of byte 0, */ + /* AU 7 to MSBit of byte 0, AU 8 to LSBit */ + /* of byte 1, etc.) */ +}; + +enum ti99_img_format +{ + if_mess, + if_v9t9, + if_pc99_fm, + if_pc99_mfm, + if_harddisk +}; + +/* + level-1 disk image descriptor +*/ +struct ti99_lvl1_imgref +{ + ti99_img_format img_format; /* tells the image format */ + imgtool::stream *file_handle; /* imgtool file handle */ + struct mess_hard_disk_file harddisk_handle; /* MAME harddisk handle (harddisk format) */ + ti99_geometry geometry; /* geometry */ + unsigned pc99_track_len; /* unformatted track length (pc99 format) */ + uint32_t *pc99_data_offset_array; /* offset for each sector (pc99 format) */ +}; + +/* + calculate CRC for data address marks or sector data + + crc (I/O): current CRC value to be updated (initialize to 0xffff before + calling this function for the first time) + value: new byte of data to update the CRC with +*/ +static void calc_crc(uint16_t *crc, uint8_t value) +{ + uint8_t l, h; + + l = value ^ (*crc >> 8); + *crc = (*crc & 0xff) | (l << 8); + l >>= 4; + l ^= (*crc >> 8); + *crc <<= 8; + *crc = (*crc & 0xff00) | l; + l = (l << 4) | (l >> 4); + h = l; + l = (l << 2) | (l >> 6); + l &= 0x1f; + *crc = *crc ^ (l << 8); + l = h & 0xf0; + *crc = *crc ^ (l << 8); + l = (h << 1) | (h >> 7); + l &= 0xe0; + *crc = *crc ^ l; +} + +/* + Parse a PC99 disk image + + file_handle (I/O): imgtool file handle + fm_format (I): if true, the image is in FM format, otherwise it is in MFM + format + pass (I): 0 for first pass, 1 for second pass + vib (O): buffer where the vib should be stored (first pass) + geometry (O): disk image geometry (second pass) + data_offset_array (O): array of data offset to generate (second pass) + out_track_len (O): track length is returned there + + Return imgtool error code +*/ +#define MAX_TRACK_LEN 6872 +#define DATA_OFFSET_NONE 0xffffffff +static int parse_pc99_image(imgtool::stream &file_handle, int fm_format, int pass, dsk_vib *vib, const ti99_geometry *geometry, uint32_t *data_offset_array, unsigned *out_track_len) +{ + int track_len, num_tracks; /* length of a track in bytes, and number of tracks */ + int phys_track; + int expected_cylinder, expected_head; + int track_start_pos, track_pos; + uint8_t c; + uint8_t cylinder, head, sector; + int seclen; + uint8_t crc1, crc2; + uint16_t crc; + long data_offset; + uint8_t track_buf[MAX_TRACK_LEN]; + int i; + + if (fm_format) + track_len = 3253; + else + track_len = 6872; + + if (out_track_len) + *out_track_len = track_len; + + if (file_handle.size() % track_len) + return IMGTOOLERR_CORRUPTIMAGE; + + num_tracks = file_handle.size() / track_len; + if (num_tracks <= 0) + return IMGTOOLERR_CORRUPTIMAGE; + + /* we only support 40-track-per-side images */ + if ((num_tracks != 40) && (num_tracks != 80)) + return IMGTOOLERR_UNIMPLEMENTED; + + if (pass == 1) + { + /* initialize offset map */ + for (head = 0; head < geometry->heads; head++) + for (cylinder = 0; cylinder < geometry->cylinders; cylinder++) + for (sector = 0; sector < geometry->secspertrack; sector++) + data_offset_array[(head*geometry->cylinders + cylinder)*geometry->secspertrack + sector] = DATA_OFFSET_NONE; + } + /* rewind to start of file */ + file_handle.seek(0, SEEK_SET); + + /* pass 0 only looks for vib in track 0; pass 1 scans every track */ + for (phys_track=0; phys_track < ((pass == 1) ? num_tracks : 1); phys_track++) + { + if (file_handle.read(track_buf, track_len) != track_len) + return IMGTOOLERR_READERROR; + + /* we only support 40-track-per-side images */ + expected_cylinder = phys_track % 40; + expected_head = phys_track / 40; + + track_start_pos = 0; + + while (track_start_pos < track_len) + { + if (fm_format) + { + do + { + c = track_buf[track_start_pos]; + track_start_pos++; + } while ((c != 0xfe) && (track_start_pos < track_len)); + + if (c != 0xfe) + break; + + track_pos = track_start_pos; + + crc = 0xffff; + calc_crc(& crc, c); + } + else + { + do + { + c = track_buf[track_start_pos]; + track_start_pos++; + } while ((c != 0xa1) && (track_start_pos < track_len)); + + if (c != 0xa1) + break; + + track_pos = track_start_pos; + + c = track_buf[track_pos]; + track_pos++; + if (track_pos == track_len) + track_pos = 0; + + if (c != 0xa1) + continue; + + c = track_buf[track_pos]; + track_pos++; + if (track_pos == track_len) + track_pos = 0; + + if (c != 0xa1) + continue; + + crc = 0xffff; + calc_crc(& crc, c); + + c = track_buf[track_pos]; + track_pos++; + if (track_pos == track_len) + track_pos = 0; + + if (c != 0xfe) + continue; + } + + c = track_buf[track_pos]; + track_pos++; + if (track_pos == track_len) + track_pos = 0; + + cylinder = c; + calc_crc(& crc, c); + + c = track_buf[track_pos]; + track_pos++; + if (track_pos == track_len) + track_pos = 0; + + head = c; + calc_crc(& crc, c); + + c = track_buf[track_pos]; + track_pos++; + if (track_pos == track_len) + track_pos = 0; + + sector = c; + calc_crc(& crc, c); + + c = track_buf[track_pos]; + track_pos++; + if (track_pos == track_len) + track_pos = 0; + + seclen = 128 << c; + calc_crc(& crc, c); + + c = track_buf[track_pos]; + track_pos++; + if (track_pos == track_len) + track_pos = 0; + + crc1 = c; + calc_crc(& crc, c); + + c = track_buf[track_pos]; + track_pos++; + if (track_pos == track_len) + track_pos = 0; + + crc2 = c; + calc_crc(& crc, c); + +#if 0 + /* CRC seems to be completely hosed */ + if (crc) + printf("aargh!"); +#endif + if ((seclen != 256) || (crc1 != 0xf7) || (crc2 != 0xf7) + || (cylinder != expected_cylinder) || (head != expected_head) + || ((pass == 1) && ((cylinder >= geometry->cylinders) + || (head >= geometry->heads) + || (sector >= geometry->secspertrack)))) + continue; + + c = track_buf[track_pos]; + track_pos++; + if (track_pos == track_len) + track_pos = 0; + + while (c == (fm_format ? 0xff : 0x4e)) + { + c = track_buf[track_pos]; + track_pos++; + if (track_pos == track_len) + track_pos = 0; + } + + while (c == 0x00) + { + c = track_buf[track_pos]; + track_pos++; + if (track_pos == track_len) + track_pos = 0; + } + + if (fm_format) + { + if (c != 0xfb) + continue; + + crc = 0xffff; + calc_crc(& crc, c); + } + else + { + if (c != 0xa1) + continue; + + c = track_buf[track_pos]; + track_pos++; + if (track_pos == track_len) + track_pos = 0; + + if (c != 0xa1) + continue; + + c = track_buf[track_pos]; + track_pos++; + if (track_pos == track_len) + track_pos = 0; + + if (c != 0xa1) + continue; + + crc = 0xffff; + calc_crc(& crc, c); + + c = track_buf[track_pos]; + track_pos++; + if (track_pos == track_len) + track_pos = 0; + + if (c != 0xfb) + continue; + } + data_offset = track_pos; + for (i=0; icylinders + cylinder)*geometry->secspertrack + sector] != DATA_OFFSET_NONE) + /* error: duplicate sector */ + return IMGTOOLERR_CORRUPTIMAGE; + data_offset_array[(head*geometry->cylinders + cylinder)*geometry->secspertrack + sector] = data_offset; + break; + } + } + } + + if (pass == 0) + return IMGTOOLERR_CORRUPTIMAGE; + + if (pass == 1) + { + /* check offset map */ + for (head = 0; head < geometry->heads; head++) + for (cylinder = 0; cylinder < geometry->cylinders; cylinder++) + for (sector = 0; sector < geometry->secspertrack; sector++) + if (data_offset_array[(head*geometry->cylinders + cylinder)*geometry->secspertrack + sector] == DATA_OFFSET_NONE) + /* error: missing sector */ + return IMGTOOLERR_CORRUPTIMAGE; + } + + return 0; +} + + +/* + Read the volume information block (aphysrec 0) assuming no geometry + information. (Called when an image is opened to figure out the + geometry information.) + + file_handle (I/O): imgtool file handle + img_format (I): image format (MESS, V9T9 or PC99) + dest (O): pointer to 256-byte destination buffer + + Return imgtool error code +*/ +static int read_image_vib_no_geometry(imgtool::stream &file_handle, ti99_img_format img_format, dsk_vib *dest) +{ + int reply; + + switch (img_format) + { + case if_mess: + case if_v9t9: + /* seek to sector */ + reply = file_handle.seek(0, SEEK_SET); + if (reply) + return IMGTOOLERR_READERROR; + /* read it */ + reply = file_handle.read(dest, 256); + if (reply != 256) + return IMGTOOLERR_READERROR; + return 0; + + case if_pc99_fm: + case if_pc99_mfm: + return parse_pc99_image(file_handle, img_format == if_pc99_fm, 0, dest, NULL, NULL, NULL); + + case if_harddisk: + /* not implemented, because we don't need it */ + break; + } + + return IMGTOOLERR_UNIMPLEMENTED; +} + +/* + Open a disk image at level 1 + + file_handle (I/O): imgtool file handle + img_format (I): image format (MESS, V9T9, PC99, or MAME harddisk) + l1_img (O): level-1 image handle + vib (O): buffer where the vib should be stored (floppy images only) + + Return imgtool error code +*/ +static imgtoolerr_t open_image_lvl1(imgtool::stream::ptr &&file_handle, ti99_img_format img_format, ti99_lvl1_imgref *l1_img, dsk_vib *vib) +{ + imgtoolerr_t err; + int reply; + uint16_t totphysrecs; + + l1_img->img_format = img_format; + + if (img_format == if_harddisk) + { + const hard_disk_info *info; + + err = imghd_open(*file_handle, &l1_img->harddisk_handle); + if (err) + return err; + + info = imghd_get_header(&l1_img->harddisk_handle); + l1_img->geometry.cylinders = info->cylinders; + l1_img->geometry.heads = info->heads; + l1_img->geometry.secspertrack = info->sectors; + if (info->sectorbytes != 256) + { + imghd_close(&l1_img->harddisk_handle); + return IMGTOOLERR_CORRUPTIMAGE; /* TODO: support 512-byte sectors */ + } + +#if 0 + /* read vib */ + reply = read_absolute_physrec(l1_img, 0, vib); + if (reply) + return reply; +#endif + } + else + { + /* read vib */ + reply = read_image_vib_no_geometry(*file_handle, img_format, vib); + if (reply) + return (imgtoolerr_t)reply; + + /* extract geometry information */ + totphysrecs = get_UINT16BE(vib->totphysrecs); + + l1_img->geometry.secspertrack = vib->secspertrack; + if (l1_img->geometry.secspertrack == 0) + /* Some images might be like this, because the original SSSD TI + controller always assumes 9. */ + l1_img->geometry.secspertrack = 9; + l1_img->geometry.cylinders = vib->cylinders; + if (l1_img->geometry.cylinders == 0) + /* Some images are like this, because the original SSSD TI + controller always assumes 40. */ + l1_img->geometry.cylinders = 40; + l1_img->geometry.heads = vib->heads; + if (l1_img->geometry.heads == 0) + /* Some images are like this, because the original SSSD TI + controller always assumes that tracks beyond 40 are on side 2. */ + l1_img->geometry.heads = totphysrecs / (l1_img->geometry.secspertrack * l1_img->geometry.cylinders); + + /* check information */ + if ((totphysrecs != (l1_img->geometry.secspertrack * l1_img->geometry.cylinders * l1_img->geometry.heads)) + || (totphysrecs < 2) + || memcmp(vib->id, "DSK", 3) || (! strchr(" P", vib->protection)) + || (((img_format == if_mess) || (img_format == if_v9t9)) + && (file_handle->size() != totphysrecs*256U))) + return (imgtoolerr_t)IMGTOOLERR_CORRUPTIMAGE; + + if ((img_format == if_pc99_fm) || (img_format == if_pc99_mfm)) + { + l1_img->pc99_data_offset_array = (uint32_t*)malloc(sizeof(*l1_img->pc99_data_offset_array)*totphysrecs); + if (! l1_img->pc99_data_offset_array) + return IMGTOOLERR_OUTOFMEMORY; + reply = parse_pc99_image(*file_handle, img_format == if_pc99_fm, 1, NULL, & l1_img->geometry, l1_img->pc99_data_offset_array, &l1_img->pc99_track_len); + if (reply) + { + free(l1_img->pc99_data_offset_array); + return (imgtoolerr_t)reply; + } + } + } + + l1_img->file_handle = file_handle.release(); // we can only do this when we're sure we're successful + + return (imgtoolerr_t)0; +} + +/* + Close a disk image at level 1 + + l1_img (I/O): level-1 image handle + + Return imgtool error code +*/ +static void close_image_lvl1(ti99_lvl1_imgref *l1_img) +{ + if (l1_img->img_format == if_harddisk) + { + imghd_close(&l1_img->harddisk_handle); + } + + delete l1_img->file_handle; + + if ((l1_img->img_format == if_pc99_fm) || (l1_img->img_format == if_pc99_mfm)) + free(l1_img->pc99_data_offset_array); +} + +/* + Convert physical sector address to offset in disk image (old MESS and V9T9 + formats only) + + l1_img (I/O): level-1 image handle + address (I): physical sector address + + Return offset in image +*/ +static inline int sector_address_to_image_offset(const ti99_lvl1_imgref *l1_img, const ti99_sector_address *address) +{ + int offset = 0; + + switch (l1_img->img_format) + { + case if_mess: + /* old MESS format */ + offset = (((address->cylinder*l1_img->geometry.heads) + address->side)*l1_img->geometry.secspertrack + address->sector)*256; + break; + + case if_v9t9: + /* V9T9 format */ + if (address->side & 1) + /* on side 1, tracks are stored in the reverse order */ + offset = (((address->side*l1_img->geometry.cylinders) + (l1_img->geometry.cylinders-1 - address->cylinder))*l1_img->geometry.secspertrack + address->sector)*256; + else + offset = (((address->side*l1_img->geometry.cylinders) + address->cylinder)*l1_img->geometry.secspertrack + address->sector)*256; + break; + + case if_pc99_fm: + case if_pc99_mfm: + /* pc99 format */ + case if_harddisk: + /* harddisk format */ + assert(1); /* not implemented */ + break; + } + + return offset; +} + +/* + Read one 256-byte sector from a disk image + + l1_img (I/O): level-1 image handle + address (I): physical sector address + dest (O): pointer to 256-byte destination buffer + + Return non-zero on error +*/ +static int read_sector(ti99_lvl1_imgref *l1_img, const ti99_sector_address *address, void *dest) +{ + int reply; + uint32_t track_len, track_offset, sector_offset; + + switch (l1_img->img_format) + { + case if_mess: + /* old MESS format */ + case if_v9t9: + /* V9T9 format */ + /* seek to sector */ + reply = l1_img->file_handle->seek(sector_address_to_image_offset(l1_img, address), SEEK_SET); + if (reply) + return 1; + /* read it */ + reply = l1_img->file_handle->read(dest, 256); + if (reply != 256) + return 1; + break; + + case if_pc99_fm: + case if_pc99_mfm: + /* pc99 format */ + track_len = l1_img->pc99_track_len; + track_offset = (address->side*l1_img->geometry.cylinders + address->cylinder)*track_len; + sector_offset = l1_img->pc99_data_offset_array[(address->side*l1_img->geometry.cylinders + address->cylinder)*l1_img->geometry.secspertrack + address->sector]; + + if ((sector_offset + 256) <= track_len) + { + /* seek to sector */ + reply = l1_img->file_handle->seek(track_offset+sector_offset, SEEK_SET); + if (reply) + return 1; + /* read it */ + reply = l1_img->file_handle->read(dest, 256); + if (reply != 256) + return 1; + } + else + { + /* seek to sector */ + reply = l1_img->file_handle->seek(track_offset+sector_offset, SEEK_SET); + if (reply) + return 1; + /* read first chunk (until end of track) */ + reply = l1_img->file_handle->read((uint8_t *)dest, track_len-sector_offset); + if (reply != track_len-sector_offset) + return 1; + + /* wrap to start of track */ + reply = l1_img->file_handle->seek(track_offset, SEEK_SET); + if (reply) + return 1; + /* read remnant of sector */ + reply = l1_img->file_handle->read((uint8_t *)dest + (track_len-sector_offset), 256-(track_len-sector_offset)); + if (reply != 256-(track_len-sector_offset)) + return 1; + } + break; + + case if_harddisk: + /* not implemented */ + assert(1); + /*return imghd_read(l1_img->harddisk_handle, ((address->cylinder*l1_img->geometry.heads) + address->side)*l1_img->geometry.secspertrack + address->sector, 1, dest) != 1;*/ + break; + } + + return 0; +} + +/* + Write one 256-byte sector to a disk image + + l1_img (I/O): level-1 image handle + address (I): physical sector address + src (I): pointer to 256-byte source buffer + + Return non-zero on error +*/ +static int write_sector(ti99_lvl1_imgref *l1_img, const ti99_sector_address *address, const void *src) +{ + int reply; + uint32_t track_len, track_offset, sector_offset; + + switch (l1_img->img_format) + { + case if_mess: + /* old MESS format */ + case if_v9t9: + /* V9T9 format */ + /* seek to sector */ + reply = l1_img->file_handle->seek(sector_address_to_image_offset(l1_img, address), SEEK_SET); + if (reply) + return 1; + /* write it */ + reply = l1_img->file_handle->write(src, 256); + if (reply != 256) + return 1; + break; + + case if_pc99_fm: + case if_pc99_mfm: + /* pc99 format */ + track_len = l1_img->pc99_track_len; + track_offset = (address->side*l1_img->geometry.cylinders + address->cylinder)*track_len; + sector_offset = l1_img->pc99_data_offset_array[(address->side*l1_img->geometry.cylinders + address->cylinder)*l1_img->geometry.secspertrack + address->sector]; + + if ((sector_offset + 256) <= track_len) + { + /* seek to sector */ + reply = l1_img->file_handle->seek(track_offset+sector_offset, SEEK_SET); + if (reply) + return 1; + /* write it */ + reply = l1_img->file_handle->write(src, 256); + if (reply != 256) + return 1; + } + else + { + /* seek to sector */ + reply = l1_img->file_handle->seek(track_offset+sector_offset, SEEK_SET); + if (reply) + return 1; + /* write first chunk (until end of track) */ + reply = l1_img->file_handle->write((uint8_t *)src, track_len-sector_offset); + if (reply != track_len-sector_offset) + return 1; + + /* wrap to start of track */ + reply = l1_img->file_handle->seek(track_offset, SEEK_SET); + if (reply) + return 1; + /* write remnant of sector */ + reply = l1_img->file_handle->write((uint8_t *)src + (track_len-sector_offset), 256-(track_len-sector_offset)); + if (reply != 256-(track_len-sector_offset)) + return 1; + } + break; + + case if_harddisk: + /* not implemented */ + assert(1); + /*return imghd_write(l1_img->harddisk_handle, ((address->cylinder*l1_img->geometry.heads) + address->side)*l1_img->geometry.secspertrack + address->sector, 1, src) != 1;*/ + break; + } + + return 0; +} + +/* + Convert physical record address to sector address (DSK format) + + aphysrec (I): absolute physrec address + geometry (I): disk image geometry + address (O): physical sector address +*/ +static void dsk_aphysrec_to_sector_address(int aphysrec, const ti99_geometry *geometry, ti99_sector_address *address) +{ + address->sector = aphysrec % geometry->secspertrack; + aphysrec /= geometry->secspertrack; + address->cylinder = aphysrec % geometry->cylinders; + address->side = aphysrec / geometry->cylinders; + if (address->side & 1) + /* on side 1, tracks are stored in the reverse order */ + address->cylinder = geometry->cylinders-1 - address->cylinder; +} + +/* + Convert physical record address to sector address (WIN format for HFDC) + + Note that physical address translation makes sense for HFDC, but not SCSI. + + aphysrec (I): absolute physrec address + geometry (I): disk image geometry + address (O): physical sector address +*/ +#ifdef UNUSED_FUNCTION +static void win_aphysrec_to_sector_address(int aphysrec, const ti99_geometry *geometry, ti99_sector_address *address) +{ + address.sector = aphysrec % l1_img->geometry.secspertrack; + aphysrec /= l1_img->geometry.secspertrack; + address.side = aphysrec % l1_img->geometry.heads; + address.cylinder = aphysrec / l1_img->geometry.heads; +} +#endif + +/* + Read one 256-byte physical record from a disk image + + l1_img (I/O): level-1 image handle + aphysrec (I): absolute physrec address + dest (O): pointer to 256-byte destination buffer + + Return non-zero on error +*/ +static int read_absolute_physrec(ti99_lvl1_imgref *l1_img, unsigned aphysrec, void *dest) +{ + ti99_sector_address address; + + + if (l1_img->img_format == if_harddisk) + { +#if 0 + win_aphysrec_to_sector_address(aphysrec, & l1_img->geometry, & address); + return read_sector(l1_img, & address, dest); +#endif + + return imghd_read(&l1_img->harddisk_handle, aphysrec, dest) != IMGTOOLERR_SUCCESS; + } + else + { + dsk_aphysrec_to_sector_address(aphysrec, & l1_img->geometry, & address); + + return read_sector(l1_img, & address, dest); + } +} + +/* + Write one 256-byte physical record to a disk image + + l1_img (I/O): level-1 image handle + aphysrec (I): absolute physrec address + src (I): pointer to 256-byte source buffer + + Return non-zero on error +*/ +static int write_absolute_physrec(ti99_lvl1_imgref *l1_img, unsigned aphysrec, const void *src) +{ + ti99_sector_address address; + + + if (l1_img->img_format == if_harddisk) + { +#if 0 + win_aphysrec_to_sector_address(aphysrec, & l1_img->geometry, & address); + return write_sector(l1_img, & address, dest); +#endif + + return imghd_write(&l1_img->harddisk_handle, aphysrec, src) != IMGTOOLERR_SUCCESS; + } + else + { + dsk_aphysrec_to_sector_address(aphysrec, & l1_img->geometry, & address); + + return write_sector(l1_img, & address, src); + } +} + +#if 0 +#pragma mark - +#pragma mark LEVEL 2 DISK ROUTINES +#endif + +/* + Level 2 implements files as a succession of 256-byte-long physical records. + + Level 2 implements allocation bitmap (and AU), disk catalog, etc. +*/ + +/* + WIN VIB/DDR record +*/ +struct win_vib_ddr +{ + char name[10]; /* disk volume name (10 characters, pad with spaces) */ + UINT16BE totAUs; /* total number of AUs */ + uint8_t secspertrack; /* HFDC: sectors per track (typically 32) */ + /* SCSI: reserved */ + union + { + struct + { + uint8_t id[3]; /* V1 VIB: 'WIN' */ + } vib_v1; + + struct /* V2 VIB: extra params */ + { + uint8_t res_AUs; /* # AUs reserved for vib, bitmap, ddr, fdir and fdr, divided by 64 */ + uint8_t step_spd; /* HFDC: step speed (0-7) */ + /* SCSI: reserved */ + uint8_t red_w_cur;/* HFDC: reduced write current cylinder, divided by 8 */ + /* SCSI: reserved */ + } vib_v2; + + struct + { + uint8_t id[3]; /* DDR: 'DIR' */ + } ddr; + } u; + UINT16BE params; /* bits 0-3: sectors/AU - 1 */ + /* HFDC: */ + /* bits 4-7: # heads - 1 */ + /* bit 8: V1: buffered head stepping flag */ + /* V2: reserved */ + /* bit 9-15: write precompensation track, divided by 16 */ + /* SCSI: */ + /* bits 4-15: reserved */ + ti99_date_time creation;/* date and time of creation */ + uint8_t num_files; /* number of files in directory */ + uint8_t num_subdirs; /* number of subdirectories in directory */ + UINT16BE fdir_AU; /* points to root directory fdir */ + union + { + struct + { + UINT16BE dsk1_AU; /* HFDC: points to current dsk1 emulation image (0 if none) */ + /* SCSI: reserved */ + } vib; + + struct + { + UINT16BE parent_ddr_AU; /* points to parent directory DDR */ + } ddr; + } u2; + UINT16BE subdir_AU[114];/* points to all subdirectory DDRs */ +}; + +/* + AU format +*/ +struct ti99_AUformat +{ + int totAUs; /* total number of AUs */ + int physrecsperAU; /* number of 256-byte physical records per AU */ +}; + +/* + DSK directory reference: 0 for root, 1 for 1st subdir, 2 for 2nd subdir, 3 + for 3rd subdir +*/ +/*typedef int dir_ref;*/ + +/* + catalog entry (used for in-memory catalog) +*/ +struct dir_entry +{ + uint16_t dir_ptr; /* DSK: unused */ + /* WIN: AU address of the DDR for this directory */ + char name[10]; /* name of this directory (copied from the VIB for DSK, DDR for WIN) */ +}; + +struct file_entry +{ + uint16_t fdr_ptr; /* DSK: aphysrec address of the FDR for this file */ + /* WIN: AU address of the FDR for this file */ + char name[10]; /* name of this file (copied from FDR) */ +}; + +struct ti99_catalog +{ + int num_subdirs; /* number of subdirectories */ + int num_files; /* number of files */ + dir_entry subdirs[114]; /* description of each subdir */ + file_entry files[128]; /* description of each file */ +}; + +/* + level-2 disk image descriptor +*/ +struct ti99_lvl2_imgref_dsk +{ + uint16_t totphysrecs; /* total number of aphysrecs (extracted from vib record in aphysrec 0) */ + ti99_catalog catalogs[4]; /* catalog of root directory and up to 3 subdirectories */ + uint16_t fdir_aphysrec[4]; /* fdir aphysrec address for root directory + and up to 3 subdirectories */ +}; + +enum win_vib_t +{ + win_vib_v1, + win_vib_v2 +}; +struct ti99_lvl2_imgref_win +{ + win_vib_t vib_version; /* version of the vib record in aphysrec 0 (see win_vib_ddr) */ +}; + +enum l2i_t +{ + L2I_DSK, + L2I_WIN +}; + +struct ti99_lvl2_imgref +{ + ti99_lvl1_imgref l1_img;/* image format, imgtool image handle, image geometry */ + ti99_AUformat AUformat; /* AU format */ + int data_offset; /* In order to reduce seek times when searching the + disk for a given file name, fdr (and ddr, and + fdir) records are preferentially allocated in + AUs n to data_offset, whereas data records are + preferentially allocated in AUs starting at + data_offset. */ + /* With the DSK disk structure, n is always 2 (if 1 + physrec per AU) or 1 (if 2 physrecs per AU or + more), and data_offset is arbitrarily chosen as + 34. */ + /* With the WIN disk structure, n depends on the + size of the volume bitmap, which itself depends + on the number of AUs on disk (we always have n + <= 33), and data_offset is read from the vib + record (except with the obsolete v1 VIB, where + we use a default value of 64). */ + char vol_name[10]; /* cached volume name (extracted from vib record in aphysrec 0) */ + + uint8_t abm[8192]; /* allocation bitmap */ + + l2i_t type; /* structure format */ + + union + { + ti99_lvl2_imgref_dsk dsk; + ti99_lvl2_imgref_win win; + }; /* structure-specific info */ +}; + +/* + file flags found in fdr (and tifiles) +*/ +enum +{ + fdr99_f_program = 0x01, /* set for program files */ + fdr99_f_int = 0x02, /* set for binary files */ + fdr99_f_wp = 0x08, /* set if file is write-protected */ + /*fdr99_f_backup = 0x10,*/ /* set if file has been modified since last backup */ + /*fdr99_f_dskimg = 0x20,*/ /* set if file is a DSK image (HFDC HD only) */ + fdr99_f_var = 0x80 /* set if file uses variable-length records*/ +}; + +/* + DSK FDR record +*/ +struct dsk_fdr +{ + char name[10]; /* file name (10 characters, pad with spaces) */ + UINT16BE xreclen; /* extended record len: if record len is >= 256, */ + /* reclen is set to 0 and the actual reclen is */ + /* stored here (Myarc HFDC only). TI reserved */ + /* this field for data chain pointer extension, */ + /* but this was never implemented. */ + uint8_t flags; /* file status flags (see enum above) */ + uint8_t recsperphysrec; /* logical records per physrec */ + /* ignored for variable length record files and */ + /* program files */ + UINT16BE fphysrecs; /* file length in physrecs */ + /* Note that the HFDC defines this field as the */ + /* number of allocated physrecs in the cluster */ + /* chain (i.e. rounded on the next AU */ + /* boundary), so level-3 routines should use */ + /* the fixrecs field instead to determine the */ + /* logical length of field. IIRC, the HFDC */ + /* implementation is regarded as a bug because */ + /* program files do not define the fixrecs field */ + /* field, so program field saved by the HFDC */ + /* DSR may be larger than they should. */ + uint8_t eof; /* EOF offset in last physrec for variable length */ + /* record files and program files (0->256) */ + uint8_t reclen; /* logical record size in bytes ([1,255] 0->256) */ + /* Maximum allowable record size for variable */ + /* length record files. Reserved for program */ + /* files (set to 0). Set to 0 if reclen >=256 */ + /* (HFDC only). */ + UINT16LE fixrecs; /* file length in logical records */ + /* For variable length record files, number of */ + /* 256-byte records actually used. */ + ti99_date_time creation;/* date and time of creation (HFDC and BwG only; */ + /* reserved in TI) */ + ti99_date_time update; /* date and time of last write to file (HFDC and */ + /* BwG only; reserved in TI) */ + uint8_t clusters[76][3]; /* data cluster table: 0 through 76 entries (3 */ + /* bytes each), one entry for each file cluster. */ + /* 12 bits: address of first AU of cluster */ + /* 12 bits: offset of last 256-byte record in cluster */ +}; + +/* + WIN FDR record +*/ +struct win_fdr +{ + char name[10]; /* file name (10 characters, pad with spaces) */ + UINT16BE xreclen; /* extended record len: if record len is >= 256, */ + /* reclen is set to 0 and the actual reclen is */ + /* stored here (Myarc HFDC only). TI reserved */ + /* this field for data chain pointer extension, */ + /* but this was never implemented. */ + uint8_t flags; /* file status flags (see enum above) */ + uint8_t recsperphysrec; /* logical records per physrec */ + /* ignored for variable length record files and */ + /* program files */ + UINT16BE fphysrecs_LSW; /* eldest FDR: file length in physrecs */ + /* Note that the HFDC defines this field as the */ + /* number of allocated physrecs in the cluster */ + /* chain (i.e. rounded on the next AU */ + /* boundary), so level-3 routines should use */ + /* the fixrecs field instead to determine the */ + /* logical length of field. IIRC, the HFDC */ + /* implementation is regarded as a bug because */ + /* program files do not define the fixrecs field */ + /* field, so program field saved by the HFDC */ + /* DSR may be larger than they should. */ + /* other sibling FDRs: index of the first file */ + /* physrec in this particular sibling FDR */ + uint8_t eof; /* EOF offset in last physrec for variable length */ + /* record files and program files (0->256)*/ + uint8_t reclen; /* logical record size in bytes ([1,255]) */ + /* Maximum allowable record size for variable */ + /* length record files. Reserved for program */ + /* files (set to 0). Set to 0 if reclen >=256 */ + /* (HFDC only). */ + UINT16LE fixrecs_LSW; /* file length in logical records */ + /* For variable length record files, number of */ + /* 256-byte records actually used. */ + ti99_date_time creation;/* date and time of creation */ + ti99_date_time update; /* date and time of last write to file */ + + char id[2]; /* 'FI' */ + UINT16BE prevsibFDR_AU; /* address of the AU where previous sibling FDR is */ + /* (see also xinfo_LSB) */ + UINT16BE nextsibFDR_AU; /* address of the AU where next sibling FDR is */ + /* (see also xinfo_LSB) */ + UINT16BE sibFDR_AUlen; /* total number of data AUs allocated in this particular sibling FDR */ + UINT16BE parent_FDIR_AU;/* FDIR the file is listed in */ + uint8_t xinfo_MSB; /* extended information (MSByte) */ + /* bits 0-3: MSN of fphysrecs */ + /* bits 4-7: MSN of fixrecs */ + uint8_t xinfo_LSB; /* extended information (LSByte) */ + /* bits 8-11: physrec offset within AU for */ + /* previous sibling FDR (see prevsibFDR_AU) */ + /* bits 12-15: physrec offset within AU for */ + /* next sibling FDR (see nextsibFDR_AU) */ + UINT16BE clusters[54][2];/* data cluster table: 0 through 54 entries (4 */ + /* bytes each), one entry for each file cluster. */ + /* 16 bits: address of first AU of cluster */ + /* 16 bits: address of last AU of cluster */ +}; + +/* + tifile header: stand-alone file +*/ +struct tifile_header +{ + char tifiles[8]; /* always '\7TIFILES' */ + UINT16BE fphysrecs; /* file length in physrecs */ + uint8_t flags; /* see enum above */ + uint8_t recsperphysrec; /* records per physrec */ + uint8_t eof; /* current position of eof in last physrec (0->255)*/ + uint8_t reclen; /* bytes per record ([1,255] 0->256) */ + UINT16BE fixrecs; /* file length in records */ + uint8_t res[128-16]; /* reserved */ + /* * variant a: */ + /* 112 chars: 0xCA53 repeated 56 times */ + /* * variant b: */ + /* 4 chars: unknown */ + /* 108 chars: 0xCA53 repeated 54 times */ + /* * variant c: */ + /* 10 chars: original TI file name filed with spaces */ + /* 102 chars: spaces */ + /* * variant d: */ + /* 10 chars: original TI file name filed with spaces */ + /* 2 chars: CR+LF */ + /* 98 chars: spaces */ + /* 2 chars: CR+LF */ + /* * variant e: */ + /* 10 chars: original TI file name */ + /* 4 bytes: unknown */ + /* 4 bytes: time & date of creation */ + /* 4 bytes: time & date of last update */ + /* 90 chars: spaces */ + /* * variant f: */ + /* 6 bytes: 'MYTERM' */ + /* 4 bytes: time & date of creation */ + /* 4 bytes: time & date of last update */ + /* 2 bytes: unknown (always >0000) */ + /* 96 chars: 0xCA53 repeated 56 times */ +}; + +/* + level-2 file descriptor +*/ +struct ti99_lvl2_fileref_dsk +{ + struct ti99_lvl2_imgref *l2_img; + int fdr_aphysrec; + dsk_fdr fdr; +}; + +struct ti99_lvl2_fileref_win +{ + struct ti99_lvl2_imgref *l2_img; + unsigned fphysrecs; /* copy of field in the eldest FDR */ + unsigned eldestfdr_aphysrec; /* aphysrec address of the eldest FDR */ + unsigned curfdr_aphysrec; /* aphysrec address of the currently open sibling FDR */ + win_fdr curfdr; /* buffer with currently open sibling FDR */ +}; + +struct ti99_lvl2_fileref_tifiles +{ + imgtool::stream *file_handle; + tifile_header hdr; +}; + +enum l2f_type_t +{ + L2F_DSK, + L2F_WIN, + L2F_TIFILES +}; + +struct ti99_lvl2_fileref +{ + l2f_type_t type; + union + { + ti99_lvl2_fileref_dsk dsk; + ti99_lvl2_fileref_win win; + ti99_lvl2_fileref_tifiles tifiles; + }; +}; + +static struct ti99_lvl2_imgref *get_lvl2_imgref(imgtool::image &image) +{ + return (struct ti99_lvl2_imgref *) image.extra_bytes(); +} + +/* + Compare two (possibly empty) catalog entry for qsort +*/ +static int cat_file_compare_qsort(const void *p1, const void *p2) +{ + const file_entry *entry1 = (const file_entry *)p1; + const file_entry *entry2 = (const file_entry *)p2; + + if ((entry1->fdr_ptr == 0) && (entry2->fdr_ptr == 0)) + return 0; + else if (entry1->fdr_ptr == 0) + return +1; + else if (entry2->fdr_ptr == 0) + return -1; + else + return memcmp(entry1->name, entry2->name, 10); +} + +static int cat_dir_compare_qsort(const void *p1, const void *p2) +{ + const dir_entry *entry1 = (const dir_entry *)p1; + const dir_entry *entry2 = (const dir_entry *)p2; + + if ((entry1->dir_ptr == 0) && (entry2->dir_ptr == 0)) + return 0; + else if (entry1->dir_ptr == 0) + return +1; + else if (entry2->dir_ptr == 0) + return -1; + else + return memcmp(entry1->name, entry2->name, 10); +} + +/* + Read a directory catalog from disk image + + l2_img: image reference + aphysrec: physical record address of the FDIR + dest: pointer to the destination buffer where the catalog should be written + + Return an error code if there was an error, 0 otherwise. +*/ +static int dsk_read_catalog(struct ti99_lvl2_imgref *l2_img, int aphysrec, ti99_catalog *dest) +{ + int totphysrecs = l2_img->dsk.totphysrecs; + UINT16BE fdir_buf[128]; + dsk_fdr fdr; + int i; + int reply; + + + /* Read FDIR record */ + reply = read_absolute_physrec(& l2_img->l1_img, aphysrec, fdir_buf); + if (reply) + return IMGTOOLERR_READERROR; + + /* Copy FDIR info to catalog structure */ + for (i=0; i<128; i++) + dest->files[i].fdr_ptr = get_UINT16BE(fdir_buf[i]); + + /* Check FDIR pointers and check and extract file names from DDRs */ + for (i=0; i<128; i++) + { + if (dest->files[i].fdr_ptr >= totphysrecs) + { + return IMGTOOLERR_CORRUPTIMAGE; + } + else if (dest->files[i].fdr_ptr) + { + reply = read_absolute_physrec(& l2_img->l1_img, dest->files[i].fdr_ptr, &fdr); + if (reply) + return IMGTOOLERR_READERROR; + + /* check and copy file name */ + if (check_fname(fdr.name)) + return IMGTOOLERR_CORRUPTIMAGE; + memcpy(dest->files[i].name, fdr.name, 10); + } + } + + /* Check catalog */ + for (i=0; i<127; i++) + { + if (((! dest->files[i].fdr_ptr) && dest->files[i+1].fdr_ptr) + || ((dest->files[i].fdr_ptr && dest->files[i+1].fdr_ptr) && (memcmp(dest->files[i].name, dest->files[i+1].name, 10) >= 0))) + { + /* if the catalog is not sorted, we repair it */ + qsort(dest->files, std::size(dest->files), sizeof(dest->files[0]), + cat_file_compare_qsort); + break; + } + } + + /* Set file count */ + for (i=0; (i<128) && (dest->files[i].fdr_ptr != 0); i++) + ; + dest->num_files = i; + + /* Set subdir count to 0 (subdirs are loaded elsewhere) */ + dest->num_subdirs = 0; + + return 0; +} + +/* + Read a directory catalog from disk image + + l2_img: image reference + DDR_AU: AU address of the VIB/DDR + dest: pointer to the destination buffer where the catalog should be written + + Return an error code if there was an error, 0 otherwise. +*/ +static int win_read_catalog(struct ti99_lvl2_imgref *l2_img, int DDR_AU, ti99_catalog *dest) +{ + win_vib_ddr ddr_buf; + UINT16BE fdir_buf[128]; + win_fdr fdr_buf; + int i; + int reply; + + + /* Read DDR record */ + reply = read_absolute_physrec(& l2_img->l1_img, DDR_AU*l2_img->AUformat.physrecsperAU, &ddr_buf); + if (reply) + return IMGTOOLERR_READERROR; + + /* sanity checks */ + if ((ddr_buf.num_files > 127) || (ddr_buf.num_subdirs > 114) || (get_UINT16BE(ddr_buf.fdir_AU) > l2_img->AUformat.totAUs)) + return IMGTOOLERR_CORRUPTIMAGE; + + /* set file count and subdir count */ + dest->num_files = ddr_buf.num_files; + dest->num_subdirs = ddr_buf.num_subdirs; + + /* Copy DDR info to catalog structure */ + for (i=0; isubdirs[i].dir_ptr = get_UINT16BE(ddr_buf.subdir_AU[i]); + + /* Read FDIR record */ + reply = read_absolute_physrec(& l2_img->l1_img, get_UINT16BE(ddr_buf.fdir_AU)*l2_img->AUformat.physrecsperAU, fdir_buf); + if (reply) + return IMGTOOLERR_READERROR; + + /* Copy FDIR info to catalog structure */ + for (i=0; inum_files; i++) + dest->files[i].fdr_ptr = get_UINT16BE(fdir_buf[i]); + + /* Check DDR pointers and check and extract file names from FDRs */ + for (i=0; inum_subdirs; i++) + { + if (dest->subdirs[i].dir_ptr >= l2_img->AUformat.totAUs) + { + return IMGTOOLERR_CORRUPTIMAGE; + } + else if (dest->subdirs[i].dir_ptr) + { + reply = read_absolute_physrec(& l2_img->l1_img, dest->subdirs[i].dir_ptr*l2_img->AUformat.physrecsperAU, &ddr_buf); + if (reply) + return IMGTOOLERR_READERROR; + + /* check and copy file name */ + if (check_fname(ddr_buf.name)) + return IMGTOOLERR_CORRUPTIMAGE; + memcpy(dest->subdirs[i].name, ddr_buf.name, 10); + } + } + + /* Check FDIR pointers and check and extract file names from FDRs */ + for (i=0; inum_files; i++) + { + if (dest->files[i].fdr_ptr >= l2_img->AUformat.totAUs) + { + return IMGTOOLERR_CORRUPTIMAGE; + } + else if (dest->files[i].fdr_ptr) + { + reply = read_absolute_physrec(& l2_img->l1_img, dest->files[i].fdr_ptr*l2_img->AUformat.physrecsperAU, &fdr_buf); + if (reply) + return IMGTOOLERR_READERROR; + + /* check and copy file name */ + if (check_fname(fdr_buf.name)) + return IMGTOOLERR_CORRUPTIMAGE; + memcpy(dest->files[i].name, fdr_buf.name, 10); + } + } + + /* Check catalog */ + + /* Check subdir order */ + for (i=0; inum_subdirs-1; i++) + { + if (((! dest->subdirs[i].dir_ptr) && dest->subdirs[i+1].dir_ptr) + || ((dest->subdirs[i].dir_ptr && dest->subdirs[i+1].dir_ptr) && (memcmp(dest->subdirs[i].name, dest->subdirs[i+1].name, 10) >= 0))) + { + /* if the subdir catalog is not sorted, we repair it */ + qsort(dest->subdirs, dest->num_subdirs, sizeof(dest->subdirs[0]), cat_dir_compare_qsort); + break; + } + } + + /* Fix subdir count */ + while (dest->num_subdirs && (dest->subdirs[dest->num_subdirs-1].dir_ptr == 0)) + dest->num_subdirs--; + + /* Check file order */ + for (i=0; inum_files-1; i++) + { + if (((! dest->files[i].fdr_ptr) && dest->files[i+1].fdr_ptr) + || ((dest->files[i].fdr_ptr && dest->files[i+1].fdr_ptr) && (memcmp(dest->files[i].name, dest->files[i+1].name, 10) >= 0))) + { + /* if the file catalog is not sorted, we repair it */ + qsort(dest->files, dest->num_files, sizeof(dest->files[0]), cat_file_compare_qsort); + break; + } + } + + /* Fix file count */ + while (dest->num_files && (dest->files[dest->num_files-1].fdr_ptr == 0)) + dest->num_files--; + + return 0; +} + +/* + Search for a file path on a floppy image + + l2_img: image reference + fpath: path of the file to search + parent_ref_valid: set to true if either the file was found or the file was + not found but its parent dir was + parent_ref: reference to parent dir (0 for root) + out_is_dir: true if element is a directory + catalog_index: on output, index of file catalog entry (may be NULL) +*/ +static int dsk_find_catalog_entry(struct ti99_lvl2_imgref *l2_img, const char *fpath, int *parent_ref_valid, int *parent_ref, int *out_is_dir, int *catalog_index) +{ + int i; + const ti99_catalog *cur_catalog; + const char *element_start, *element_end; + int element_len; + char element[10]; + int is_dir = false; + + + cur_catalog = & l2_img->dsk.catalogs[0]; + if (parent_ref_valid) + (* parent_ref_valid) = false; + if (parent_ref) + *parent_ref = 0; + + element_start = fpath; + do + { + /* find next path element */ + element_end = strchr(element_start, '.'); + element_len = element_end ? (element_end - element_start) : strlen(element_start); + if ((element_len > 10) || (element_len == 0)) + return IMGTOOLERR_BADFILENAME; + /* last path element */ + if ((!element_end) && parent_ref_valid) + (* parent_ref_valid) = true; + + /* generate file name */ + memcpy(element, element_start, element_len); + memset(element+element_len, ' ', 10-element_len); + + /* search entry in subdirectories */ + for (i = 0; inum_subdirs; i++) + { + if (! memcmp(element, cur_catalog->subdirs[i].name, 10)) + { + is_dir = true; + break; + } + } + + /* if it failed, search entry in files */ + if (i == cur_catalog->num_subdirs) + { + for (i = 0; inum_files; i++) + { + if (! memcmp(element, cur_catalog->files[i].name, 10)) + { + is_dir = false; + break; + } + } + /* exit if not found */ + if (i == cur_catalog->num_files) + { + return IMGTOOLERR_FILENOTFOUND; + } + } + + /* iterate */ + if (element_end) + { + element_start = element_end+1; + + if (! is_dir) + /* this is not a directory */ + return IMGTOOLERR_BADFILENAME; + + /* initialize cur_catalog */ + cur_catalog = & l2_img->dsk.catalogs[i+1]; + if (parent_ref) + *parent_ref = i+1; + } + else + element_start = NULL; + } + while (element_start); + + if (out_is_dir) + *out_is_dir = is_dir; + + if (catalog_index) + *catalog_index = i; + + return 0; +} + +/* + Search for a file path on a harddisk image + + l2_img: image reference + fpath: path of the file to search + parent_ref_valid: set to true if either the file was found or the file was + not found but its parent dir was + parent_ddr_AU: parent DDR AU address (0 for root) + parent_catalog: catalog of parent dir (cannot be NULL) + out_is_dir: true if element is a directory + catalog_index: on output, index of file catalog entry (may be NULL) +*/ +static int win_find_catalog_entry(struct ti99_lvl2_imgref *l2_img, const char *fpath, + int *parent_ref_valid, int *parent_ddr_AU, ti99_catalog *parent_catalog, + int *out_is_dir, int *catalog_index) +{ + int i; + const char *element_start, *element_end; + int element_len; + char element[10]; + int is_dir = false; + int errorcode; + + if (parent_ref_valid) + (* parent_ref_valid) = false; + if (parent_ddr_AU) + *parent_ddr_AU = 0; + + errorcode = win_read_catalog(l2_img, 0, parent_catalog); + if (errorcode) + return errorcode; + + element_start = fpath; + do + { + /* find next path element */ + element_end = strchr(element_start, '.'); + element_len = element_end ? (element_end - element_start) : strlen(element_start); + if ((element_len > 10) || (element_len == 0)) + return IMGTOOLERR_BADFILENAME; + /* last path element */ + if ((!element_end) && parent_ref_valid) + (* parent_ref_valid) = true; + + /* generate file name */ + memcpy(element, element_start, element_len); + memset(element+element_len, ' ', 10-element_len); + + /* search entry in subdirectories */ + for (i = 0; inum_subdirs; i++) + { + if (! memcmp(element, parent_catalog->subdirs[i].name, 10)) + { + is_dir = true; + break; + } + } + + /* if it failed, search entry in files */ + if (i == parent_catalog->num_subdirs) + { + for (i = 0; inum_files; i++) + { + if (! memcmp(element, parent_catalog->files[i].name, 10)) + { + is_dir = false; + break; + } + } + /* exit if not found */ + if (i == parent_catalog->num_files) + { + return IMGTOOLERR_FILENOTFOUND; + } + } + + /* iterate */ + if (element_end) + { + element_start = element_end+1; + + if (! is_dir) + /* this is not a directory */ + return IMGTOOLERR_BADFILENAME; + + if (parent_ddr_AU) + *parent_ddr_AU = parent_catalog->subdirs[i].dir_ptr; + + errorcode = win_read_catalog(l2_img, parent_catalog->subdirs[i].dir_ptr, parent_catalog); + if (errorcode) + return errorcode; + } + else + element_start = NULL; + } + while (element_start); + + if (out_is_dir) + *out_is_dir = is_dir; + + if (catalog_index) + *catalog_index = i; + + return 0; +} + +/* + Allocate one AU on disk, for use as a fdr record + + l2_img: image reference + fdr_AU: on output, address of allocated AU +*/ +static int alloc_fdr_AU(struct ti99_lvl2_imgref *l2_img, unsigned *fdr_AU) +{ + int totAUs = l2_img->AUformat.totAUs; + int i; + + for (i=0; iabm[i >> 3] & (1 << (i & 7)))) + { + *fdr_AU = i; + l2_img->abm[i >> 3] |= 1 << (i & 7); + + return 0; + } + } + + return IMGTOOLERR_NOSPACE; +} + +static inline int get_dsk_fdr_cluster_baseAU(struct ti99_lvl2_imgref *l2_img, dsk_fdr *fdr, int cluster_index) +{ + int reply; + + /* read base AU/physrec for this cluster */ + reply = ((fdr->clusters[cluster_index][1] & 0xf) << 8) | fdr->clusters[cluster_index][0]; + /* convert to AU address */ + if (l2_img->AUformat.physrecsperAU <= 2) + reply /= l2_img->AUformat.physrecsperAU; + + return reply; +} + +static inline int get_dsk_fdr_cluster_baseaphysrec(struct ti99_lvl2_imgref *l2_img, dsk_fdr *fdr, int cluster_index) +{ + int reply; + + /* read base AU/physrec for this cluster */ + reply = ((fdr->clusters[cluster_index][1] & 0xf) << 8) | fdr->clusters[cluster_index][0]; + /* convert to physrec address */ + if (l2_img->AUformat.physrecsperAU > 2) + reply *= l2_img->AUformat.physrecsperAU; + + return reply; +} + +static inline int get_dsk_fdr_cluster_lastfphysrec(dsk_fdr *fdr, int cluster_index) +{ + return (fdr->clusters[cluster_index][2] << 4) | (fdr->clusters[cluster_index][1] >> 4); +} + +static inline void set_dsk_fdr_cluster_lastfphysrec(dsk_fdr *fdr, int cluster_index, int data) +{ + fdr->clusters[cluster_index][1] = (fdr->clusters[cluster_index][1] & 0x0f) | (data << 4); + fdr->clusters[cluster_index][2] = data >> 4; +} + +static inline void set_dsk_fdr_cluster(struct ti99_lvl2_imgref *l2_img, dsk_fdr *fdr, int cluster_index, int baseAU, int lastfphysrec) +{ + /* convert AU address to FDR value */ + if (l2_img->AUformat.physrecsperAU <= 2) + baseAU *= l2_img->AUformat.physrecsperAU; + + /* write cluster entry */ + fdr->clusters[cluster_index][0] = baseAU; + fdr->clusters[cluster_index][1] = ((baseAU >> 8) & 0x0f) | (lastfphysrec << 4); + fdr->clusters[cluster_index][2] = lastfphysrec >> 4; +} + +static inline unsigned get_win_fdr_fphysrecs(win_fdr *fdr) +{ + return (((unsigned) fdr->xinfo_MSB << 12) & 0xf0000) | get_UINT16BE(fdr->fphysrecs_LSW); +} + +static inline void set_win_fdr_fphysrecs(win_fdr *fdr, unsigned data) +{ + fdr->xinfo_MSB = (fdr->xinfo_MSB & 0x0f) | ((data >> 12) & 0xf0); + set_UINT16BE(&fdr->fphysrecs_LSW, data & 0xffff); +} + +static inline unsigned get_win_fdr_fixrecs(win_fdr *fdr) +{ + return (((unsigned) fdr->xinfo_MSB << 16) & 0xf0000) | get_UINT16LE(fdr->fixrecs_LSW); +} + +static inline void set_win_fdr_fixrecs(win_fdr *fdr, unsigned data) +{ + fdr->xinfo_MSB = (fdr->xinfo_MSB & 0xf0) | ((data >> 16) & 0x0f); + set_UINT16LE(&fdr->fixrecs_LSW, data & 0xffff); +} + +static inline unsigned get_win_fdr_prevsibFDR_aphysrec(struct ti99_lvl2_imgref *l2_img, win_fdr *fdr) +{ + unsigned prevsibFDR_AU = get_UINT16BE(fdr->prevsibFDR_AU); + + return prevsibFDR_AU + ? (prevsibFDR_AU * l2_img->AUformat.physrecsperAU + ((fdr->xinfo_LSB >> 4) & 0xf)) + : 0; +} + +static inline unsigned get_win_fdr_nextsibFDR_aphysrec(struct ti99_lvl2_imgref *l2_img, win_fdr *fdr) +{ + unsigned nextsibFDR_AU = get_UINT16BE(fdr->nextsibFDR_AU); + + return nextsibFDR_AU + ? (nextsibFDR_AU * l2_img->AUformat.physrecsperAU + (fdr->xinfo_LSB & 0xf)) + : 0; +} + +static inline unsigned get_win_fdr_cursibFDR_basefphysrec(win_fdr *fdr) +{ + return get_UINT16BE(fdr->prevsibFDR_AU) ? get_win_fdr_fphysrecs(fdr) : 0; +} + +/* + Advance to next sibling FDR +*/ +static int win_goto_next_sibFDR(ti99_lvl2_fileref_win *win_file) +{ + if (get_UINT16BE(win_file->curfdr.nextsibFDR_AU) == 0) + return IMGTOOLERR_UNEXPECTED; + + win_file->curfdr_aphysrec = get_win_fdr_nextsibFDR_aphysrec(win_file->l2_img, &win_file->curfdr); + if (read_absolute_physrec(& win_file->l2_img->l1_img, win_file->curfdr_aphysrec, &win_file->curfdr)) + return IMGTOOLERR_READERROR; + + return 0; +} + +/* + Back to previous sibling FDR +*/ +static int win_goto_prev_sibFDR(ti99_lvl2_fileref_win *win_file) +{ + if (get_UINT16BE(win_file->curfdr.prevsibFDR_AU) == 0) + return IMGTOOLERR_UNEXPECTED; + + win_file->curfdr_aphysrec = get_win_fdr_prevsibFDR_aphysrec(win_file->l2_img, &win_file->curfdr); + if (read_absolute_physrec(& win_file->l2_img->l1_img, win_file->curfdr_aphysrec, &win_file->curfdr)) + return IMGTOOLERR_READERROR; + + return 0; +} + +/* + Append a new sibling FDR at the end of the sibling FDR list, and open it. + + You must have gone to the end of the list. +*/ +static int win_alloc_sibFDR(ti99_lvl2_fileref_win *win_file) +{ + unsigned oldfdr_AU, oldfdr_physrecinAU; + unsigned newfdr_AU, newfdr_physrecinAU; + int allocated = false; + int errorcode; + unsigned cursibFDR_basefphysrec; + + if (get_UINT16BE(win_file->curfdr.nextsibFDR_AU)) + return IMGTOOLERR_UNEXPECTED; + + oldfdr_AU = win_file->curfdr_aphysrec / win_file->l2_img->AUformat.physrecsperAU; + oldfdr_physrecinAU = win_file->curfdr_aphysrec % win_file->l2_img->AUformat.physrecsperAU; + + if (oldfdr_physrecinAU != (win_file->l2_img->AUformat.physrecsperAU - 1)) + { /* current AU is not full */ + newfdr_AU = oldfdr_AU; + newfdr_physrecinAU = oldfdr_physrecinAU + 1; + } + else + { /* current AU is full: allocate another */ + errorcode = alloc_fdr_AU(win_file->l2_img, &newfdr_AU); + if (errorcode) + return errorcode; + newfdr_physrecinAU = 0; + allocated = true; + } + + set_UINT16BE(&win_file->curfdr.nextsibFDR_AU, newfdr_AU); + win_file->curfdr.xinfo_LSB = (win_file->curfdr.xinfo_LSB & 0xf0) | newfdr_physrecinAU; + + /* save current sibling FDR */ + if (write_absolute_physrec(& win_file->l2_img->l1_img, win_file->curfdr_aphysrec, &win_file->curfdr)) + { + /* clear pointer */ + set_UINT16BE(&win_file->curfdr.nextsibFDR_AU, 0); + win_file->curfdr.xinfo_LSB = win_file->curfdr.xinfo_LSB & 0xf0; + if (allocated) + /* free AU */ + win_file->l2_img->abm[newfdr_AU >> 3] |= 1 << (newfdr_AU & 7); + return IMGTOOLERR_WRITEERROR; + } + + /* now update in-memory structure to describe new sibling FDR */ + cursibFDR_basefphysrec = get_win_fdr_cursibFDR_basefphysrec(&win_file->curfdr) + + get_UINT16BE(win_file->curfdr.sibFDR_AUlen) * win_file->l2_img->AUformat.physrecsperAU; + + set_UINT16BE(&win_file->curfdr.nextsibFDR_AU, 0); + set_UINT16BE(&win_file->curfdr.prevsibFDR_AU, oldfdr_AU); + win_file->curfdr.xinfo_LSB = oldfdr_physrecinAU << 4; + + win_file->curfdr_aphysrec = newfdr_AU * win_file->l2_img->AUformat.physrecsperAU + newfdr_physrecinAU; + + set_win_fdr_fphysrecs(&win_file->curfdr, cursibFDR_basefphysrec); + set_UINT16BE(&win_file->curfdr.sibFDR_AUlen, 0); + memset(win_file->curfdr.clusters, 0, sizeof(win_file->curfdr.clusters)); + + return 0; +} + +/* + Extend a file with nb_alloc_physrecs extra physrecs + + dsk_file: file reference + nb_alloc_physrecs: number of physical records to allocate +*/ +static int dsk_alloc_file_physrecs(ti99_lvl2_fileref_dsk *dsk_file, int nb_alloc_physrecs) +{ + int totAUs = dsk_file->l2_img->AUformat.totAUs; + int free_physrecs; + int fphysrecs; + int i; + int cluster_index; + int last_sec, p_last_sec = 0; + int cur_block_seclen; + int cluster_baseAU, cluster_AUlen; + int first_best_block_baseAU = 0, first_best_block_seclen; + int second_best_block_baseAU = 0, second_best_block_seclen; + int search_start; + + /* compute free space */ + free_physrecs = 0; + for (i=0; il2_img->abm[i >> 3] & (1 << (i & 7)))) + free_physrecs += dsk_file->l2_img->AUformat.physrecsperAU; + } + + /* check we have enough free space */ + if (free_physrecs < nb_alloc_physrecs) + return IMGTOOLERR_NOSPACE; + + /* current number of data physrecs in file */ + fphysrecs = get_UINT16BE(dsk_file->fdr.fphysrecs); + + if (fphysrecs == 0) + { /* cluster array must be empty */ + cluster_index = 0; + } + else + { /* try to extend last block */ + last_sec = -1; + for (cluster_index=0; cluster_index<76; cluster_index++) + { + p_last_sec = last_sec; + last_sec = get_dsk_fdr_cluster_lastfphysrec(&dsk_file->fdr, cluster_index); + if (last_sec >= (fphysrecs-1)) + break; + } + if (cluster_index == 76) + /* that sucks */ + return IMGTOOLERR_CORRUPTIMAGE; + + if (last_sec > (fphysrecs-1)) + { /* some extra space has already been allocated */ + cur_block_seclen = last_sec - (fphysrecs-1); + if (cur_block_seclen > nb_alloc_physrecs) + cur_block_seclen = nb_alloc_physrecs; + + fphysrecs += cur_block_seclen; + set_UINT16BE(& dsk_file->fdr.fphysrecs, fphysrecs); + nb_alloc_physrecs -= cur_block_seclen; + if (! nb_alloc_physrecs) + return 0; /* done */ + } + + /* round up to next AU boundary */ + last_sec = last_sec + dsk_file->l2_img->AUformat.physrecsperAU - (last_sec % dsk_file->l2_img->AUformat.physrecsperAU) - 1; + + if (last_sec > (fphysrecs-1)) + { /* some extra space has already been allocated */ + cur_block_seclen = last_sec - (fphysrecs-1); + if (cur_block_seclen > nb_alloc_physrecs) + cur_block_seclen = nb_alloc_physrecs; + + fphysrecs += cur_block_seclen; + set_UINT16BE(& dsk_file->fdr.fphysrecs, fphysrecs); + set_dsk_fdr_cluster_lastfphysrec(&dsk_file->fdr, cluster_index, fphysrecs-1); + nb_alloc_physrecs -= cur_block_seclen; + if (! nb_alloc_physrecs) + return 0; /* done */ + } + + /* read base AU address for this cluster */ + cluster_baseAU = get_dsk_fdr_cluster_baseAU(dsk_file->l2_img, &dsk_file->fdr, cluster_index); + /* point past cluster end */ + cluster_baseAU += (last_sec-p_last_sec/*+file->l2_img->AUformat.physrecsperAU-1*/) / dsk_file->l2_img->AUformat.physrecsperAU; + /* count free physrecs after last block */ + cur_block_seclen = 0; + for (i=cluster_baseAU; (! (dsk_file->l2_img->abm[i >> 3] & (1 << (i & 7)))) && (cur_block_seclen < nb_alloc_physrecs) && (i < totAUs); i++) + cur_block_seclen += dsk_file->l2_img->AUformat.physrecsperAU; + if (cur_block_seclen) + { /* extend last block */ + if (cur_block_seclen > nb_alloc_physrecs) + cur_block_seclen = nb_alloc_physrecs; + + fphysrecs += cur_block_seclen; + set_UINT16BE(& dsk_file->fdr.fphysrecs, fphysrecs); + last_sec += cur_block_seclen; + nb_alloc_physrecs -= cur_block_seclen; + set_dsk_fdr_cluster_lastfphysrec(&dsk_file->fdr, cluster_index, last_sec); + cluster_AUlen = (cur_block_seclen + dsk_file->l2_img->AUformat.physrecsperAU - 1) / dsk_file->l2_img->AUformat.physrecsperAU; + for (i=0; il2_img->abm[(i+cluster_baseAU) >> 3] |= 1 << ((i+cluster_baseAU) & 7); + if (! nb_alloc_physrecs) + return 0; /* done */ + } + cluster_index++; + if (cluster_index == 76) + /* that sucks */ + return IMGTOOLERR_NOSPACE; + } + + search_start = dsk_file->l2_img->data_offset; /* initially, search for free space only in data space */ + while (nb_alloc_physrecs) + { + /* find smallest data block at least nb_alloc_physrecs in length, and largest data block less than nb_alloc_physrecs in length */ + first_best_block_seclen = INT_MAX; + second_best_block_seclen = 0; + for (i=search_start; il2_img->abm[i >> 3] & (1 << (i & 7)))) + { /* found one free block */ + /* compute its length */ + cluster_baseAU = i; + cur_block_seclen = 0; + while ((il2_img->abm[i >> 3] & (1 << (i & 7))))) + { + cur_block_seclen += dsk_file->l2_img->AUformat.physrecsperAU; + i++; + } + /* compare to previous best and second-best blocks */ + if ((cur_block_seclen < first_best_block_seclen) && (cur_block_seclen >= nb_alloc_physrecs)) + { + first_best_block_baseAU = cluster_baseAU; + first_best_block_seclen = cur_block_seclen; + if (cur_block_seclen == nb_alloc_physrecs) + /* no need to search further */ + break; + } + else if ((cur_block_seclen > second_best_block_seclen) && (cur_block_seclen < nb_alloc_physrecs)) + { + second_best_block_baseAU = cluster_baseAU; + second_best_block_seclen = cur_block_seclen; + } + } + } + + if (first_best_block_seclen != INT_MAX) + { /* found one contiguous block which can hold it all */ + fphysrecs += nb_alloc_physrecs; + set_UINT16BE(& dsk_file->fdr.fphysrecs, fphysrecs); + + set_dsk_fdr_cluster(dsk_file->l2_img, &dsk_file->fdr, cluster_index, first_best_block_baseAU, fphysrecs-1); + cluster_AUlen = (nb_alloc_physrecs + dsk_file->l2_img->AUformat.physrecsperAU - 1) / dsk_file->l2_img->AUformat.physrecsperAU; + for (i=0; il2_img->abm[(i+first_best_block_baseAU) >> 3] |= 1 << ((i+first_best_block_baseAU) & 7); + + nb_alloc_physrecs = 0; + } + else if (second_best_block_seclen != 0) + { /* jeez, we need to fragment it. We use the largest smaller block to limit fragmentation. */ + fphysrecs += second_best_block_seclen; + set_UINT16BE(& dsk_file->fdr.fphysrecs, fphysrecs); + + set_dsk_fdr_cluster(dsk_file->l2_img, &dsk_file->fdr, cluster_index, second_best_block_baseAU, fphysrecs-1); + cluster_AUlen = (second_best_block_seclen + dsk_file->l2_img->AUformat.physrecsperAU - 1) / dsk_file->l2_img->AUformat.physrecsperAU; + for (i=0; il2_img->abm[(i+second_best_block_baseAU) >> 3] |= 1 << ((i+second_best_block_baseAU) & 7); + + nb_alloc_physrecs -= second_best_block_seclen; + + cluster_index++; + if (cluster_index == 76) + /* that sucks */ + return IMGTOOLERR_NOSPACE; + } + else if (search_start != 0) + { /* we did not find any free physrec in the data section of the disk */ + search_start = 0; /* time to fall back to fdr space */ + } + else + return IMGTOOLERR_NOSPACE; /* This should never happen, as we pre-check that there is enough free space */ + } + + return 0; +} + +/* + Extend a file with nb_alloc_physrecs extra physrecs + + win_file: file reference + nb_alloc_physrecs: number of physical records to allocate +*/ +static int win_alloc_file_physrecs(ti99_lvl2_fileref_win *win_file, int nb_alloc_physrecs) +{ + int totAUs = win_file->l2_img->AUformat.totAUs; + int free_physrecs; + int fphysrecs; + int i; + int cluster_index; + int num_fphysrec; + int cur_block_seclen; + int cluster_baseAU, cluster_AUlen; + int first_best_block_baseAU = 0, first_best_block_seclen; + int second_best_block_baseAU = 0, second_best_block_seclen; + int search_start; + int errorcode; + + /* compute free space */ + free_physrecs = 0; + for (i=0; il2_img->abm[i >> 3] & (1 << (i & 7)))) + free_physrecs += win_file->l2_img->AUformat.physrecsperAU; + } + + /* check we have enough free space */ + if (free_physrecs < nb_alloc_physrecs) + return IMGTOOLERR_NOSPACE; + + /* move to last sibling non-empty FDR */ + while ((get_UINT16BE(win_file->curfdr.nextsibFDR_AU) != 0) && + (get_UINT16BE(win_file->curfdr.clusters[53][0]) != 0)) + { + errorcode = win_goto_next_sibFDR(win_file); + if (errorcode) + return errorcode; + } + if ((get_UINT16BE(win_file->curfdr.clusters[0][0]) == 0) && (get_UINT16BE(win_file->curfdr.prevsibFDR_AU) != 0)) + { /* this is annoying: we have found a sibling FDR filled with 0s: rewind + to last non-empty sibling if applicable */ + errorcode = win_goto_prev_sibFDR(win_file); + if (errorcode) + return errorcode; + } + + /* current number of data physrecs in file */ + fphysrecs = win_file->fphysrecs; + + /* current number of allocated physrecs */ + num_fphysrec = get_win_fdr_cursibFDR_basefphysrec(&win_file->curfdr) + + get_UINT16BE(win_file->curfdr.sibFDR_AUlen) * win_file->l2_img->AUformat.physrecsperAU; + + if (num_fphysrec > fphysrecs) + { /* some extra space has already been allocated */ + cur_block_seclen = num_fphysrec - fphysrecs; + if (cur_block_seclen > nb_alloc_physrecs) + cur_block_seclen = nb_alloc_physrecs; + + fphysrecs += cur_block_seclen; + win_file->fphysrecs = fphysrecs; + /* TODO: update eldest FDR fphysrecs field */ + /*set_win_fdr_fphysrecs(& win_file->rootfdr.fphysrecs, fphysrecs);*/ + nb_alloc_physrecs -= cur_block_seclen; + if (! nb_alloc_physrecs) + return 0; /* done */ + } + + /* find last non-empty cluster */ + for (cluster_index=0; (cluster_index<54) && (get_UINT16BE(win_file->curfdr.clusters[cluster_index][0]) != 0); cluster_index++) + ; + /* if we are dealing with an empty file, we will have (cluster_index == 0)... */ + if (cluster_index != 0) + { + cluster_index--; + /* try to extend last cluster */ + /* point past cluster end */ + cluster_baseAU = get_UINT16BE(win_file->curfdr.clusters[cluster_index][1]) + 1; + /* count free physrecs after last block */ + cur_block_seclen = 0; + for (i=cluster_baseAU; (! (win_file->l2_img->abm[i >> 3] & (1 << (i & 7)))) && (cur_block_seclen < nb_alloc_physrecs) && (i < totAUs); i++) + cur_block_seclen += win_file->l2_img->AUformat.physrecsperAU; + if (cur_block_seclen) + { /* extend last block */ + if (cur_block_seclen > nb_alloc_physrecs) + cur_block_seclen = nb_alloc_physrecs; + + fphysrecs += cur_block_seclen; + cluster_AUlen = (cur_block_seclen + win_file->l2_img->AUformat.physrecsperAU - 1) / win_file->l2_img->AUformat.physrecsperAU; + win_file->fphysrecs = fphysrecs; + /* TODO: update eldest FDR fphysrecs field */ + /*set_win_fdr_fphysrecs(& win_file->rootfdr.fphysrecs, fphysrecs);*/ + set_UINT16BE(&win_file->curfdr.sibFDR_AUlen, + get_UINT16BE(win_file->curfdr.sibFDR_AUlen)+cluster_AUlen); + set_UINT16BE(&win_file->curfdr.clusters[cluster_index][1], + get_UINT16BE(win_file->curfdr.clusters[cluster_index][1])+cluster_AUlen); + for (i=0; il2_img->abm[(i+cluster_baseAU) >> 3] |= 1 << ((i+cluster_baseAU) & 7); + nb_alloc_physrecs -= cur_block_seclen; + if (! nb_alloc_physrecs) + return 0; /* done */ + } + /* now point to first free entry in cluster table */ + cluster_index++; + } + + search_start = win_file->l2_img->data_offset; /* initially, search for free space only in data space */ + while (nb_alloc_physrecs) + { + /* find smallest data block at least nb_alloc_physrecs in length, and largest data block less than nb_alloc_physrecs in length */ + first_best_block_seclen = INT_MAX; + second_best_block_seclen = 0; + for (i=search_start; il2_img->abm[i >> 3] & (1 << (i & 7)))) + { /* found one free block */ + /* compute its length */ + cluster_baseAU = i; + cur_block_seclen = 0; + while ((il2_img->abm[i >> 3] & (1 << (i & 7))))) + { + cur_block_seclen += win_file->l2_img->AUformat.physrecsperAU; + i++; + } + /* compare to previous best and second-best blocks */ + if ((cur_block_seclen < first_best_block_seclen) && (cur_block_seclen >= nb_alloc_physrecs)) + { + first_best_block_baseAU = cluster_baseAU; + first_best_block_seclen = cur_block_seclen; + if (cur_block_seclen == nb_alloc_physrecs) + /* no need to search further */ + break; + } + else if ((cur_block_seclen > second_best_block_seclen) && (cur_block_seclen < nb_alloc_physrecs)) + { + second_best_block_baseAU = cluster_baseAU; + second_best_block_seclen = cur_block_seclen; + } + } + } + + if ((first_best_block_seclen != INT_MAX) || (second_best_block_seclen != 0)) + { + if (cluster_index == 54) + { + /* end of cluster list: go to next sibling FDR */ + if (write_absolute_physrec(& win_file->l2_img->l1_img, win_file->curfdr_aphysrec, &win_file->curfdr)) + return IMGTOOLERR_WRITEERROR; + if (get_UINT16BE(win_file->curfdr.nextsibFDR_AU) != 0) + { /* read next sibling FDR */ + errorcode = win_goto_next_sibFDR(win_file); + if (errorcode) + return errorcode; + } + else + { /* allocate new sibling FDR */ + errorcode = win_alloc_sibFDR(win_file); + if (errorcode) + return errorcode; + } + cluster_index = 0; + } + } + + if (first_best_block_seclen != INT_MAX) + { /* found one contiguous block which can hold it all */ + fphysrecs += nb_alloc_physrecs; + cluster_AUlen = (nb_alloc_physrecs + win_file->l2_img->AUformat.physrecsperAU - 1) / win_file->l2_img->AUformat.physrecsperAU; + win_file->fphysrecs = fphysrecs; + /* TODO: update eldest FDR fphysrecs field */ + /*set_win_fdr_fphysrecs(& win_file->rootfdr.fphysrecs, fphysrecs);*/ + set_UINT16BE(&win_file->curfdr.sibFDR_AUlen, + get_UINT16BE(win_file->curfdr.sibFDR_AUlen)+cluster_AUlen); + set_UINT16BE(&win_file->curfdr.clusters[cluster_index][0], first_best_block_baseAU); + set_UINT16BE(&win_file->curfdr.clusters[cluster_index][1], + first_best_block_baseAU+cluster_AUlen-1); + + for (i=0; il2_img->abm[(i+first_best_block_baseAU) >> 3] |= 1 << ((i+first_best_block_baseAU) & 7); + + nb_alloc_physrecs = 0; + } + else if (second_best_block_seclen != 0) + { /* jeez, we need to fragment it. We use the largest smaller block to limit fragmentation. */ + fphysrecs += second_best_block_seclen; + cluster_AUlen = (second_best_block_seclen + win_file->l2_img->AUformat.physrecsperAU - 1) / win_file->l2_img->AUformat.physrecsperAU; + win_file->fphysrecs = fphysrecs; + /* TODO: update eldest FDR fphysrecs field */ + /*set_win_fdr_fphysrecs(& win_file->rootfdr.fphysrecs, fphysrecs);*/ + set_UINT16BE(&win_file->curfdr.sibFDR_AUlen, + get_UINT16BE(win_file->curfdr.sibFDR_AUlen)+cluster_AUlen); + set_UINT16BE(&win_file->curfdr.clusters[cluster_index][0], second_best_block_baseAU); + set_UINT16BE(&win_file->curfdr.clusters[cluster_index][1], + second_best_block_baseAU+cluster_AUlen-1); + + for (i=0; il2_img->abm[(i+second_best_block_baseAU) >> 3] |= 1 << ((i+second_best_block_baseAU) & 7); + + nb_alloc_physrecs -= second_best_block_seclen; + + /* now point to first free entry in cluster table */ + cluster_index++; + } + else if (search_start != 0) + { /* we did not find any free physrec in the data section of the disk */ + search_start = 0; /* time to fall back to fdr space */ + } + else + return IMGTOOLERR_NOSPACE; /* This should never happen, as we pre-check that there is enough free space */ + } + + return 0; +} + +/* + Allocate a new (empty) file +*/ +static int new_file_lvl2_dsk(struct ti99_lvl2_imgref *l2_img, int parent_ref, char filename[10], struct ti99_lvl2_fileref *l2_file) +{ + ti99_catalog *catalog = &l2_img->dsk.catalogs[parent_ref]; + unsigned fdr_AU, fdr_aphysrec; + int catalog_index, i; + int reply = 0; + int errorcode; + + + if (catalog->num_files >= 127) + /* if num_files == 128, catalog is full */ + /* if num_files == 127, catalog is not full, but we don't want to write + a 128th entry for compatibility with some existing DSRs that detect the + end of the FDIR array with a 0 entry (and do not check that the index + has not reached 128) */ + return IMGTOOLERR_NOSPACE; + + /* find insertion point in catalog */ + for (i=0; (i < catalog->num_files) && ((reply = memcmp(catalog->files[i].name, filename, 10)) < 0); i++) + ; + + if ((inum_files) && (reply == 0)) + /* file already exists */ + return IMGTOOLERR_BADFILENAME; + else + { + /* otherwise insert new entry */ + catalog_index = i; + errorcode = alloc_fdr_AU(l2_img, &fdr_AU); + if (errorcode) + return errorcode; + fdr_aphysrec = fdr_AU * l2_img->AUformat.physrecsperAU; + + /* shift catalog entries until the insertion point */ + for (i=catalog->num_files; i>catalog_index; i--) + catalog->files[i] = catalog->files[i-1]; + + /* write new catalog entry */ + catalog->files[catalog_index].fdr_ptr = fdr_aphysrec; + memcpy(catalog->files[catalog_index].name, filename, 10); + + /* update catalog len */ + catalog->num_files++; + } + + /* set up file handle */ + l2_file->type = L2F_DSK; + l2_file->dsk.l2_img = l2_img; + l2_file->dsk.fdr_aphysrec = fdr_aphysrec; + memset(&l2_file->dsk.fdr, 0, sizeof(l2_file->dsk.fdr)); + memcpy(l2_file->dsk.fdr.name, filename, 10); + + return 0; +} + +/* + Allocate a new (empty) file +*/ +static int new_file_lvl2_win(struct ti99_lvl2_imgref *l2_img, ti99_catalog *parent_catalog, char filename[10], struct ti99_lvl2_fileref *l2_file) +{ + unsigned fdr_AU; + int catalog_index, i; + int reply = 0; + int errorcode; + + + if (parent_catalog->num_files >= 127) + /* if num_files == 127, catalog is full */ + return IMGTOOLERR_NOSPACE; + + /* find insertion point in catalog */ + for (i=0; (i < parent_catalog->num_files) && ((reply = memcmp(parent_catalog->files[i].name, filename, 10)) < 0); i++) + ; + + if ((inum_files) && (reply == 0)) + /* file already exists */ + return IMGTOOLERR_BADFILENAME; + else + { + /* otherwise insert new entry */ + catalog_index = i; + errorcode = alloc_fdr_AU(l2_img, &fdr_AU); + if (errorcode) + return errorcode; + + /* shift catalog entries until the insertion point */ + for (i=parent_catalog->num_files; i>catalog_index; i--) + parent_catalog->files[i] = parent_catalog->files[i-1]; + + /* write new catalog entry */ + parent_catalog->files[catalog_index].fdr_ptr = fdr_AU; + memcpy(parent_catalog->files[catalog_index].name, filename, 10); + + /* update catalog len */ + parent_catalog->num_files++; + } + + /* set up file handle */ + l2_file->type = L2F_WIN; + l2_file->win.l2_img = l2_img; + l2_file->win.fphysrecs = 0; + l2_file->win.eldestfdr_aphysrec = fdr_AU * l2_img->AUformat.physrecsperAU; + l2_file->win.curfdr_aphysrec = l2_file->win.eldestfdr_aphysrec; + memset(&l2_file->win.curfdr, 0, sizeof(l2_file->win.curfdr)); + memcpy(l2_file->win.curfdr.name, filename, 10); + + return 0; +} + +/* + Allocate a new (empty) file +*/ +static int new_file_lvl2_tifiles(imgtool::stream &file_handle, struct ti99_lvl2_fileref *l2_file) +{ + /* set up file handle */ + l2_file->type = L2F_TIFILES; + l2_file->tifiles.file_handle = &file_handle; + memset(&l2_file->tifiles.hdr, 0, sizeof(l2_file->tifiles.hdr)); + l2_file->tifiles.hdr.tifiles[0] = '\7'; + l2_file->tifiles.hdr.tifiles[1] = 'T'; + l2_file->tifiles.hdr.tifiles[2] = 'I'; + l2_file->tifiles.hdr.tifiles[3] = 'F'; + l2_file->tifiles.hdr.tifiles[4] = 'I'; + l2_file->tifiles.hdr.tifiles[5] = 'L'; + l2_file->tifiles.hdr.tifiles[6] = 'E'; + l2_file->tifiles.hdr.tifiles[7] = 'S'; + + return 0; +} + +/* + Open an existing file on a floppy image + + l2_img: level 2 image the file is located on + fpath: access path to the file + file: set up if open is successful +*/ +static int open_file_lvl2_dsk(struct ti99_lvl2_imgref *l2_img, const char *fpath, struct ti99_lvl2_fileref *l2_file) +{ + int parent_ref, is_dir, catalog_index; + int reply; + + + reply = dsk_find_catalog_entry(l2_img, fpath, NULL, &parent_ref, &is_dir, &catalog_index); + if (reply) + return reply; + + if (is_dir) + /* this is not a file */ + return IMGTOOLERR_BADFILENAME; + + l2_file->type = L2F_DSK; + l2_file->dsk.l2_img = l2_img; + l2_file->dsk.fdr_aphysrec = l2_img->dsk.catalogs[parent_ref].files[catalog_index].fdr_ptr; + if (read_absolute_physrec(& l2_img->l1_img, l2_file->dsk.fdr_aphysrec, &l2_file->dsk.fdr)) + return IMGTOOLERR_READERROR; + + return 0; +} + +/* + Open an existing file on a harddisk image + + l2_img: level 2 image the file is located on + fpath: access path to the file + file: set up if open is successful +*/ +static int open_file_lvl2_win(struct ti99_lvl2_imgref *l2_img, const char *fpath, struct ti99_lvl2_fileref *l2_file) +{ + int parent_ref, is_dir, catalog_index; + ti99_catalog catalog; + int reply; + + reply = win_find_catalog_entry(l2_img, fpath, NULL, &parent_ref, &catalog, &is_dir, &catalog_index); + if (reply) + return reply; + + if (is_dir) + /* this is not a file */ + return IMGTOOLERR_BADFILENAME; + + l2_file->type = L2F_WIN; + l2_file->win.l2_img = l2_img; + l2_file->win.eldestfdr_aphysrec = catalog.files[catalog_index].fdr_ptr * l2_img->AUformat.physrecsperAU; + l2_file->win.curfdr_aphysrec = l2_file->win.eldestfdr_aphysrec; + if (read_absolute_physrec(& l2_img->l1_img, l2_file->win.curfdr_aphysrec, &l2_file->win.curfdr)) + return IMGTOOLERR_READERROR; + l2_file->win.fphysrecs = get_win_fdr_fphysrecs(&l2_file->win.curfdr); + + /* check integrity of FDR sibling chain */ + /* note that as we check that the back chain is consistent with the forward + chain, we will also detect any cycle in the sibling chain, so we do not + need to check against them explicitely */ + if (get_UINT16BE(l2_file->win.curfdr.prevsibFDR_AU) != 0) + return IMGTOOLERR_CORRUPTIMAGE; + + { + int i, pastendoflist_flag; + unsigned cur_fphysrec, sibFDR_AUlen; + win_fdr *cur_fdr, fdr_buf; + unsigned curfdr_aphysrec, prevfdr_aphysrec; + + cur_fphysrec = 0; + pastendoflist_flag = 0; + cur_fdr = &l2_file->win.curfdr; + curfdr_aphysrec = l2_file->win.eldestfdr_aphysrec; + + while (1) + { + sibFDR_AUlen = 0; + i=0; + if (! pastendoflist_flag) + { + /* compute number of allocated AUs and check number of AUs */ + for (; i<54; i++) + { + if (get_UINT16BE(cur_fdr->clusters[i][0]) == 0) + { + pastendoflist_flag = true; + break; + } + sibFDR_AUlen += get_UINT16BE(cur_fdr->clusters[i][1]) + - get_UINT16BE(cur_fdr->clusters[i][0]) + + 1; + } + } + /* clear remainder of cluster table */ + for (; i<54; i++) + { +#if 0 + set_UINT16BE(&cur_fdr->clusters[i][0], 0); + set_UINT16BE(&cur_fdr->clusters[i][1], 0); +#endif + if ((get_UINT16BE(cur_fdr->clusters[i][0]) != 0) || (get_UINT16BE(cur_fdr->clusters[i][1]) != 0)) + return IMGTOOLERR_CORRUPTIMAGE; + } + + /* check sibFDR_AUlen field */ + if (get_UINT16BE(cur_fdr->sibFDR_AUlen) != sibFDR_AUlen) + return IMGTOOLERR_CORRUPTIMAGE; + + /* update current file physrec position to point to end of sibling FDR */ + cur_fphysrec += sibFDR_AUlen * l2_file->win.l2_img->AUformat.physrecsperAU; + + /* exit loop if end of sibling chain */ + if (! get_UINT16BE(cur_fdr->nextsibFDR_AU)) + break; + + /* otherwise read next FDR */ + if (get_UINT16BE(cur_fdr->nextsibFDR_AU) >= l2_file->win.l2_img->AUformat.totAUs) + return IMGTOOLERR_CORRUPTIMAGE; + + prevfdr_aphysrec = curfdr_aphysrec; + curfdr_aphysrec = get_win_fdr_nextsibFDR_aphysrec(l2_file->win.l2_img, cur_fdr); + if (read_absolute_physrec(& l2_file->win.l2_img->l1_img, curfdr_aphysrec, &fdr_buf)) + return IMGTOOLERR_READERROR; + cur_fdr = &fdr_buf; + + /* check that back chaining is consistent with forward chaining */ + if (get_win_fdr_prevsibFDR_aphysrec(l2_file->win.l2_img, &fdr_buf) != prevfdr_aphysrec) + return IMGTOOLERR_CORRUPTIMAGE; + /* check fphysrecs field */ + if (get_win_fdr_fphysrecs(&fdr_buf) != cur_fphysrec) + return IMGTOOLERR_CORRUPTIMAGE; + + /* check consistency of informative fields (name, record format, flags, etc) */ + if (memcmp(fdr_buf.name, l2_file->win.curfdr.name, 10) + || (get_UINT16BE(fdr_buf.xreclen) != get_UINT16BE(l2_file->win.curfdr.xreclen)) + || (fdr_buf.flags != l2_file->win.curfdr.flags) + || (fdr_buf.recsperphysrec != l2_file->win.curfdr.recsperphysrec) + || (fdr_buf.eof != l2_file->win.curfdr.eof) + || (fdr_buf.reclen != l2_file->win.curfdr.reclen) + || (get_UINT16LE(fdr_buf.fixrecs_LSW) != get_UINT16LE(l2_file->win.curfdr.fixrecs_LSW)) + || memcmp(&fdr_buf.creation, &l2_file->win.curfdr.creation, 4) + || memcmp(&fdr_buf.update, &l2_file->win.curfdr.update, 4) + /*|| memcmp(fdr_buf.id, l2_file->win.curfdr.id, 2)*/ + || (get_UINT16BE(fdr_buf.parent_FDIR_AU) != get_UINT16BE(l2_file->win.curfdr.parent_FDIR_AU)) + || ((fdr_buf.xinfo_MSB & 0xf) != (l2_file->win.curfdr.xinfo_MSB & 0xf))) + return IMGTOOLERR_CORRUPTIMAGE; + } + if (cur_fphysrec < l2_file->win.fphysrecs) + return IMGTOOLERR_CORRUPTIMAGE; + } + + return 0; +} + +/* + Open an existing file in TIFILES format +*/ +static int open_file_lvl2_tifiles(imgtool::stream &file_handle, struct ti99_lvl2_fileref *l2_file) +{ + /* set up file handle */ + l2_file->type = L2F_TIFILES; + l2_file->tifiles.file_handle = &file_handle; + + /* seek to header */ + if (l2_file->tifiles.file_handle->seek(0, SEEK_SET)) + return IMGTOOLERR_READERROR; + /* read it */ + if (l2_file->tifiles.file_handle->read(&l2_file->tifiles.hdr, sizeof(l2_file->tifiles.hdr)) != sizeof(l2_file->tifiles.hdr)) + return IMGTOOLERR_READERROR; + + return 0; +} + +/* + compute the aphysrec address for a given file physical record (fphysrec) + + l2_img: image where the file is located +*/ +static int dsk_fphysrec_to_aphysrec(ti99_lvl2_fileref_dsk *dsk_file, unsigned fphysrec, unsigned *aphysrec) +{ + int cluster_index; + int cluster_firstfphysrec, cluster_lastfphysrec; + int cluster_baseaphysrec; + + + /* check parameter */ + if (fphysrec >= get_UINT16BE(dsk_file->fdr.fphysrecs)) + return IMGTOOLERR_UNEXPECTED; + + + /* search for the cluster in the data chain pointers array */ + cluster_firstfphysrec = 0; + for (cluster_index=0; cluster_index<76; cluster_index++) + { + /* read curent file block table entry */ + cluster_lastfphysrec = get_dsk_fdr_cluster_lastfphysrec(&dsk_file->fdr, cluster_index); + if (cluster_lastfphysrec >= fphysrec) + break; + cluster_firstfphysrec = cluster_lastfphysrec+1; + } + if (cluster_index == 76) + /* if not found, the file is corrupt */ + return IMGTOOLERR_CORRUPTIMAGE; + + /* read base aphysrec address for this cluster */ + cluster_baseaphysrec = get_dsk_fdr_cluster_baseaphysrec(dsk_file->l2_img, &dsk_file->fdr, cluster_index); + /* return absolute physrec address */ + *aphysrec = cluster_baseaphysrec + (fphysrec - cluster_firstfphysrec); + return 0; +} + +/* + compute the aphysrec address for a given file physical record (fphysrec) + + l2_img: image where the file is located +*/ +static int win_fphysrec_to_aphysrec(ti99_lvl2_fileref_win *win_file, unsigned fphysrec, unsigned *aphysrec) +{ + int cluster_index; + int cluster_firstfphysrec, cluster_lastfphysrec; + int cluster_baseaphysrec; + int errorcode; + + + /* check parameter */ + if (fphysrec >= win_file->fphysrecs) + return IMGTOOLERR_UNEXPECTED; + + /* look for correct sibling */ + if (fphysrec < get_win_fdr_cursibFDR_basefphysrec(& win_file->curfdr)) + { + while (fphysrec < get_win_fdr_cursibFDR_basefphysrec(& win_file->curfdr)) + { + if (get_UINT16BE(win_file->curfdr.prevsibFDR_AU) == 0) + return IMGTOOLERR_CORRUPTIMAGE; + errorcode = win_goto_prev_sibFDR(win_file); + if (errorcode) + return errorcode; + } + } + else /*if (fphysrec >= get_win_fdr_cursibFDR_basefphysrec(& dsk_file->curfdr))*/ + { + while (fphysrec >= (get_win_fdr_cursibFDR_basefphysrec(& win_file->curfdr) + + get_UINT16BE(win_file->curfdr.sibFDR_AUlen) * win_file->l2_img->AUformat.physrecsperAU)) + { + if (get_UINT16BE(win_file->curfdr.nextsibFDR_AU) == 0) + return IMGTOOLERR_CORRUPTIMAGE; + errorcode = win_goto_next_sibFDR(win_file); + if (errorcode) + return errorcode; + } + } + + + /* search for the cluster in the data chain pointers array */ + cluster_firstfphysrec = get_win_fdr_cursibFDR_basefphysrec(& win_file->curfdr); + for (cluster_index = 0; cluster_index<54; cluster_index++) + { + cluster_lastfphysrec = cluster_firstfphysrec + + (get_UINT16BE(win_file->curfdr.clusters[cluster_index][1]) + - get_UINT16BE(win_file->curfdr.clusters[cluster_index][0]) + + 1) + * win_file->l2_img->AUformat.physrecsperAU; + if (fphysrec < cluster_lastfphysrec) + break; + + cluster_firstfphysrec = cluster_lastfphysrec; + } + + if (cluster_index == 54) + return IMGTOOLERR_CORRUPTIMAGE; + + + /* read base aphysrec address for this cluster */ + cluster_baseaphysrec = get_UINT16BE(win_file->curfdr.clusters[cluster_index][0]) * win_file->l2_img->AUformat.physrecsperAU; + /* return absolute physrec address */ + *aphysrec = cluster_baseaphysrec + (fphysrec - cluster_firstfphysrec); + return 0; +} + +/* + read a 256-byte physical record from a file +*/ +static int read_file_physrec(struct ti99_lvl2_fileref *l2_file, unsigned fphysrec, void *dest) +{ + int errorcode; + unsigned aphysrec; + + switch (l2_file->type) + { + case L2F_DSK: + /* compute absolute physrec address */ + errorcode = dsk_fphysrec_to_aphysrec(&l2_file->dsk, fphysrec, &aphysrec); + if (errorcode) + return errorcode; + /* read physrec */ + if (read_absolute_physrec(& l2_file->dsk.l2_img->l1_img, aphysrec, dest)) + return IMGTOOLERR_READERROR; + break; + + case L2F_WIN: + /* compute absolute physrec address */ + errorcode = win_fphysrec_to_aphysrec(&l2_file->win, fphysrec, &aphysrec); + if (errorcode) + return errorcode; + /* read physrec */ + if (read_absolute_physrec(& l2_file->win.l2_img->l1_img, aphysrec, dest)) + return IMGTOOLERR_READERROR; + break; + + case L2F_TIFILES: + /* seek to physrec */ + if (l2_file->tifiles.file_handle->seek(128+256*fphysrec, SEEK_SET)) + return IMGTOOLERR_READERROR; + /* read it */ + if (l2_file->tifiles.file_handle->read(dest, 256) != 256) + return IMGTOOLERR_READERROR; + break; + } + + return 0; +} + +/* + read a 256-byte physical record from a file +*/ +static int write_file_physrec(struct ti99_lvl2_fileref *l2_file, unsigned fphysrec, const void *src) +{ + int errorcode; + unsigned aphysrec; + + switch (l2_file->type) + { + case L2F_DSK: + /* compute absolute physrec address */ + errorcode = dsk_fphysrec_to_aphysrec(&l2_file->dsk, fphysrec, &aphysrec); + if (errorcode) + return errorcode; + /* write physrec */ + if (write_absolute_physrec(& l2_file->dsk.l2_img->l1_img, aphysrec, src)) + return IMGTOOLERR_WRITEERROR; + break; + + case L2F_WIN: + /* compute absolute physrec address */ + errorcode = win_fphysrec_to_aphysrec(&l2_file->win, fphysrec, &aphysrec); + if (errorcode) + return errorcode; + /* write physrec */ + if (write_absolute_physrec(& l2_file->win.l2_img->l1_img, aphysrec, src)) + return IMGTOOLERR_WRITEERROR; + break; + + case L2F_TIFILES: + /* seek to physrec */ + if (l2_file->tifiles.file_handle->seek(128+256*fphysrec, SEEK_SET)) + return IMGTOOLERR_WRITEERROR; + /* write it */ + if (l2_file->tifiles.file_handle->write(src, 256) != 256) + return IMGTOOLERR_WRITEERROR; + break; + } + + return 0; +} + +/* + Write a field in every fdr record associated to a file +*/ +#ifdef UNUSED_FUNCTION +static int set_win_fdr_field(struct ti99_lvl2_fileref *l2_file, size_t offset, size_t size, void *data) +{ + win_fdr fdr_buf; + unsigned fdr_aphysrec; + int errorcode = 0; + + for (fdr_aphysrec = l2_file->win.eldestfdr_aphysrec; + fdr_aphysrec && ((errorcode = (read_absolute_physrec(&l2_file->win.l2_img->l1_img, fdr_aphysrec, &fdr_buf) ? IMGTOOLERR_READERROR : 0)) == 0); + fdr_aphysrec = get_win_fdr_nextsibFDR_physrec(l2_file->win.l2_img, &fdr_buf)) + { + memcpy(((uint8_t *) &fdr_buf) + offset, data, size); + if (write_absolute_physrec(&l2_file->win.l2_img->l1_img, fdr_aphysrec, &fdr_buf)) + { + errorcode = IMGTOOLERR_WRITEERROR; + break; + } + } + + return errorcode; +} +#endif + +static uint8_t get_file_flags(struct ti99_lvl2_fileref *l2_file) +{ + int reply = 0; + + switch (l2_file->type) + { + case L2F_DSK: + reply = l2_file->dsk.fdr.flags; + break; + + case L2F_WIN: + reply = l2_file->win.curfdr.flags; + break; + + case L2F_TIFILES: + reply = l2_file->tifiles.hdr.flags; + break; + } + + return reply; +} + +static void set_file_flags(struct ti99_lvl2_fileref *l2_file, uint8_t data) +{ + switch (l2_file->type) + { + case L2F_DSK: + l2_file->dsk.fdr.flags = data; + break; + + case L2F_WIN: + l2_file->win.curfdr.flags = data; + break; + + case L2F_TIFILES: + l2_file->tifiles.hdr.flags = data; + break; + } +} + +static uint8_t get_file_recsperphysrec(struct ti99_lvl2_fileref *l2_file) +{ + int reply = 0; + + switch (l2_file->type) + { + case L2F_DSK: + reply = l2_file->dsk.fdr.recsperphysrec; + break; + + case L2F_WIN: + reply = l2_file->win.curfdr.recsperphysrec; + break; + + case L2F_TIFILES: + reply = l2_file->tifiles.hdr.recsperphysrec; + break; + } + + return reply; +} + +static void set_file_recsperphysrec(struct ti99_lvl2_fileref *l2_file, uint8_t data) +{ + switch (l2_file->type) + { + case L2F_DSK: + l2_file->dsk.fdr.recsperphysrec = data; + break; + + case L2F_WIN: + l2_file->win.curfdr.recsperphysrec = data; + break; + + case L2F_TIFILES: + l2_file->tifiles.hdr.recsperphysrec = data; + break; + } +} + +static unsigned get_file_fphysrecs(struct ti99_lvl2_fileref *l2_file) +{ + int reply = 0; + + switch (l2_file->type) + { + case L2F_DSK: + reply = get_UINT16BE(l2_file->dsk.fdr.fphysrecs); + break; + + case L2F_WIN: + reply = l2_file->win.fphysrecs; + break; + + case L2F_TIFILES: + reply = get_UINT16BE(l2_file->tifiles.hdr.fphysrecs); + break; + } + + return reply; +} + +static int set_file_fphysrecs(struct ti99_lvl2_fileref *l2_file, unsigned data) +{ + switch (l2_file->type) + { + case L2F_DSK: + if (data >= 65536) + return IMGTOOLERR_UNIMPLEMENTED; + set_UINT16BE(&l2_file->dsk.fdr.fphysrecs, data); + break; + + case L2F_WIN: + l2_file->win.fphysrecs = data; + break; + + case L2F_TIFILES: + if (data >= 65536) + return IMGTOOLERR_UNIMPLEMENTED; + set_UINT16BE(&l2_file->tifiles.hdr.fphysrecs, data); + break; + } + + return 0; +} + +static uint8_t get_file_eof(struct ti99_lvl2_fileref *l2_file) +{ + int reply = 0; + + switch (l2_file->type) + { + case L2F_DSK: + reply = l2_file->dsk.fdr.eof; + break; + + case L2F_WIN: + reply = l2_file->win.curfdr.eof; + break; + + case L2F_TIFILES: + reply = l2_file->tifiles.hdr.eof; + break; + } + + return reply; +} + +static void set_file_eof(struct ti99_lvl2_fileref *l2_file, uint8_t data) +{ + switch (l2_file->type) + { + case L2F_DSK: + l2_file->dsk.fdr.eof = data; + break; + + case L2F_WIN: + l2_file->win.curfdr.eof = data; + break; + + case L2F_TIFILES: + l2_file->tifiles.hdr.eof = data; + break; + } +} + +static uint16_t get_file_reclen(struct ti99_lvl2_fileref *l2_file) +{ + int reply = 0; + + switch (l2_file->type) + { + case L2F_DSK: + reply = l2_file->dsk.fdr.reclen; + if ((reply == 0) && (! (l2_file->dsk.fdr.flags & (fdr99_f_program /*| fdr99_f_var*/)))) + reply = get_UINT16BE(l2_file->dsk.fdr.xreclen); + break; + + case L2F_WIN: + reply = l2_file->win.curfdr.reclen; + if ((reply == 0) && (! (l2_file->win.curfdr.flags & (fdr99_f_program /*| fdr99_f_var*/)))) + reply = get_UINT16BE(l2_file->win.curfdr.xreclen); + break; + + case L2F_TIFILES: + reply = l2_file->tifiles.hdr.reclen; + break; + } + + return reply; +} + +static int set_file_reclen(struct ti99_lvl2_fileref *l2_file, uint16_t data) +{ + switch (l2_file->type) + { + case L2F_DSK: + if (data < 256) + { + l2_file->dsk.fdr.reclen = data; + set_UINT16BE(&l2_file->dsk.fdr.xreclen, 0); + } + else + { + l2_file->dsk.fdr.reclen = 0; + set_UINT16BE(&l2_file->dsk.fdr.xreclen, data); + } + break; + + case L2F_WIN: + if (data < 256) + { + l2_file->win.curfdr.reclen = data; + set_UINT16BE(&l2_file->win.curfdr.xreclen, 0); + } + else + { + l2_file->win.curfdr.reclen = 0; + set_UINT16BE(&l2_file->win.curfdr.xreclen, data); + } + break; + + case L2F_TIFILES: + if (data >= 256) + return IMGTOOLERR_UNIMPLEMENTED; + l2_file->tifiles.hdr.reclen = data; + break; + } + + return 0; +} + +static unsigned get_file_fixrecs(struct ti99_lvl2_fileref *l2_file) +{ + int reply = 0; + + switch (l2_file->type) + { + case L2F_DSK: + reply = get_UINT16LE(l2_file->dsk.fdr.fixrecs); + break; + + case L2F_WIN: + reply = get_win_fdr_fixrecs(&l2_file->win.curfdr); + break; + + case L2F_TIFILES: + reply = get_UINT16BE(l2_file->tifiles.hdr.fixrecs); + break; + } + + return reply; +} + +static int set_file_fixrecs(struct ti99_lvl2_fileref *l2_file, unsigned data) +{ + switch (l2_file->type) + { + case L2F_DSK: + if (data >= 65536) + return IMGTOOLERR_UNIMPLEMENTED; + set_UINT16LE(&l2_file->dsk.fdr.fixrecs, data); + break; + + case L2F_WIN: + set_win_fdr_fixrecs(&l2_file->win.curfdr, data); + break; + + case L2F_TIFILES: + if (data >= 65536) + return IMGTOOLERR_UNIMPLEMENTED; + set_UINT16BE(&l2_file->tifiles.hdr.fixrecs, data); + break; + } + + return 0; +} + +static void get_file_creation_date(struct ti99_lvl2_fileref *l2_file, ti99_date_time *reply) +{ + switch (l2_file->type) + { + case L2F_DSK: + *reply = l2_file->dsk.fdr.creation; + break; + + case L2F_WIN: + *reply = l2_file->win.curfdr.creation; + break; + + case L2F_TIFILES: + memset(reply, 0, sizeof(*reply)); + break; + } +} + +static void set_file_creation_date(struct ti99_lvl2_fileref *l2_file, ti99_date_time data) +{ + switch (l2_file->type) + { + case L2F_DSK: + l2_file->dsk.fdr.creation = data; + break; + + case L2F_WIN: + l2_file->win.curfdr.creation = data; + break; + + case L2F_TIFILES: + break; + } +} + +static void get_file_update_date(struct ti99_lvl2_fileref *l2_file, ti99_date_time *reply) +{ + switch (l2_file->type) + { + case L2F_DSK: + *reply = l2_file->dsk.fdr.update; + break; + + case L2F_WIN: + *reply = l2_file->win.curfdr.update; + break; + + case L2F_TIFILES: + memset(reply, 0, sizeof(*reply)); + break; + } +} + +static void set_file_update_date(struct ti99_lvl2_fileref *l2_file, ti99_date_time data) +{ + switch (l2_file->type) + { + case L2F_DSK: + l2_file->dsk.fdr.update = data; + break; + + case L2F_WIN: + l2_file->win.curfdr.update = data; + break; + + case L2F_TIFILES: + break; + } +} + +#ifdef UNUSED_FUNCTION +static void current_date_time(ti99_date_time *reply) +{ + /* All these functions should be ANSI */ + time_t cur_time = time(NULL); + struct tm expanded_time = *localtime(& cur_time); + + reply->time_MSB = (expanded_time.tm_hour << 3) | (expanded_time.tm_min >> 3); + reply->time_LSB = (expanded_time.tm_min << 5) | (expanded_time.tm_sec >> 1); + reply->date_MSB = ((expanded_time.tm_year % 100) << 1) | ((expanded_time.tm_mon+1) >> 3); + reply->date_LSB = ((expanded_time.tm_mon+1) << 5) | expanded_time.tm_mday; +} +#endif + +#if 0 +#pragma mark - +#pragma mark LEVEL 3 DISK ROUTINES +#endif + +/* + Level 3 implements files as a succession of logical records. + + There are three types of files: + * program files that are not implemented at level 3 (no logical record) + * files with fixed-size records (random-access files) + * files with variable-size records (sequential-access) +*/ + +struct ti99_lvl3_fileref +{ + ti99_lvl2_fileref l2_file; + + int cur_log_rec; + int cur_phys_rec; + int cur_pos_in_phys_rec; +}; + +#ifdef UNUSED_FUNCTION +/* + Open a file on level 3. + + To open a file on level 3, you must open (or create) the file on level 2, + then pass the file reference to open_file_lvl3. +*/ +static int open_file_lvl3(ti99_lvl3_fileref *l3_file) +{ + l3_file->cur_log_rec = 0; + l3_file->cur_phys_rec = 0; + l3_file->cur_pos_in_phys_rec = 0; + + /*if ()*/ + + return 0; +} + +/* + Test a file for EOF +*/ +static int is_eof(ti99_lvl3_fileref *l3_file) +{ + int flags = get_file_flags(&l3_file->l2_file); + int fphysrecs = get_file_fphysrecs(&l3_file->l2_file); + int fdr_eof = get_file_eof(&l3_file->l2_file); + + if (flags & fdr99_f_var) + { + return (l3_file->cur_phys_rec >= fphysrecs); + } + else + { + return ((l3_file->cur_phys_rec >= fphysrecs) + || ((l3_file->cur_phys_rec == (fphysrecs-1)) + && (l3_file->cur_pos_in_phys_rec >= (fdr_eof ? fdr_eof : 256)))); + } +} + +/* + Read next record from a file +*/ +static int read_next_record(ti99_lvl3_fileref *l3_file, void *dest, int *out_reclen) +{ + int errorcode; + uint8_t physrec_buf[256]; + int reclen; + int flags = get_file_flags(&l3_file->l2_file); + int fphysrecs = get_file_fphysrecs(&l3_file->l2_file); + int fdr_eof = get_file_eof(&l3_file->l2_file); + + if (flags & fdr99_f_program) + { + /* program files have no level-3 record */ + return IMGTOOLERR_UNEXPECTED; + } + else if (flags & fdr99_f_var) + { + /* variable-length records */ + if (is_eof(l3_file)) + return IMGTOOLERR_UNEXPECTED; + errorcode = read_file_physrec(&l3_file->l2_file, l3_file->cur_phys_rec, physrec_buf); + if (errorcode) + return errorcode; + /* read reclen */ + reclen = physrec_buf[l3_file->cur_pos_in_phys_rec]; + /* check integrity */ + if ((reclen == 0xff) || (reclen > get_file_reclen(&l3_file->l2_file)) + || ((l3_file->cur_pos_in_phys_rec + 1 + reclen) > 256)) + return IMGTOOLERR_CORRUPTIMAGE; + /* copy to buffer */ + memcpy(dest, physrec_buf + l3_file->cur_pos_in_phys_rec + 1, reclen); + l3_file->cur_pos_in_phys_rec += reclen + 1; + /* skip to next physrec if needed */ + if ((l3_file->cur_pos_in_phys_rec == 256) + || (physrec_buf[l3_file->cur_pos_in_phys_rec] == 0xff)) + { + l3_file->cur_pos_in_phys_rec = 0; + l3_file->cur_phys_rec++; + } + (* out_reclen) = reclen; + } + else + { + /* fixed len records */ + reclen = get_file_reclen(&l3_file->l2_file); + if (is_eof(l3_file)) + return IMGTOOLERR_UNEXPECTED; + if ((l3_file->cur_pos_in_phys_rec + reclen) > 256) + { + l3_file->cur_pos_in_phys_rec = 0; + l3_file->cur_phys_rec++; + } + if ((l3_file->cur_phys_rec >= fphysrecs) + || ((l3_file->cur_phys_rec == (fphysrecs-1)) + && ((l3_file->cur_pos_in_phys_rec + reclen) >= (fdr_eof ? fdr_eof : 256)))) + return IMGTOOLERR_CORRUPTIMAGE; + errorcode = read_file_physrec(&l3_file->l2_file, l3_file->cur_phys_rec, physrec_buf); + if (errorcode) + return errorcode; + memcpy(dest, physrec_buf + l3_file->cur_pos_in_phys_rec, reclen); + l3_file->cur_pos_in_phys_rec += reclen; + if (l3_file->cur_pos_in_phys_rec == 256) + { + l3_file->cur_pos_in_phys_rec = 0; + l3_file->cur_phys_rec++; + } + (* out_reclen) = reclen; + } + + return 0; +} +#endif + +#if 0 +#pragma mark - +#pragma mark IMGTOOL MODULE IMPLEMENTATION +#endif + +/* + ti99 catalog iterator, used when imgtool reads the catalog +*/ +struct dsk_iterator +{ + struct ti99_lvl2_imgref *image; + int level; + int listing_subdirs; /* true if we are listing subdirectories at current level */ + int index[2]; /* current index in the disk catalog */ + ti99_catalog *cur_catalog; /* current catalog */ +}; + +struct win_iterator +{ + struct ti99_lvl2_imgref *image; + int level; + int listing_subdirs; /* true if we are listing subdirectories at current level */ + int index[MAX_DIR_LEVEL]; /* current index in the disk catalog */ + ti99_catalog catalog[MAX_DIR_LEVEL]; /* current catalog */ +}; + + +static imgtoolerr_t dsk_image_init_mess(imgtool::image &image, imgtool::stream::ptr &&stream); +static imgtoolerr_t dsk_image_init_v9t9(imgtool::image &image, imgtool::stream::ptr &&stream); +static imgtoolerr_t dsk_image_init_pc99_fm(imgtool::image &image, imgtool::stream::ptr &&stream); +static imgtoolerr_t dsk_image_init_pc99_mfm(imgtool::image &image, imgtool::stream::ptr &&stream); +static imgtoolerr_t win_image_init(imgtool::image &image, imgtool::stream::ptr &&stream); +static void ti99_image_exit(imgtool::image &img); +static void ti99_image_info(imgtool::image &img, std::ostream &stream); +static imgtoolerr_t dsk_image_beginenum(imgtool::directory &enumeration, const char *path); +static imgtoolerr_t dsk_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent); +static imgtoolerr_t win_image_beginenum(imgtool::directory &enumeration, const char *path); +static imgtoolerr_t win_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent); +static imgtoolerr_t ti99_image_freespace(imgtool::partition &partition, uint64_t *size); +static imgtoolerr_t ti99_image_readfile(imgtool::partition &partition, const char *fpath, const char *fork, imgtool::stream &destf); +static imgtoolerr_t ti99_image_writefile(imgtool::partition &partition, const char *fpath, const char *fork, imgtool::stream &sourcef, util::option_resolution *writeoptions); +static imgtoolerr_t dsk_image_deletefile(imgtool::partition &partition, const char *fpath); +static imgtoolerr_t win_image_deletefile(imgtool::partition &partition, const char *fpath); +static imgtoolerr_t dsk_image_create_mess(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *createoptions); +static imgtoolerr_t dsk_image_create_v9t9(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *createoptions); + +enum +{ + dsk_createopts_volname = 'A', + dsk_createopts_sides = 'B', + dsk_createopts_tracks = 'C', + dsk_createopts_sectors = 'D', + dsk_createopts_protection = 'E', + dsk_createopts_density = 'F' +}; + +OPTION_GUIDE_START( dsk_create_optionguide ) + OPTION_STRING(dsk_createopts_volname, "label", "Volume name" ) + OPTION_INT(dsk_createopts_sides, "sides", "Sides" ) + OPTION_INT(dsk_createopts_tracks, "tracks", "Tracks" ) + OPTION_INT(dsk_createopts_sectors, "sectors", "Sectors (1->9 for SD, 1->18 for DD, 1->36 for HD)" ) + OPTION_INT(dsk_createopts_protection, "protection", "Protection (0 for normal, 1 for protected)" ) + OPTION_ENUM_START(dsk_createopts_density, "density", "Density" ) + OPTION_ENUM( 0, "Auto", "Auto" ) + OPTION_ENUM( 1, "SD", "Single Density" ) + OPTION_ENUM( 2, "DD", "Double Density" ) + OPTION_ENUM( 3, "HD", "High Density" ) + OPTION_ENUM_END +OPTION_GUIDE_END + +#define dsk_create_optionspecs "B1-[2];C1-[40]-80;D1-[18]-36;E[0]-1;F[0]-3" + +static void ti99_getinfo(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(ti99_lvl2_imgref); break; + case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(dsk_iterator); break; + + case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), "\r"); break; + case IMGTOOLINFO_PTR_CLOSE: info->close = ti99_image_exit; break; + case IMGTOOLINFO_PTR_INFO: info->info = ti99_image_info; break; + case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = ti99_image_freespace; break; + case IMGTOOLINFO_PTR_READ_FILE: info->read_file = ti99_image_readfile; break; + case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = ti99_image_writefile; break; + } +} + +static void ti99_dsk_getinfo(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + case IMGTOOLINFO_STR_FILE_EXTENSIONS: strcpy(info->s = imgtool_temp_str(), "dsk"); break; + case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = dsk_image_beginenum; break; + case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = dsk_image_nextenum; break; + case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = dsk_image_deletefile; break; + default: ti99_getinfo(imgclass, state, info); + } +} + +void ti99_old_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "ti99_old"); break; + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "TI99 Diskette (old MESS format)"); break; + case IMGTOOLINFO_PTR_OPEN: info->open = dsk_image_init_mess; break; + case IMGTOOLINFO_PTR_CREATE: info->create = dsk_image_create_mess; break; + case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE: info->createimage_optguide = &dsk_create_optionguide; break; + case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC: strcpy(info->s = imgtool_temp_str(), dsk_create_optionspecs); break; + default: ti99_dsk_getinfo(imgclass, state, info); break; + } +} + +void ti99_v9t9_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "v9t9"); break; + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "TI99 Diskette (V9T9 format)"); break; + case IMGTOOLINFO_PTR_OPEN: info->open = dsk_image_init_v9t9; break; + case IMGTOOLINFO_PTR_CREATE: info->create = dsk_image_create_v9t9; break; + case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE: info->createimage_optguide = &dsk_create_optionguide; break; + case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC: strcpy(info->s = imgtool_temp_str(), dsk_create_optionspecs); break; + default: ti99_dsk_getinfo(imgclass, state, info); break; + } +} + +void ti99_pc99fm_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "pc99fm"); break; + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "TI99 Diskette (PC99 FM format)"); break; + case IMGTOOLINFO_PTR_OPEN: info->open = dsk_image_init_pc99_fm; break; + case IMGTOOLINFO_PTR_CREATE: /* info->create = dsk_image_create_pc99fm; */ break; + default: ti99_dsk_getinfo(imgclass, state, info); break; + } +} + +void ti99_pc99mfm_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "pc99mfm"); break; + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "TI99 Diskette (PC99 MFM format)"); break; + case IMGTOOLINFO_PTR_OPEN: info->open = dsk_image_init_pc99_mfm; break; + case IMGTOOLINFO_PTR_CREATE: /* info->create = dsk_image_create_pc99mfm; */ break; + default: ti99_dsk_getinfo(imgclass, state, info); break; + } +} + +void ti99_ti99hd_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "ti99hd"); break; + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "TI99 Harddisk"); break; + case IMGTOOLINFO_PTR_OPEN: info->open = win_image_init; break; + case IMGTOOLINFO_PTR_CREATE: /* info->create = hd_image_create; */ break; + + case IMGTOOLINFO_STR_FILE_EXTENSIONS: strcpy(info->s = imgtool_temp_str(), "hd"); break; + case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = win_image_beginenum; break; + case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = win_image_nextenum; break; + case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = win_image_deletefile; break; + default: ti99_getinfo(imgclass, state, info); + } +} + + + +/* + Open a file as a ti99_image (common code). +*/ +static imgtoolerr_t dsk_image_init(imgtool::image &img, imgtool::stream::ptr &&stream, ti99_img_format img_format) +{ + struct ti99_lvl2_imgref *image = get_lvl2_imgref(img); + dsk_vib vib; + imgtoolerr_t reply; + int totphysrecs; + unsigned fdir_aphysrec; + int i; + + /* open disk image at level 1 */ + reply = open_image_lvl1(std::move(stream), img_format, &image->l1_img, &vib); + if (reply) + return reply; + + /* open disk image at level 2 */ + image->type = L2I_DSK; + + /* @BN@ */ + /* Copy in the allocation bit map! */ + memcpy(image->abm, vib.abm, 200); + /* first compute AU size and number of AUs */ + totphysrecs = get_UINT16BE(vib.totphysrecs); + + image->AUformat.physrecsperAU = (totphysrecs + 1599) / 1600; + /* round to next larger power of 2 */ + for (i = 1; i < image->AUformat.physrecsperAU; i <<= 1) + ; + image->AUformat.physrecsperAU = i; + image->AUformat.totAUs = totphysrecs / image->AUformat.physrecsperAU; + + /* extract number of physrecs */ + image->dsk.totphysrecs = get_UINT16BE(vib.totphysrecs); + + /* read and check main volume catalog */ + reply = (imgtoolerr_t)dsk_read_catalog(image, 1, &image->dsk.catalogs[0]); + if (reply) + return reply; + + image->dsk.fdir_aphysrec[0] = 1; + + /* read and check subdirectory catalogs */ + /* Note that the reserved areas used for HFDC subdirs may be used for other + purposes by other FDRs, so, if we get any error, we will assume there is no + subdir after all... */ + image->dsk.catalogs[0].num_subdirs = 0; + for (i=0; i<3; i++) + { + fdir_aphysrec = get_UINT16BE(vib.subdir[i].fdir_aphysrec); + if ((! memcmp(vib.subdir[i].name, "\0\0\0\0\0\0\0\0\0\0", 10)) + || (! memcmp(vib.subdir[i].name, " ", 10))) + { + /* name is empty: fine with us unless there is a fdir pointer */ + if (fdir_aphysrec != 0) + { + image->dsk.catalogs[0].num_subdirs = 0; + break; + } + } + else if (check_fname(vib.subdir[i].name)) + { + /* name is invalid: this is not an HFDC format floppy */ + image->dsk.catalogs[0].num_subdirs = 0; + break; + } + else + { + /* there is a non-empty name */ + if ((fdir_aphysrec == 0) || (fdir_aphysrec >= totphysrecs)) + { + /* error: fdir pointer is invalid or NULL */ + image->dsk.catalogs[0].num_subdirs = 0; + break; + } + /* fill in descriptor fields */ + image->dsk.fdir_aphysrec[image->dsk.catalogs[0].num_subdirs+1] = fdir_aphysrec; + /*image->dsk.catalogs[0].subdirs[image->dsk.catalogs[0].num_subdirs].dir_ptr = fdir_aphysrec;*/ + memcpy(image->dsk.catalogs[0].subdirs[image->dsk.catalogs[0].num_subdirs].name, vib.subdir[i].name, 10); + reply = (imgtoolerr_t) dsk_read_catalog(image, fdir_aphysrec, &image->dsk.catalogs[image->dsk.catalogs[0].num_subdirs+1]); + if (reply) + { + /* error: invalid fdir */ + image->dsk.catalogs[0].num_subdirs = 0; + break; + } + /* found valid subdirectory: increment subdir count */ + image->dsk.catalogs[0].num_subdirs++; + } + } + + /* extract volume name */ + memcpy(image->vol_name, vib.name, 10); + + /* initialize default data_offset */ + image->data_offset = 32+2; + + return (imgtoolerr_t)0; +} + +/* + Open a file as a ti99_image (MESS format). +*/ +static imgtoolerr_t dsk_image_init_mess(imgtool::image &image, imgtool::stream::ptr &&stream) +{ + return dsk_image_init(image, std::move(stream), if_mess); +} + +/* + Open a file as a ti99_image (V9T9 format). +*/ +static imgtoolerr_t dsk_image_init_v9t9(imgtool::image &image, imgtool::stream::ptr &&stream) +{ + return dsk_image_init(image, std::move(stream), if_v9t9); +} + +/* + Open a file as a ti99_image (PC99 FM format). +*/ +static imgtoolerr_t dsk_image_init_pc99_fm(imgtool::image &image, imgtool::stream::ptr &&stream) +{ + return dsk_image_init(image, std::move(stream), if_pc99_fm); +} + +/* + Open a file as a ti99_image (PC99 MFM format). +*/ +static imgtoolerr_t dsk_image_init_pc99_mfm(imgtool::image &image, imgtool::stream::ptr &&stream) +{ + return dsk_image_init(image, std::move(stream), if_pc99_mfm); +} + +/* + Open a file as a ti99_image (harddisk format). +*/ +static imgtoolerr_t win_image_init(imgtool::image &img, imgtool::stream::ptr &&stream) +{ + struct ti99_lvl2_imgref *image = get_lvl2_imgref(img); + win_vib_ddr vib; + int reply; + int i; + + /* open disk image at level 1 */ + reply = open_image_lvl1(std::move(stream), if_harddisk, & image->l1_img, NULL); + if (reply) + return (imgtoolerr_t)reply; + + /* open disk image at level 2 */ + image->type = L2I_WIN; + + /* read VIB */ + reply = read_absolute_physrec(&image->l1_img, 0, &vib); + if (reply) + return (imgtoolerr_t)reply; + + /* guess VIB version */ + image->win.vib_version = memcmp(vib.u.vib_v1.id, "WIN", 3) ? win_vib_v2 : win_vib_v1; + + /* extract AU size and number of AUs */ + image->AUformat.physrecsperAU = ((get_UINT16BE(vib.params) >> 12) & 0xf) + 1; + image->AUformat.totAUs = get_UINT16BE(vib.totAUs); + + /* extract volume name */ + memcpy(image->vol_name, vib.name, 10); + + /* extract data_offset */ + switch (image->win.vib_version) + { + case win_vib_v1: + image->data_offset = 64; + break; + + case win_vib_v2: + image->data_offset = vib.u.vib_v2.res_AUs * 64; + break; + } + + /* read allocation bitmap (aphysrecs 1 through n, n<=33) */ + for (i=0; i < (image->AUformat.totAUs+2047)/2048; i++) + { + reply = read_absolute_physrec(&image->l1_img, i+1, image->abm+i*256); + if (reply) + return (imgtoolerr_t)reply; + } + + return (imgtoolerr_t)0; +} + +/* + close a ti99_image +*/ +static void ti99_image_exit(imgtool::image &img) +{ + struct ti99_lvl2_imgref *image = get_lvl2_imgref(img); + + close_image_lvl1(&image->l1_img); +} + +/* + get basic information on a ti99_image + + Currently returns the volume name +*/ +static void ti99_image_info(imgtool::image &img, std::ostream &stream) +{ + struct ti99_lvl2_imgref *image = get_lvl2_imgref(img); + char vol_name[11]; + + fname_to_str(vol_name, image->vol_name, 11); + + stream << vol_name; +} + +/* + Open the disk catalog for enumeration +*/ +static imgtoolerr_t dsk_image_beginenum(imgtool::directory &enumeration, const char *path) +{ + struct ti99_lvl2_imgref *image = get_lvl2_imgref(enumeration.image()); + dsk_iterator *iter = (dsk_iterator *) enumeration.extra_bytes(); + + iter->image = image; + iter->level = 0; + iter->listing_subdirs = 1; + iter->index[0] = 0; + iter->cur_catalog = &iter->image->dsk.catalogs[0]; + + return (imgtoolerr_t)0; +} + +/* + Enumerate disk catalog next entry +*/ +static imgtoolerr_t dsk_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent) +{ + dsk_iterator *iter = (dsk_iterator*) enumeration.extra_bytes(); + dsk_fdr fdr; + int reply; + unsigned fdr_aphysrec; + + + ent.corrupt = 0; + ent.eof = 0; + + /* iterate through catalogs to next file or dir entry */ + while ((iter->level >= 0) + && (iter->index[iter->level] >= (iter->listing_subdirs + ? iter->cur_catalog->num_subdirs + : iter->cur_catalog->num_files))) + { + if (iter->listing_subdirs) + { + iter->listing_subdirs = 0; + iter->index[iter->level] = 0; + } + else + { + iter->listing_subdirs = 1; + if (! iter->level) + iter->level = -1; + else + { + iter->level = 0; + iter->index[0]++; + iter->cur_catalog = &iter->image->dsk.catalogs[0]; + } + } + } + + if (iter->level < 0) + { + ent.eof = 1; + } + else + { + if (iter->listing_subdirs) + { + fname_to_str(ent.filename, iter->image->dsk.catalogs[0].subdirs[iter->index[iter->level]].name, std::size(ent.filename)); + + /* set type of DIR */ + snprintf(ent.attr, std::size(ent.attr), "DIR"); + + /* len in physrecs */ + /* @BN@ return length in bytes */ + /* ent.filesize = 1; */ + ent.filesize = 256; + + /* recurse subdirectory */ + iter->listing_subdirs = 0; /* no need to list subdirs as only the + root dir has subdirs in DSK format */ + iter->level = 1; + iter->cur_catalog = &iter->image->dsk.catalogs[iter->index[0]+1]; + iter->index[iter->level] = 0; + } + else + { + fdr_aphysrec = iter->cur_catalog->files[iter->index[iter->level]].fdr_ptr; + reply = read_absolute_physrec(& iter->image->l1_img, fdr_aphysrec, & fdr); + if (reply) + return IMGTOOLERR_READERROR; +#if 0 + fname_to_str(ent.filename, fdr.name, std::size(ent.filename)); +#else + { + char buf[11]; + + ent.filename[0] = '\0'; + if (iter->level) + { + fname_to_str(ent.filename, iter->image->dsk.catalogs[0].subdirs[iter->index[0]].name, std::size(ent.filename)); + strncat(ent.filename, ".", std::size(ent.filename) - 1); + } + fname_to_str(buf, fdr.name, 11); + strncat(ent.filename, buf, std::size(ent.filename) - 1); + } +#endif + /* parse flags */ + if (fdr.flags & fdr99_f_program) + snprintf(ent.attr, std::size(ent.attr), "PGM%s", + (fdr.flags & fdr99_f_wp) ? " R/O" : ""); + else + snprintf(ent.attr, std::size(ent.attr), "%c/%c %d%s", + (fdr.flags & fdr99_f_int) ? 'I' : 'D', + (fdr.flags & fdr99_f_var) ? 'V' : 'F', + fdr.reclen, + (fdr.flags & fdr99_f_wp) ? " R/O" : ""); + /* len in physrecs */ + /* @BN@ return length in bytes */ + /* ent.filesize = get_UINT16BE(fdr.fphysrecs); */ + ent.filesize = (get_UINT16BE(fdr.fphysrecs)+1)*256; + + iter->index[iter->level]++; + } + } + + return (imgtoolerr_t)0; +} + +/* + Open the disk catalog for enumeration +*/ +static imgtoolerr_t win_image_beginenum(imgtool::directory &enumeration, const char *path) +{ + struct ti99_lvl2_imgref *image = get_lvl2_imgref(enumeration.image()); + win_iterator *iter = (win_iterator *) enumeration.extra_bytes(); + imgtoolerr_t errorcode; + + iter->image = image; + iter->level = 0; + iter->listing_subdirs = 1; + iter->index[0] = 0; + errorcode = (imgtoolerr_t)win_read_catalog(image, 0, &iter->catalog[0]); + if (errorcode) + return errorcode; + + return (imgtoolerr_t)0; +} + +/* + Enumerate disk catalog next entry +*/ +static imgtoolerr_t win_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent) +{ + win_iterator *iter = (win_iterator *) enumeration.extra_bytes(); + unsigned fdr_aphysrec; + win_fdr fdr; + int reply; + int i; + + + ent.corrupt = 0; + ent.eof = 0; + + /* iterate through catalogs to next file or dir entry */ + while ((iter->level >= 0) + && (iter->index[iter->level] >= (iter->listing_subdirs + ? iter->catalog[iter->level].num_subdirs + : iter->catalog[iter->level].num_files))) + { + if (iter->listing_subdirs) + { + iter->listing_subdirs = 0; + iter->index[iter->level] = 0; + } + else + { + iter->listing_subdirs = 1; + iter->level--; + iter->index[iter->level]++; + } + } + + if (iter->level < 0) + { + ent.eof = 1; + } + else + { + if (iter->listing_subdirs) + { +#if 0 + fname_to_str(ent.filename, iter->catalog[iter->level].subdirs[iter->index[iter->level]].name, std::size(ent.filename)); +#else + { + char buf[11]; + + ent.filename[0] = '\0'; + for (i=0; ilevel; i++) + { + fname_to_str(buf, iter->catalog[i].subdirs[iter->index[i]].name, 11); + strncat(ent.filename, buf, std::size(ent.filename) - 1); + strncat(ent.filename, ".", std::size(ent.filename) - 1); + } + fname_to_str(buf, iter->catalog[iter->level].subdirs[iter->index[iter->level]].name, 11); + strncat(ent.filename, buf, std::size(ent.filename) - 1); + } +#endif + + /* set type of DIR */ + snprintf(ent.attr, std::size(ent.attr), "DIR"); + + /* len in physrecs */ + /* @BN@ return length in bytes */ + /* ent.filesize = 2; */ + ent.filesize = 512; + + /* recurse subdirectory */ + /*iter->listing_subdirs = 1;*/ + iter->level++; + iter->index[iter->level] = 0; + reply = win_read_catalog(iter->image, iter->catalog[iter->level-1].subdirs[iter->index[iter->level-1]].dir_ptr, &iter->catalog[iter->level]); + if (reply) + { + ent.corrupt = 1; + return (imgtoolerr_t)reply; + } + } + else + { + fdr_aphysrec = iter->catalog[iter->level].files[iter->index[iter->level]].fdr_ptr*iter->image->AUformat.physrecsperAU; + reply = read_absolute_physrec(& iter->image->l1_img, fdr_aphysrec, & fdr); + if (reply) + return IMGTOOLERR_READERROR; +#if 0 + fname_to_str(ent.filename, iter->catalog[iter->level].files[iter->index[iter->level]].name, std::size(ent.filename)); +#else + { + char buf[11]; + + ent.filename[0] = '\0'; + for (i=0; ilevel; i++) + { + fname_to_str(buf, iter->catalog[i].subdirs[iter->index[i]].name, 11); + strncat(ent.filename, buf, std::size(ent.filename) - 1); + strncat(ent.filename, ".", std::size(ent.filename) - 1); + } + fname_to_str(buf, iter->catalog[iter->level].files[iter->index[iter->level]].name, 11); + strncat(ent.filename, buf, std::size(ent.filename) - 1); + } +#endif + /* parse flags */ + if (fdr.flags & fdr99_f_program) + snprintf(ent.attr, std::size(ent.attr), "PGM%s", + (fdr.flags & fdr99_f_wp) ? " R/O" : ""); + else + snprintf(ent.attr, std::size(ent.attr), "%c/%c %d%s", + (fdr.flags & fdr99_f_int) ? 'I' : 'D', + (fdr.flags & fdr99_f_var) ? 'V' : 'F', + fdr.reclen, + (fdr.flags & fdr99_f_wp) ? " R/O" : ""); + /* len in physrecs */ + /* @BN@ return length in bytes */ + /* ent.filesize = get_win_fdr_fphysrecs(&fdr); */ + ent.filesize = (get_win_fdr_fphysrecs(&fdr)+1)*256; + + iter->index[iter->level]++; + } + } + + return (imgtoolerr_t)0; +} + +/* + Compute free space on disk image (in AUs) +*/ +static imgtoolerr_t ti99_image_freespace(imgtool::partition &partition, uint64_t *size) +{ + imgtool::image &img(partition.image()); + struct ti99_lvl2_imgref *image = get_lvl2_imgref(img); + size_t freeAUs; + int i; + + freeAUs = 0; + for (i=0; iAUformat.totAUs; i++) + { + if (! (image->abm[i >> 3] & (1 << (i & 7)))) + freeAUs++; + } + + /* @BN@ return free space in bytes */ + /* *size = freeAUs; */ + *size = freeAUs*256; + + return IMGTOOLERR_SUCCESS; +} + +/* + Extract a file from a ti99_image. The file is saved in tifile format. +*/ +static imgtoolerr_t ti99_image_readfile(imgtool::partition &partition, const char *fpath, const char *fork, imgtool::stream &destf) +{ + imgtool::image &img(partition.image()); +#if 1 + + /* extract data as TIFILES */ + struct ti99_lvl2_imgref *image = get_lvl2_imgref(img); + ti99_lvl2_fileref src_file; + ti99_lvl2_fileref dst_file; + ti99_date_time date_time; + int fphysrecs; + int i; + uint8_t buf[256]; + imgtoolerr_t errorcode; + + + if (check_fpath(fpath)) + return IMGTOOLERR_BADFILENAME; + + /* open file on TI image */ + switch (image->type) + { + case L2I_DSK: + errorcode = (imgtoolerr_t)open_file_lvl2_dsk(image, fpath, &src_file); + break; + + case L2I_WIN: + errorcode = (imgtoolerr_t)open_file_lvl2_win(image, fpath, &src_file); + break; + + default: + errorcode = (imgtoolerr_t)-1; + break; + } + if (errorcode) + return errorcode; + + errorcode = (imgtoolerr_t)new_file_lvl2_tifiles(destf, &dst_file); + if (errorcode) + return errorcode; + + /* write TIFILE header */ + /* set up parameters */ + set_file_flags(&dst_file, get_file_flags(&src_file)); + set_file_recsperphysrec(&dst_file, get_file_recsperphysrec(&src_file)); + errorcode = (imgtoolerr_t)set_file_fphysrecs(&dst_file, get_file_fphysrecs(&src_file)); + if (errorcode) + return errorcode; + set_file_eof(&dst_file, get_file_eof(&src_file)); + errorcode = (imgtoolerr_t)set_file_reclen(&dst_file, get_file_reclen(&src_file)); + if (errorcode) + return errorcode; + errorcode = (imgtoolerr_t)set_file_fixrecs(&dst_file, get_file_fixrecs(&src_file)); + if (errorcode) + return errorcode; + + get_file_creation_date(&src_file, &date_time); +#if 0 + if ((date_time.time_MSB == 0) || (date_time.time_LSB == 0) + || (date_time.date_MSB == 0) || (date_time.date_LSB == 0)) + current_date_time(&date_time); +#endif + set_file_creation_date(&dst_file, date_time); + + get_file_update_date(&src_file, &date_time); +#if 0 + if ((date_time.time_MSB == 0) || (date_time.time_LSB == 0) + || (date_time.date_MSB == 0) || (date_time.date_LSB == 0)) + current_date_time(&date_time); +#endif + set_file_update_date(&dst_file, date_time); + + if (destf.write(& dst_file.tifiles.hdr, 128) != 128) + return (imgtoolerr_t)IMGTOOLERR_WRITEERROR; + + /* copy data to TIFILE */ + fphysrecs = get_file_fphysrecs(&src_file); + + for (i=0; itype) + { + case L2I_DSK: + errorcode = open_file_lvl2_dsk(image, fpath, &src_file.l2_file); + break; + + case L2I_WIN: + errorcode = open_file_lvl2_win(image, fpath, &src_file.l2_file); + break; + } + if (errorcode) + return errorcode; + + errorcode = open_file_lvl3(& src_file); + if (errorcode) + return errorcode; + + /* write text data */ + while (! is_eof(& src_file)) + { + errorcode = read_next_record(& src_file, buf, & reclen); + if (errorcode) + return errorcode; + if (destf.write(buf, reclen) != reclen) + return IMGTOOLERR_WRITEERROR; + if (destf.write(&lineend, 1) != 1) + return IMGTOOLERR_WRITEERROR; + } + + return 0; + +#endif +} + +/* + Add a file to a ti99_image. The file must be in tifile format. +*/ +static imgtoolerr_t ti99_image_writefile(imgtool::partition &partition, const char *fpath, const char *fork, imgtool::stream &sourcef, util::option_resolution *writeoptions) +{ + imgtool::image &img(partition.image()); + struct ti99_lvl2_imgref *image = get_lvl2_imgref(img); + const char *filename; + char ti_fname[10]; + ti99_lvl2_fileref src_file; + ti99_lvl2_fileref dst_file; + ti99_date_time date_time; + int i; + int fphysrecs; + uint8_t buf[256]; + imgtoolerr_t errorcode; + int parent_ref_valid, parent_ref = 0; + ti99_catalog *catalog, catalog_buf = {0, }; + + + (void) writeoptions; + + if (check_fpath(fpath)) + return IMGTOOLERR_BADFILENAME; + + switch (image->type) + { + case L2I_DSK: + errorcode = (imgtoolerr_t)dsk_find_catalog_entry(image, fpath, &parent_ref_valid, &parent_ref, NULL, NULL); + if (errorcode == 0) + /* file already exists: causes an error for now */ + return (imgtoolerr_t)IMGTOOLERR_UNEXPECTED; + else if ((! parent_ref_valid) || (errorcode != IMGTOOLERR_FILENOTFOUND)) + return (imgtoolerr_t)errorcode; + break; + + case L2I_WIN: + errorcode = (imgtoolerr_t)win_find_catalog_entry(image, fpath, &parent_ref_valid, &parent_ref, &catalog_buf, NULL, NULL); + if (errorcode == 0) + /* file already exists: causes an error for now */ + return (imgtoolerr_t)IMGTOOLERR_UNEXPECTED; + else if ((! parent_ref_valid) || (errorcode != IMGTOOLERR_FILENOTFOUND)) + return (imgtoolerr_t)errorcode; + break; + } + + errorcode =(imgtoolerr_t) open_file_lvl2_tifiles(sourcef, & src_file); + if (errorcode) + return (imgtoolerr_t)errorcode; + + /* create new file */ + filename = strrchr(fpath, '.'); + if (filename) + filename++; + else + filename = fpath; + str_to_fname(ti_fname, filename); + switch (image->type) + { + case L2I_DSK: + errorcode = (imgtoolerr_t)new_file_lvl2_dsk(image, parent_ref, ti_fname, &dst_file); + if (errorcode) + return (imgtoolerr_t)errorcode; + break; + + case L2I_WIN: + errorcode = (imgtoolerr_t)new_file_lvl2_win(image, &catalog_buf, ti_fname, &dst_file); + if (errorcode) + return (imgtoolerr_t)errorcode; + break; + } + + /* set up parameters */ + set_file_flags(&dst_file, get_file_flags(&src_file)); + set_file_recsperphysrec(&dst_file, get_file_recsperphysrec(&src_file)); + errorcode = (imgtoolerr_t)set_file_fphysrecs(&dst_file, /*get_file_fphysrecs(&src_file)*/0); + if (errorcode) + return (imgtoolerr_t)errorcode; + set_file_eof(&dst_file, get_file_eof(&src_file)); + errorcode = (imgtoolerr_t)set_file_reclen(&dst_file, get_file_reclen(&src_file)); + if (errorcode) + return (imgtoolerr_t)errorcode; + errorcode = (imgtoolerr_t)set_file_fixrecs(&dst_file, get_file_fixrecs(&src_file)); + if (errorcode) + return (imgtoolerr_t)errorcode; + + get_file_creation_date(&src_file, &date_time); +#if 0 + if ((date_time.time_MSB == 0) || (date_time.time_LSB == 0) + || (date_time.date_MSB == 0) || (date_time.date_LSB == 0)) + current_date_time(&date_time); +#endif + set_file_creation_date(&dst_file, date_time); + + get_file_update_date(&src_file, &date_time); +#if 0 + if ((date_time.time_MSB == 0) || (date_time.time_LSB == 0) + || (date_time.date_MSB == 0) || (date_time.date_LSB == 0)) + current_date_time(&date_time); +#endif + set_file_update_date(&dst_file, date_time); + + /* alloc data physrecs */ + fphysrecs = get_file_fphysrecs(&src_file); + switch (dst_file.type) + { + case L2F_DSK: + errorcode = (imgtoolerr_t)dsk_alloc_file_physrecs(&dst_file.dsk, fphysrecs); + if (errorcode) + return (imgtoolerr_t)errorcode; + break; + + case L2F_WIN: + errorcode = (imgtoolerr_t)win_alloc_file_physrecs(&dst_file.win, fphysrecs); + if (errorcode) + return (imgtoolerr_t)errorcode; + break; + + case L2F_TIFILES: + break; + } + + /* copy data */ + for (i=0; itype) + { + case L2I_DSK: + if (write_absolute_physrec(& image->l1_img, dst_file.dsk.fdr_aphysrec, &dst_file.dsk.fdr)) + return (imgtoolerr_t)IMGTOOLERR_WRITEERROR; + break; + + case L2I_WIN: + /* save fphysrecs field as well */ + if (dst_file.win.curfdr_aphysrec == dst_file.win.eldestfdr_aphysrec) + set_win_fdr_fphysrecs(&dst_file.win.curfdr, dst_file.win.fphysrecs); + if (write_absolute_physrec(& image->l1_img, dst_file.win.curfdr_aphysrec, &dst_file.win.curfdr)) + return (imgtoolerr_t)IMGTOOLERR_WRITEERROR; + if (dst_file.win.curfdr_aphysrec != dst_file.win.eldestfdr_aphysrec) + { + dst_file.win.curfdr_aphysrec = dst_file.win.eldestfdr_aphysrec; + if (read_absolute_physrec(& image->l1_img, dst_file.win.curfdr_aphysrec, &dst_file.win.curfdr)) + return (imgtoolerr_t)IMGTOOLERR_WRITEERROR; + set_win_fdr_fphysrecs(&dst_file.win.curfdr, dst_file.win.fphysrecs); + if (write_absolute_physrec(& image->l1_img, dst_file.win.curfdr_aphysrec, &dst_file.win.curfdr)) + return (imgtoolerr_t)IMGTOOLERR_WRITEERROR; + } + break; + } + + /* update catalog */ + switch (image->type) + { + case L2I_DSK: + catalog = &image->dsk.catalogs[parent_ref]; + for (i=0; i<128; i++) + { + buf[2*i] = catalog->files[i].fdr_ptr >> 8; + buf[2*i+1] = catalog->files[i].fdr_ptr & 0xff; + } + if (write_absolute_physrec(& image->l1_img, image->dsk.fdir_aphysrec[parent_ref], buf)) + return (imgtoolerr_t)IMGTOOLERR_WRITEERROR; + break; + + case L2I_WIN: + { + win_vib_ddr parent_ddr; + int fdir_AU; + + /* update VIB/DDR and get FDIR AU address */ + if (read_absolute_physrec(& image->l1_img, parent_ref * image->AUformat.physrecsperAU, &parent_ddr)) + return (imgtoolerr_t)IMGTOOLERR_READERROR; + parent_ddr.num_files = catalog_buf.num_files; + if (write_absolute_physrec(& image->l1_img, parent_ref * image->AUformat.physrecsperAU, &parent_ddr)) + return (imgtoolerr_t)IMGTOOLERR_WRITEERROR; + fdir_AU = get_UINT16BE(parent_ddr.fdir_AU); + + /* generate FDIR and save it */ + for (i=0; i<127; i++) + { + buf[2*i] = catalog_buf.files[i].fdr_ptr >> 8; + buf[2*i+1] = catalog_buf.files[i].fdr_ptr & 0xff; + } + buf[254] = parent_ref >> 8; + buf[255] = parent_ref & 0xff; + if (write_absolute_physrec(& image->l1_img, fdir_AU * image->AUformat.physrecsperAU, buf)) + return (imgtoolerr_t)IMGTOOLERR_WRITEERROR; + } + break; + } + + /* update bitmap */ + switch (image->type) + { + case L2I_DSK: + { + dsk_vib vib; + + if (read_absolute_physrec(& image->l1_img, 0, &vib)) + return (imgtoolerr_t)IMGTOOLERR_READERROR; + memcpy(vib.abm, image->abm, 200); + if (write_absolute_physrec(& image->l1_img, 0, &vib)) + return (imgtoolerr_t)IMGTOOLERR_WRITEERROR; + } + break; + + case L2I_WIN: + /* save allocation bitmap (aphysrecs 1 through n, n<=33) */ + for (i=0; i < (image->AUformat.totAUs+2047)/2048; i++) + if (write_absolute_physrec(&image->l1_img, i+1, image->abm+i*256)) + return (imgtoolerr_t)IMGTOOLERR_WRITEERROR; + break; + } + + return (imgtoolerr_t)0; +} + +/* + Delete a file from a ti99_image. +*/ +static imgtoolerr_t dsk_image_deletefile(imgtool::partition &partition, const char *fpath) +{ + imgtool::image &img(partition.image()); + struct ti99_lvl2_imgref *image = get_lvl2_imgref(img); + dsk_fdr fdr; + int i, cluster_index; + unsigned cur_AU, cluster_lastfphysrec; +// int fphysrecs; + int parent_ref, is_dir, catalog_index; + imgtoolerr_t errorcode; + uint8_t buf[256]; + ti99_catalog *catalog; + + + if (check_fpath(fpath)) + return IMGTOOLERR_BADFILENAME; + + errorcode = (imgtoolerr_t)dsk_find_catalog_entry(image, fpath, NULL, &parent_ref, &is_dir, &catalog_index); + if (errorcode) + return errorcode; + + if (is_dir) + { + catalog = &image->dsk.catalogs[catalog_index+1]; + + if ((catalog->num_files != 0) || (catalog->num_subdirs != 0)) + return IMGTOOLERR_UNIMPLEMENTED; + + catalog = &image->dsk.catalogs[0]; + + /* free fdir AU */ + cur_AU = image->dsk.fdir_aphysrec[catalog_index+1] / image->AUformat.physrecsperAU; + image->abm[cur_AU >> 3] &= ~ (1 << (cur_AU & 7)); + + /* delete catalog entry */ + for (i=catalog_index; i<2; i++) + { + catalog->subdirs[i] = catalog->subdirs[i+1]; + image->dsk.fdir_aphysrec[i+1] = image->dsk.fdir_aphysrec[i+2]; + } + memset(catalog->subdirs[2].name, 0, 10); + catalog->subdirs[2].dir_ptr = 0; + image->dsk.fdir_aphysrec[3] = 0; + catalog->num_subdirs--; + + /* update directory and bitmap in vib */ + { + dsk_vib vib; + + if (read_absolute_physrec(& image->l1_img, 0, &vib)) + return IMGTOOLERR_READERROR; + + for (i=0; i<3; i++) + { + memcpy(vib.subdir[i].name, catalog->subdirs[i].name, 10); + set_UINT16BE(&vib.subdir[i].fdir_aphysrec, image->dsk.fdir_aphysrec[i+1]); + } + + memcpy(vib.abm, image->abm, 200); + + if (write_absolute_physrec(& image->l1_img, 0, &vib)) + return IMGTOOLERR_WRITEERROR; + } + } + else + { + catalog = &image->dsk.catalogs[parent_ref]; + + if (read_absolute_physrec(& image->l1_img, catalog->files[catalog_index].fdr_ptr, &fdr)) + return IMGTOOLERR_READERROR; + + /* free data AUs */ +// fphysrecs = + get_UINT16BE(fdr.fphysrecs); + + i = 0; + cluster_index = 0; + while (cluster_index < 76) + { + cur_AU = get_dsk_fdr_cluster_baseAU(image, & fdr, cluster_index); + if (cur_AU == 0) + /* end of cluster list */ + break; + cluster_lastfphysrec = get_dsk_fdr_cluster_lastfphysrec(& fdr, cluster_index); + + while (i<=cluster_lastfphysrec) + { + /* the condition below is an error, but it should not prevent + us from deleting the file */ + if (cur_AU >= image->AUformat.totAUs) + /*return IMGTOOLERR_CORRUPTIMAGE;*/ + break; + + image->abm[cur_AU >> 3] &= ~ (1 << (cur_AU & 7)); + + i += image->AUformat.physrecsperAU; + cur_AU++; + } + + cluster_index++; + } + /* the condition below is an error, but it should not prevent us from + deleting the file */ +#if 0 + if (ifiles[catalog_index].fdr_ptr / image->AUformat.physrecsperAU; + image->abm[cur_AU >> 3] &= ~ (1 << (cur_AU & 7)); + + /* delete catalog entry */ + for (i=catalog_index; i<127; i++) + catalog->files[i] = catalog->files[i+1]; + catalog->files[127].fdr_ptr = 0; + catalog->num_files--; + + /* update parent fdir */ + for (i=0; i<128; i++) + { + buf[2*i] = catalog->files[i].fdr_ptr >> 8; + buf[2*i+1] = catalog->files[i].fdr_ptr & 0xff; + } + if (write_absolute_physrec(& image->l1_img, image->dsk.fdir_aphysrec[parent_ref], buf)) + return IMGTOOLERR_WRITEERROR; + + /* update bitmap */ + { + dsk_vib vib; + + if (read_absolute_physrec(& image->l1_img, 0, &vib)) + return IMGTOOLERR_READERROR; + memcpy(vib.abm, image->abm, 200); + if (write_absolute_physrec(& image->l1_img, 0, &vib)) + return IMGTOOLERR_WRITEERROR; + } + } + + return (imgtoolerr_t)0; +} + +static imgtoolerr_t win_image_deletefile(imgtool::partition &partition, const char *fpath) +{ + imgtool::image &img(partition.image()); + struct ti99_lvl2_imgref *image = get_lvl2_imgref(img); + int parent_ddr_AU, is_dir, catalog_index; + win_fdr fdr; + int i; + unsigned cur_AU, end_AU; + unsigned curfdr_aphysrec; + unsigned cursibFDR_index, endsibFDR_index = 0; + int errorcode; + uint8_t buf[256]; + ti99_catalog catalog; + + + if (check_fpath(fpath)) + return (imgtoolerr_t)IMGTOOLERR_BADFILENAME; + + errorcode = win_find_catalog_entry(image, fpath, NULL, &parent_ddr_AU, &catalog, &is_dir, &catalog_index); + if (errorcode) + return (imgtoolerr_t)errorcode; + + if (is_dir) + { + unsigned DDR_AU; + win_vib_ddr ddr_buf; + + /* Read DDR record */ + DDR_AU = catalog.subdirs[catalog_index].dir_ptr; + if (read_absolute_physrec(& image->l1_img, DDR_AU*image->AUformat.physrecsperAU, &ddr_buf)) + return (imgtoolerr_t)IMGTOOLERR_READERROR; + + /* read FDIR AU pointer */ + cur_AU = get_UINT16BE(ddr_buf.fdir_AU); + + /* sanity checks */ + if ((ddr_buf.num_files > 127) || (ddr_buf.num_subdirs > 114) || (cur_AU > image->AUformat.totAUs)) + return (imgtoolerr_t)IMGTOOLERR_CORRUPTIMAGE; + + if ((ddr_buf.num_files != 0) || (ddr_buf.num_subdirs != 0)) + return (imgtoolerr_t)IMGTOOLERR_UNIMPLEMENTED; + + /* free fdir AU */ + image->abm[cur_AU >> 3] &= ~ (1 << (cur_AU & 7)); + + /* free ddr AU */ + image->abm[DDR_AU >> 3] &= ~ (1 << (DDR_AU & 7)); + + /* delete parent catalog entry */ + for (i=catalog_index; i<113; i++) + catalog.subdirs[i] = catalog.subdirs[i+1]; + catalog.subdirs[113].dir_ptr = 0; + catalog.num_subdirs--; + + /* update parent VIB/DDR */ + if (read_absolute_physrec(& image->l1_img, parent_ddr_AU * image->AUformat.physrecsperAU, &ddr_buf)) + return (imgtoolerr_t)IMGTOOLERR_READERROR; + ddr_buf.num_subdirs = catalog.num_subdirs; + for (i=0; i<114; i++) + set_UINT16BE(&ddr_buf.subdir_AU[i], catalog.subdirs[i].dir_ptr); + if (write_absolute_physrec(& image->l1_img, parent_ddr_AU * image->AUformat.physrecsperAU, &ddr_buf)) + return (imgtoolerr_t)IMGTOOLERR_WRITEERROR; + + /* save allocation bitmap (aphysrecs 1 through n, n<=33) */ + for (i=0; i < (image->AUformat.totAUs+2047)/2048; i++) + if (write_absolute_physrec(&image->l1_img, i+1, image->abm+i*256)) + return (imgtoolerr_t)IMGTOOLERR_WRITEERROR; + } + else + { + /* check integrity of FDR sibling chain, and go to last sibling */ + /* note that as we check that the back chain is consistent with the forward + chain, we will also detect any cycle in the sibling chain, so we do not + need to check against them explicitly */ + { + int pastendoflist_flag; + unsigned prevfdr_aphysrec; + + + pastendoflist_flag = 0; + cursibFDR_index = 0; + curfdr_aphysrec = catalog.files[catalog_index].fdr_ptr * image->AUformat.physrecsperAU; + if (read_absolute_physrec(& image->l1_img, curfdr_aphysrec, &fdr)) + return (imgtoolerr_t)IMGTOOLERR_READERROR; + + if (get_UINT16BE(fdr.prevsibFDR_AU) != 0) + return (imgtoolerr_t)IMGTOOLERR_CORRUPTIMAGE; + + while (1) + { + if (! pastendoflist_flag) + { + /* look for end of list */ + for (i=0; i<54; i++) + { + if (get_UINT16BE(fdr.clusters[i][0]) == 0) + { + endsibFDR_index = cursibFDR_index; + pastendoflist_flag = true; + break; + } + } + } + + /* exit loop if end of sibling chain */ + if (! get_UINT16BE(fdr.nextsibFDR_AU)) + break; + + /* otherwise read next FDR */ + if (get_UINT16BE(fdr.nextsibFDR_AU) >= image->AUformat.totAUs) + return (imgtoolerr_t)IMGTOOLERR_CORRUPTIMAGE; + + prevfdr_aphysrec = curfdr_aphysrec; + curfdr_aphysrec = get_win_fdr_nextsibFDR_aphysrec(image, &fdr); + if (read_absolute_physrec(& image->l1_img, curfdr_aphysrec, &fdr)) + return (imgtoolerr_t)IMGTOOLERR_READERROR; + cursibFDR_index++; + + /* check that back chaining is consistent with forward chaining */ + if (get_win_fdr_prevsibFDR_aphysrec(image, &fdr) != prevfdr_aphysrec) + return (imgtoolerr_t)IMGTOOLERR_CORRUPTIMAGE; + } + if (! pastendoflist_flag) + endsibFDR_index = cursibFDR_index; + } + + + while (1) + { + if (cursibFDR_index <= endsibFDR_index) + { + for (i = 0; i < 54; i++) + { + cur_AU = get_UINT16BE(fdr.clusters[i][0]); + if (cur_AU == 0) + /* end of cluster list */ + break; + end_AU = get_UINT16BE(fdr.clusters[i][1]); + + while (cur_AU<=end_AU) + { + /* the condition below is an error, but it should not prevent + us from deleting the file */ + if (cur_AU >= image->AUformat.totAUs) + /*return IMGTOOLERR_CORRUPTIMAGE;*/ + break; + + image->abm[cur_AU >> 3] &= ~ (1 << (cur_AU & 7)); + + cur_AU++; + } + } + } + + /* free fdr AU if applicable */ + if ((curfdr_aphysrec % image->AUformat.physrecsperAU) == 0) + { + cur_AU = curfdr_aphysrec / image->AUformat.physrecsperAU; + image->abm[cur_AU >> 3] &= ~ (1 << (cur_AU & 7)); + } + + if (cursibFDR_index == 0) + break; + curfdr_aphysrec = get_win_fdr_prevsibFDR_aphysrec(image, &fdr); + if (read_absolute_physrec(& image->l1_img, curfdr_aphysrec, &fdr)) + return (imgtoolerr_t)IMGTOOLERR_READERROR; + cursibFDR_index--; + } + + + /* delete catalog entry */ + for (i=catalog_index; i<127; i++) + catalog.files[i] = catalog.files[i+1]; + catalog.files[127].fdr_ptr = 0; + catalog.num_files--; + + + /* update VIB/DDR and get FDIR AU address */ + { + win_vib_ddr parent_ddr; + + if (read_absolute_physrec(& image->l1_img, parent_ddr_AU * image->AUformat.physrecsperAU, &parent_ddr)) + return (imgtoolerr_t)IMGTOOLERR_READERROR; + parent_ddr.num_files = catalog.num_files; + if (write_absolute_physrec(& image->l1_img, parent_ddr_AU * image->AUformat.physrecsperAU, &parent_ddr)) + return (imgtoolerr_t)IMGTOOLERR_WRITEERROR; + cur_AU = get_UINT16BE(parent_ddr.fdir_AU); + } + + /* generate FDIR and save it */ + for (i=0; i<127; i++) + { + buf[2*i] = catalog.files[i].fdr_ptr >> 8; + buf[2*i+1] = catalog.files[i].fdr_ptr & 0xff; + } + buf[254] = parent_ddr_AU >> 8; + buf[255] = parent_ddr_AU & 0xff; + if (write_absolute_physrec(& image->l1_img, cur_AU * image->AUformat.physrecsperAU, buf)) + return (imgtoolerr_t)IMGTOOLERR_WRITEERROR; + + /* save allocation bitmap (aphysrecs 1 through n, n<=33) */ + for (i=0; i < (image->AUformat.totAUs+2047)/2048; i++) + if (write_absolute_physrec(&image->l1_img, i+1, image->abm+i*256)) + return (imgtoolerr_t)IMGTOOLERR_WRITEERROR; + } + + return (imgtoolerr_t)0; +} + +/* + Create a blank ti99_image (common code). + + Supports MESS and V9T9 formats only +*/ +static imgtoolerr_t dsk_image_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *createoptions, ti99_img_format img_format) +{ + const char *volname; + int density; + ti99_lvl1_imgref l1_img; + int protected_var; + + int totphysrecs, physrecsperAU, totAUs; + + dsk_vib vib; + uint8_t empty_sec[256]; + + int i; + + l1_img.img_format = img_format; + l1_img.file_handle = stream.get(); // can't release here + + /* read options */ + volname = createoptions->lookup_string(dsk_createopts_volname).c_str(); + l1_img.geometry.heads = createoptions->lookup_int(dsk_createopts_sides); + l1_img.geometry.cylinders = createoptions->lookup_int(dsk_createopts_tracks); + l1_img.geometry.secspertrack = createoptions->lookup_int(dsk_createopts_sectors); + protected_var = createoptions->lookup_int(dsk_createopts_protection); + density = createoptions->lookup_int(dsk_createopts_density); + + /* NPW 03-DEC-2003 - Fixes a crash if volname is NULL */ + if (!volname) + volname = ""; + + totphysrecs = l1_img.geometry.secspertrack * l1_img.geometry.cylinders * l1_img.geometry.heads; + physrecsperAU = (totphysrecs + 1599) / 1600; + /* round to next larger power of 2 */ + for (i = 1; i < physrecsperAU; i <<= 1) + ; + physrecsperAU = i; + totAUs = totphysrecs / physrecsperAU; + + /* auto-density */ + if (density == 0) + { + if (l1_img.geometry.secspertrack <= 9) + density = 1; + else if (l1_img.geometry.secspertrack <= 18) + density = 2; + else + density = 3; + } + + /* check volume name */ + if (strchr(volname, '.') || strchr(volname, ' ') || (strlen(volname) > 10)) + return /*IMGTOOLERR_BADFILENAME*/IMGTOOLERR_PARAMCORRUPT; + + /* FM disks can hold fewer sector per track than MFM disks */ + if ((density == 1) && (l1_img.geometry.secspertrack > 9)) + return IMGTOOLERR_PARAMTOOLARGE; + + /* DD disks can hold fewer sector per track than HD disks */ + if ((density == 2) && (l1_img.geometry.secspertrack > 18)) + return IMGTOOLERR_PARAMTOOLARGE; + + /* check total disk size */ + if (totphysrecs < 2) + return IMGTOOLERR_PARAMTOOSMALL; + + /* initialize vib in aphysrec 0 */ + + /* set volume name */ + str_to_fname(vib.name, volname); + + /* set every header field */ + set_UINT16BE(& vib.totphysrecs, totphysrecs); + vib.secspertrack = l1_img.geometry.secspertrack; + vib.id[0] = 'D'; + vib.id[1] = 'S'; + vib.id[2] = 'K'; + vib.protection = protected_var ? 'P' : ' '; + vib.cylinders = l1_img.geometry.cylinders; + vib.heads = l1_img.geometry.heads; + vib.density = density; + for (i=0; i<3; i++) + { + memset(vib.subdir[i].name, '\0', 10); + set_UINT16BE(& vib.subdir[i].fdir_aphysrec, 0); + } + + /* clear bitmap */ + for (i=0; i < (totAUs>>3); i++) + vib.abm[i] = 0; + + if (totAUs & 7) + { + vib.abm[i] = 0xff << (totAUs & 7); + i++; + } + + for (; i < 200; i++) + vib.abm[i] = 0xff; + + /* mark first 2 aphysrecs (vib and fdir) as used */ + vib.abm[0] |= (physrecsperAU == 1) ? 3 : 1; + + /* write aphysrec 0 */ + if (write_absolute_physrec(&l1_img, 0, &vib)) + return IMGTOOLERR_WRITEERROR; + + + /* now clear every other physrecs, including the FDIR record */ + memset(empty_sec, 0, sizeof(empty_sec)); + + for (i=1; i +#include +#include +#include + +/* Max sector length is bytes. Generally 256, except for a few older disk +units which use 288-byte-long sectors, and SCSI units which generally use +standard 512-byte-long sectors. */ +/* I chose a limit of 512. No need to use more until someone creates CD-ROMs +for TI990. */ +#define MAX_SECTOR_SIZE 512 +#define MIN_SECTOR_SIZE 256 + +#define MAX_CYLINDERS 2047 +#define MAX_HEADS 31 +#define MAX_SECTORS_PER_TRACK 256 /* 255 for 512-byte-long sector, so that the total number of words that can be recorded on a track may fit in one 16-bit word (store registers command) */ + +/* Max path len in chars: this value is 48 characters in DX10, but since the +path includes at least a leading '.', our value is 47. */ +/* Since path generally include a volume name that can be up to 8 character +long, we should display a warning message when the user wants to create a path +that is longer than 39 characters. */ +#define MAX_PATH_LEN 47 +#define MAX_SAFE_PATH_LEN 39 + +#define MAX_DIR_LEVEL 25 /* We need to put a recursion limit to avoid endless recursion hazard */ + + +struct UINT16BE +{ + uint8_t bytes[2]; +}; + +struct UINT32BE +{ + uint8_t bytes[4]; +}; + +static inline uint16_t get_UINT16BE(UINT16BE word) +{ + return (word.bytes[0] << 8) | word.bytes[1]; +} + +static inline void set_UINT16BE(UINT16BE *word, uint16_t data) +{ + word->bytes[0] = (data >> 8) & 0xff; + word->bytes[1] = data & 0xff; +} + +static inline uint32_t get_UINT32BE(UINT32BE word) +{ + return (word.bytes[0] << 24) | (word.bytes[1] << 16) | (word.bytes[2] << 8) | word.bytes[3]; +} + +static inline void set_UINT32BE(UINT32BE *word, uint32_t data) +{ + word->bytes[0] = (data >> 24) & 0xff; + word->bytes[1] = (data >> 16) & 0xff; + word->bytes[2] = (data >> 8) & 0xff; + word->bytes[3] = data & 0xff; +} + +/* + disk image header +*/ +struct disk_image_header +{ + UINT32BE cylinders; /* number of cylinders on hard disk (big-endian) */ + UINT32BE heads; /* number of heads on hard disk (big-endian) */ + UINT32BE sectors_per_track; /* number of sectors per track on hard disk (big-endian) */ + UINT32BE bytes_per_sector; /* number of bytes of data per sector on hard disk (big-endian) */ +}; + +enum +{ + header_len = sizeof(disk_image_header) +}; + +/* + Disk structure: + + Track 0 Sector 0: see below + Track 0 Sector 1: list of bad ADU + Track 0 Sector 2 through N: disk allocation bitmap + Track 1 Sector 0 through N-2: optional disk program image loader + Track 1 Sector N-1: copy of Track 0 Sector 0 + Track 1 Sector N: copy of Track 0 Sector 1 + Last cylinder has diagnostic information (reported as .S$DIAG) + Remaining sectors are used for fdr and data. +*/ + +/* + SC0 record (Disk sector 0) +*/ +struct ti990_sc0 +{ + char vnm[8]; /* volume name */ + UINT16BE tna; /* total number of ADUs */ + uint8_t sbm; /* starting sector of bit maps */ + uint8_t tbm; /* total bit maps */ + UINT16BE rl; /* track 01 record length */ + UINT16BE slt; /* system loader track address */ + uint8_t fill00[6]; /* * * RESERVED * * */ + UINT16BE nba; /* total number of bad ADUs on disk */ + UINT16BE sle; /* system loader entry point */ + UINT16BE sll; /* system loader length */ + uint8_t fill01[8]; /* * * RESERVED * * */ + UINT16BE lt1; /* system loader track (copy 2) */ + uint8_t fill02[8]; /* * * RESERVED * * */ + char pi1[8]; /* primary system file name */ + char pi2[8]; /* secondary system file name */ + UINT16BE pif; /* system selector */ + UINT16BE vda; /* volume directory ADU */ + UINT16BE vpl; /* vcatalog physical record length */ + UINT16BE spa; /* sectors per ADU */ + uint8_t dcd[4]; /* disk creation date */ + char pf1[8]; /* primary program file */ + char pf2[8]; /* secondary program file */ + UINT16BE pff; /* program file switch */ + char of1[8]; /* primary overlay file */ + char of2[8]; /* secondary overlay file */ + UINT16BE off; /* overlay file switch */ + char il1[8]; /* primary intermediate loader */ + char il2[8]; /* secondary intermediate loader */ + UINT16BE ilf; /* intermediate loader flag */ + char din[8]; /* diagnostic file name */ + UINT16BE dif; /* diagnostic flag */ + UINT16BE drs; /* default physical record size "DBUILD DETERMINES DEFAULT PRS" (whatever it means) */ + UINT16BE bal; /* starting sector of bad ADU list */ + UINT16BE spr; /* track 0 sectors per record */ + char wf1[8]; /* WCS primary microcode file */ + char wf2[8]; /* WCS secondary microcode file */ + UINT16BE wff; /* WCS flag switch */ + UINT16BE vif; /* track 1 select flag (whatever it means) volume information copied flag */ + UINT16BE sta; /* state of disk: */ + /* 1 = disk surface has not been tested for defects */ + /* 2 = disk surface has been tested, but no file system has been installed */ + /* 3 = a file system has been installed */ + UINT16BE dct; /* disk creation time */ + UINT16BE fsf; /* * * RESERVED * * */ + /* SCOSIZ = >AA */ +}; + +/* + DOR (Directory Overhead Record) +*/ +struct ti990_dor +{ + UINT16BE nrc; /* # records in directory (minus DOR) nrc = nfl + nar (+ tfc???) */ + UINT16BE nfl; /* # files currently in directory */ + UINT16BE nar; /* # of available records */ + UINT16BE tfc; /* number of temporary files */ + char dnm[8]; /* directory file name (VCATALOG for root) */ + UINT16BE lvl; /* level # of directory (0 for root, 1 for children of root, 2 for grandchildren, etc) */ + char pnm[8]; /* name of parent directory (VCATALOG for root, even though it makes little sense) */ + UINT16BE prs; /* "default physical record length (used for file creation)" */ + /* DORSIZ = >1C */ +}; + +/* + file flags found in fdr +*/ +enum +{ + ace_flg_rdf = 0x8000, /* read access flag */ + ace_flg_wrf = 0x4000, /* write access flag */ + ace_flg_dlf = 0x2000, /* delete access flag */ + ace_flg_exf = 0x1000, /* execute access flag */ + ace_flg_ctf = 0x0800, /* control access flag */ + + fdr_fl1_sec = 0x8000, /* file secured flag */ + + fdr_flg_fu = 0xc000, /* file usage bits */ + fdr_flg_fu_shift = 14, + fdr_flg_fmt = 0x3000, /* file format bits */ + fdr_flg_fmt_shift = 12, + fdr_flg_all = 0x0800, /* extendable file flag */ + fdr_flg_ft = 0x0600, /* file type bits */ + fdr_flg_ft_shift = 9, + fdr_flg_wpb = 0x0100, /* write protect bit */ + fdr_flg_dpb = 0x0080, /* delete protect bit */ + fdr_flg_tmp = 0x0040, /* temporary file flag */ + fdr_flg_blb = 0x0020, /* blocked file flag */ + fdr_flg_ali = 0x0010, /* alias flag bit */ + fdr_flg_fwt = 0x0008, /* forced write / partial logging */ + fdr_flg_cdr = 0x0001 /* record is CDR */ +}; + +/* + ACE subrecord found in FDR +*/ +struct ti990_ace +{ + char agn[8]; /* access group name */ + UINT16BE flg; /* flags */ +}; + +/* + FDR record +*/ +struct ti990_fdr +{ + UINT16BE hkc; /* hask key count: the number of file descriptor records that are present in the directory that hashed to this record number */ + UINT16BE hkv; /* hask key value: the result of the hash algorithm for the file name actually covered in this record */ + char fnm[8]; /* file name */ + uint8_t rsv[2]; /* reserved */ + UINT16BE fl1; /* flags word 1 */ + UINT16BE flg; /* flags word 2 */ + UINT16BE prs; /* physical record size */ + UINT16BE lrs; /* logical record size */ + UINT16BE pas; /* primary allocation size: # of ADUs allocated in primary allocation block? */ + UINT16BE paa; /* primary allocation address: first ADU of primary allocation block? */ + UINT16BE sas; /* secondary allocation size: used to determinate the # of blocks allocated per secondary allocation */ + UINT16BE saa; /* offset of secondary table: ???? "offset into this FDR of the secondary allocation table, if any. No secondary allocation table is denoted by 0. Secondary allocations are present only for unbounded files." */ + UINT16BE rfa; /* record number of first alias */ + UINT32BE eom; /* end of medium record number */ + UINT32BE bkm; /* end of medium block number */ + UINT16BE ofm; /* end of medium offset / */ + /* prelog number for KIF */ + UINT32BE fbq; /* free block queue head */ + UINT16BE btr; /* B-tree roots block # */ + UINT32BE ebq; /* empty block queue head */ + UINT16BE kdr; /* key descriptions record # */ + uint8_t ud[6]; /* last update date */ + uint8_t cd[6]; /* creation date */ + uint8_t apb; /* ADU's per block */ + uint8_t bpa; /* blocks per ADU */ + UINT16BE mrs; /* minimum KIF record size */ + uint8_t sat[64]; /* secondary allocation table: 16 2-word entries. The first word of an entry contains the size, in ADUs, of the secondary allocation. The second word contains the starting ADU of the allocation. */ + +/* bytes >86 to >100 are optional */ + uint8_t res[10]; /* reserved: seem to be actually meaningful (at least under DX10 3.6.x) */ + char uid[8]; /* user id of file creator */ + UINT16BE psa; /* public security attribute */ + ti990_ace ace[9]; /* 9 access control entries */ + uint8_t fil[2]; /* not used */ +}; + +/* + ADR record: variant of FDR for Aliases + + The fields marked here with *** are in the ADR template to maintain + compatibility with the FDR template. +*/ +struct ti990_adr +{ + UINT16BE hkc; /* hask key count */ + UINT16BE hkv; /* hask key value */ + char fnm[8]; /* file name */ + char psw[4]; /* "password" (whatever it means) */ + UINT16BE flg; /* flags (same as fdr.flg) */ + UINT16BE fill00; /* *** physical record size */ + UINT16BE fill01; /* *** logical record size */ + UINT16BE fill02; /* *** primary allocation size */ + UINT16BE fill03; /* *** primary allocation address */ + UINT16BE fill04; /* *** secondary allocation size */ + UINT16BE fill05; /* *** secondary allocation address */ + UINT16BE rna; /* record number of next ADR */ + UINT16BE raf; /* record # of actual FDR (from 1 through dor.nrc) */ +}; + +/* + CDR record: variant of FDR for Channel + + The CDR is the permanent record of a channel. It is carried as an alias + of the program file in which the channel owner task resides. +*/ +struct ti990_cdr +{ + UINT16BE hkc; /* hask key count */ + UINT16BE hkv; /* hask key value */ + char fnm[8]; /* file name */ + UINT16BE fill00; /* reserved */ + UINT16BE fill01; /* reserved */ + UINT16BE fdf; /* flags (same as fdr.flg) */ + uint8_t flg; /* channel flzgs */ + uint8_t iid; /* owner task installed ID */ + uint8_t typ; /* default resource type */ + uint8_t tf; /* resource type flags */ + UINT16BE mxl; /* maximum message length */ + uint8_t fill04[6]; /* reserved (and, no, I don't know where fill02 and fill03 have gone) */ + UINT16BE rna; /* record number of next CDR or ADR */ + UINT16BE raf; /* record # of actual FDR */ + uint8_t fill05[110]; /* reserved */ + char uid[8]; /* user ID of channel creator */ + UINT16BE psa; /* public security attribute */ + uint8_t scg[94]; /* "SDT with 9 control groups" (whatever it means - and, no, 94 is not dividable by 9) */ + uint8_t fill06[8]; /* reserved */ +}; + +/* + Based on contents of the flags field, catalog entries may be either an FDR, + an ADR or a CDR. + + They may be a KDR, too, but confusion is impossible because the KDR FILL00 + field starts at offset 4, and therefore if we try to interpret a KDR as an + FDR (or ADR, CDR), we will find fnm[0] and assume the FDR is empty. +*/ +union directory_entry +{ + ti990_fdr fdr; + ti990_adr adr; + ti990_cdr cdr; +}; + +#if 0 + +/* + tifile header: stand-alone file +*/ +struct tifile_header +{ + char tifiles[8]; /* always '\7TIFILES' */ + uint8_t secsused_MSB; /* file length in sectors (big-endian) */ + uint8_t secsused_LSB; + uint8_t flags; /* see enum above */ + uint8_t recspersec; /* records per sector */ + uint8_t eof; /* current position of eof in last sector (0->255)*/ + uint8_t reclen; /* bytes per record ([1,255] 0->256) */ + uint8_t fixrecs_MSB; /* file length in records (big-endian) */ + uint8_t fixrecs_LSB; + uint8_t res[128-16]; /* reserved */ +}; + +/* + catalog entry (used for in-memory catalog) +*/ +struct catalog_entry +{ + uint16_t fdr_secnum; + char filename[10]; +}; + +#endif + +/* + Disk geometry +*/ +struct ti990_geometry +{ + unsigned int cylinders, heads, sectors_per_track, bytes_per_sector; +}; + +/* + Physical sector address +*/ +struct ti990_phys_sec_address +{ + int cylinder; + int head; + int sector; +}; + +/* + ti99 disk image descriptor +*/ +struct ti990_image +{ + imgtool::stream *file_handle; /* imgtool file handle */ + ti990_geometry geometry; /* geometry */ + ti990_sc0 sec0; /* cached copy of sector 0 */ +}; + +/* + ti990 catalog iterator, used when imgtool reads the catalog +*/ +struct ti990_iterator +{ + ti990_image *image; + int level; /* current recursion level */ + int nrc[MAX_DIR_LEVEL]; /* length of disk catalogs in records */ + int index[MAX_DIR_LEVEL]; /* current index in the disk catalog */ + directory_entry xdr[MAX_DIR_LEVEL]; /* fdr records */ +}; + +static ti990_image *get_ti990_image(imgtool::image &image) +{ + return (ti990_image *)image.extra_bytes(); +} + + +static imgtoolerr_t ti990_image_init(imgtool::image &img, imgtool::stream::ptr &&stream); +static void ti990_image_exit(imgtool::image &img); +static void ti990_image_info(imgtool::image &img, std::ostream &stream); +static imgtoolerr_t ti990_image_beginenum(imgtool::directory &enumeration, const char *path); +static imgtoolerr_t ti990_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent); +static void ti990_image_closeenum(imgtool::directory &enumeration); +static imgtoolerr_t ti990_image_freespace(imgtool::partition &partition, uint64_t *size); +#ifdef UNUSED_FUNCTION +static imgtoolerr_t ti990_image_readfile(imgtool::partition &partition, const char *fpath, imgtool::stream *destf); +static imgtoolerr_t ti990_image_writefile(imgtool::partition &partition, const char *fpath, imgtool::stream *sourcef, util::option_resolution *writeoptions); +static imgtoolerr_t ti990_image_deletefile(imgtool::partition &partition, const char *fpath); +#endif +static imgtoolerr_t ti990_image_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *createoptions); + +enum +{ + /*ti990_createopts_volname = 'A',*/ + ti990_createopts_cylinders = 'B', + ti990_createopts_heads = 'C', + ti990_createopts_sectors = 'D', + ti990_createopts_sectorsize = 'E' +}; + +OPTION_GUIDE_START( ti990_create_optionguide ) + /*OPTION_STRING(ti990_createopts_volname, "label", "Volume name" )*/ + OPTION_INT(ti990_createopts_cylinders, "cylinders", "Cylinders" ) + OPTION_INT(ti990_createopts_heads, "heads", "Heads" ) + OPTION_INT(ti990_createopts_sectors, "sectors", "Sectors" ) + OPTION_INT(ti990_createopts_sectorsize, "bytes per sector (typically 256)", "Bytes Per Sector" ) +OPTION_GUIDE_END + +#define symb2str2(a) #a +#define symb2str(a) symb2str2(a) +#define ti990_create_optionspecs "B1-[145]-" symb2str(MAX_CYLINDERS)";C1-[4]-" symb2str(MAX_HEADS)";D1-[32]-" symb2str(MAX_SECTORS_PER_TRACK)";E" symb2str(MIN_SECTOR_SIZE)"-[256]-" symb2str(MAX_SECTOR_SIZE)";" + +void ti990_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "ti990hd"); break; + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "TI990 Hard Disk"); break; + case IMGTOOLINFO_STR_FILE_EXTENSIONS: strcpy(info->s = imgtool_temp_str(), "hd"); break; + case IMGTOOLINFO_STR_EOLN: /* strcpy(info->s = imgtool_temp_str(), "\r"); */ break; + + case IMGTOOLINFO_PTR_OPEN: info->open = ti990_image_init; break; + case IMGTOOLINFO_PTR_CLOSE: info->close = ti990_image_exit; break; + case IMGTOOLINFO_PTR_INFO: info->info = ti990_image_info; break; + case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = ti990_image_beginenum; break; + case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = ti990_image_nextenum; break; + case IMGTOOLINFO_PTR_CLOSE_ENUM: info->close_enum = ti990_image_closeenum; break; + case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = ti990_image_freespace; break; + case IMGTOOLINFO_PTR_READ_FILE: /* info->read_file = ti990_image_readfile; */ break; + case IMGTOOLINFO_PTR_WRITE_FILE: /* info->write_file = ti990_image_writefile; */ break; + case IMGTOOLINFO_PTR_DELETE_FILE: /* info->delete_file = ti990_image_deletefile; */ break; + case IMGTOOLINFO_PTR_CREATE: info->create = ti990_image_create; break; + + case IMGTOOLINFO_PTR_CREATEIMAGE_OPTGUIDE: info->createimage_optguide = &ti990_create_optionguide; break; + case IMGTOOLINFO_STR_CREATEIMAGE_OPTSPEC: strcpy(info->s = imgtool_temp_str(), ti990_create_optionspecs); break; + } +} + +#ifdef UNUSED_FUNCTION +/* + Convert a C string to a 8-character file name (padded with spaces if necessary) +*/ +static void str_to_fname(char dst[8], const char *src) +{ + int i; + + + i = 0; + + /* copy 8 characters at most */ + if (src) + while ((i<8) && (src[i]!='\0')) + { + dst[i] = src[i]; + i++; + } + + /* pad with spaces */ + while (i<8) + { + dst[i] = ' '; + i++; + } +} +#endif + +/* + Convert a 8-character file name to a C string (removing trailing spaces if necessary) +*/ +static void fname_to_str(char *dst, const char src[8], int n) +{ + int i; + int last_nonspace; + + + /* copy 8 characters at most */ + if (--n > 8) + n = 8; + + /* copy filename */ + i = 0; + last_nonspace = -1; + + while (icylinder*geometry->heads) + address->head)*geometry->sectors_per_track + address->sector)*geometry->bytes_per_sector + header_len; + + return offset; +} + +/* + Read one sector from a disk image + + file_handle: imgtool file handle + address: physical sector address + geometry: disk geometry (sectors per track, tracks per side, sides) + dest: pointer to destination buffer + len: length of data to read +*/ +static int read_sector_physical_len(imgtool::stream &file_handle, const ti990_phys_sec_address *address, const ti990_geometry *geometry, void *dest, int len) +{ + int reply; + + if (len > geometry->bytes_per_sector) + return 1; + + /* seek to sector */ + reply = file_handle.seek(phys_address_to_offset(address, geometry), SEEK_SET); + if (reply) + return 1; + /* read it */ + reply = file_handle.read(dest, len); + if (reply != len) + return 1; + + return 0; +} + +#ifdef UNUSED_FUNCTION +/* + Read one sector from a disk image + + file_handle: imgtool file handle + address: physical sector address + geometry: disk geometry (sectors per track, tracks per side, sides) + dest: pointer to a destination buffer of geometry->bytes_per_sector bytes +*/ +static int read_sector_physical(imgtool::stream *file_handle, const ti990_phys_sec_address *address, const ti990_geometry *geometry, void *dest) +{ + return read_sector_physical_len(file_handle, address, geometry, dest, geometry->bytes_per_sector); +} +#endif + +/* + Write one sector to a disk image + + file_handle: imgtool file handle + address: physical sector address + geometry: disk geometry (sectors per track, tracks per side, sides) + src: pointer to source buffer + len: length of source buffer +*/ +static int write_sector_physical_len(imgtool::stream &file_handle, const ti990_phys_sec_address *address, const ti990_geometry *geometry, const void *src, int len) +{ + int reply; + + if (len > geometry->bytes_per_sector) + return 1; + + /* seek to sector */ + reply = file_handle.seek(phys_address_to_offset(address, geometry), SEEK_SET); + if (reply) + return 1; + /* write it */ + reply = file_handle.write(src, len); + if (reply != len) + return 1; + /* pad with 0s if needed */ + if (len < geometry->bytes_per_sector) + { + reply = file_handle.fill(0, geometry->bytes_per_sector - len); + + if (reply != geometry->bytes_per_sector - len) + return 1; + } + + return 0; +} + +#ifdef UNUSED_FUNCTION +/* + Write one sector to a disk image + + file_handle: imgtool file handle + address: physical sector address + geometry: disk geometry (sectors per track, tracks per side, sides) + dest: pointer to a source buffer of geometry->bytes_per_sector bytes +*/ +static int write_sector_physical(imgtool::stream *file_handle, const ti990_phys_sec_address *address, const ti990_geometry *geometry, const void *src) +{ + return write_sector_physical_len(file_handle, address, geometry, src, geometry->bytes_per_sector); +} +#endif + +/* + Convert logical sector address to physical sector address +*/ +static void log_address_to_phys_address(int secnum, const ti990_geometry *geometry, ti990_phys_sec_address *address) +{ + address->sector = secnum % geometry->sectors_per_track; + secnum /= geometry->sectors_per_track; + address->head = secnum % geometry->heads; + address->cylinder = secnum / geometry->heads; +} + +/* + Read one sector from a disk image + + file_handle: imgtool file handle + secnum: logical sector address + geometry: disk geometry (sectors per track, tracks per side, sides) + dest: pointer to destination buffer + len: length of data to read +*/ +static int read_sector_logical_len(imgtool::stream &file_handle, int secnum, const ti990_geometry *geometry, void *dest, int len) +{ + ti990_phys_sec_address address; + + + log_address_to_phys_address(secnum, geometry, &address); + + return read_sector_physical_len(file_handle, &address, geometry, dest, len); +} + +#ifdef UNUSED_FUNCTION +/* + Read one sector from a disk image + + file_handle: imgtool file handle + secnum: logical sector address + geometry: disk geometry (sectors per track, tracks per side, sides) + dest: pointer to a destination buffer of geometry->bytes_per_sector bytes +*/ +static int read_sector_logical(imgtool::stream *file_handle, int secnum, const ti990_geometry *geometry, void *dest) +{ + return read_sector_logical_len(file_handle, secnum, geometry, dest, geometry->bytes_per_sector); +} +#endif + +/* + Write one sector to a disk image + + file_handle: imgtool file handle + secnum: logical sector address + geometry: disk geometry (sectors per track, tracks per side, sides) + src: pointer to source buffer + len: length of source buffer +*/ +static int write_sector_logical_len(imgtool::stream &file_handle, int secnum, const ti990_geometry *geometry, const void *src, int len) +{ + ti990_phys_sec_address address; + + + log_address_to_phys_address(secnum, geometry, &address); + + return write_sector_physical_len(file_handle, &address, geometry, src, len); +} + +/* + Write one sector to a disk image + + file_handle: imgtool file handle + secnum: logical sector address + geometry: disk geometry (sectors per track, tracks per side, sides) + dest: pointer to a source buffer of geometry->bytes_per_sector bytes +*/ +static int write_sector_logical(imgtool::stream &file_handle, int secnum, const ti990_geometry *geometry, const void *src) +{ + return write_sector_logical_len(file_handle, secnum, geometry, src, geometry->bytes_per_sector); +} + +#if 0 +#pragma mark - +#pragma mark CATALOG FILE ROUTINES +#endif + +#ifdef UNUSED_FUNCTION +/* + Find the catalog entry and fdr record associated with a file name + + image: ti990_image image record + fpath: path of the file to search + fdr: pointer to buffer where the fdr record should be stored (may be NULL) + catalog_index: on output, index of file catalog entry (may be NULL) + out_fdr_secnum: on output, sector address of the fdr (may be NULL) + out_parent_fdr_secnum: on output, sector offset of the fdr for the parent + directory fdr (-1 if root) (may be NULL) +*/ +static int find_fdr(ti990_image *image, const char fpath[MAX_PATH_LEN+1], int *catalog_index, int *out_fdr_secnum, int *out_parent_fdr_secnum) +{ + int fdr_secnum = -1, parent_fdr_secnum; + int i; + const char *element_start, *element_end; + int element_len; + char element[8]; + ti990_dor dor; + directory_entry xdr; + int hash_key; + int nrc; + unsigned base; + int reply; + int flag; + + + base = (unsigned) get_UINT16BE(image->sec0.vda) * get_UINT16BE(image->sec0.spa); + + element_start = fpath; + do + { + /* read directory header */ + reply = read_sector_logical_len(image->file_handle, base, & image->geometry, &dor, sizeof(dor)); + if (reply) + return IMGTOOLERR_READERROR; + + nrc = get_UINT16BE(dor.nrc); + + /* find next path element */ + element_end = strchr(element_start, '.'); + element_len = element_end ? (element_end - element_start) : strlen(element_start); + if ((element_len > 8) || (element_len == 0)) + return IMGTOOLERR_BADFILENAME; + /* generate file name and hash key */ + hash_key = 1; + for (i=0; ifile_handle, base + i, & image->geometry, + & xdr, 0x86); + if (reply) + return IMGTOOLERR_READERROR; + if (!memcmp(element, xdr.fdr.fnm, 8)) + { /* found match !!! */ + parent_fdr_secnum = fdr_secnum; + fdr_secnum = base + i; + break; + } + + /* next record */ + if (i != nrc) + i++; + else + i = 1; + + /* exit if we have tested every record */ + if (i == hash_key) + return IMGTOOLERR_FILENOTFOUND; + } + + /* iterate */ + if (element_end) + { + element_start = element_end+1; + /* we need to check that this is a directory */ + /* if it is an alias to a directory, we need to follow it, too. */ + + /* parse flags */ + flag = get_UINT16BE(xdr.fdr.flg); + + if (flag & fdr_flg_ali) + { + /* read original fdr */ + reply = read_sector_logical_len(image->file_handle, base + get_UINT16BE(xdr.adr.raf), + & image->geometry, & xdr, 0x86); + + flag = get_UINT16BE(xdr.fdr.flg); + } + + if (flag & fdr_flg_cdr) + /* This is a channel, not a file */ + return IMGTOOLERR_BADFILENAME; + else if (flag & fdr_flg_ali) + /* WTH??? */ + return IMGTOOLERR_CORRUPTIMAGE; + else if (((flag >> fdr_flg_fu_shift) & 3) != 1) + { + /* This file is not a directory */ + return IMGTOOLERR_BADFILENAME; + } + + /* initialize base */ + base = (unsigned) get_UINT16BE(xdr.fdr.paa) * get_UINT16BE(image->sec0.spa); + } + else + element_start = NULL; + } + while (element_start); + + if (catalog_index) + *catalog_index = i; + + if (out_fdr_secnum) + *out_fdr_secnum = fdr_secnum; + + if (out_parent_fdr_secnum) + *out_parent_fdr_secnum = parent_fdr_secnum; + + return 0; +} +#endif + +#if 0 +/* + Allocate one sector on disk, for use as a fdr record + + image: ti99_image image record + fdr_secnum: on output, logical address of a free sector +*/ +static int alloc_fdr_sector(ti99_image *image, int *fdr_secnum) +{ + int totsecs = (image->sec0.totsecsMSB << 8) | image->sec0.totsecsLSB; + int i; + + + for (i=0; isec0.abm[i >> 3] & (1 << (i & 7)))) + { + *fdr_secnum = i; + image->sec0.abm[i >> 3] |= 1 << (i & 7); + + return 0; + } + } + + return IMGTOOLERR_NOSPACE; +} + +/* + Extend a file with nb_alloc_sectors extra sectors + + image: ti99_image image record + fdr: file fdr record + nb_alloc_sectors: number of sectors to allocate +*/ +static int alloc_file_sectors(ti99_image *image, ti99_fdr *fdr, int nb_alloc_sectors) +{ + int totsecs = (image->sec0.totsecsMSB << 8) | image->sec0.totsecsLSB; + int free_sectors; + int secsused; + int i; + int lnks_index; + int cur_sec, last_sec, p_last_sec; + int cur_block_len, cur_block_start; + int first_best_block_len, first_best_block_start = 0; + int second_best_block_len, second_best_block_start = 0; + int search_start; + + /* compute free space */ + free_sectors = 0; + for (i=0; isec0.abm[i >> 3] & (1 << (i & 7)))) + free_sectors++; + } + + /* check we have enough free space */ + if (free_sectors < nb_alloc_sectors) + return IMGTOOLERR_NOSPACE; + + /* current number of data sectors in file */ + secsused = get_fdr_secsused(fdr); + + if (secsused == 0) + { /* links array must be empty */ + lnks_index = 0; + } + else + { /* try to extend last block */ + last_sec = -1; + for (lnks_index=0; lnks_index<76; lnks_index++) + { + p_last_sec = last_sec; + last_sec = (fdr->lnks[lnks_index][2] << 4) | (fdr->lnks[lnks_index][1] >> 4); + if (last_sec >= (secsused-1)) + break; + } + if (lnks_index == 76) + /* that sucks */ + return IMGTOOLERR_CORRUPTIMAGE; + + if (last_sec > (secsused-1)) + { /* some extra space has already been allocated */ + cur_block_len = last_sec - (secsused-1); + if (cur_block_len > nb_alloc_sectors) + cur_block_len = nb_alloc_sectors; + + secsused += cur_block_len; + set_fdr_secsused(fdr, secsused); + nb_alloc_sectors -= cur_block_len; + if (! nb_alloc_sectors) + return 0; /* done */ + } + + /* block base */ + cur_sec = ((fdr->lnks[lnks_index][1] & 0xf) << 8) | fdr->lnks[lnks_index][0]; + /* point past block end */ + cur_sec += last_sec-p_last_sec; + /* count free sectors after last block */ + cur_block_len = 0; + for (i=cur_sec; (! (image->sec0.abm[i >> 3] & (1 << (i & 7)))) && (cur_block_len < nb_alloc_sectors) && (i < totsecs); i++) + cur_block_len++; + if (cur_block_len) + { /* extend last block */ + secsused += cur_block_len; + set_fdr_secsused(fdr, secsused); + last_sec += cur_block_len; + nb_alloc_sectors -= cur_block_len; + fdr->lnks[lnks_index][1] = (fdr->lnks[lnks_index][1] & 0x0f) | (last_sec << 4); + fdr->lnks[lnks_index][2] = last_sec >> 4; + for (i=0; isec0.abm[(i+cur_sec) >> 3] |= 1 << ((i+cur_sec) & 7); + if (! nb_alloc_sectors) + return 0; /* done */ + } + lnks_index++; + if (lnks_index == 76) + /* that sucks */ + return IMGTOOLERR_NOSPACE; + } + + search_start = image->data_offset; /* initially, search for free space only in data space */ + while (nb_alloc_sectors) + { + /* find smallest data block at least nb_alloc_sectors in length, and largest data block less than nb_alloc_sectors in length */ + first_best_block_len = INT_MAX; + second_best_block_len = 0; + for (i=search_start; isec0.abm[i >> 3] & (1 << (i & 7)))) + { /* found one free block */ + /* compute its length */ + cur_block_start = i; + cur_block_len = 0; + while ((isec0.abm[i >> 3] & (1 << (i & 7))))) + { + cur_block_len++; + i++; + } + /* compare to previous best and second-best blocks */ + if ((cur_block_len < first_best_block_len) && (cur_block_len >= nb_alloc_sectors)) + { + first_best_block_start = cur_block_start; + first_best_block_len = cur_block_len; + if (cur_block_len == nb_alloc_sectors) + /* no need to search further */ + break; + } + else if ((cur_block_len > second_best_block_len) && (cur_block_len < nb_alloc_sectors)) + { + second_best_block_start = cur_block_start; + second_best_block_len = cur_block_len; + } + } + } + + if (first_best_block_len != INT_MAX) + { /* found one contiguous block which can hold it all */ + secsused += nb_alloc_sectors; + set_fdr_secsused(fdr, secsused); + + fdr->lnks[lnks_index][0] = first_best_block_start; + fdr->lnks[lnks_index][1] = ((first_best_block_start >> 8) & 0x0f) | ((secsused-1) << 4); + fdr->lnks[lnks_index][2] = (secsused-1) >> 4; + for (i=0; isec0.abm[(i+first_best_block_start) >> 3] |= 1 << ((i+first_best_block_start) & 7); + + nb_alloc_sectors = 0; + } + else if (second_best_block_len != 0) + { /* jeez, we need to fragment it. We use the largest smaller block to limit fragmentation. */ + secsused += second_best_block_len; + set_fdr_secsused(fdr, secsused); + + fdr->lnks[lnks_index][0] = second_best_block_start; + fdr->lnks[lnks_index][1] = ((second_best_block_start >> 8) & 0x0f) | ((secsused-1) << 4); + fdr->lnks[lnks_index][2] = (secsused-1) >> 4; + for (i=0; isec0.abm[(i+second_best_block_start) >> 3] |= 1 << ((i+second_best_block_start) & 7); + + nb_alloc_sectors -= second_best_block_len; + + lnks_index++; + if (lnks_index == 76) + /* that sucks */ + return IMGTOOLERR_NOSPACE; + } + else if (search_start != 0) + { /* we did not find any free sector in the data section of the disk */ + search_start = 0; /* time to fall back to fdr space */ + } + else + return IMGTOOLERR_NOSPACE; /* This should never happen, as we pre-check that there is enough free space */ + } + + return 0; +} + +/* + Allocate a new (empty) file +*/ +static int new_file(ti99_image *image, char filename[10], int *out_fdr_secnum/*, ti99_fdr *fdr,*/) +{ + int fdr_secnum; + int catalog_index, i; + int reply = 0; + + + /* find insertion point in catalog */ + i = 0; + if ((image->catalog[0].fdr_secnum == 0) && (image->catalog[1].fdr_secnum != 0)) + i = 1; /* skip empty entry 0 (it must be a non-listable catalog) */ + + for (; (i<128) && ((fdr_secnum = image->catalog[i].fdr_secnum) != 0) && ((reply = memcmp(image->catalog[i].filename, filename, 10)) < 0); i++) + ; + + if (i == 128) + /* catalog is full */ + return IMGTOOLERR_NOSPACE; + else if (fdr_secnum && (reply == 0)) + /* file already exists */ + return IMGTOOLERR_BADFILENAME; + else + { + catalog_index = i; + reply = alloc_fdr_sector(image, &fdr_secnum); + if (reply) + return reply; + + /* look for first free entry in catalog */ + for (i=catalog_index; image->catalog[i].fdr_secnum != 0; i++) + ; + + if (i == 128) + /* catalog is full */ + return IMGTOOLERR_NOSPACE; + + /* shift catalog entries until the insertion point */ + for (/*i=127*/; i>catalog_index; i--) + image->catalog[i] = image->catalog[i-1]; + + /* write new catalog entry */ + image->catalog[catalog_index].fdr_secnum = fdr_secnum; + memcpy(image->catalog[catalog_index].filename, filename, 10); + if (out_fdr_secnum) + *out_fdr_secnum = fdr_secnum; + } + + return 0; +} + +/* + Compare two (possibly empty) catalog entry for qsort +*/ +static int qsort_catalog_compare(const void *p1, const void *p2) +{ + const catalog_entry *entry1 = p1; + const catalog_entry *entry2 = p2; + + if ((entry1->fdr_secnum == 0) && (entry2->fdr_secnum == 0)) + return 0; + else if (entry1->fdr_secnum == 0) + return +1; + else if (entry2->fdr_secnum == 0) + return -1; + else + return memcmp(entry1->filename, entry2->filename, 10); +} +#endif + +/* + Open a file as a ti990_image. +*/ +static imgtoolerr_t ti990_image_init(imgtool::image &img, imgtool::stream::ptr &&stream) +{ + ti990_image *image = get_ti990_image(img); + disk_image_header header; + int reply; + unsigned totsecs; + + image->file_handle = stream.get(); + reply = image->file_handle->read(&header, sizeof(header)); + if (reply != sizeof(header)) + return IMGTOOLERR_READERROR; + + image->geometry.cylinders = get_UINT32BE(header.cylinders); + image->geometry.heads = get_UINT32BE(header.heads); + image->geometry.sectors_per_track = get_UINT32BE(header.sectors_per_track); + image->geometry.bytes_per_sector = get_UINT32BE(header.bytes_per_sector); + + totsecs = image->geometry.cylinders*image->geometry.heads*image->geometry.sectors_per_track; + + if ((image->geometry.bytes_per_sector < MIN_SECTOR_SIZE) + || (image->geometry.bytes_per_sector > MAX_SECTOR_SIZE) + || (image->geometry.sectors_per_track > MAX_SECTORS_PER_TRACK) + || (image->geometry.heads > MAX_HEADS) + || (image->geometry.cylinders > MAX_CYLINDERS) + || (totsecs < 1) + || (image->file_handle->size() != header_len + totsecs*image->geometry.bytes_per_sector)) + { + return IMGTOOLERR_CORRUPTIMAGE; + } + + { + ti990_phys_sec_address address = { 0, 0, 0 }; + reply = read_sector_physical_len(*image->file_handle, &address, &image->geometry, & image->sec0, sizeof(image->sec0)); + } + if (reply) + { + return IMGTOOLERR_READERROR; + } + + if ((get_UINT16BE(image->sec0.tna)*get_UINT16BE(image->sec0.spa) > totsecs) + || ((get_UINT16BE(image->sec0.spa) != 1) && (get_UINT16BE(image->sec0.spa) % 3))) + { + return IMGTOOLERR_CORRUPTIMAGE; + } + + if ((get_UINT16BE(image->sec0.vpl) != 0x86) && (get_UINT16BE(image->sec0.vpl) != 0x100)) + { + return IMGTOOLERR_CORRUPTIMAGE; + } + + image->file_handle = stream.release(); + return IMGTOOLERR_SUCCESS; +} + +/* + close a ti990_image +*/ +static void ti990_image_exit(imgtool::image &img) +{ + ti990_image *image = get_ti990_image(img); + delete image->file_handle; +} + +/* + get basic information on a ti990_image + + Currently returns the volume name +*/ +static void ti990_image_info(imgtool::image &img, std::ostream &stream) +{ + ti990_image *image = get_ti990_image(img); + char vol_name[9]; + + fname_to_str(vol_name, image->sec0.vnm, 9); + + stream << vol_name; +} + +/* + Open the disk catalog for enumeration +*/ +static imgtoolerr_t ti990_image_beginenum(imgtool::directory &enumeration, const char *path) +{ + ti990_image *image = (ti990_image *) enumeration.image().extra_bytes(); + ti990_iterator *iter = (ti990_iterator *) enumeration.extra_bytes(); + ti990_dor dor; + int reply; + + iter->image = image; + + reply = read_sector_logical_len(*iter->image->file_handle, + (unsigned) get_UINT16BE(iter->image->sec0.vda) * get_UINT16BE(iter->image->sec0.spa), + & iter->image->geometry, &dor, sizeof(dor)); + + if (reply) + return IMGTOOLERR_READERROR; + + iter->level = 0; + iter->nrc[0] = get_UINT16BE(dor.nrc); + iter->index[0] = 0; + + return IMGTOOLERR_SUCCESS; +} + +/* + Enumerate disk catalog next entry +*/ +static imgtoolerr_t ti990_image_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent) +{ + ti990_iterator *iter = (ti990_iterator *) enumeration.extra_bytes(); + int flag; + int reply = 0; + + + ent.corrupt = 0; + ent.eof = 0; + + while ((iter->level >= 0) + && (! (reply = read_sector_logical_len(*iter->image->file_handle, + iter->level ? (unsigned) get_UINT16BE(iter->xdr[iter->level-1].fdr.paa) * get_UINT16BE(iter->image->sec0.spa) + (iter->index[iter->level]+1) + : (unsigned) get_UINT16BE(iter->image->sec0.vda) * get_UINT16BE(iter->image->sec0.spa) + (iter->index[iter->level]+1), + & iter->image->geometry, &iter->xdr[iter->level], + iter->level ? get_UINT16BE(iter->xdr[iter->level-1].fdr.prs) + : get_UINT16BE(iter->image->sec0.vpl)))) + && (iter->xdr[iter->level].fdr.fnm[0] == 0)) + { + iter->index[iter->level]++; + while (iter->index[iter->level] >= iter->nrc[iter->level]) + iter->level--; + } + + if (iter->level < 0) + { + ent.eof = 1; + } + else if (reply) + return IMGTOOLERR_READERROR; + else + { +#if 0 + fname_to_str(ent.filename, iter->xdr[iter->level].fdr.fnm, std::size(ent.filename)); +#else + { + int i; + char buf[9]; + + ent.filename[0] = '\0'; + for (i=0; ilevel; i++) + { + fname_to_str(buf, iter->xdr[i].fdr.fnm, 9); + strncat(ent.filename, buf, std::size(ent.filename) - 1); + strncat(ent.filename, ".", std::size(ent.filename) - 1); + } + fname_to_str(buf, iter->xdr[iter->level].fdr.fnm, 9); + strncat(ent.filename, buf, std::size(ent.filename) - 1); + } +#endif + + /* parse flags */ + flag = get_UINT16BE(iter->xdr[iter->level].fdr.flg); + if (flag & fdr_flg_cdr) + { + snprintf(ent.attr, std::size(ent.attr), "CHANNEL"); + + ent.filesize = 0; + } + else if (flag & fdr_flg_ali) + { + ti990_fdr target_fdr; + char buf[9]; + + reply = read_sector_logical_len(*iter->image->file_handle, + iter->level ? ((unsigned) get_UINT16BE(iter->xdr[iter->level-1].fdr.paa) * get_UINT16BE(iter->image->sec0.spa) + get_UINT16BE(iter->xdr[iter->level].adr.raf)) + : ((unsigned) get_UINT16BE(iter->image->sec0.vda) * get_UINT16BE(iter->image->sec0.spa) + get_UINT16BE(iter->xdr[iter->level].adr.raf)), + & iter->image->geometry, &target_fdr, + iter->level ? get_UINT16BE(iter->xdr[iter->level-1].fdr.prs) + : get_UINT16BE(iter->image->sec0.vpl)); + + fname_to_str(buf, target_fdr.fnm, 9); + + snprintf(ent.attr, std::size(ent.attr), "ALIAS OF %s", buf); + + ent.filesize = 0; + } + else + { + const char *fmt, *type; + + switch ((flag >> fdr_flg_fmt_shift) & 3) + { + case 0: + fmt = "NBS"; + break; + case 1: + fmt = "BS "; + break; + default: + fmt = "???"; + break; + } + + switch ((flag >> fdr_flg_fu_shift) & 3) + { + case 1: + type = "DIR"; + break; + case 2: + type = "PRO"; + break; + case 3: + type = "IMG"; + break; + default: + switch ((flag >> fdr_flg_ft_shift) & 3) + { + case 1: + type = "SEQ"; + break; + case 2: + type = "REL"; + break; + case 3: + type = "KIF"; + break; + default: + type = "???"; + break; + } + break; + } + snprintf(ent.attr, std::size(ent.attr), + "%s %c %s%s%s%s%s", fmt, (flag & fdr_flg_all) ? 'N' : 'C', type, + (flag & fdr_flg_blb) ? "" : " BLK", + (flag & fdr_flg_tmp) ? " TMP" : "", + (flag & fdr_flg_wpb) ? " WPT" : "", + (flag & fdr_flg_dpb) ? " DPT" : ""); + + /* len in blocks */ + ent.filesize = get_UINT32BE(iter->xdr[iter->level].fdr.bkm); + if ((((flag >> fdr_flg_ft_shift) & 3) == 3) || get_UINT16BE(iter->xdr[iter->level].fdr.ofm)) + ent.filesize++; + + /* convert to ADUs */ + if (iter->xdr[iter->level].fdr.apb > 1) + /* more than one ADU per block */ + ent.filesize *= iter->xdr[iter->level].fdr.apb; + else if (iter->xdr[iter->level].fdr.bpa > 1) + /* more than one block per ADU */ + ent.filesize = (ent.filesize + iter->xdr[iter->level].fdr.bpa - 1) / iter->xdr[iter->level].fdr.bpa; + } + iter->index[iter->level]++; + + if (get_UINT16BE(iter->xdr[iter->level].fdr.saa) != 0) + printf("roupoupou"); + else + { + int i; + for (i=0; i<64; i++) + if (iter->xdr[iter->level].fdr.sat[i]) + { + printf("roupoupou"); + break; + } + } + + /* recurse subdirectory if applicable */ + if ((((flag >> fdr_flg_fu_shift) & 3) == 1) + && (! (flag & fdr_flg_ali)) + && (! (flag & fdr_flg_cdr)) + && (iter->level < MAX_DIR_LEVEL) + && ! ((iter->level == 0) && ! memcmp(iter->xdr[iter->level].fdr.fnm, "VCATALOG", 8))) + { + ti990_dor dor; + + if (get_UINT16BE(iter->xdr[iter->level].fdr.saa) != 0) + printf("ninou"); + + read_sector_logical_len(*iter->image->file_handle, + get_UINT16BE(iter->xdr[iter->level].fdr.paa) * get_UINT16BE(iter->image->sec0.spa), + & iter->image->geometry, &dor, sizeof(dor)); + + iter->level++; + iter->nrc[iter->level] = get_UINT16BE(dor.nrc)/*get_UINT32BE(iter->fdr[iter->level-1].eom)-1*/; + iter->index[iter->level] = 0; +#if 0 + if (get_UINT16BE(dor.nrc) != (get_UINT32BE(iter->xdr[iter->level-1].fdr.eom)-1)) + printf("hiha"); +#endif + } + + /* go to upper level if applicable */ + while (iter->index[iter->level] >= iter->nrc[iter->level]) + iter->level--; + } + + return (imgtoolerr_t)0; +} + +/* + Free enumerator +*/ +static void ti990_image_closeenum(imgtool::directory &enumeration) +{ +} + +/* + Compute free space on disk image (in ADUs) +*/ +static imgtoolerr_t ti990_image_freespace(imgtool::partition &partition, uint64_t *size) +{ + imgtool::image &img(partition.image()); + ti990_image *image = get_ti990_image(img); + int totadus = get_UINT16BE(image->sec0.tna); + int adu, record, sub_adu; + char buf[256]; + size_t freeadus = 0; + + if ((totadus+2031)/2032 != image->sec0.tbm) + /*return IMGTOOLERR_CORRUPTIMAGE;*/ + return (imgtoolerr_t)0; + + adu = 0; + record = 0; + sub_adu = 0; + while (adufile_handle, image->sec0.sbm + record, &image->geometry, buf, sizeof(buf)); + + while ((adu < totadus) && (sub_adu < 2032)) + { + if (! (buf[(sub_adu >> 3) + 2] & (1 << (sub_adu & 7)))) + freeadus++; + sub_adu++; + adu++; + } + sub_adu = 0; + record++; + } + + * size = freeadus; + + return IMGTOOLERR_SUCCESS; +} + +#ifdef UNUSED_FUNCTION +/* + Extract a file from a ti990_image. +*/ +static imgtoolerr_t ti990_image_readfile(imgtool::partition &partition, const char *fpath, imgtool::stream *destf) +{ + imgtool::image *img = &partition->image(); + ti990_image *image = get_ti990_image(img); + int catalog_index, fdr_secnum, parent_fdr_secnum; + imgtoolerr_t reply; + + + reply = find_fdr(image, fpath, &catalog_index, &fdr_secnum, &parent_fdr_secnum); + if (reply) + return reply; + + /* ... */ + + return 0; + +#if 0 + ti99_image *image = (ti99_image*) img; + int totsecs = (image->sec0.totsecsMSB << 8) | image->sec0.totsecsLSB; + char ti_fname[10]; + ti99_fdr fdr; + tifile_header dst_header; + int i, lnks_index; + int cur_sec, last_sec; + int secsused; + uint8_t buf[256]; + int reply; + + str_to_fname(ti_fname, filename); + + reply = find_fdr(image, ti_fname, &fdr, NULL); + if (reply) + return reply; + + dst_header.tifiles[0] = '\7'; + dst_header.tifiles[1] = 'T'; + dst_header.tifiles[2] = 'I'; + dst_header.tifiles[3] = 'F'; + dst_header.tifiles[4] = 'I'; + dst_header.tifiles[5] = 'L'; + dst_header.tifiles[6] = 'E'; + dst_header.tifiles[7] = 'S'; + dst_header.secsused_MSB = fdr.secsused_MSB; + dst_header.secsused_LSB = fdr.secsused_LSB; + dst_header.flags = fdr.flags; + dst_header.recspersec = fdr.recspersec; + dst_header.eof = fdr.eof; + dst_header.reclen = fdr.reclen; + dst_header.fixrecs_MSB = fdr.fixrecs_MSB; + dst_header.fixrecs_LSB = fdr.fixrecs_LSB; + for (i=0; i<(128-14); i++) + dst_header.res[i] = 0; + + if (destf->write(& dst_header, 128) != 128) + return IMGTOOLERR_WRITEERROR; + + + secsused = get_fdr_secsused(&fdr); + + i = 0; /* file logical sector #0 */ + lnks_index = 0; /* start of file block table */ + while (i> 4); + + /* copy current file block */ + while ((i<=last_sec) && (i= totsecs) + return IMGTOOLERR_CORRUPTIMAGE; + + if (read_sector_logical(image->file_handle, cur_sec, & image->geometry, buf)) + return IMGTOOLERR_READERROR; + + if (destf->write(buf, 256) != 256) + return IMGTOOLERR_WRITEERROR; + + i++; + cur_sec++; + } + + /* next entry in file block table */ + lnks_index++; + } + + return 0; +#endif +} + +/* + Add a file to a ti990_image. +*/ +static imgtoolerr_t ti990_image_writefile(imgtool::partition &partition, const char *fpath, imgtool::stream *sourcef, util::option_resolution *writeoptions) +{ + imgtool::image *img = &partition->image(); + ti990_image *image = get_ti990_image(img); + int catalog_index, fdr_secnum, parent_fdr_secnum; + imgtoolerr_t reply; + + + /* check that file does not exist */ + reply = find_fdr(image, fpath, &catalog_index, &fdr_secnum, &parent_fdr_secnum); + if ((reply) && (reply != IMGTOOLERR_FILENOTFOUND)) + return reply; + + /* ... */ + + return 0; + +#if 0 + ti99_image *image = (ti99_image*) img; + int totsecs = (image->sec0.totsecsMSB << 8) | image->sec0.totsecsLSB; + char ti_fname[10]; + ti99_fdr fdr; + tifile_header src_header; + int i, lnks_index; + int cur_sec, last_sec; + int secsused; + uint8_t buf[256]; + int reply; + int fdr_secnum; + + str_to_fname(ti_fname, filename); + + reply = find_fdr(image, ti_fname, &fdr, NULL); + if (reply == 0) + { /* file already exists: causes an error for now */ + return IMGTOOLERR_UNEXPECTED; + } + else if (reply != IMGTOOLERR_FILENOTFOUND) + { + return reply; + } + + if (sourcef->read(& src_header, 128) != 128) + return IMGTOOLERR_READERROR; + + /* create new file */ + reply = new_file(image, ti_fname, &fdr_secnum); + if (reply) + return reply; + + memcpy(fdr.name, ti_fname, 10); + fdr.res10[1] = fdr.res10[0] = 0; + fdr.flags = src_header.flags; + fdr.recspersec = src_header.recspersec; + fdr.secsused_MSB = /*src_header.secsused_MSB*/0; + fdr.secsused_LSB = /*src_header.secsused_LSB*/0; + fdr.eof = src_header.eof; + fdr.reclen = src_header.reclen; + fdr.fixrecs_LSB = src_header.fixrecs_LSB; + fdr.fixrecs_MSB = src_header.fixrecs_MSB; + for (i=0; i<8; i++) + fdr.res20[i] = 0; + for (i=0; i<76; i++) + fdr.lnks[i][2] = fdr.lnks[i][1] = fdr.lnks[i][0] = 0; + + /* alloc data sectors */ + secsused = (src_header.secsused_MSB << 8) | src_header.secsused_LSB; + reply = alloc_file_sectors(image, &fdr, secsused); + if (reply) + return reply; + + /* copy data */ + i = 0; + lnks_index = 0; + while (i> 4); + + while ((i= totsecs) + return IMGTOOLERR_CORRUPTIMAGE; + + if (sourcef->read(buf, 256) != 256) + return IMGTOOLERR_READERROR; + + if (write_sector_logical(image->file_handle, cur_sec, & image->geometry, buf)) + return IMGTOOLERR_WRITEERROR; + + i++; + cur_sec++; + } + + lnks_index++; + } + + /* write fdr */ + if (write_sector_logical(image->file_handle, fdr_secnum, & image->geometry, &fdr)) + return IMGTOOLERR_WRITEERROR; + + /* update catalog */ + for (i=0; i<128; i++) + { + buf[2*i] = image->catalog[i].fdr_secnum >> 8; + buf[2*i+1] = image->catalog[i].fdr_secnum & 0xff; + } + if (write_sector_logical(image->file_handle, 1, & image->geometry, buf)) + return IMGTOOLERR_WRITEERROR; + + /* update bitmap */ + if (write_sector_logical(image->file_handle, 0, & image->geometry, &image->sec0)) + return IMGTOOLERR_WRITEERROR; + + return 0; +#endif +} + +/* + Delete a file from a ti990_image. +*/ +static imgtoolerr_t ti990_image_deletefile(imgtool::partition &partition, const char *fpath) +{ + imgtool::image *img = &partition->image(); + ti990_image *image = get_ti990_image(img); + int catalog_index, fdr_secnum, parent_fdr_secnum; + imgtoolerr_t reply; + + + reply = find_fdr(image, fpath, &catalog_index, &fdr_secnum, &parent_fdr_secnum); + if (reply) + return reply; + + /* ... */ + + return 0; + +#if 0 + ti99_image *image = (ti99_image*) img; + int totsecs = (image->sec0.totsecsMSB << 8) | image->sec0.totsecsLSB; + char ti_fname[10]; + ti99_fdr fdr; + int i, lnks_index; + int cur_sec, last_sec; + int secsused; + int catalog_index; + int reply; + uint8_t buf[256]; + + str_to_fname(ti_fname, filename); + + reply = find_fdr(image, ti_fname, &fdr, &catalog_index); + if (reply) + return reply; + + /* free data sectors */ + secsused = get_fdr_secsused(&fdr); + + i = 0; + lnks_index = 0; + while (i> 4); + + while ((i= totsecs) + return IMGTOOLERR_CORRUPTIMAGE; + + image->sec0.abm[cur_sec >> 3] &= ~ (1 << (cur_sec & 7)); + + i++; + cur_sec++; + } + + lnks_index++; + } + + /* free fdr sector */ + image->sec0.abm[image->catalog[catalog_index].fdr_secnum >> 3] &= ~ (1 << (image->catalog[catalog_index].fdr_secnum & 7)); + + /* delete catalog entry */ + for (i=catalog_index; i<127; i++) + image->catalog[i] = image->catalog[i+1]; + image->catalog[127].fdr_secnum = 0; + + /* update catalog */ + for (i=0; i<128; i++) + { + buf[2*i] = image->catalog[i].fdr_secnum >> 8; + buf[2*i+1] = image->catalog[i].fdr_secnum & 0xff; + } + if (write_sector_logical(image->file_handle, 1, & image->geometry, buf)) + return IMGTOOLERR_WRITEERROR; + + /* update bitmap */ + if (write_sector_logical(image->file_handle, 0, & image->geometry, &image->sec0)) + return IMGTOOLERR_WRITEERROR; + + return 0; +#endif +} +#endif + +/* + Create a blank ti990_image. +*/ +static imgtoolerr_t ti990_image_create(imgtool::image &image, imgtool::stream::ptr &&stream, util::option_resolution *createoptions) +{ + //const char *volname; + ti990_geometry geometry; + unsigned totsecs; + disk_image_header header; + ti990_sc0 sec0; + uint8_t empty_sec[MAX_SECTOR_SIZE]; + int reply; + int i; + + /* read options */ + //volname = createoptions->lookup_string(ti990_createopts_volname); + geometry.cylinders = createoptions->lookup_int(ti990_createopts_cylinders); + geometry.heads = createoptions->lookup_int(ti990_createopts_heads); + geometry.sectors_per_track = createoptions->lookup_int(ti990_createopts_sectors); + geometry.bytes_per_sector = createoptions->lookup_int(ti990_createopts_sectorsize); + + totsecs = geometry.cylinders * geometry.heads * geometry.sectors_per_track; + + /* write header */ + set_UINT32BE(& header.cylinders, geometry.cylinders); + set_UINT32BE(& header.heads, geometry.heads); + set_UINT32BE(& header.sectors_per_track, geometry.sectors_per_track); + set_UINT32BE(& header.bytes_per_sector, geometry.bytes_per_sector); + + reply = stream->write(&header, sizeof(header)); + if (reply != sizeof(header)) + { + return IMGTOOLERR_WRITEERROR; + } + + + /* initialize sector 0 */ + /* mark disk as uninitialized */ + set_UINT16BE(& sec0.sta, 2); + + + /* write sector 0 */ + if (write_sector_logical(*stream, 0, & geometry, &sec0)) + return IMGTOOLERR_WRITEERROR; + + + /* now clear every other sector, including catalog */ + memset(empty_sec, 0, geometry.bytes_per_sector); + + for (i=1; i +#include +#include +#include + +/* + +sector format + +| GAP1 | IDAM | GAP2 | DATA | CHECKSUM | ... + +GAP1 = 80 80 80 80 80 00 6 +IDAM = FE E7 18 C3 Track# Sector# Checksum-8 of prev. 2 bytes 7 +GAP2 = 80 80 80 80 80 80 00 C3 18 E7 FE 11 +DATA = 126 bytes actual data 126 + = nextTrack# nextSector# 2 +CHECKSUM = Checksum-16 of the previous 128 bytes 2 + --- + 154 bytes + +sectors 0 to 14 are used for the disk directory. sector 15 is used +as track map, with one bit for each sector used. + +*/ + +#define DATA_SIZE (126) +#define SECTOR_SIZE (0x9b) +#define MAX_DIRENTS (15*8) + +/* vzdos directry entry */ +struct vzdos_dirent +{ + char ftype; + char delimitor; + char fname[8]; + uint8_t start_track; + uint8_t start_sector; + uint16_t start_address; + uint16_t end_address; + + vzdos_dirent() + { + ftype = delimitor = '\0'; + fname[0] = '\0'; + start_track = start_sector = 0; + start_address = end_address = 0; + } +}; + +struct vz_iterator +{ + int index; + int eof; +}; + +static const uint8_t sector_order[] = +{ + 0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13 +}; + +/********************************************************************* + Internal functions +*********************************************************************/ + +/* get length of filename without trailing spaces */ +static int vzdos_get_fname_len(const char *fname) +{ + int len; + + for (len = 7; len > 0; len--) + if (fname[len] != 0x20) + break; + + return len; +} + +/* calculate checksum-16 of buffer */ +static uint16_t chksum16(uint8_t *buffer, int len) +{ + int i; + uint16_t sum = 0; + + for (i = 0; i < len; i++) + sum += buffer[i]; + + return sum; +} + +/* returns the offset where the actual sector data starts */ +static imgtoolerr_t vzdos_get_data_start(imgtool::image &img, int track, int sector, int *start) +{ + imgtoolerr_t ret; + uint8_t buffer[25]; /* enough to read the sector header */ + + ret = (imgtoolerr_t)floppy_read_sector(imgtool_floppy(img), 0, track, sector_order[sector], 0, &buffer, sizeof(buffer)); + if (ret) return ret; + + /* search for start of data */ + if (memcmp(buffer + 19, "\xC3\x18\xE7\xFE", 4) == 0) + *start = 23; + else if (memcmp(buffer + 20, "\xC3\x18\xE7\xFE", 4) == 0) + *start = 24; + else if (memcmp(buffer + 21, "\xC3\x18\xE7\xFE", 4) == 0) + *start = 25; + else + return IMGTOOLERR_CORRUPTIMAGE; + + return IMGTOOLERR_SUCCESS; +} + +/* return the actual data of a sector */ +static imgtoolerr_t vzdos_read_sector_data(imgtool::image &img, int track, int sector, uint8_t *data) +{ + int ret, data_start; + uint8_t buffer[DATA_SIZE + 4]; /* data + checksum */ + + ret = vzdos_get_data_start(img, track, sector, &data_start); + if (ret) return (imgtoolerr_t)ret; + + ret = floppy_read_sector(imgtool_floppy(img), 0, track, sector_order[sector], data_start, &buffer, sizeof(buffer)); + if (ret) return (imgtoolerr_t)ret; + + /* verify sector checksums */ + if (pick_integer_le(buffer, DATA_SIZE + 2, 2) != chksum16(buffer, DATA_SIZE + 2)) + return IMGTOOLERR_CORRUPTFILE; + + memcpy(data, &buffer, DATA_SIZE + 2); + + return IMGTOOLERR_SUCCESS; +} + +/* write data to sector */ +static imgtoolerr_t vzdos_write_sector_data(imgtool::image &img, int track, int sector, uint8_t *data) +{ + int ret, data_start; + uint8_t buffer[DATA_SIZE + 4]; /* data + checksum */ + + ret = vzdos_get_data_start(img, track, sector, &data_start); + if (ret) return (imgtoolerr_t)ret; + + memcpy(buffer, data, DATA_SIZE + 2); + place_integer_le(buffer, DATA_SIZE + 2, 2, chksum16(data, DATA_SIZE + 2)); + + ret = floppy_write_sector(imgtool_floppy(img), 0, track, sector_order[sector], data_start, buffer, sizeof(buffer), 0); /* TODO: pass ddam argument from imgtool */ + if (ret) return (imgtoolerr_t)ret; + + return IMGTOOLERR_SUCCESS; +} + +/* write formatted empty sector */ +static imgtoolerr_t vzdos_clear_sector(imgtool::image &img, int track, int sector) +{ + uint8_t data[DATA_SIZE + 2]; + + memset(data, 0x00, sizeof(data)); + + return vzdos_write_sector_data(img, track, sector, data); +} + +/* return a directory entry for an index */ +static imgtoolerr_t vzdos_get_dirent(imgtool::image &img, int index, vzdos_dirent *ent) +{ + int ret, entry; + uint8_t buffer[DATA_SIZE + 2]; + + ret = vzdos_read_sector_data(img, 0, (int) index / 8, buffer); + if (ret) return (imgtoolerr_t)ret; + + entry = ((index % 8) * sizeof(vzdos_dirent)); + + memcpy(ent, &buffer[entry], 10); + ent->start_track = pick_integer_le(&buffer[entry], 10, 1); + ent->start_sector = pick_integer_le(&buffer[entry], 11, 1); + ent->start_address = pick_integer_le(&buffer[entry], 12, 2); + ent->end_address = pick_integer_le(&buffer[entry], 14, 2); + + if (ent->ftype == 0x00) + return IMGTOOLERR_FILENOTFOUND; + + /* check values */ + if (ent->start_track > 39) + return IMGTOOLERR_CORRUPTFILE; + + if (ent->start_sector > 15) + return IMGTOOLERR_CORRUPTFILE; + + return IMGTOOLERR_SUCCESS; +} + +/* save a directory entry to disk */ +static imgtoolerr_t vzdos_set_dirent(imgtool::image &img, int index, vzdos_dirent ent) +{ + int ret, entry; + uint8_t buffer[DATA_SIZE + 2]; + + /* read current sector with entries */ + ret = vzdos_read_sector_data(img, 0, (int) index / 8, buffer); + if (ret) return (imgtoolerr_t)ret; + + entry = ((index % 8) * sizeof(vzdos_dirent)); + + memcpy(&buffer[entry], &ent, 10); + place_integer_le(buffer, entry + 10, 1, ent.start_track); + place_integer_le(buffer, entry + 11, 1, ent.start_sector); + place_integer_le(buffer, entry + 12, 2, ent.start_address); + place_integer_le(buffer, entry + 14, 2, ent.end_address); + + /* save new sector */ + ret = vzdos_write_sector_data(img, 0, (int) index / 8, buffer); + if (ret) return (imgtoolerr_t)ret; + + return IMGTOOLERR_SUCCESS; +} + +/* clear a directory entry */ +static imgtoolerr_t vzdos_clear_dirent(imgtool::image &img, int index) +{ + int ret; + vzdos_dirent entry; + + memset(&entry, 0x00, sizeof(vzdos_dirent)); + + ret = vzdos_set_dirent(img, index, entry); + if (ret) return (imgtoolerr_t)ret; + + return IMGTOOLERR_SUCCESS; +} + +/* search the index for a directory entry */ +static imgtoolerr_t vzdos_searchentry(imgtool::image &image, const char *fname, int *entry) { + int i, len, ret; + vzdos_dirent ent; + char filename[9]; + + /* check for invalid filenames */ + if (strlen(fname) > 8) + return IMGTOOLERR_BADFILENAME; + + /* TODO: check for invalid characters */ + + *entry = -1; + + for (i = 0; i < MAX_DIRENTS; i++) { + ret = vzdos_get_dirent(image, i, &ent); + if (ret) return (imgtoolerr_t)ret; + + len = vzdos_get_fname_len(ent.fname) + 1; + + if (strlen(fname) != len) + continue; + + memset(filename, 0x00, sizeof(filename)); + memcpy(filename, ent.fname, len); + + if (!core_stricmp(fname, filename)) { + *entry = i; + break; + } + + } + + if (*entry == -1) + return IMGTOOLERR_FILENOTFOUND; + + return IMGTOOLERR_SUCCESS; +} + +/* return a directory entry for a filename */ +static imgtoolerr_t vzdos_get_dirent_fname(imgtool::image &img, const char *fname, vzdos_dirent *ent) +{ + int ret, index; + + ret = vzdos_searchentry(img, fname, &index); + if (ret) return (imgtoolerr_t)ret; + + ret = vzdos_get_dirent(img, index, ent); + if (ret) return (imgtoolerr_t)ret; + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t vzdos_toggle_trackmap(imgtool::image &img, int track, int sector, int clear) +{ + int ret, value, bit; + uint8_t buffer[DATA_SIZE + 2]; + + /* get trackmap from image */ + ret = vzdos_read_sector_data(img, 0, 15, buffer); + if (ret) return (imgtoolerr_t)ret; + + value = (int) (floor(((double)track * 16 + sector) / 8) - 1); + bit = 0x01 << ((track * 16 + sector) % 8); + + if (clear) + buffer[value-1] &= ~bit; + else + buffer[value-1] |= bit; + + ret = vzdos_write_sector_data(img, 0, 15, buffer); + if (ret) return (imgtoolerr_t)ret; + + return IMGTOOLERR_SUCCESS; +} + +/* clear a trackmap entry */ +static imgtoolerr_t vzdos_clear_trackmap(imgtool::image &img, int track, int sector) +{ + return vzdos_toggle_trackmap(img, track, sector, 1); +} + +/* enable a trackmap entry */ +static imgtoolerr_t vzdos_set_trackmap(imgtool::image &img, int track, int sector) +{ + return vzdos_toggle_trackmap(img, track, sector, 0); +} + +/* return the status of a trackmap entry */ +static imgtoolerr_t vzdos_get_trackmap(imgtool::image &img, int track, int sector, int *used) +{ + int ret, value, bit; + uint8_t buffer[DATA_SIZE + 2]; + + /* get trackmap from image */ + ret = vzdos_read_sector_data(img, 0, 15, buffer); + if (ret) return (imgtoolerr_t)ret; + + value = (int) (floor(((double)track * 16 + sector) / 8) - 1); + bit = 0x01 << ((track * 16 + sector) % 8); + + if (buffer[value-1] & bit) + *used = 1; + else + *used = 0; + + return IMGTOOLERR_SUCCESS; +} + +/* return the next free sector */ +static imgtoolerr_t vzdos_free_trackmap(imgtool::image &img, int *track, int *sector) +{ + int ret, used = 0; + + for (*track = 1; *track < 40; (*track)++) { + for (*sector = 0; *sector < 16; (*sector)++) { + ret = vzdos_get_trackmap(img, *track, *sector, &used); + if (ret) return (imgtoolerr_t)ret; + if (!used) return IMGTOOLERR_SUCCESS; + } + } + + return IMGTOOLERR_NOSPACE; +} + +static imgtoolerr_t vzdos_write_formatted_sector(imgtool::image &img, int track, int sector) +{ + int ret; + uint8_t sector_data[DATA_SIZE + 4 + 24]; + + static const uint8_t sector_header[24] = { + 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0xFE, 0xE7, + 0x18, 0xC3, 0x00, 0x00, 0x00, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x00, 0xC3, 0x18, 0xE7, 0xFE + }; + + memset(sector_data, 0x00, sizeof(sector_data)); + memcpy(sector_data, sector_header, sizeof(sector_header)); + + sector_data[10] = (uint8_t) track; /* current track */ + sector_data[11] = (uint8_t) sector; /* current sector */ + sector_data[12] = (uint8_t) track + sector; /* checksum-8 */ + + ret = floppy_write_sector(imgtool_floppy(img), 0, track, sector_order[sector], 0, sector_data, sizeof(sector_data), 0); /* TODO: pass ddam argument from imgtool */ + if (ret) return (imgtoolerr_t)ret; + + return IMGTOOLERR_SUCCESS; +} + +/********************************************************************* + Imgtool module code +*********************************************************************/ + +static imgtoolerr_t vzdos_diskimage_beginenum(imgtool::directory &enumeration, const char *path) +{ + vz_iterator *iter; + + iter = (vz_iterator *) enumeration.extra_bytes(); + if (!iter) return IMGTOOLERR_OUTOFMEMORY; + + iter->index = 1; + iter->eof = 0; + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t vzdos_diskimage_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent) +{ + vz_iterator *iter = (vz_iterator *) enumeration.extra_bytes(); + + if (iter->eof == 1 || iter->index > MAX_DIRENTS) { + ent.eof = 1; + + } else { + const char *type; + int ret, len; + vzdos_dirent dirent; + + ret = vzdos_get_dirent(enumeration.image(), iter->index - 1, &dirent); + + if (ret == IMGTOOLERR_FILENOTFOUND) + { + iter->eof = 1; + ent.eof = 1; + return IMGTOOLERR_SUCCESS; + } + + if (ret == IMGTOOLERR_CORRUPTFILE) + ent.corrupt = 1; + + /* kill trailing spaces */ + for (len = 7; len > 0; len--) { + if (dirent.fname[len] != 0x20) { + break; + } + } + + memcpy(ent.filename, &dirent.fname, len + 1); + ent.filesize = dirent.end_address - dirent.start_address; + + switch (dirent.ftype) + { + case 0x01: type = "Deleted"; break; + case 'T': type = "Basic"; break; + case 'B': type = "Binary"; break; + case 'D': type = "Data"; break; + case 'F': type = "Quickwrite"; break; + case 'A': type = "Assembler"; break; + case 'S': type = "Diskops"; break; + case 'W': type = "Wordpro"; break; + default: type = "Unknown"; + } + + snprintf(ent.attr, std::size(ent.attr), "%s", type); + + iter->index++; + } + + return IMGTOOLERR_SUCCESS; +} + +/* TRK 0 sector 15 is used to hold the track map of the disk with one bit + corresponding to a sector used. */ +static imgtoolerr_t vzdos_diskimage_freespace(imgtool::partition &partition, uint64_t *size) +{ + imgtoolerr_t ret; + int i; + imgtool::image &image(partition.image()); + uint8_t c, v, buffer[DATA_SIZE + 2]; + *size = 0; + + ret = vzdos_read_sector_data(image, 0, 15, buffer); + if (ret) return ret; + + for (i = 0; i < DATA_SIZE; i++) { + v = buffer[i]; + for (c = 0; v; c++) { + v &= v - 1; + } + *size += c * DATA_SIZE; + } + *size = (DATA_SIZE * 16 * 39) - *size; + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t vzdos_diskimage_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf) +{ + imgtoolerr_t ret; + imgtool::image &image(partition.image()); + int filesize, track, sector; + vzdos_dirent ent; + uint8_t buffer[DATA_SIZE + 2]; + + ret = vzdos_get_dirent_fname(image, filename, &ent); + if (ret) return ret; + + filesize = ent.end_address - ent.start_address; + track = ent.start_track; + sector = ent.start_sector; + + while (filesize > 0) { + int towrite; + + ret = vzdos_read_sector_data(image, track, sector, buffer); + if (ret) return ret; + + /* detect sectors pointing to themselfs */ + if ((track == (int)pick_integer_le(buffer, DATA_SIZE, 1)) && (sector == (int)pick_integer_le(buffer, DATA_SIZE + 1, 1))) + return IMGTOOLERR_CORRUPTIMAGE; + + /* load next track and sector values */ + track = pick_integer_le(buffer, DATA_SIZE, 1); + sector = pick_integer_le(buffer, DATA_SIZE + 1, 1); + + /* track 0 is invalid */ + if ((track == 0) && (filesize > DATA_SIZE)) + return IMGTOOLERR_CORRUPTIMAGE; + + /* write either DATA_SIZE or the remaining bytes */ + towrite = filesize > DATA_SIZE ? DATA_SIZE : filesize; + + if (destf.write(buffer, towrite) != towrite) + return IMGTOOLERR_WRITEERROR; + + filesize -= DATA_SIZE; + } + + return IMGTOOLERR_SUCCESS; +} + +/* deletes directory entry, clears trackmap entries and sectors */ +static imgtoolerr_t vzdos_diskimage_deletefile(imgtool::partition &partition, const char *fname) +{ + imgtoolerr_t ret; + imgtool::image &img(partition.image()); + int index, filesize, track, sector, next_track, next_sector; + vzdos_dirent entry, next_entry; + uint8_t buffer[DATA_SIZE + 2]; + + ret = vzdos_get_dirent_fname(img, fname, &entry); + if (ret) return ret; + + ret = (imgtoolerr_t)floppy_read_sector(imgtool_floppy(img), 0, entry.start_track, sector_order[entry.start_sector], 24, &buffer, DATA_SIZE + 2); + if (ret) return ret; + + filesize = entry.end_address - entry.start_address; + track = entry.start_track; + sector = entry.start_sector; + + /* delete directory entry */ + ret = vzdos_searchentry(img, fname, &index); + if (ret) return ret; + + ret = vzdos_get_dirent(img, index + 1, &next_entry); + + if (ret == IMGTOOLERR_FILENOTFOUND) { + /* we are the last directory entry, just delete it */ + ret = vzdos_clear_dirent(img, index); + if (ret) return ret; + + } else if (ret) { + /* an error occurred */ + return ret; + + } else { + ret = vzdos_set_dirent(img, index++, next_entry); + if (ret) return ret; + + while ((ret = vzdos_get_dirent(img, index + 1, &next_entry)) != IMGTOOLERR_FILENOTFOUND) { + if (ret) return ret; + ret = vzdos_set_dirent(img, index++, next_entry); + if (ret) return ret; + } + + ret = vzdos_clear_dirent(img, index); + if (ret) return ret; + + } + + /* clear sectors and trackmap entries */ + while (filesize > 0) { + filesize -= DATA_SIZE; + + /* clear trackmap entry */ + ret = vzdos_clear_trackmap(img, track, sector); + if (ret) return ret; + + /* load next track and sector values */ + next_track = pick_integer_le(buffer, DATA_SIZE, 1); + next_sector = pick_integer_le(buffer, DATA_SIZE + 1, 1); + + /* overwrite sector with default values */ + ret = vzdos_clear_sector(img, track, sector); + if (ret) return ret; + + /* read next sector */ + track = next_track; + sector = next_sector; + + if (filesize > 0) { + ret = (imgtoolerr_t)floppy_read_sector(imgtool_floppy(img), 0, track, sector_order[sector], 24, &buffer, DATA_SIZE + 2); + if (ret) return ret; + } + + } + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t vzdos_writefile(imgtool::partition &partition, int offset, imgtool::stream &sourcef, vzdos_dirent *entry) +{ + imgtoolerr_t ret; + imgtool::image &img(partition.image()); + int index, track, sector, toread, next_track, next_sector; + vzdos_dirent temp_entry; + uint64_t filesize = 0, freespace = 0; + uint8_t buffer[DATA_SIZE + 2]; + char filename[9]; + + /* is the image writeable? */ + if (floppy_is_read_only(imgtool_floppy(img))) + return IMGTOOLERR_READONLY; + + /* check for already existing filename -> overwrite */ + strcpy(filename, entry->fname); + filename[vzdos_get_fname_len(entry->fname) + 1] = 0x00; + ret = vzdos_get_dirent_fname(img, filename, &temp_entry); + if (!ret) { + /* file already exists, delete it */ + ret = vzdos_diskimage_deletefile(partition, filename); + if (ret) return ret; + } else if (ret != IMGTOOLERR_FILENOTFOUND) { + /* another error occurred, return it */ + return ret; + } + + ret = (imgtoolerr_t) sourcef.seek(offset, SEEK_SET); + if (ret) return ret; + + /* check if there is enough space */ + filesize = sourcef.size() - offset; + + ret = vzdos_diskimage_freespace(partition, &freespace); + if (ret) return ret; + + if (filesize > freespace) + return IMGTOOLERR_NOSPACE; + + /* get next free track and sector */ + ret = vzdos_free_trackmap(img, &track, §or); + if (ret) return ret; + + entry->end_address = entry->start_address + (unsigned int) filesize; + entry->start_track = track; + entry->start_sector = sector; + + /* search for next free directory entry */ + for (index = 0; index < MAX_DIRENTS; index++) { + ret = vzdos_get_dirent(img, index, &temp_entry); + if (ret == IMGTOOLERR_FILENOTFOUND) + break; + else if (ret) + return (ret); + } + + /* write directory entry to disk */ + ret = vzdos_set_dirent(img, index, *entry); + if (ret) return ret; + + next_track = 0; + next_sector = 0; + + /* write data to disk */ + while (filesize > 0) { + toread = filesize > DATA_SIZE ? DATA_SIZE : filesize; + sourcef.read(buffer, toread); + + filesize -= toread; + + /* mark sector as used */ + ret = vzdos_set_trackmap(img, track, sector); + if (ret) return ret; + + /* get track and sector for next sector */ + if (filesize > 0) { + ret = vzdos_free_trackmap(img, &next_track, &next_sector); + if (ret) return ret; + } else { + next_track = 0; + next_sector = 0; + } + buffer[DATA_SIZE] = next_track; + buffer[DATA_SIZE + 1] = next_sector; + + /* write sector to disk */ + ret = vzdos_write_sector_data(img, track, sector, buffer); + if (ret) return ret; + + track = next_track; + sector = next_sector; + } + + return IMGTOOLERR_SUCCESS; + +} + +/* create a new file or overwrite a file */ +static imgtoolerr_t vzdos_diskimage_writefile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts) +{ + imgtoolerr_t ret; + int ftype; + vzdos_dirent entry; + + /* TODO: check for leading spaces and strip */ + if (strlen(filename) > 8 || strlen(filename) < 1) + return IMGTOOLERR_BADFILENAME; + + /* prepare directory entry */ + ftype = opts->lookup_int('T'); + + switch (ftype) { + case 0: + entry.ftype = 'T'; + entry.start_address = 31465; + break; + case 1: + entry.ftype = 'B'; + entry.start_address = 31465; /* ??? */ + break; + case 2: + entry.ftype = 'D'; + entry.start_address = 31465; /* ??? */ + break; + default: + break; + } + + entry.delimitor = ':'; + memset(&entry.fname, 0x20, 8); /* pad with spaces */ + memcpy(&entry.fname, filename, strlen(filename)); + + /* write file to disk */ + ret = vzdos_writefile(partition, 0, sourcef, &entry); + if (ret) return ret; + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t vzdos_diskimage_suggesttransfer(imgtool::partition &partition, const char *fname, imgtool_transfer_suggestion *suggestions, size_t suggestions_length) +{ + imgtoolerr_t ret; + imgtool::image &image(partition.image()); + vzdos_dirent entry; + + if (fname) { + ret = vzdos_get_dirent_fname(image, fname, &entry); + if (ret) return ret; + + switch (entry.ftype) { + case 'B': + suggestions[0].viability = SUGGESTION_RECOMMENDED; + suggestions[0].filter = filter_vzsnapshot_getinfo; + suggestions[0].description = "VZ Snapshot"; + suggestions[1].viability = SUGGESTION_POSSIBLE; + suggestions[1].filter = NULL; + suggestions[1].description = "Raw"; + break; + case 'T': + suggestions[0].viability = SUGGESTION_RECOMMENDED; + suggestions[0].filter = filter_vzsnapshot_getinfo; + suggestions[0].description = "VZ Snapshot"; + suggestions[1].viability = SUGGESTION_POSSIBLE; + suggestions[1].filter = NULL; + suggestions[1].description = "Raw"; + suggestions[2].viability = SUGGESTION_POSSIBLE; + suggestions[2].filter = filter_vzbas_getinfo; + suggestions[2].description = "Tokenized Basic"; + break; + default: + suggestions[0].viability = SUGGESTION_RECOMMENDED; + suggestions[0].filter = NULL; + suggestions[0].description = "Raw"; + } + + } else { + suggestions[0].viability = SUGGESTION_RECOMMENDED; + suggestions[0].filter = NULL; + suggestions[0].description = "Raw"; + suggestions[1].viability = SUGGESTION_POSSIBLE; + suggestions[1].filter = filter_vzsnapshot_getinfo; + suggestions[1].description = "VZ Snapshot"; + } + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t vzdos_diskimage_create(imgtool::image &img, imgtool::stream::ptr &&dummy, util::option_resolution *opts) +{ + imgtoolerr_t ret; + int track, sector; + + for (track = 0; track < 40; track++) { + for (sector = 0; sector < 16; sector++) { + ret = vzdos_write_formatted_sector(img, track, sector); + if (ret) return ret; + } + } + + return IMGTOOLERR_SUCCESS; +} + +/********************************************************************* + Imgtool vz filter code +*********************************************************************/ + +static imgtoolerr_t vzsnapshot_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf) +{ + imgtoolerr_t ret; + imgtool::image &image(partition.image()); + vzdos_dirent entry; + uint8_t header[24]; + + /* get directory entry from disk */ + ret = vzdos_get_dirent_fname(image, filename, &entry); + if (ret) return ret; + + /* prepare header */ + header[0] = 'V'; + header[1] = 'Z'; + header[2] = 'F'; + + switch (entry.ftype) { + case 'B': + header[3] = '1'; + header[21] = 0xF1; + break; + case 'T': + header[3] = '0'; + header[21] = 0xF0; + break; + default: + memset(header, 0x00, 4); + header[21] = 0x00; + } + + memset(header + 4, 0x00, 17); + memcpy(header + 4, entry.fname, vzdos_get_fname_len(entry.fname) + 1); + place_integer_le(header, 22, 2, entry.start_address); + + /* write header to file */ + destf.write(header, sizeof(header)); + + /* write data to file */ + ret = vzdos_diskimage_readfile(partition, filename, "", destf); + if (ret) return ret; + + return IMGTOOLERR_SUCCESS; +} + +static imgtoolerr_t vzsnapshot_writefile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts) +{ + imgtoolerr_t ret; + int fnameopt; + vzdos_dirent entry; + uint8_t header[24]; + + /* get header infos from file */ + sourcef.read(header, sizeof(header)); + + /* prepare directory entry */ + entry.ftype = header[21] == 0xF1 ? 'B' : 'T'; + entry.delimitor = ':'; + entry.start_address = pick_integer_le(header, 22, 2); + + /* filename from header or directly? */ + fnameopt = opts->lookup_int('F'); + + if (fnameopt == 0) { + memcpy(&entry.fname, &header[4], 8); + } else { + /* TODO: check for leading spaces and strip */ + if (strlen(filename) > 8 || strlen(filename) < 1) + return IMGTOOLERR_BADFILENAME; + + memcpy(&entry.fname, filename, strlen(filename) - 3); + } + + /* write file to disk */ + ret = vzdos_writefile(partition, 24, sourcef, &entry); + if (ret) return ret; + + return IMGTOOLERR_SUCCESS; +} + +void filter_vzsnapshot_getinfo(uint32_t state, union filterinfo *info) +{ + switch(state) + { + case FILTINFO_STR_NAME: info->s = "vzsnapshot"; break; + case FILTINFO_STR_HUMANNAME: info->s = "VZ Snapshot"; break; + case FILTINFO_STR_EXTENSION: info->s = "vz"; break; + case FILTINFO_PTR_READFILE: info->read_file = vzsnapshot_readfile; break; + case FILTINFO_PTR_WRITEFILE: info->write_file = vzsnapshot_writefile; break; + } +} + +/********************************************************************* + Imgtool module declaration +*********************************************************************/ + +OPTION_GUIDE_START(vzdos_writefile_optionguide) + OPTION_ENUM_START( 'T', "ftype", "File type" ) + OPTION_ENUM( 0, "basic", "Basic" ) + OPTION_ENUM( 1, "binary", "Binary" ) + OPTION_ENUM( 2, "data", "Data" ) + OPTION_ENUM_END + OPTION_ENUM_START( 'F', "fname", "Filename" ) + OPTION_ENUM( 0, "intern", "Filename from VZ-Header" ) + OPTION_ENUM( 1, "extern", "Actual filename" ) + OPTION_ENUM_END +OPTION_GUIDE_END + +/* +T Basic Editor File +B Binary File +D Sequential Access Program Data File +F Quickwrite Document +A Russell Harrison's Edit Ass. File +S Dave Mitchell/Mark Hardwood Edit Ass. File Start Addr A280H +S Quickwrite/Diskops System File/Label Except Above +W Edit Ass. with Diskops File Start Addr A813H +W E & F Word Pro with Patch 3.3 File End Addr D000H +W Russell Harrison Word Pro File Except above two +*/ + +void vzdos_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) +{ + switch(state) + { + /* --- the following bits of info are returned as 64-bit signed integers --- */ + case IMGTOOLINFO_INT_PREFER_UCASE: info->i = 1; break; + case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(vz_iterator); break; + + /* --- the following bits of info are returned as NULL-terminated strings --- */ + case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "vzdos"); break; + case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "VZ-DOS format"); break; + case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break; + case IMGTOOLINFO_STR_WRITEFILE_OPTSPEC: strcpy(info->s = imgtool_temp_str(), "T[0]-2;F[0]-1"); break; + case IMGTOOLINFO_STR_EOLN: info->s = NULL; break; + + /* --- the following bits of info are returned as pointers to data or functions --- */ + case IMGTOOLINFO_PTR_MAKE_CLASS: info->make_class = imgtool_floppy_make_class; break; + case IMGTOOLINFO_PTR_OPEN: info->open = NULL; break; + case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = vzdos_diskimage_beginenum; break; + case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = vzdos_diskimage_nextenum; break; + case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = vzdos_diskimage_freespace; break; + case IMGTOOLINFO_PTR_READ_FILE: info->read_file = vzdos_diskimage_readfile; break; + case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = vzdos_diskimage_writefile; break; + case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = vzdos_diskimage_deletefile; break; + case IMGTOOLINFO_PTR_FLOPPY_CREATE: info->create = vzdos_diskimage_create; break; + case IMGTOOLINFO_PTR_WRITEFILE_OPTGUIDE: info->writefile_optguide = &vzdos_writefile_optionguide; break; + case IMGTOOLINFO_PTR_SUGGEST_TRANSFER: info->suggest_transfer = vzdos_diskimage_suggesttransfer; break; + case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_vz; break; + } +} diff --git a/src/tools/imgtool/stream.cpp b/src/tools/imgtool/stream.cpp new file mode 100644 index 0000000..014d573 --- /dev/null +++ b/src/tools/imgtool/stream.cpp @@ -0,0 +1,694 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + stream.cpp + + Code for implementing Imgtool streams + +***************************************************************************/ + +#include "stream.h" + +#include "imgtool.h" + +#include "corefile.h" +#include "ioprocs.h" +#include "unzip.h" + +#include +#include +#include + +#include // for crc32 + + +namespace imgtool { + +namespace { + +class stream_read_wrapper : public virtual util::random_read +{ +public: + stream_read_wrapper(stream::ptr &&stream, std::uint8_t filler) noexcept + : m_stream(stream.release()) + , m_filler(filler) + , m_close(true) + { + assert(m_stream); + } + + stream_read_wrapper(stream &stream, std::uint8_t filler) noexcept + : m_stream(&stream) + , m_filler(filler) + , m_close(false) + { + } + + virtual ~stream_read_wrapper() + { + if (m_close) + delete m_stream; + } + + virtual std::error_condition seek(std::int64_t offset, int whence) noexcept override + { + m_stream->seek(offset, whence); + return std::error_condition(); + } + + virtual std::error_condition tell(std::uint64_t &result) noexcept override + { + result = m_stream->tell(); + return std::error_condition(); + } + + virtual std::error_condition length(std::uint64_t &result) noexcept override + { + result = m_stream->size(); + return std::error_condition(); + } + + virtual std::error_condition read(void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + actual = m_stream->read(buffer, length); + if (actual < length) + std::memset(reinterpret_cast(buffer) + actual, m_filler, length - actual); + return std::error_condition(); + } + + virtual std::error_condition read_at(std::uint64_t offset, void *buffer, std::size_t length, std::size_t &actual) noexcept override + { + std::uint64_t const pos = m_stream->tell(); + m_stream->seek(offset, SEEK_SET); + actual = m_stream->read(buffer, length); + m_stream->seek(pos, SEEK_SET); + if (actual < length) + std::memset(reinterpret_cast(buffer) + actual, m_filler, length - actual); + return std::error_condition(); + } + +protected: + stream *const m_stream; + std::uint8_t m_filler; + bool const m_close; +}; + + +class stream_read_write_wrapper : public stream_read_wrapper, public util::random_read_write +{ +public: + using stream_read_wrapper::stream_read_wrapper; + + virtual std::error_condition finalize() noexcept override + { + return std::error_condition(); + } + + virtual std::error_condition flush() noexcept override + { + return std::error_condition(); + } + + virtual std::error_condition write(void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + std::uint64_t const pos = m_stream->tell(); + std::uint64_t size = m_stream->size(); + if (size < pos) + { + m_stream->seek(size, SEEK_SET); + size += m_stream->fill(m_filler, pos - size); + } + actual = (size >= pos) ? m_stream->write(buffer, length) : 0U; + return (actual == length) ? std::error_condition() : std::errc::io_error; + } + + virtual std::error_condition write_at(std::uint64_t offset, void const *buffer, std::size_t length, std::size_t &actual) noexcept override + { + std::uint64_t const pos = m_stream->tell(); + std::uint64_t size = m_stream->size(); + if (offset > size) + { + m_stream->seek(size, SEEK_SET); + size += m_stream->fill(m_filler, offset - size); + } + else + { + m_stream->seek(offset, SEEK_SET); + } + actual = (size >= offset) ? m_stream->write(buffer, length) : 0U; + m_stream->seek(pos, SEEK_SET); + return (actual == length) ? std::error_condition() : std::errc::io_error; + } +}; + +} // anonymous namespace + + +util::random_read::ptr stream_read(stream::ptr &&s, std::uint8_t filler) noexcept +{ + util::random_read::ptr result; + if (s) + result.reset(new (std::nothrow) stream_read_wrapper(std::move(s), filler)); + return result; +} + +util::random_read::ptr stream_read(stream &s, std::uint8_t filler) noexcept +{ + util::random_read::ptr result(new (std::nothrow) stream_read_wrapper(s, filler)); + return result; +} + +util::random_read_write::ptr stream_read_write(stream::ptr &&s, std::uint8_t filler) noexcept +{ + util::random_read_write::ptr result; + if (s) + result.reset(new (std::nothrow) stream_read_write_wrapper(std::move(s), filler)); + return result; +} + +util::random_read_write::ptr stream_read_write(stream &s, std::uint8_t filler) noexcept +{ + util::random_read_write::ptr result(new (std::nothrow) stream_read_write_wrapper(s, filler)); + return result; +} + + + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +stream::stream(bool wp) + : imgtype(IMG_FILE) + , write_protect(wp) + , position(0) + , filesize(0) + , file() + , buffer(nullptr) +{ +} + + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +stream::stream(bool wp, util::core_file::ptr &&f) + : imgtype(IMG_FILE) + , write_protect(wp) + , position(0) + , filesize(0) + , file(std::move(f)) + , buffer(nullptr) +{ + file->length(filesize); // FIXME: check error return +} + + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +stream::stream(bool wp, std::size_t size) + : imgtype(IMG_MEM) + , write_protect(wp) + , position(0) + , filesize(size) + , file() + , buffer(reinterpret_cast(malloc(size))) +{ +} + + +//------------------------------------------------- +// ctor +//------------------------------------------------- + +stream::stream(bool wp, std::size_t size, void *buf) + : imgtype(IMG_MEM) + , write_protect(wp) + , position(0) + , filesize(size) + , file() + , buffer(reinterpret_cast(buf)) +{ +} + + +//------------------------------------------------- +// dtor +//------------------------------------------------- + +stream::~stream() +{ + if (buffer) + free(buffer); +} + + +//------------------------------------------------- +// open_zip +//------------------------------------------------- + +stream::ptr stream::open_zip(const std::string &zipname, const char *subname, int read_or_write) +{ + if (read_or_write) + return stream::ptr(); + + /* check to see if the file exists */ + FILE *f = fopen(zipname.c_str(), "r"); + if (!f) + return stream::ptr(); + fclose(f); + + stream::ptr imgfile(new stream(true)); + + imgfile->imgtype = IMG_MEM; + + util::archive_file::ptr z; + util::archive_file::open_zip(zipname, z); + if (!z) + return stream::ptr(); + + int zipent = z->first_file(); + while ((zipent >= 0) && subname && strcmp(subname, z->current_name().c_str())) + zipent = z->next_file(); + if (zipent < 0) + return stream::ptr(); + + imgfile->filesize = z->current_uncompressed_length(); + imgfile->buffer = reinterpret_cast(malloc(z->current_uncompressed_length())); + if (!imgfile->buffer) + return stream::ptr(); + + if (z->decompress(imgfile->buffer, z->current_uncompressed_length())) + return stream::ptr(); + + return imgfile; +} + + + +//------------------------------------------------- +// open +//------------------------------------------------- + +stream::ptr stream::open(const std::string &filename, int read_or_write) +{ + static const uint32_t write_modes[] = + { + OPEN_FLAG_READ, + OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, + OPEN_FLAG_READ | OPEN_FLAG_WRITE, + OPEN_FLAG_READ | OPEN_FLAG_WRITE | OPEN_FLAG_CREATE + }; + stream::ptr s; + char c; + + // maybe we are just a ZIP? + if (core_filename_ends_with(filename, ".zip")) + return open_zip(filename, nullptr, read_or_write); + + util::core_file::ptr f = nullptr; + auto const filerr = util::core_file::open(filename, write_modes[read_or_write], f); + if (filerr) + { + if (!read_or_write) + { + int const len = filename.size(); + + // can't open the file; try opening ZIP files with other names + std::vector buf(len + 1); + strcpy(&buf[0], filename.c_str()); + + for (int i = len-1; !s && (i >= 0); i--) + { + if ((buf[i] == '\\') || (buf[i] == '/')) + { + c = buf[i]; + buf[i] = '\0'; + s = open_zip(&buf[0], &buf[i + 1], read_or_write); + buf[i] = c; + } + } + + if (s) + return s; + } + + // ah well, it was worth a shot + return stream::ptr(); + } + + stream::ptr imgfile(new stream(read_or_write ? false : true, std::move(f))); + + // normal file + return imgfile; +} + + +//------------------------------------------------- +// open_write_stream +//------------------------------------------------- + +stream::ptr stream::open_write_stream(int size) +{ + stream::ptr imgfile(new stream(false, size)); + if (!imgfile->buffer) + return stream::ptr(); + + return imgfile; +} + + +//------------------------------------------------- +// open_mem +//------------------------------------------------- + +stream::ptr stream::open_mem(void *buf, size_t sz) +{ + stream::ptr imgfile(new stream(false, sz, buf)); + + return imgfile; +} + + +//------------------------------------------------- +// core_file +//------------------------------------------------- + +util::core_file *stream::core_file() +{ + return (imgtype == IMG_FILE) ? file.get() : nullptr; +} + + +//------------------------------------------------- +// read +//------------------------------------------------- + +uint32_t stream::read(void *buf, uint32_t sz) +{ + size_t result = 0; + + switch(imgtype) + { + case IMG_FILE: + if (!file->seek(position, SEEK_SET)) + file->read(buf, sz, result); // FIXME: check error return + break; + + case IMG_MEM: + // do we have to limit sz? + if (sz > (filesize - position)) + sz = uint32_t(filesize - position); + + memcpy(buf, buffer + position, sz); + result = sz; + break; + + default: + assert(0); + break; + } + position += result; + return result; +} + + +//------------------------------------------------- +// write +//------------------------------------------------- + +uint32_t stream::write(const void *buf, uint32_t sz) +{ + size_t result = 0; + + switch(imgtype) + { + case IMG_MEM: + if (!write_protect) + { + // do we have to expand the buffer? + const uint64_t end = position + sz; + if ((filesize < end) && (size_t(end) == end)) + { + // try to expand the buffer + void *const new_buffer = realloc(buffer, size_t(end)); + if (new_buffer) + { + buffer = reinterpret_cast(new_buffer); + filesize = end; + } + } + + // do we have to limit sz? + if (sz > (filesize - position)) + sz = uint32_t(filesize - position); + + memcpy(buffer + position, buf, sz); + result = sz; + } + break; + + case IMG_FILE: + if (!file->seek(position, SEEK_SET)) + file->write(buf, sz, result); // FIXME: check error return + break; + + default: + assert(0); + break; + } + + // advance the file pointer + position += result; + + // did we grow the file + if (position > filesize) + filesize = position; + return result; +} + + +//------------------------------------------------- +// size +//------------------------------------------------- + +uint64_t stream::size() const +{ + return filesize; +} + + +//------------------------------------------------- +// getptr +//------------------------------------------------- + +void *stream::getptr() +{ + void *ptr; + + switch(imgtype) + { + case IMG_MEM: + ptr = buffer; + break; + + default: + ptr = nullptr; + break; + } + return ptr; +} + + +//------------------------------------------------- +// seek +//------------------------------------------------- + +int stream::seek(int64_t pos, int where) +{ + switch(where) + { + case SEEK_CUR: + pos += position; + break; + case SEEK_END: + pos += size(); + break; + } + + if (pos < 0) + position = 0; + else + position = std::min(size(), uint64_t(pos)); + + if (position < pos) + fill('\0', pos - position); + + return 0; +} + + +//------------------------------------------------- +// tell +//------------------------------------------------- + +uint64_t stream::tell() +{ + return position; +} + + +//------------------------------------------------- +// transfer +//------------------------------------------------- + +uint64_t stream::transfer(stream &dest, stream &source, uint64_t sz) +{ + uint64_t result = 0; + uint64_t readsz; + char buf[1024]; + + while(sz && (readsz = source.read(buf, std::min(sz, uint64_t(sizeof(buf)))))) + { + dest.write(buf, readsz); + sz -= readsz; + result += readsz; + } + return result; +} + + +//------------------------------------------------- +// transfer_all +//------------------------------------------------- + +uint64_t stream::transfer_all(stream &dest, stream &source) +{ + return transfer(dest, source, source.size()); +} + + +//------------------------------------------------- +// crc +//------------------------------------------------- + +int stream::crc(unsigned long *result) +{ + size_t sz; + void *ptr; + + switch(imgtype) + { + case IMG_MEM: + *result = crc32(0, (unsigned char *) buffer, (size_t) filesize); + break; + + default: + sz = size(); + ptr = malloc(sz); + if (!ptr) + return IMGTOOLERR_OUTOFMEMORY; + seek(0, SEEK_SET); + if (read(ptr, sz) != sz) + { + free(ptr); + return IMGTOOLERR_READERROR; + } + *result = crc32(0, (const Bytef*)ptr, sz); + free(ptr); + break; + } + return 0; +} + + +//------------------------------------------------- +// file_crc +//------------------------------------------------- + +int stream::file_crc(const char *fname, unsigned long *result) +{ + int err; + stream::ptr f; + + f = stream::open(fname, OSD_FOPEN_READ); + if (!f) + return IMGTOOLERR_FILENOTFOUND; + + err = f->crc(result); + return err; +} + + +//------------------------------------------------- +// fill +//------------------------------------------------- + +uint64_t stream::fill(unsigned char b, uint64_t sz) +{ + uint64_t outsz; + char buf[1024]; + + outsz = 0; + memset(buf, b, (std::min)(sz, sizeof(buf))); + + while (sz) + { + outsz += write(buf, (std::min)(sz, sizeof(buf))); + sz -= (std::min)(sz, sizeof(buf)); + } + return outsz; +} + + +//------------------------------------------------- +// is_read_only +//------------------------------------------------- + +bool stream::is_read_only() +{ + return write_protect; +} + + +//------------------------------------------------- +// putc +//------------------------------------------------- + +uint32_t stream::putc(char c) +{ + return write(&c, 1); +} + + +//------------------------------------------------- +// puts +//------------------------------------------------- + +uint32_t stream::puts(const char *s) +{ + return write(s, strlen(s)); +} + + +//------------------------------------------------- +// printf +//------------------------------------------------- + +uint32_t stream::printf(const char *fmt, ...) +{ + va_list va; + char buf[256]; + + va_start(va, fmt); + vsprintf(buf, fmt, va); + va_end(va); + + return puts(buf); +} + +} // namespace imgtool diff --git a/src/tools/imgtool/stream.h b/src/tools/imgtool/stream.h new file mode 100644 index 0000000..36872e3 --- /dev/null +++ b/src/tools/imgtool/stream.h @@ -0,0 +1,95 @@ +// license:BSD-3-Clause +// copyright-holders:Nathan Woods +/*************************************************************************** + + stream.h + + Code for implementing Imgtool streams + +***************************************************************************/ +#ifndef MAME_TOOLS_IMGTOOL_STREAM_H +#define MAME_TOOLS_IMGTOOL_STREAM_H + +#pragma once + +#include "utilfwd.h" + +#include "osdcomm.h" + +#include +#include +#include + + +namespace imgtool { + +class stream +{ +public: + typedef std::unique_ptr ptr; + + ~stream(); + + static imgtool::stream::ptr open(const std::string &filename, int read_or_write); /* similar params to mame_fopen */ + static imgtool::stream::ptr open_write_stream(int filesize); + static imgtool::stream::ptr open_mem(void *buf, size_t sz); + + util::core_file *core_file(); + uint32_t read(void *buf, uint32_t sz); + uint32_t write(const void *buf, uint32_t sz); + uint64_t size() const; + int seek(int64_t pos, int where); + uint64_t tell(); + void *getptr(); + uint32_t putc(char c); + uint32_t puts(const char *s); + uint32_t printf(const char *fmt, ...) ATTR_PRINTF(2, 3); + + // transfers sz bytes from source to dest + static uint64_t transfer(imgtool::stream &dest, imgtool::stream &source, uint64_t sz); + static uint64_t transfer_all(imgtool::stream &dest, imgtool::stream &source); + + // fills sz bytes with b + uint64_t fill(unsigned char b, uint64_t sz); + + // returns the CRC of a file + int crc(unsigned long *result); + static int file_crc(const char *fname, unsigned long *result); + + // returns whether a stream is read only or not + bool is_read_only(); + +private: + enum imgtype_t + { + IMG_FILE, + IMG_MEM + }; + + imgtype_t imgtype; + bool write_protect; + std::uint64_t position; + std::uint64_t filesize; + + std::unique_ptr file; + std::uint8_t *buffer; + + // ctors + stream(bool wp); + stream(bool wp, std::unique_ptr &&f); + stream(bool wp, std::size_t size); + stream(bool wp, std::size_t size, void *buf); + + // private methods + static stream::ptr open_zip(const std::string &zipname, const char *subname, int read_or_write); +}; + + +std::unique_ptr stream_read(stream::ptr &&s, std::uint8_t filler) noexcept; +std::unique_ptr stream_read(stream &s, std::uint8_t filler) noexcept; +std::unique_ptr stream_read_write(stream::ptr &&s, std::uint8_t filler) noexcept; +std::unique_ptr stream_read_write(stream &s, std::uint8_t filler) noexcept; + +} // namespace imgtool + +#endif // MAME_TOOLS_IMGTOOL_STREAM_H diff --git a/src/tools/jedutil.cpp b/src/tools/jedutil.cpp new file mode 100644 index 0000000..a4ea6b5 --- /dev/null +++ b/src/tools/jedutil.cpp @@ -0,0 +1,8261 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + jedutil.c + + JEDEC file utilities. + +**************************************************************************** + + Binary file format: + + Offset + 0 = Total number of fuses (32 bits) + 4 = Raw fuse data, packed 8 bits at a time, LSB to MSB + +**************************************************************************** + + Known types: + + 20-pin devices: + PAL10H8 = QP20 QF0320 + PAL12H6 = QP20 QF0320 + PAL14H4 = QP20 QF0448 + PAL16H2 = QP20 QF0512 + PAL16C1 = QP20 QF0512 + PAL10L8 = QP20 QF0320 + PAL12L6 = QP20 QF0320 + PAL14L4 = QP20 QF0448 + PAL16L2 = QP20 QF0512 + + PAL10P8 = QP20 QF0328 + PAL12P6 = QP20 QF0390 + PAL14P4 = QP20 QF0452 + PAL16P2 = QP20 QF0514 + PAL16P8 = QP20 QF2056 + PAL16RP4 = QP20 QF2056 + PAL16RP6 = QP20 QF2056 + PAL16RP8 = QP20 QF2056 + + 15S8 = QP20 QF0448 + + CK2605 = QP20 QF1106 + + PLS153 = QP20 QF1842 + + PAL16L8 = QP20 QF2048 + + PAL16R4 = QP20 QF2048 + PAL16R6 = QP20 QF2048 + PAL16R8 = QP20 QF2048 + PAL16RA8 = QP20 QF2056? + + PAL16V8R = QP20 QF2194 + PALCE16V8 = QP20 QF2194 + GAL16V8A = QP20 QF2194 + ATF16V8B = QP20 QF2194 + + 18CV8 = QP20 QF2696 + + AMPAL18P8 = QP20 QF2600 + + 5C032 = QP20 + + PLUS16L8 = QP20 + PLUS16R4 = QP20 + PLUS16R6 = QP20 + PLUS16R8 = QP20 + + EPL10P8 = QP20 + EPL12P6 = QP20 + EPL14P4 = QP20 + EPL16P2 = QP20 + EPL16P8 = QP20 QF2072 + EPL16RP8 = QP20 + EPL16RP6 = QP20 + EPL16RP4 = QP20 + + PAL16A4 = QP20 + PAL16X4 = QP20 + + 24-pin devices: + PAL6L16 = QP24 QF0192 + PAL8L14 = QP24 QF0224 + PAL12H10 = QP24 QF0480 + PAL12L10 = QP24 QF0480 + PAL14H8 = QP24 QF0560 + PAL14L8 = QP24 QF0560 + PAL16H6 = QP24 QF0640 + PAL16L6 = QP24 QF0640 + PAL18H4 = QP24 QF0720 + PAL18L4 = QP24 QF0720 + PAL20C1 = QP24 QF0640 + PAL20L2 = QP24 QF0640 + + PAL20L8 = QP24 QF2560 + PAL20L10 = QP24 QF1600 + PAL20R4 = QP24 QF2560 + PAL20R6 = QP24 QF2560 + PAL20R8 = QP24 QF2560 + + PAL20RA10 = QP24 QF3210 + + PAL20X4 = QP24 QF1600 + PAL20X8 = QP24 QF1600 + PAL20X10 = QP24 QF1600 + + GAL20V8A = QP24 QF2706 + PALCE20V8 = QP24 QF2706 + ATF20V10B = QP24 QF2706 + + GAL22V10 = QP24 QF5892 + ATF22V10C = QP24 QF5892 (GAL Mode) + + PALCE22V10 = QP24 QF5828 + PAL22V10 = QP24 QF5828 + ATF22V10C = QP24 QF5828 (PAL Mode) + + ATF22V10C = QP24 QF5893 (Power Down Mode) + + PLS173 = QP24 + + GAL6001 = QP24 + + 28-pin devices: + PLS100 = QP28 QF1928 (Tri-State) + 82S100 = QP20 QF1928 (Tri-State) + PLS101 = QP20 QF1928 (Open Collector) + 82S101 = QP20 QF1928 (Open Collector) + +**************************************************************************** + + Thanks to Charles MacDonald (http://cgfm2.emuviews.com/) for providing + information on how to decode the PLS153/82S153 and CK2605 fuse map. + +***************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include + +#include "corestr.h" +#include "ioprocs.h" +#include "jedparse.h" +#include "plaparse.h" + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define NO_OUTPUT_ENABLE_FUSE_ROW 0xFFFF + +/* Output pin flags */ +#define OUTPUT_ACTIVELOW 0x00000001 +#define OUTPUT_ACTIVEHIGH 0x00000002 +#define OUTPUT_COMBINATORIAL 0x00000004 +#define OUTPUT_REGISTERED 0x00000008 +#define OUTPUT_FEEDBACK_OUTPUT 0x00000010 /* Feedback state depends on output enable */ +#define OUTPUT_FEEDBACK_COMBINATORIAL 0x00000020 /* Feedback state independent of output enable */ +#define OUTPUT_FEEDBACK_REGISTERED 0x00000040 /* Feedback state independent of output enable */ +#define OUTPUT_FEEDBACK_NONE 0x00000080 /* Feedback not available */ + +/* + Output Feedback Output + + OE -----------| + | + |-\ + IN ----------| >----|----< OUT > + |-/ | + | + FEEDBACK ------------| + + + + Output Feedback Combinatorial/Registered + + OE ----------------| + | + |-\ + IN ----------|----| >----< OUT > + | |-/ + | + FEEDBACK ----| +*/ + + + +/* Fuse state flag */ +#define LOW_FUSE_BLOWN 0x00000001 +#define HIGH_FUSE_BLOWN 0x00000002 +#define LOWHIGH_FUSE_BLOWN 0x00000004 +#define NO_FUSE_BLOWN 0x00000008 + + + +/* Symbols */ +#define AND_SYMBOL "&" +#define OR_SYMBOL "+" +#define XOR_SYMBOL ":+:" + +#define LOW_SYMBOL "/" + +#define INPUT_SYMBOL "i" +#define OUTPUT_SYMBOL "o" +#define REGISTERED_FEEDBACK_OUTPUT_SYMBOL "rfo" +#define OUTPUT_FEEDBACK_SYMBOL "of" +#define REGISTERED_FEEDBACK_SYMBOL "rf" + +#define COMBINATORIAL_ASSIGNMENT "=" +#define REGISTERED_ASSIGNMENT ":=" + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +typedef int (*command_func_type)(int argc, char *argv[]); + +typedef struct _command_entry command_entry; +struct _command_entry +{ + const char *command; + command_func_type command_func; +}; + + + +/* Pin fuse row configuration */ +typedef struct _pin_fuse_rows pin_fuse_rows; +struct _pin_fuse_rows +{ + uint16_t pin; /* Pin number */ + uint16_t fuserowoutputenable; /* Fuse row for the output enable */ + uint16_t fuserowtermstart; /* Fuse row for the first term */ + uint16_t fuserowtermend; /* Fuse row for the last term */ +}; + + + +/* Pin fuse column configuration */ +typedef struct _pin_fuse_columns pin_fuse_columns; +struct _pin_fuse_columns +{ + uint16_t pin; /* Pin number */ + uint16_t lowfusecolumn; /* Column number for low output */ + uint16_t highfusecolumn; /* Column number for high output */ +}; + + +typedef struct _pal_data pal_data; + +typedef void (*print_product_terms_func)(const pal_data* pal, const jed_data* jed); +typedef void (*config_pins_func)(const pal_data* pal, const jed_data* jed); +typedef int (*is_product_term_enabled_func)(const pal_data* pal, const jed_data* jed, uint16_t fuserow); +typedef uint16_t (*get_pin_fuse_state_func)(const pal_data* pal, const jed_data* jed, uint16_t pin, uint16_t fuserow); + +struct _pal_data +{ + const char *name; + uint32_t numfuses; + const pin_fuse_rows *pinfuserows; + uint16_t pinfuserowscount; + const pin_fuse_columns *pinfusecolumns; + uint16_t pinfusecolumnscount; + print_product_terms_func print_product_terms; + config_pins_func config_pins; + is_product_term_enabled_func is_product_term_enabled; + get_pin_fuse_state_func get_pin_fuse_state; +}; + + + +/* Pin output configuration */ +typedef struct _pin_output_config pin_output_config; +struct _pin_output_config +{ + uint16_t pin; + uint16_t flags; +}; + + + +typedef std::vector pal_data_vector; + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +static void print_pal10l8_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal10h8_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal12l6_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal12h6_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal14l4_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal14h4_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal16l2_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal16h2_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal16c1_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal16l8_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal16r4_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal16r6_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal16r8_product_terms(const pal_data* pal, const jed_data* jed); +static void print_gal16v8_product_terms(const pal_data* pal, const jed_data* jed); +static void print_peel18cv8_product_terms(const pal_data* pal, const jed_data* jed); +static void print_ampal18p8_product_terms(const pal_data* pal, const jed_data* jed); +static void print_gal18v10_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal20l8_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal20l10_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal20r4_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal20r6_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal20r8_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal20ra10_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal20x4_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal20x8_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal20x10_product_terms(const pal_data* pal, const jed_data* jed); +static void print_gal20v8_product_terms(const pal_data* pal, const jed_data* jed); +static void print_palce22v10_pal22v10_product_terms(const pal_data* pal, const jed_data* jed); +static void print_gal22v10_product_terms(const pal_data* pal, const jed_data* jed); +static void print_atf22v10_power_down_mode_product_terms(const pal_data* pal, const jed_data* jed); +static void print_82s153_pls153_product_terms(const pal_data* pal, const jed_data* jed); +static void print_ck2605_product_terms(const pal_data* pal, const jed_data* jed); +#if defined(ricoh_pals) +static void print_epl10p8_product_terms(const pal_data* pal, const jed_data* jed); +static void print_epl12p6_product_terms(const pal_data* pal, const jed_data* jed); +static void print_epl14p4_product_terms(const pal_data* pal, const jed_data* jed); +static void print_epl16p2_product_terms(const pal_data* pal, const jed_data* jed); +static void print_epl16p8_product_terms(const pal_data* pal, const jed_data* jed); +static void print_epl16rp8_product_terms(const pal_data* pal, const jed_data* jed); +static void print_epl16rp6_product_terms(const pal_data* pal, const jed_data* jed); +static void print_epl16rp4_product_terms(const pal_data* pal, const jed_data* jed); +#endif +static void print_pal10p8_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal12p6_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal14p4_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal16p2_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal16p8_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal16rp4_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal16rp6_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal16rp8_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal6l16_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal8l14_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal12h10_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal12l10_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal14h8_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal14l8_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal16h6_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal16l6_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal18h4_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal18l4_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal20c1_product_terms(const pal_data* pal, const jed_data* jed); +static void print_pal20l2_product_terms(const pal_data* pal, const jed_data* jed); +static void print_82s100_pls100_product_terms(const pal_data* pal, const jed_data* jed); + + + +static void config_pal10l8_pins(const pal_data* pal, const jed_data* jed); +static void config_pal10h8_pins(const pal_data* pal, const jed_data* jed); +static void config_pal12l6_pins(const pal_data* pal, const jed_data* jed); +static void config_pal12h6_pins(const pal_data* pal, const jed_data* jed); +static void config_pal14l4_pins(const pal_data* pal, const jed_data* jed); +static void config_pal14h4_pins(const pal_data* pal, const jed_data* jed); +static void config_pal16l2_pins(const pal_data* pal, const jed_data* jed); +static void config_pal16h2_pins(const pal_data* pal, const jed_data* jed); +static void config_pal16c1_pins(const pal_data* pal, const jed_data* jed); +static void config_pal16l8_pins(const pal_data* pal, const jed_data* jed); +static void config_pal16r4_pins(const pal_data* pal, const jed_data* jed); +static void config_pal16r6_pins(const pal_data* pal, const jed_data* jed); +static void config_pal16r8_pins(const pal_data* pal, const jed_data* jed); +static void config_gal16v8_pins(const pal_data* pal, const jed_data* jed); +static void config_peel18cv8_pins(const pal_data* pal, const jed_data* jed); +static void config_ampal18p8_pins(const pal_data* pal, const jed_data* jed); +static void config_gal18v10_pins(const pal_data* pal, const jed_data* jed); +static void config_pal20l8_pins(const pal_data* pal, const jed_data* jed); +static void config_pal20l10_pins(const pal_data* pal, const jed_data* jed); +static void config_pal20r4_pins(const pal_data* pal, const jed_data* jed); +static void config_pal20r6_pins(const pal_data* pal, const jed_data* jed); +static void config_pal20r8_pins(const pal_data* pal, const jed_data* jed); +static void config_pal20ra10_pins(const pal_data* pal, const jed_data* jed); +static void config_pal20x4_pins(const pal_data* pal, const jed_data* jed); +static void config_pal20x8_pins(const pal_data* pal, const jed_data* jed); +static void config_pal20x10_pins(const pal_data* pal, const jed_data* jed); +static void config_gal20v8_pins(const pal_data* pal, const jed_data* jed); +static void config_palce22v10_pal22v10_pins(const pal_data* pal, const jed_data* jed); +static void config_gal22v10_pins(const pal_data* pal, const jed_data* jed); +static void config_atf22v10_power_down_mode_pins(const pal_data* pal, const jed_data* jed); +static void config_82s153_pls153_pins(const pal_data* pal, const jed_data* jed); +static void config_ck2605_pins(const pal_data* pal, const jed_data* jed); +#if defined(ricoh_pals) +static void config_epl10p8_pins(const pal_data* pal, const jed_data* jed); +static void config_epl12p6_pins(const pal_data* pal, const jed_data* jed); +static void config_epl14p4_pins(const pal_data* pal, const jed_data* jed); +static void config_epl16p2_pins(const pal_data* pal, const jed_data* jed); +static void config_epl16p8_pins(const pal_data* pal, const jed_data* jed); +static void config_epl16rp8_pins(const pal_data* pal, const jed_data* jed); +static void config_epl16rp6_pins(const pal_data* pal, const jed_data* jed); +static void config_epl16rp4_pins(const pal_data* pal, const jed_data* jed); +#endif +static void config_pal10p8_pins(const pal_data* pal, const jed_data* jed); +static void config_pal12p6_pins(const pal_data* pal, const jed_data* jed); +static void config_pal14p4_pins(const pal_data* pal, const jed_data* jed); +static void config_pal16p2_pins(const pal_data* pal, const jed_data* jed); +static void config_pal16p8_pins(const pal_data* pal, const jed_data* jed); +static void config_pal16rp4_pins(const pal_data* pal, const jed_data* jed); +static void config_pal16rp6_pins(const pal_data* pal, const jed_data* jed); +static void config_pal16rp8_pins(const pal_data* pal, const jed_data* jed); +static void config_pal6l16_pins(const pal_data* pal, const jed_data* jed); +static void config_pal8l14_pins(const pal_data* pal, const jed_data* jed); +static void config_pal12h10_pins(const pal_data* pal, const jed_data* jed); +static void config_pal12l10_pins(const pal_data* pal, const jed_data* jed); +static void config_pal14h8_pins(const pal_data* pal, const jed_data* jed); +static void config_pal14l8_pins(const pal_data* pal, const jed_data* jed); +static void config_pal16h6_pins(const pal_data* pal, const jed_data* jed); +static void config_pal16l6_pins(const pal_data* pal, const jed_data* jed); +static void config_pal18h4_pins(const pal_data* pal, const jed_data* jed); +static void config_pal18l4_pins(const pal_data* pal, const jed_data* jed); +static void config_pal20c1_pins(const pal_data* pal, const jed_data* jed); +static void config_pal20l2_pins(const pal_data* pal, const jed_data* jed); +static void config_82s100_pls100_pins(const pal_data* pal, const jed_data* jed); + + + +static int is_gal16v8_product_term_enabled(const pal_data* pal, const jed_data* jed, uint16_t fuserow); + + + +static int is_gal20v8_product_term_enabled(const pal_data* pal, const jed_data* jed, uint16_t fuserow); + + + +static uint16_t get_peel18cv8_pin_fuse_state(const pal_data* pal, const jed_data* jed, uint16_t pin, uint16_t fuserow); + + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +static uint8_t *dstbuf; +static size_t dstbuflen; + +static uint16_t inputpins[26]; +static uint16_t inputpinscount; + +static pin_output_config outputpins[26]; +static uint16_t outputpinscount; + +static pin_fuse_rows pal10l8pinfuserows[] = { + {12, NO_OUTPUT_ENABLE_FUSE_ROW, 280, 300}, + {13, NO_OUTPUT_ENABLE_FUSE_ROW, 240, 260}, + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 200, 220}, + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 160, 180}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 120, 140}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 80, 100}, + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 40, 60}, + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 20}}; + +static pin_fuse_rows pal10h8pinfuserows[] = { + {12, NO_OUTPUT_ENABLE_FUSE_ROW, 280, 300}, + {13, NO_OUTPUT_ENABLE_FUSE_ROW, 240, 260}, + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 200, 220}, + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 160, 180}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 120, 140}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 80, 100}, + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 40, 60}, + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 20}}; + +static pin_fuse_rows pal12l6pinfuserows[] = { + {13, NO_OUTPUT_ENABLE_FUSE_ROW, 288, 360}, + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 240, 264}, + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 192, 216}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 144, 168}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 96, 120}, + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 72}}; + +static pin_fuse_rows pal12h6pinfuserows[] = { + {13, NO_OUTPUT_ENABLE_FUSE_ROW, 288, 360}, + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 240, 264}, + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 192, 216}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 144, 168}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 96, 120}, + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 72}}; + +static pin_fuse_rows pal14l4pinfuserows[] = { + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 336, 420}, + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 224, 308}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 112, 196}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 84}}; + +static pin_fuse_rows pal14h4pinfuserows[] = { + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 336, 420}, + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 224, 308}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 112, 196}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 84}}; + +static pin_fuse_rows pal16l2pinfuserows[] = { + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 256, 480}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 224}}; + +static pin_fuse_rows pal16h2pinfuserows[] = { + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 256, 480}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 224}}; + +static pin_fuse_rows pal16c1pinfuserows[] = { + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 480}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 480}}; + +static pin_fuse_rows pal16l8pinfuserows[] = { + {12, 1792, 1824, 2016}, + {13, 1536, 1568, 1760}, + {14, 1280, 1312, 1504}, + {15, 1024, 1056, 1248}, + {16, 768, 800, 992}, + {17, 512, 544, 736}, + {18, 256, 288, 480}, + {19, 0, 32, 224}}; + +static pin_fuse_rows pal16r4pinfuserows[] = { + {12, 1792, 1824, 2016}, + {13, 1536, 1568, 1760}, + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 1280, 1504}, /* Registered Output */ + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 1024, 1248}, /* Registered Output */ + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 768, 992}, /* Registered Output */ + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 512, 736}, /* Registered Output */ + {18, 256, 288, 480}, + {19, 0, 32, 224}}; + +static pin_fuse_rows pal16r6pinfuserows[] = { + {12, 1792, 1824, 2016}, + {13, NO_OUTPUT_ENABLE_FUSE_ROW, 1536, 1760}, /* Registered Output */ + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 1280, 1504}, /* Registered Output */ + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 1024, 1248}, /* Registered Output */ + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 768, 992}, /* Registered Output */ + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 512, 736}, /* Registered Output */ + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 256, 480}, /* Registered Output */ + {19, 0, 32, 224}}; + +static pin_fuse_rows pal16r8pinfuserows[] = { + {12, NO_OUTPUT_ENABLE_FUSE_ROW, 1792, 2016}, /* Registered Output */ + {13, NO_OUTPUT_ENABLE_FUSE_ROW, 1536, 1760}, /* Registered Output */ + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 1280, 1504}, /* Registered Output */ + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 1024, 1248}, /* Registered Output */ + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 768, 992}, /* Registered Output */ + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 512, 736}, /* Registered Output */ + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 256, 480}, /* Registered Output */ + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 224}}; /* Registered Output */ + +static pin_fuse_rows gal16v8pinfuserows[] = { + {12, 0, 0, 0}, + {13, 0, 0, 0}, + {14, 0, 0, 0}, + {15, 0, 0, 0}, + {16, 0, 0, 0}, + {17, 0, 0, 0}, + {18, 0, 0, 0}, + {19, 0, 0, 0}}; + +static pin_fuse_rows peel18cv8pinfuserows[] = { + {12, 2556, 2016, 2268}, + {13, 2520, 1728, 1980}, + {14, 2484, 1440, 1692}, + {15, 2448, 1152, 1404}, + {16, 2412, 864, 1116}, + {17, 2376, 576, 828}, + {18, 2340, 288, 540}, + {19, 2304, 0, 252}}; + +static pin_fuse_rows ampal18p8pinfuserows[] = { + {12, 2268, 2304, 2556}, + {13, 1944, 1980, 2232}, + {14, 1620, 1656, 1908}, + {15, 1296, 1332, 1584}, + {16, 972, 1008, 1260}, + {17, 648, 684, 936}, + {18, 324, 360, 612}, + {19, 0, 36, 288}}; + +static pin_fuse_rows gal18v10pinfuserows[] = { + {9, 3096, 3132, 3384}, + {11, 2772, 2808, 3060}, + {12, 2448, 2484, 2736}, + {13, 2124, 2160, 2412}, + {14, 1728, 1764, 2088}, + {15, 1332, 1368, 1692}, + {16, 1008, 1044, 1296}, + {17, 684, 720, 972}, + {18, 360, 396, 648}, + {19, 36, 72, 324}}; + +static pin_fuse_rows pal20l8pinfuserows[] = { + {15, 2240, 2280, 2520}, + {16, 1920, 1960, 2200}, + {17, 1600, 1640, 1880}, + {18, 1280, 1320, 1560}, + {19, 960, 1000, 1240}, + {20, 640, 680, 920}, + {21, 320, 360, 600}, + {22, 0, 40, 280}}; + +static pin_fuse_rows pal20l10pinfuserows[] = { + {14, 1440, 1480, 1560}, + {15, 1280, 1320, 1400}, + {16, 1120, 1160, 1240}, + {17, 960, 1000, 1080}, + {18, 800, 840, 920}, + {19, 640, 680, 760}, + {20, 480, 520, 600}, + {21, 320, 360, 440}, + {22, 160, 200, 280}, + {23, 0, 40, 120}}; + +static pin_fuse_rows pal20r4pinfuserows[] = { + {15, 2240, 2280, 2520}, + {16, 1920, 1960, 2200}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 1600, 1880}, /* Registered Output */ + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 1280, 1560}, /* Registered Output */ + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 960, 1240}, /* Registered Output */ + {20, NO_OUTPUT_ENABLE_FUSE_ROW, 640, 920}, /* Registered Output */ + {21, 320, 360, 600}, + {22, 0, 40, 280}}; + +static pin_fuse_rows pal20r6pinfuserows[] = { + {15, 2240, 2280, 2520}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 1920, 2200}, /* Registered Output */ + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 1600, 1880}, /* Registered Output */ + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 1280, 1560}, /* Registered Output */ + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 960, 1240}, /* Registered Output */ + {20, NO_OUTPUT_ENABLE_FUSE_ROW, 640, 920}, /* Registered Output */ + {21, NO_OUTPUT_ENABLE_FUSE_ROW, 320, 600}, /* Registered Output */ + {22, 0, 40, 280}}; + +static pin_fuse_rows pal20r8pinfuserows[] = { + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 2240, 2520}, /* Registered Output */ + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 1920, 2200}, /* Registered Output */ + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 1600, 1880}, /* Registered Output */ + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 1280, 1560}, /* Registered Output */ + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 960, 1240}, /* Registered Output */ + {20, NO_OUTPUT_ENABLE_FUSE_ROW, 640, 920}, /* Registered Output */ + {21, NO_OUTPUT_ENABLE_FUSE_ROW, 320, 600}, /* Registered Output */ + {22, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 280}}; /* Registered Output */ + +static pin_fuse_rows pal20ra10pinfuserows[] = { + { 14, 0, 0, 0 }, /* Registered Output */ + { 15, 0, 0, 0 }, /* Registered Output */ + { 16, 0, 0, 0 }, /* Registered Output */ + { 17, 0, 0, 0 }, /* Registered Output */ + { 18, 0, 0, 0 }, /* Registered Output */ + { 19, 0, 0, 0 }, /* Registered Output */ + { 20, 0, 0, 0 }, /* Registered Output */ + { 21, 0, 0, 0 }, /* Registered Output */ + { 22, 0, 0, 0 }, /* Registered Output */ + { 23, 0, 0, 0 }}; /* Registered Output */ + +static pin_fuse_rows pal20x4pinfuserows[] = { + {14, 1440, 1480, 1560}, + {15, 1280, 1320, 1400}, + {16, 1120, 1160, 1240}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 960, 1080}, /* Registered Output */ + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 800, 920}, /* Registered Output */ + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 640, 760}, /* Registered Output */ + {20, NO_OUTPUT_ENABLE_FUSE_ROW, 480, 600}, /* Registered Output */ + {21, 320, 360, 440}, + {22, 160, 200, 280}, + {23, 0, 40, 120}}; + +static pin_fuse_rows pal20x8pinfuserows[] = { + {14, 1440, 1480, 1560}, + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 1280, 1400}, /* Registered Output */ + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 1120, 1240}, /* Registered Output */ + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 960, 1080}, /* Registered Output */ + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 800, 920}, /* Registered Output */ + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 640, 760}, /* Registered Output */ + {20, NO_OUTPUT_ENABLE_FUSE_ROW, 480, 600}, /* Registered Output */ + {21, NO_OUTPUT_ENABLE_FUSE_ROW, 320, 440}, /* Registered Output */ + {22, NO_OUTPUT_ENABLE_FUSE_ROW, 160, 280}, /* Registered Output */ + {23, 0, 40, 120}}; + +static pin_fuse_rows pal20x10pinfuserows[] = { + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 1440, 1560}, /* Registered Output */ + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 1280, 1400}, /* Registered Output */ + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 1120, 1240}, /* Registered Output */ + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 960, 1080}, /* Registered Output */ + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 800, 920}, /* Registered Output */ + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 640, 760}, /* Registered Output */ + {20, NO_OUTPUT_ENABLE_FUSE_ROW, 480, 600}, /* Registered Output */ + {21, NO_OUTPUT_ENABLE_FUSE_ROW, 320, 440}, /* Registered Output */ + {22, NO_OUTPUT_ENABLE_FUSE_ROW, 160, 280}, /* Registered Output */ + {23, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 120}}; /* Registered Output */ + +static pin_fuse_rows gal20v8pinfuserows[] = { + {15, 0, 0, 0}, + {16, 0, 0, 0}, + {17, 0, 0, 0}, + {18, 0, 0, 0}, + {19, 0, 0, 0}, + {20, 0, 0, 0}, + {21, 0, 0, 0}, + {22, 0, 0, 0}}; + +static pin_fuse_rows palce22v10_pal22v10pinfuserows[] = { + {14, 5368, 5412, 5720}, + {15, 4884, 4928, 5324}, + {16, 4312, 4356, 4840}, + {17, 3652, 3696, 4268}, + {18, 2904, 2948, 3608}, + {19, 2156, 2200, 2860}, + {20, 1496, 1540, 2112}, + {21, 924, 968, 1452}, + {22, 440, 484, 880}, + {23, 44, 88, 396}}; + +static pin_fuse_rows gal22v10pinfuserows[] = { + {14, 5368, 5412, 5720}, + {15, 4884, 4928, 5324}, + {16, 4312, 4356, 4840}, + {17, 3652, 3696, 4268}, + {18, 2904, 2948, 3608}, + {19, 2156, 2200, 2860}, + {20, 1496, 1540, 2112}, + {21, 924, 968, 1452}, + {22, 440, 484, 880}, + {23, 44, 88, 396}}; + +static pin_fuse_rows atf22v10powerdownmodepinfuserows[] = { + {14, 5368, 5412, 5720}, + {15, 4884, 4928, 5324}, + {16, 4312, 4356, 4840}, + {17, 3652, 3696, 4268}, + {18, 2904, 2948, 3608}, + {19, 2156, 2200, 2860}, + {20, 1496, 1540, 2112}, + {21, 924, 968, 1452}, + {22, 440, 484, 880}, + {23, 44, 88, 396}}; + +static pin_fuse_rows _82s153_pls153pinfuserows[] = { + {9, 1472, 0, 0}, + {11, 1508, 0, 0}, + {12, 1544, 0, 0}, + {13, 1580, 0, 0}, + {14, 1616, 0, 0}, + {15, 1652, 0, 0}, + {16, 1688, 0, 0}, + {17, 1724, 0, 0}, + {18, 1760, 0, 0}, + {19, 1796, 0, 0}}; + +static pin_fuse_rows ck2605pinfuserows[] = { + {9, 736, 0, 0}, + {11, 772, 0, 0}, + {12, 808, 0, 0}, + {13, 844, 0, 0}, + {14, 880, 0, 0}, + {15, 916, 0, 0}, + {16, 952, 0, 0}, + {17, 988, 0, 0}, + {18, 1024, 0, 0}, + {19, 1060, 0, 0}}; + +#if defined(ricoh_pals) +static pin_fuse_rows epl10p8pinfuserows[] = { + {12, NO_OUTPUT_ENABLE_FUSE_ROW, 560, 620}, + {13, NO_OUTPUT_ENABLE_FUSE_ROW, 480, 540}, + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 400, 460}, + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 320, 380}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 240, 300}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 160, 220}, + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 80, 140}, + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 60}}; + +static pin_fuse_rows epl12p6pinfuserows[] = { + {13, NO_OUTPUT_ENABLE_FUSE_ROW, 576, 744}, + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 480, 552}, + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 384, 456}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 288, 360}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 192, 264}, + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 168}}; + +static pin_fuse_rows epl14p4pinfuserows[] = { + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 672, 868}, + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 448, 644}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 224, 420}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 196}}; + +static pin_fuse_rows epl16p2pinfuserows[] = { + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 512, 992}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 480}}; + +static pin_fuse_rows epl16p8pinfuserows[] = { + {12, 1792, 1824, 2016}, + {13, 1536, 1568, 1760}, + {14, 1280, 1312, 1504}, + {15, 1024, 1056, 1248}, + {16, 768, 800, 992}, + {17, 512, 544, 736}, + {18, 256, 288, 480}, + {19, 0, 32, 224}}; + +static pin_fuse_rows epl16rp8pinfuserows[] = { + {12, NO_OUTPUT_ENABLE_FUSE_ROW, 1792, 2016}, /* Registered Output */ + {13, NO_OUTPUT_ENABLE_FUSE_ROW, 1536, 1760}, /* Registered Output */ + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 1280, 1504}, /* Registered Output */ + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 1024, 1248}, /* Registered Output */ + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 768, 992}, /* Registered Output */ + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 512, 736}, /* Registered Output */ + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 256, 480}, /* Registered Output */ + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 224}}; /* Registered Output */ + +static pin_fuse_rows epl16rp6pinfuserows[] = { + {12, 1792, 1824, 2016}, + {13, NO_OUTPUT_ENABLE_FUSE_ROW, 1536, 1760}, /* Registered Output */ + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 1280, 1504}, /* Registered Output */ + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 1024, 1248}, /* Registered Output */ + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 768, 992}, /* Registered Output */ + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 512, 736}, /* Registered Output */ + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 256, 480}, /* Registered Output */ + {19, 0, 32, 224}}; + +static pin_fuse_rows epl16rp4pinfuserows[] = { + {12, 1792, 1824, 2016}, + {13, 1536, 1568, 1760}, + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 1280, 1504}, /* Registered Output */ + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 1024, 1248}, /* Registered Output */ + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 768, 992}, /* Registered Output */ + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 512, 736}, /* Registered Output */ + {18, 256, 288, 480}, + {19, 0, 32, 224}}; +#endif + +static pin_fuse_rows pal10p8pinfuserows[] = { + {12, NO_OUTPUT_ENABLE_FUSE_ROW, 280, 300}, + {13, NO_OUTPUT_ENABLE_FUSE_ROW, 240, 260}, + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 200, 220}, + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 160, 180}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 120, 140}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 80, 100}, + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 40, 60}, + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 20}}; + +static pin_fuse_rows pal12p6pinfuserows[] = { + {13, NO_OUTPUT_ENABLE_FUSE_ROW, 288, 360}, + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 240, 264}, + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 192, 216}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 144, 168}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 96, 120}, + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 72}}; + +static pin_fuse_rows pal14p4pinfuserows[] = { + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 336, 420}, + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 224, 308}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 112, 196}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 84}}; + +static pin_fuse_rows pal16p2pinfuserows[] = { + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 256, 480}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 224}}; + +static pin_fuse_rows pal16p8pinfuserows[] = { + {12, 1792, 1824, 2016}, + {13, 1536, 1568, 1760}, + {14, 1280, 1312, 1504}, + {15, 1024, 1056, 1248}, + {16, 768, 800, 992}, + {17, 512, 544, 736}, + {18, 256, 288, 480}, + {19, 0, 32, 224}}; + +static pin_fuse_rows pal16rp4pinfuserows[] = { + {12, 1792, 1824, 2016}, + {13, 1536, 1568, 1760}, + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 1280, 1504}, /* Registered Output */ + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 1024, 1248}, /* Registered Output */ + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 768, 992}, /* Registered Output */ + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 512, 736}, /* Registered Output */ + {18, 256, 288, 480}, + {19, 0, 32, 224}}; + +static pin_fuse_rows pal16rp6pinfuserows[] = { + {12, 1792, 1824, 2016}, + {13, NO_OUTPUT_ENABLE_FUSE_ROW, 1536, 1760}, /* Registered Output */ + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 1280, 1504}, /* Registered Output */ + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 1024, 1248}, /* Registered Output */ + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 768, 992}, /* Registered Output */ + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 512, 736}, /* Registered Output */ + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 256, 480}, /* Registered Output */ + {19, 0, 32, 224}}; + +static pin_fuse_rows pal16rp8pinfuserows[] = { + {12, NO_OUTPUT_ENABLE_FUSE_ROW, 1792, 2016}, /* Registered Output */ + {13, NO_OUTPUT_ENABLE_FUSE_ROW, 1536, 1760}, /* Registered Output */ + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 1280, 1504}, /* Registered Output */ + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 1024, 1248}, /* Registered Output */ + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 768, 992}, /* Registered Output */ + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 512, 736}, /* Registered Output */ + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 256, 480}, /* Registered Output */ + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 224}}; /* Registered Output */ + +static pin_fuse_rows pal6l16pinfuserows[] = { + {1, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 0}, + {2, NO_OUTPUT_ENABLE_FUSE_ROW, 24, 24}, + {3, NO_OUTPUT_ENABLE_FUSE_ROW, 36, 36}, + {10, NO_OUTPUT_ENABLE_FUSE_ROW, 132, 132}, + {11, NO_OUTPUT_ENABLE_FUSE_ROW, 168, 168}, + {13, NO_OUTPUT_ENABLE_FUSE_ROW, 180, 180}, + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 156, 156}, + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 144, 144}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 120, 120}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 108, 108}, + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 96, 96}, + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 84, 84}, + {20, NO_OUTPUT_ENABLE_FUSE_ROW, 72, 72}, + {21, NO_OUTPUT_ENABLE_FUSE_ROW, 60, 60}, + {22, NO_OUTPUT_ENABLE_FUSE_ROW, 48, 48}, + {23, NO_OUTPUT_ENABLE_FUSE_ROW, 12, 12}}; + +static pin_fuse_rows pal8l14pinfuserows[] = { + {1, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 0}, + {2, NO_OUTPUT_ENABLE_FUSE_ROW, 32, 32}, + {11, NO_OUTPUT_ENABLE_FUSE_ROW, 192, 192}, + {13, NO_OUTPUT_ENABLE_FUSE_ROW, 208, 208}, + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 176, 176}, + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 160, 160}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 144, 144}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 128, 128}, + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 112, 112}, + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 96, 96}, + {20, NO_OUTPUT_ENABLE_FUSE_ROW, 80, 80}, + {21, NO_OUTPUT_ENABLE_FUSE_ROW, 64, 64}, + {22, NO_OUTPUT_ENABLE_FUSE_ROW, 48, 48}, + {23, NO_OUTPUT_ENABLE_FUSE_ROW, 16, 16}}; + +static pin_fuse_rows pal12h10pinfuserows[] = { + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 432, 456}, + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 384, 408}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 336, 360}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 288, 312}, + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 240, 264}, + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 192, 216}, + {20, NO_OUTPUT_ENABLE_FUSE_ROW, 144, 168}, + {21, NO_OUTPUT_ENABLE_FUSE_ROW, 96, 120}, + {22, NO_OUTPUT_ENABLE_FUSE_ROW, 48, 72}, + {23, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 24}}; + +static pin_fuse_rows pal12l10pinfuserows[] = { + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 432, 456}, + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 384, 408}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 336, 360}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 288, 312}, + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 240, 264}, + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 192, 216}, + {20, NO_OUTPUT_ENABLE_FUSE_ROW, 144, 168}, + {21, NO_OUTPUT_ENABLE_FUSE_ROW, 96, 120}, + {22, NO_OUTPUT_ENABLE_FUSE_ROW, 48, 72}, + {23, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 24}}; + +static pin_fuse_rows pal14h8pinfuserows[] = { + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 448, 532}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 392, 420}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 336, 364}, + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 280, 308}, + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 224, 252}, + {20, NO_OUTPUT_ENABLE_FUSE_ROW, 168, 196}, + {21, NO_OUTPUT_ENABLE_FUSE_ROW, 112, 140}, + {22, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 84}}; + +static pin_fuse_rows pal14l8pinfuserows[] = { + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 448, 532}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 392, 420}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 336, 364}, + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 280, 308}, + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 224, 252}, + {20, NO_OUTPUT_ENABLE_FUSE_ROW, 168, 196}, + {21, NO_OUTPUT_ENABLE_FUSE_ROW, 112, 140}, + {22, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 84}}; + +static pin_fuse_rows pal16h6pinfuserows[] = { + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 512, 608}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 384, 480}, + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 320, 352}, + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 256, 288}, + {20, NO_OUTPUT_ENABLE_FUSE_ROW, 128, 224}, + {21, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 96}}; + +static pin_fuse_rows pal16l6pinfuserows[] = { + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 512, 608}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 384, 480}, + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 320, 352}, + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 256, 288}, + {20, NO_OUTPUT_ENABLE_FUSE_ROW, 128, 224}, + {21, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 96}}; + +static pin_fuse_rows pal18h4pinfuserows[] = { + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 504, 684}, + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 360, 468}, + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 216, 324}, + {20, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 180}}; + +static pin_fuse_rows pal18l4pinfuserows[] = { + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 504, 684}, + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 360, 468}, + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 216, 324}, + {20, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 180}}; + +static pin_fuse_rows pal20c1pinfuserows[] = { + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 280}, + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 320, 600}}; + +static pin_fuse_rows pal20l2pinfuserows[] = { + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 320, 600}, + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 280}}; + +static pin_fuse_rows _82s100_pls100_pinfuserows[] = { + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 0}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 0}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 0}, + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 0}, + {13, NO_OUTPUT_ENABLE_FUSE_ROW, 0}, + {12, NO_OUTPUT_ENABLE_FUSE_ROW, 0}, + {11, NO_OUTPUT_ENABLE_FUSE_ROW, 0}, + {10, NO_OUTPUT_ENABLE_FUSE_ROW, 0}}; + +static pin_fuse_columns pal10l8pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 7, 6}, + {5, 9, 8}, + {6, 11, 10}, + {7, 13, 12}, + {8, 15, 14}, + {9, 17, 16}, + {11, 19, 18}}; + +static pin_fuse_columns pal10h8pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 7, 6}, + {5, 9, 8}, + {6, 11, 10}, + {7, 13, 12}, + {8, 15, 14}, + {9, 17, 16}, + {11, 19, 18}}; + +static pin_fuse_columns pal12l6pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 11, 10}, + {6, 13, 12}, + {7, 15, 14}, + {8, 17, 16}, + {9, 21, 20}, + {11, 23, 22}, + {12, 19, 18}, + {19, 7, 6}}; + +static pin_fuse_columns pal12h6pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 11, 10}, + {6, 13, 12}, + {7, 15, 14}, + {8, 17, 16}, + {9, 21, 20}, + {11, 23, 22}, + {12, 19, 18}, + {19, 7, 6}}; + +static pin_fuse_columns pal14l4pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 15, 14}, + {7, 17, 16}, + {8, 21, 20}, + {9, 25, 24}, + {11, 27, 26}, + {12, 23, 22}, + {13, 19, 18}, + {18, 11, 10}, + {19, 7, 6}}; + +static pin_fuse_columns pal14h4pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 15, 14}, + {7, 17, 16}, + {8, 21, 20}, + {9, 25, 24}, + {11, 27, 26}, + {12, 23, 22}, + {13, 19, 18}, + {18, 11, 10}, + {19, 7, 6}}; + +static pin_fuse_columns pal16l2pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {11, 31, 30}, + {12, 27, 26}, + {13, 23, 22}, + {14, 19, 18}, + {17, 15, 14}, + {18, 11, 10}, + {19, 7, 6}}; + +static pin_fuse_columns pal16h2pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {11, 31, 30}, + {12, 27, 26}, + {13, 23, 22}, + {14, 19, 18}, + {17, 15, 14}, + {18, 11, 10}, + {19, 7, 6}}; + +static pin_fuse_columns pal16c1pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {11, 31, 30}, + {12, 27, 26}, + {13, 23, 22}, + {14, 19, 18}, + {17, 15, 14}, + {18, 11, 10}, + {19, 7, 6}}; + +static pin_fuse_columns pal16l8pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {11, 31, 30}, + {13, 27, 26}, + {14, 23, 22}, + {15, 19, 18}, + {16, 15, 14}, + {17, 11, 10}, + {18, 7, 6}}; + +static pin_fuse_columns pal16r4pinfusecolumns[] = { + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {12, 31, 30}, + {13, 27, 26}, + {14, 23, 22}, /* Registered Output */ + {15, 19, 18}, /* Registered Output */ + {16, 15, 14}, /* Registered Output */ + {17, 11, 10}, /* Registered Output */ + {18, 7, 6}, + {19, 3, 2}}; + +static pin_fuse_columns pal16r6pinfusecolumns[] = { + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {12, 31, 30}, + {13, 27, 26}, /* Registered Output */ + {14, 23, 22}, /* Registered Output */ + {15, 19, 18}, /* Registered Output */ + {16, 15, 14}, /* Registered Output */ + {17, 11, 10}, /* Registered Output */ + {18, 7, 6}, /* Registered Output */ + {19, 3, 2}}; + +static pin_fuse_columns pal16r8pinfusecolumns[] = { + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {12, 31, 30}, /* Registered Output */ + {13, 27, 26}, /* Registered Output */ + {14, 23, 22}, /* Registered Output */ + {15, 19, 18}, /* Registered Output */ + {16, 15, 14}, /* Registered Output */ + {17, 11, 10}, /* Registered Output */ + {18, 7, 6}, /* Registered Output */ + {19, 3, 2}}; /* Registered Output */ + +static pin_fuse_columns gal16v8pinfusecolumns[] = { + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}}; + +static pin_fuse_columns peel18cv8pinfusecolumns[] = { + {1, 1, 0}, + {2, 5, 4}, + {3, 9, 8}, + {4, 13, 12}, + {5, 17, 16}, + {6, 21, 20}, + {7, 25, 24}, + {8, 29, 28}, + {9, 33, 32}, + {11, 3, 2}, + {12, 35, 34}, + {13, 31, 30}, + {14, 27, 26}, + {15, 23, 22}, + {16, 19, 18}, + {17, 15, 14}, + {18, 11, 10}, + {19, 7, 6}}; + +static pin_fuse_columns ampal18p8pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {11, 31, 30}, + {12, 35, 34}, + {13, 27, 26}, + {14, 23, 22}, + {15, 19, 18}, + {16, 15, 14}, + {17, 11, 10}, + {18, 7, 6}, + {19, 33, 32}}; + +static pin_fuse_columns gal18v10pinfusecolumns[] = { + {1, 1, 0}, + {2, 5, 4}, + {3, 9, 8}, + {4, 13, 12}, + {5, 17, 16}, + {6, 21, 20}, + {7, 25, 24}, + {8, 29, 28}, + {9, 35, 34}, + {11, 33, 32}, + {12, 31, 30}, + {13, 27, 26}, + {14, 23, 22}, + {15, 19, 18}, + {16, 15, 14}, + {17, 11, 10}, + {18, 7, 6}, + {19, 3, 2}}; + +static pin_fuse_columns pal20l8pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {10, 33, 32}, + {11, 37, 36}, + {13, 39, 38}, + {14, 35, 34}, + {16, 31, 30}, + {17, 27, 26}, + {18, 23, 22}, + {19, 19, 18}, + {20, 15, 14}, + {21, 11, 10}, + {23, 7, 6}}; + +static pin_fuse_columns pal20l10pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {10, 33, 32}, + {11, 37, 36}, + {13, 39, 38}, + {15, 35, 34}, + {16, 31, 30}, + {17, 27, 26}, + {18, 23, 22}, + {19, 19, 18}, + {20, 15, 14}, + {21, 11, 10}, + {22, 7, 6}}; + +static pin_fuse_columns pal20r4pinfusecolumns[] = { + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {10, 33, 32}, + {11, 37, 36}, + {14, 39, 38}, + {15, 35, 34}, + {16, 31, 30}, + {17, 27, 26}, + {18, 23, 22}, + {19, 19, 18}, + {20, 15, 14}, + {21, 11, 10}, + {22, 7, 6}, + {23, 3, 2}}; + +static pin_fuse_columns pal20r6pinfusecolumns[] = { + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {10, 33, 32}, + {11, 37, 36}, + {14, 39, 38}, + {15, 35, 34}, + {16, 31, 30}, + {17, 27, 26}, + {18, 23, 22}, + {19, 19, 18}, + {20, 15, 14}, + {21, 11, 10}, + {22, 7, 6}, + {23, 3, 2}}; + +static pin_fuse_columns pal20r8pinfusecolumns[] = { + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {10, 33, 32}, + {11, 37, 36}, + {14, 39, 38}, + {15, 35, 34}, + {16, 31, 30}, + {17, 27, 26}, + {18, 23, 22}, + {19, 19, 18}, + {20, 15, 14}, + {21, 11, 10}, + {22, 7, 6}, + {23, 3, 2}}; + +static pin_fuse_columns pal20ra10pinfusecolumns[] = { + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {10, 33, 32}, + {11, 37, 36}, + {14, 39, 38}, + {15, 35, 34}, + {16, 31, 30}, + {17, 27, 26}, + {18, 23, 22}, + {19, 19, 18}, + {20, 15, 14}, + {21, 11, 10}, + {22, 7, 6}, + {23, 3, 2}}; + +static pin_fuse_columns pal20x4pinfusecolumns[] = { + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {10, 33, 32}, + {11, 37, 36}, + {14, 39, 38}, + {15, 35, 34}, + {16, 31, 30}, + {17, 27, 26}, + {18, 23, 22}, + {19, 19, 18}, + {20, 15, 14}, + {21, 11, 10}, + {22, 7, 6}, + {23, 3, 2}}; + +static pin_fuse_columns pal20x8pinfusecolumns[] = { + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {10, 33, 32}, + {11, 37, 36}, + {14, 39, 38}, + {15, 35, 34}, + {16, 31, 30}, + {17, 27, 26}, + {18, 23, 22}, + {19, 19, 18}, + {20, 15, 14}, + {21, 11, 10}, + {22, 7, 6}, + {23, 3, 2}}; + +static pin_fuse_columns pal20x10pinfusecolumns[] = { + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {10, 33, 32}, + {11, 37, 36}, + {14, 39, 38}, + {15, 35, 34}, + {16, 31, 30}, + {17, 27, 26}, + {18, 23, 22}, + {19, 19, 18}, + {20, 15, 14}, + {21, 11, 10}, + {22, 7, 6}, + {23, 3, 2} +}; + +static pin_fuse_columns gal20v8pinfusecolumns[] = { + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}, + {0, 0, 0}}; + +static pin_fuse_columns palce22v10_pal22v10pinfusecolumns[] = { + {1, 1, 0}, + {2, 5, 4}, + {3, 9, 8}, + {4, 13, 12}, + {5, 17, 16}, + {6, 21, 20}, + {7, 25, 24}, + {8, 29, 28}, + {9, 33, 32}, + {10, 37, 36}, + {11, 41, 40}, + {13, 43, 42}, + {14, 39, 38}, + {15, 35, 34}, + {16, 31, 30}, + {17, 27, 26}, + {18, 23, 22}, + {19, 19, 18}, + {20, 15, 14}, + {21, 11, 10}, + {22, 7, 6}, + {23, 3, 2}}; + +static pin_fuse_columns gal22v10pinfusecolumns[] = { + {1, 1, 0}, + {2, 5, 4}, + {3, 9, 8}, + {4, 13, 12}, + {5, 17, 16}, + {6, 21, 20}, + {7, 25, 24}, + {8, 29, 28}, + {9 , 33, 32}, + {10, 37, 36}, + {11, 41, 40}, + {13, 43, 42}, + {14, 39, 38}, + {15, 35, 34}, + {16, 31, 30}, + {17, 27, 26}, + {18, 23, 22}, + {19, 19, 18}, + {20, 15, 14}, + {21, 11, 10}, + {22, 7, 6}, + {23, 3, 2}}; + +static pin_fuse_columns atf22v10powerdownmodepinfusecolumns[] = { + {1, 1, 0}, + {2, 5, 4}, + {3, 9, 8}, + {4, 13, 12}, + {5, 17, 16}, + {6, 21, 20}, + {7, 25, 24}, + {8, 29, 28}, + {9 , 33, 32}, + {10, 37, 36}, + {11, 41, 40}, + {13, 43, 42}, + {14, 39, 38}, + {15, 35, 34}, + {16, 31, 30}, + {17, 27, 26}, + {18, 23, 22}, + {19, 19, 18}, + {20, 15, 14}, + {21, 11, 10}, + {22, 7, 6}, + {23, 3, 2}}; + +static pin_fuse_columns _82s153_pls153pinfusecolumns[] = { + {1, 1, 0}, + {2, 3, 2}, + {3, 5, 4}, + {4, 7, 6}, + {5, 9, 8}, + {6, 11, 10}, + {7, 13, 12}, + {8, 15, 14}, + {9, 17, 16}, + {11, 19, 18}, + {12, 21, 20}, + {13, 23, 22}, + {14, 25, 24}, + {15, 27, 26}, + {16, 29, 28}, + {17, 31, 30}, + {18, 33, 32}, + {19, 35, 34}}; + +static pin_fuse_columns ck2605pinfusecolumns[] = { + {1, 1, 0}, + {2, 3, 2}, + {3, 5, 4}, + {4, 7, 6}, + {5, 9, 8}, + {6, 11, 10}, + {7, 13, 12}, + {8, 15, 14}, + {9, 17, 16}, + {11, 19, 18}, + {12, 21, 20}, + {13, 23, 22}, + {14, 25, 24}, + {15, 27, 26}, + {16, 29, 28}, + {17, 31, 30}, + {18, 33, 32}, + {19, 35, 34}}; + +#if defined(ricoh_pals) +static pin_fuse_columns epl10p8pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 7, 6}, + {5, 9, 8}, + {6, 11, 10}, + {7, 13, 12}, + {8, 15, 14}, + {9, 17, 16}, + {11, 19, 18}}; + +static pin_fuse_columns epl12p6pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 11, 10}, + {6, 13, 12}, + {7, 15, 14}, + {8, 17, 16}, + {9, 19, 18}, + {11, 21, 20}, + {12, 19, 18}, + {19, 7, 6}}; + +static pin_fuse_columns epl14p4pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 15, 14}, + {7, 17, 16}, + {8, 21, 20}, + {9, 25, 24}, + {11, 27, 26}, + {12, 23, 22}, + {13, 19, 18}, + {18, 11, 10}, + {19, 7, 6}}; + +static pin_fuse_columns epl16p2pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {11, 31, 30}, + {12, 27, 26}, + {13, 23, 22}, + {14, 19, 18}, + {17, 15, 14}, + {18, 11, 10}, + {19, 7, 6}}; + +static pin_fuse_columns epl16p8pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {11, 31, 30}, + {13, 27, 26}, + {14, 23, 22}, + {15, 19, 18}, + {16, 15, 14}, + {17, 11, 10}, + {18, 7, 6}}; + +static pin_fuse_columns epl16rp8pinfusecolumns[] = { + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {12, 31, 30}, + {13, 27, 26}, + {14, 23, 22}, + {15, 19, 18}, + {16, 15, 14}, + {17, 11, 10}, + {18, 7, 6}, + {19, 3, 2}}; + +static pin_fuse_columns epl16rp6pinfusecolumns[] = { + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {12, 31, 30}, + {13, 27, 26}, + {14, 23, 22}, + {15, 19, 18}, + {16, 15, 14}, + {17, 11, 10}, + {18, 7, 6}, + {19, 3, 2}}; + +static pin_fuse_columns epl16rp4pinfusecolumns[] = { + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {12, 31, 30}, + {13, 27, 26}, + {18, 7, 6}, + {19, 3, 2}}; +#endif + +static pin_fuse_columns pal10p8pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 7, 6}, + {5, 9, 8}, + {6, 11, 10}, + {7, 13, 12}, + {8, 15, 14}, + {9, 17, 16}, + {11, 19, 18}}; + +static pin_fuse_columns pal12p6pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 11, 10}, + {6, 13, 12}, + {7, 15, 14}, + {8, 17, 16}, + {9, 21, 20}, + {11, 23, 22}, + {12, 19, 18}, + {19, 7, 6}}; + +static pin_fuse_columns pal14p4pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 15, 14}, + {7, 17, 16}, + {8, 21, 20}, + {9, 25, 24}, + {11, 27, 26}, + {12, 23, 22}, + {13, 19, 18}, + {18, 11, 10}, + {19, 7, 6}}; + +static pin_fuse_columns pal16p2pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {11, 31, 30}, + {12, 27, 26}, + {13, 23, 22}, + {14, 19, 18}, + {17, 15, 14}, + {18, 11, 10}, + {19, 7, 6}}; + +static pin_fuse_columns pal16p8pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {11, 31, 30}, + {13, 27, 26}, + {14, 23, 22}, + {15, 19, 18}, + {16, 15, 14}, + {17, 11, 10}, + {18, 7, 6}}; + +static pin_fuse_columns pal16rp4pinfusecolumns[] = { + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {12, 31, 30}, + {13, 27, 26}, + {14, 23, 22}, /* Registered Output */ + {15, 19, 18}, /* Registered Output */ + {16, 15, 14}, /* Registered Output */ + {17, 11, 10}, /* Registered Output */ + {18, 7, 6}, + {19, 3, 2}}; + +static pin_fuse_columns pal16rp6pinfusecolumns[] = { + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {12, 31, 30}, + {13, 27, 26}, /* Registered Output */ + {14, 23, 22}, /* Registered Output */ + {15, 19, 18}, /* Registered Output */ + {16, 15, 14}, /* Registered Output */ + {17, 11, 10}, /* Registered Output */ + {18, 7, 6}, /* Registered Output */ + {19, 3, 2}}; + +static pin_fuse_columns pal16rp8pinfusecolumns[] = { + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {12, 31, 30}, /* Registered Output */ + {13, 27, 26}, /* Registered Output */ + {14, 23, 22}, /* Registered Output */ + {15, 19, 18}, /* Registered Output */ + {16, 15, 14}, /* Registered Output */ + {17, 11, 10}, /* Registered Output */ + {18, 7, 6}, /* Registered Output */ + {19, 3, 2}}; /* Registered Output */ + +static pin_fuse_columns pal6l16pinfusecolumns[] = { + {4, 1, 0}, + {5, 3, 2}, + {6, 5, 4}, + {7, 7, 6}, + {8, 9, 8}, + {9, 11, 10}}; + +static pin_fuse_columns pal8l14pinfusecolumns[] = { + {3, 1, 0}, + {4, 3, 2}, + {5, 5, 4}, + {6, 7, 6}, + {7, 9, 8}, + {8, 11, 10}, + {9, 13, 12}, + {10, 15, 14}}; + +static pin_fuse_columns pal12h10pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 7, 6}, + {5, 9, 8}, + {6, 11, 10}, + {7, 13, 12}, + {8, 15, 14}, + {9, 17, 16}, + {10, 19, 18}, + {11, 21, 20}, + {13, 23, 22}}; + +static pin_fuse_columns pal12l10pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 7, 6}, + {5, 9, 8}, + {6, 11, 10}, + {7, 13, 12}, + {8, 15, 14}, + {9, 17, 16}, + {10, 19, 18}, + {11, 21, 20}, + {13, 23, 22}}; + +static pin_fuse_columns pal14h8pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 11, 10}, + {6, 13, 12}, + {7, 15, 14}, + {8, 17, 16}, + {9, 19, 18}, + {10, 21, 20}, + {11, 25, 24}, + {13, 27, 26}, + {14, 23, 22}, + {23, 7, 6}}; + +static pin_fuse_columns pal14l8pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 11, 10}, + {6, 13, 12}, + {7, 15, 14}, + {8, 17, 16}, + {9, 19, 18}, + {10, 21, 20}, + {11, 25, 24}, + {13, 27, 26}, + {14, 23, 22}, + {23, 7, 6}}; + +static pin_fuse_columns pal16h6pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 15, 14}, + {7, 17, 16}, + {8, 19, 18}, + {9, 21, 20}, + {10, 25, 24}, + {11, 29, 28}, + {13, 31, 30}, + {14, 27, 26}, + {15, 23, 22}, + {22, 11, 10}, + {23, 7, 6}}; + +static pin_fuse_columns pal16l6pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 15, 14}, + {7, 17, 16}, + {8, 19, 18}, + {9, 21, 20}, + {10, 25, 24}, + {11, 29, 28}, + {13, 31, 30}, + {14, 27, 26}, + {15, 23, 22}, + {22, 11, 10}, + {23, 7, 6}}; + +static pin_fuse_columns pal18h4pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 19, 18}, + {8, 21, 20}, + {9, 25, 24}, + {10, 29, 28}, + {11, 33, 32}, + {13, 35, 34}, + {14, 31, 30}, + {15, 27, 26}, + {16, 23, 22}, + {21, 15, 14}, + {22, 11, 10}, + {23, 7, 6}}; + +static pin_fuse_columns pal18l4pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 19, 18}, + {8, 21, 20}, + {9, 25, 24}, + {10, 29, 28}, + {11, 33, 32}, + {13, 35, 34}, + {14, 31, 30}, + {15, 27, 26}, + {16, 23, 22}, + {21, 15, 14}, + {22, 11, 10}, + {23, 7, 6}}; + +static pin_fuse_columns pal20c1pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {10, 33, 32}, + {11, 37, 36}, + {13, 39, 38}, + {14, 35, 34}, + {15, 31, 30}, + {16, 27, 26}, + {17, 23, 22}, + {20, 19, 18}, + {21, 15, 14}, + {22, 11, 10}, + {23, 7, 6}}; + +static pin_fuse_columns pal20l2pinfusecolumns[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {10, 33, 32}, + {11, 37, 36}, + {13, 39, 38}, + {14, 35, 34}, + {15, 31, 30}, + {16, 27, 26}, + {17, 23, 22}, + {20, 19, 18}, + {21, 15, 14}, + {22, 11, 10}, + {23, 7, 6}}; + +static pin_fuse_columns _82s100_pls100_pinfusecolumns[] = { + {9, 1, 0}, + {8, 3, 2}, + {7, 5, 4}, + {6, 7, 6}, + {5, 9, 8}, + {4, 11, 10}, + {3, 13, 12}, + {2, 15, 14}, + {27, 17, 16}, + {26, 19, 18}, + {25, 21, 20}, + {24, 23, 22}, + {23, 25, 24}, + {22, 27, 26}, + {21, 29, 28}, + {20, 31, 30}}; + +static pal_data paldata[] = { + {"PAL10L8", 320, + pal10l8pinfuserows, std::size(pal10l8pinfuserows), + pal10l8pinfusecolumns, std::size(pal10l8pinfusecolumns), + print_pal10l8_product_terms, + config_pal10l8_pins, + nullptr, + nullptr}, + {"PAL10H8", 320, + pal10h8pinfuserows, std::size(pal10h8pinfuserows), + pal10h8pinfusecolumns, std::size(pal10h8pinfusecolumns), + print_pal10h8_product_terms, + config_pal10h8_pins, + nullptr, + nullptr}, + {"PAL12H6", 384, + pal12h6pinfuserows, std::size(pal12h6pinfuserows), + pal12h6pinfusecolumns, std::size(pal12h6pinfusecolumns), + print_pal12h6_product_terms, + config_pal12h6_pins, + nullptr, + nullptr}, + {"PAL14H4", 448, + pal14h4pinfuserows, std::size(pal14h4pinfuserows), + pal14h4pinfusecolumns, std::size(pal14h4pinfusecolumns), + print_pal14h4_product_terms, + config_pal14h4_pins, + nullptr, + nullptr}, + {"PAL16H2", 512, + pal16h2pinfuserows, std::size(pal16h2pinfuserows), + pal16h2pinfusecolumns, std::size(pal16h2pinfusecolumns), + print_pal16h2_product_terms, + config_pal16h2_pins, + nullptr, + nullptr}, + {"PAL16C1", 512, + pal16c1pinfuserows, std::size(pal16c1pinfuserows), + pal16c1pinfusecolumns, std::size(pal16c1pinfusecolumns), + print_pal16c1_product_terms, + config_pal16c1_pins, + nullptr, + nullptr}, + {"PAL12L6", 384, + pal12l6pinfuserows, std::size(pal12l6pinfuserows), + pal12l6pinfusecolumns, std::size(pal12l6pinfusecolumns), + print_pal12l6_product_terms, + config_pal12l6_pins, + nullptr, + nullptr}, + {"PAL14L4", 448, + pal14l4pinfuserows, std::size(pal14l4pinfuserows), + pal14l4pinfusecolumns, std::size(pal14l4pinfusecolumns), + print_pal14l4_product_terms, + config_pal14l4_pins, + nullptr, + nullptr}, + {"PAL16L2", 512, + pal16l2pinfuserows, std::size(pal16l2pinfuserows), + pal16l2pinfusecolumns, std::size(pal16l2pinfusecolumns), + print_pal16l2_product_terms, + config_pal16l2_pins, + nullptr, + nullptr}, + /*{"15S8", 0, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL},*/ + {"PAL16L8", 2048, + pal16l8pinfuserows, std::size(pal16l8pinfuserows), + pal16l8pinfusecolumns, std::size(pal16l8pinfusecolumns), + print_pal16l8_product_terms, + config_pal16l8_pins, + nullptr, + nullptr}, + {"PAL16R4", 2048, + pal16r4pinfuserows, std::size(pal16r4pinfuserows), + pal16r4pinfusecolumns, std::size(pal16r4pinfusecolumns), + print_pal16r4_product_terms, + config_pal16r4_pins, + nullptr, + nullptr}, + {"PAL16R6", 2048, + pal16r6pinfuserows, std::size(pal16r6pinfuserows), + pal16r6pinfusecolumns, std::size(pal16r6pinfusecolumns), + print_pal16r6_product_terms, + config_pal16r6_pins, + nullptr, + nullptr}, + {"PAL16R8", 2048, + pal16r8pinfuserows, std::size(pal16r8pinfuserows), + pal16r8pinfusecolumns, std::size(pal16r8pinfusecolumns), + print_pal16r8_product_terms, + config_pal16r8_pins, + nullptr, + nullptr}, + /*{"PAL16RA8", 0, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL}, + {"PAL16V8R", 0, NULL, 0, NULL, 0, NULL, NULL, NULL, NULL},*/ // PAL16V8 same fusemap as GAL16V8? + {"PALCE16V8", 2194, + gal16v8pinfuserows, std::size(gal16v8pinfuserows), + gal16v8pinfusecolumns, std::size(gal16v8pinfusecolumns), + print_gal16v8_product_terms, + config_gal16v8_pins, + nullptr, + nullptr}, + {"GAL16V8", 2194, + gal16v8pinfuserows, std::size(gal16v8pinfuserows), + gal16v8pinfusecolumns, std::size(gal16v8pinfusecolumns), + print_gal16v8_product_terms, + config_gal16v8_pins, + is_gal16v8_product_term_enabled, + nullptr}, + {"ATF16V8", 2194, + gal16v8pinfuserows, std::size(gal16v8pinfuserows), + gal16v8pinfusecolumns, std::size(gal16v8pinfusecolumns), + print_gal16v8_product_terms, + config_gal16v8_pins, + is_gal16v8_product_term_enabled, + nullptr}, + {"18CV8", 2696, + peel18cv8pinfuserows, std::size(peel18cv8pinfuserows), + peel18cv8pinfusecolumns, std::size(peel18cv8pinfusecolumns), + print_peel18cv8_product_terms, + config_peel18cv8_pins, + nullptr, + get_peel18cv8_pin_fuse_state}, + {"AMPAL18P8", 2600, + ampal18p8pinfuserows, std::size(ampal18p8pinfuserows), + ampal18p8pinfusecolumns, std::size(ampal18p8pinfusecolumns), + print_ampal18p8_product_terms, + config_ampal18p8_pins, + nullptr, + nullptr}, + {"GAL18V10", 3540, + gal18v10pinfuserows, std::size(gal18v10pinfuserows), + gal18v10pinfusecolumns, std::size(gal18v10pinfusecolumns), + print_gal18v10_product_terms, + config_gal18v10_pins, + nullptr, + nullptr}, + {"PAL20L8", 2560, + pal20l8pinfuserows, std::size(pal20l8pinfuserows), + pal20l8pinfusecolumns, std::size(pal20l8pinfusecolumns), + print_pal20l8_product_terms, + config_pal20l8_pins, + nullptr, + nullptr}, + {"PAL20L10", 1600, + pal20l10pinfuserows, std::size(pal20l10pinfuserows), + pal20l10pinfusecolumns, std::size(pal20l10pinfusecolumns), + print_pal20l10_product_terms, + config_pal20l10_pins, + nullptr, + nullptr}, + {"PAL20R4", 2560, + pal20r4pinfuserows, std::size(pal20r4pinfuserows), + pal20r4pinfusecolumns, std::size(pal20r4pinfusecolumns), + print_pal20r4_product_terms, + config_pal20r4_pins, + nullptr, + nullptr}, + {"PAL20R6", 2560, + pal20r6pinfuserows, std::size(pal20r6pinfuserows), + pal20r6pinfusecolumns, std::size(pal20r6pinfusecolumns), + print_pal20r6_product_terms, + config_pal20r6_pins, + nullptr, + nullptr}, + {"PAL20R8", 2560, + pal20r8pinfuserows, std::size(pal20r8pinfuserows), + pal20r8pinfusecolumns, std::size(pal20r8pinfusecolumns), + print_pal20r8_product_terms, + config_pal20r8_pins, + nullptr, + nullptr}, + {"PAL20RA10", 3210, + pal20ra10pinfuserows, std::size(pal20ra10pinfuserows), + pal20ra10pinfusecolumns, std::size(pal20ra10pinfusecolumns), + print_pal20ra10_product_terms, + config_pal20ra10_pins, + nullptr, + nullptr }, + {"PAL20X4", 1600, + pal20x4pinfuserows, std::size(pal20x4pinfuserows), + pal20x4pinfusecolumns, std::size(pal20x4pinfusecolumns), + print_pal20x4_product_terms, + config_pal20x4_pins, + nullptr, + nullptr}, + {"PAL20X8", 1600, + pal20x8pinfuserows, std::size(pal20x8pinfuserows), + pal20x8pinfusecolumns, std::size(pal20x8pinfusecolumns), + print_pal20x8_product_terms, + config_pal20x8_pins, + nullptr, + nullptr}, + {"PAL20X10", 1600, + pal20x10pinfuserows, std::size(pal20x10pinfuserows), + pal20x10pinfusecolumns, std::size(pal20x10pinfusecolumns), + print_pal20x10_product_terms, + config_pal20x10_pins, + nullptr, + nullptr}, + {"GAL20V8", 2706, + gal20v8pinfuserows, std::size(gal20v8pinfuserows), + gal20v8pinfusecolumns, std::size(gal20v8pinfusecolumns), + print_gal20v8_product_terms, + config_gal20v8_pins, + is_gal20v8_product_term_enabled, + nullptr}, + {"PALCE20V8", 2706, + gal20v8pinfuserows, std::size(gal20v8pinfuserows), + gal20v8pinfusecolumns, std::size(gal20v8pinfusecolumns), + print_gal20v8_product_terms, + config_gal20v8_pins, + is_gal20v8_product_term_enabled, + nullptr}, + {"ATF20V10", 2706, + gal20v8pinfuserows, std::size(gal20v8pinfuserows), + gal20v8pinfusecolumns, std::size(gal20v8pinfusecolumns), + print_gal20v8_product_terms, + config_gal20v8_pins, + is_gal20v8_product_term_enabled, + nullptr}, + {"PAL22V10", 5828, + palce22v10_pal22v10pinfuserows, std::size(palce22v10_pal22v10pinfuserows), + palce22v10_pal22v10pinfusecolumns, std::size(palce22v10_pal22v10pinfusecolumns), + print_palce22v10_pal22v10_product_terms, + config_palce22v10_pal22v10_pins, + nullptr, + nullptr}, + {"PALCE22V10", 5828, + palce22v10_pal22v10pinfuserows, std::size(palce22v10_pal22v10pinfuserows), + palce22v10_pal22v10pinfusecolumns, std::size(palce22v10_pal22v10pinfusecolumns), + print_palce22v10_pal22v10_product_terms, + config_palce22v10_pal22v10_pins, + nullptr, + nullptr}, + {"ATF22V10", 5828, + palce22v10_pal22v10pinfuserows, std::size(palce22v10_pal22v10pinfuserows), + palce22v10_pal22v10pinfusecolumns, std::size(palce22v10_pal22v10pinfusecolumns), + print_palce22v10_pal22v10_product_terms, + config_palce22v10_pal22v10_pins, + nullptr, + nullptr}, + {"GAL22V10", 5892, + gal22v10pinfuserows, std::size(gal22v10pinfuserows), + gal22v10pinfusecolumns, std::size(gal22v10pinfusecolumns), + print_gal22v10_product_terms, + config_gal22v10_pins, + nullptr, + nullptr}, + {"ATF22V10", 5892, + gal22v10pinfuserows, std::size(gal22v10pinfuserows), + gal22v10pinfusecolumns, std::size(gal22v10pinfusecolumns), + print_gal22v10_product_terms, + config_gal22v10_pins, + nullptr, + nullptr}, + {"ATF22V10", 5893, + atf22v10powerdownmodepinfuserows, std::size(atf22v10powerdownmodepinfuserows), + atf22v10powerdownmodepinfusecolumns, std::size(atf22v10powerdownmodepinfusecolumns), + print_atf22v10_power_down_mode_product_terms, + config_atf22v10_power_down_mode_pins, + nullptr, + nullptr}, + {"82S153", 1842, + _82s153_pls153pinfuserows, std::size(_82s153_pls153pinfuserows), + _82s153_pls153pinfusecolumns, std::size(_82s153_pls153pinfusecolumns), + print_82s153_pls153_product_terms, + config_82s153_pls153_pins, + nullptr, + nullptr}, + {"PLS153", 1842, + _82s153_pls153pinfuserows, std::size(_82s153_pls153pinfuserows), + _82s153_pls153pinfusecolumns, std::size(_82s153_pls153pinfusecolumns), + print_82s153_pls153_product_terms, + config_82s153_pls153_pins, + nullptr, + nullptr}, + {"CK2605", 1106, + ck2605pinfuserows, std::size(ck2605pinfuserows), + ck2605pinfusecolumns, std::size(ck2605pinfusecolumns), + print_ck2605_product_terms, + config_ck2605_pins, + nullptr, + nullptr}, +#if defined(ricoh_pals) + {"EPL10P8", 664, + epl10p8pinfuserows, std::size(epl10p8pinfuserows), + epl10p8pinfusecolumns, std::size(epl10p8pinfusecolumns), + print_epl10p8_product_terms, + config_epl10p8_pins, + nullptr, + nullptr}, + {"EPL12P6", 786, + epl12p6pinfuserows, std::size(epl12p6pinfuserows), + epl12p6pinfusecolumns, std::size(epl12p6pinfusecolumns), + print_epl12p6_product_terms, + config_epl12p6_pins, + nullptr, + nullptr}, + {"EPL14P4", 908, + epl14p4pinfuserows, std::size(epl14p4pinfuserows), + epl14p4pinfusecolumns, std::size(epl14p4pinfusecolumns), + print_epl14p4_product_terms, + config_epl14p4_pins, + nullptr, + nullptr}, + {"EPL16P2", 1030, + epl16p2pinfuserows, std::size(epl16p2pinfuserows), + epl16p2pinfusecolumns, std::size(epl16p2pinfusecolumns), + print_epl16p2_product_terms, + config_epl16p2_pins, + nullptr, + nullptr}, + {"EPL16P8", 2072, + epl16p8pinfuserows, std::size(epl16p8pinfuserows), + epl16p8pinfusecolumns, std::size(epl16p8pinfusecolumns), + print_epl16p8_product_terms, + config_epl16p8_pins, + nullptr, + nullptr}, + {"EPL16RP8", 2072, + epl16rp8pinfuserows, std::size(epl16rp8pinfuserows), + epl16rp8pinfusecolumns, std::size(epl16rp8pinfusecolumns), + print_epl16rp8_product_terms, + config_epl16rp8_pins, + nullptr, + nullptr}, + {"EPL16RP6", 2072, + epl16rp6pinfuserows, std::size(epl16rp6pinfuserows), + epl16rp6pinfusecolumns, std::size(epl16rp6pinfusecolumns), + print_epl16rp6_product_terms, + config_epl16rp6_pins, + nullptr, + nullptr}, + {"EPL16RP4", 2072, + epl16rp4pinfuserows, std::size(epl16rp4pinfuserows), + epl16rp4pinfusecolumns, std::size(epl16rp4pinfusecolumns), + print_epl16rp4_product_terms, + config_epl16rp4_pins, + nullptr, + nullptr}, +#endif + {"PAL10P8", 328, + pal10p8pinfuserows, std::size(pal10p8pinfuserows), + pal10p8pinfusecolumns, std::size(pal10p8pinfusecolumns), + print_pal10p8_product_terms, + config_pal10p8_pins, + nullptr, + nullptr}, + {"PAL12P6", 390, + pal12p6pinfuserows, std::size(pal12p6pinfuserows), + pal12p6pinfusecolumns, std::size(pal12p6pinfusecolumns), + print_pal12p6_product_terms, + config_pal12p6_pins, + nullptr, + nullptr}, + {"PAL14P4", 452, + pal14p4pinfuserows, std::size(pal14p4pinfuserows), + pal14p4pinfusecolumns, std::size(pal14p4pinfusecolumns), + print_pal14p4_product_terms, + config_pal14p4_pins, + nullptr, + nullptr}, + {"PAL16P2", 514, + pal16p2pinfuserows, std::size(pal16p2pinfuserows), + pal16p2pinfusecolumns, std::size(pal16p2pinfusecolumns), + print_pal16p2_product_terms, + config_pal16p2_pins, + nullptr, + nullptr}, + {"PAL16P8", 2056, + pal16p8pinfuserows, std::size(pal16p8pinfuserows), + pal16p8pinfusecolumns, std::size(pal16p8pinfusecolumns), + print_pal16p8_product_terms, + config_pal16p8_pins, + nullptr, + nullptr}, + {"PAL16RP4", 2056, + pal16rp4pinfuserows, std::size(pal16rp4pinfuserows), + pal16rp4pinfusecolumns, std::size(pal16rp4pinfusecolumns), + print_pal16rp4_product_terms, + config_pal16rp4_pins, + nullptr, + nullptr}, + {"PAL16RP6", 2056, + pal16rp6pinfuserows, std::size(pal16rp6pinfuserows), + pal16rp6pinfusecolumns, std::size(pal16rp6pinfusecolumns), + print_pal16rp6_product_terms, + config_pal16rp6_pins, + nullptr, + nullptr}, + {"PAL16RP8", 2056, + pal16rp8pinfuserows, std::size(pal16rp8pinfuserows), + pal16rp8pinfusecolumns, std::size(pal16rp8pinfusecolumns), + print_pal16rp8_product_terms, + config_pal16rp8_pins, + nullptr, + nullptr}, + {"PAL6L16", 192, + pal6l16pinfuserows, std::size(pal6l16pinfuserows), + pal6l16pinfusecolumns, std::size(pal6l16pinfusecolumns), + print_pal6l16_product_terms, + config_pal6l16_pins, + nullptr, + nullptr}, + {"PAL8L14", 224, + pal8l14pinfuserows, std::size(pal8l14pinfuserows), + pal8l14pinfusecolumns, std::size(pal8l14pinfusecolumns), + print_pal8l14_product_terms, + config_pal8l14_pins, + nullptr, + nullptr}, + {"PAL12H10", 480, + pal12h10pinfuserows, std::size(pal12h10pinfuserows), + pal12h10pinfusecolumns, std::size(pal12h10pinfusecolumns), + print_pal12h10_product_terms, + config_pal12h10_pins, + nullptr, + nullptr}, + {"PAL12L10", 480, + pal12l10pinfuserows, std::size(pal12l10pinfuserows), + pal12l10pinfusecolumns, std::size(pal12l10pinfusecolumns), + print_pal12l10_product_terms, + config_pal12l10_pins, + nullptr, + nullptr}, + {"PAL14H8", 560, + pal14h8pinfuserows, std::size(pal14h8pinfuserows), + pal14h8pinfusecolumns, std::size(pal14h8pinfusecolumns), + print_pal14h8_product_terms, + config_pal14h8_pins, + nullptr, + nullptr}, + {"PAL14L8", 560, + pal14l8pinfuserows, std::size(pal14l8pinfuserows), + pal14l8pinfusecolumns, std::size(pal14l8pinfusecolumns), + print_pal14l8_product_terms, + config_pal14l8_pins, + nullptr, + nullptr}, + {"PAL16H6", 640, + pal16h6pinfuserows, std::size(pal16h6pinfuserows), + pal16h6pinfusecolumns, std::size(pal16h6pinfusecolumns), + print_pal16h6_product_terms, + config_pal16h6_pins, + nullptr, + nullptr}, + {"PAL16L6", 640, + pal16l6pinfuserows, std::size(pal16l6pinfuserows), + pal16l6pinfusecolumns, std::size(pal16l6pinfusecolumns), + print_pal16l6_product_terms, + config_pal16l6_pins, + nullptr, + nullptr}, + {"PAL18H4", 720, + pal18h4pinfuserows, std::size(pal18h4pinfuserows), + pal18h4pinfusecolumns, std::size(pal18h4pinfusecolumns), + print_pal18h4_product_terms, + config_pal18h4_pins, + nullptr, + nullptr}, + {"PAL18L4", 720, + pal18l4pinfuserows, std::size(pal18l4pinfuserows), + pal18l4pinfusecolumns, std::size(pal18l4pinfusecolumns), + print_pal18l4_product_terms, + config_pal18l4_pins, + nullptr, + nullptr}, + {"PAL20C1", 640, + pal20c1pinfuserows, std::size(pal20c1pinfuserows), + pal20c1pinfusecolumns, std::size(pal20c1pinfusecolumns), + print_pal20c1_product_terms, + config_pal20c1_pins, + nullptr, + nullptr}, + {"PAL20L2", 640, + pal20l2pinfuserows, std::size(pal20l2pinfuserows), + pal20l2pinfusecolumns, std::size(pal20l2pinfusecolumns), + print_pal20l2_product_terms, + config_pal20l2_pins, + nullptr, + nullptr}, + {"82S100", 1928, + _82s100_pls100_pinfuserows, std::size(_82s100_pls100_pinfuserows), + _82s100_pls100_pinfusecolumns, std::size(_82s100_pls100_pinfusecolumns), + print_82s100_pls100_product_terms, + config_82s100_pls100_pins, + nullptr, + nullptr}, + {"PLS100", 1928, + _82s100_pls100_pinfuserows, std::size(_82s100_pls100_pinfuserows), + _82s100_pls100_pinfusecolumns, std::size(_82s100_pls100_pinfusecolumns), + print_82s100_pls100_product_terms, + config_82s100_pls100_pins, + nullptr, + nullptr}, + {"82S101", 1928, + _82s100_pls100_pinfuserows, std::size(_82s100_pls100_pinfuserows), + _82s100_pls100_pinfusecolumns, std::size(_82s100_pls100_pinfusecolumns), + print_82s100_pls100_product_terms, + config_82s100_pls100_pins, + nullptr, + nullptr}, + {"PLS101", 1928, + _82s100_pls100_pinfuserows, std::size(_82s100_pls100_pinfuserows), + _82s100_pls100_pinfusecolumns, std::size(_82s100_pls100_pinfusecolumns), + print_82s100_pls100_product_terms, + config_82s100_pls100_pins, + nullptr, + nullptr}}; + +/*************************************************************************** + CORE IMPLEMENTATION +***************************************************************************/ + +/*------------------------------------------------- + is_jed_file - test if the file extension is + that of a JED file +-------------------------------------------------*/ + +static int is_jed_file(const char *file) +{ + int len; + + /* does the source end in '.jed'? */ + len = strlen(file); + + return (file[len - 4] == '.' && + tolower((uint8_t)file[len - 3]) == 'j' && + tolower((uint8_t)file[len - 2]) == 'e' && + tolower((uint8_t)file[len - 1]) == 'd'); +} + + + +/*------------------------------------------------- + is_pla_file - test if the file extension is + that of a Berkeley standard PLA file +-------------------------------------------------*/ + +static int is_pla_file(const char *file) +{ + int len; + + /* does the source end in '.pla'? */ + len = strlen(file); + + return (file[len - 4] == '.' && + tolower((uint8_t)file[len - 3]) == 'p' && + tolower((uint8_t)file[len - 2]) == 'l' && + tolower((uint8_t)file[len - 1]) == 'a'); +} + + + +/*------------------------------------------------- + find_pal_data - finds the data associated + with a pal name +-------------------------------------------------*/ + +static void find_pal_data(const char *name, pal_data_vector& pal_data_vector) +{ + int index; + + for (index = 0; index < std::size(paldata); ++index) + { + if (!core_stricmp(name, paldata[index].name)) + { + pal_data_vector.push_back(&paldata[index]); + } + } +} + + + +/*------------------------------------------------- + find_fuse_rows - finds the fuse row data for + an output pin. +-------------------------------------------------*/ + +static const pin_fuse_rows* find_fuse_rows(const pal_data* pal, uint16_t pin) +{ + uint16_t index; + + for (index = 0; index < pal->pinfuserowscount; ++index) + { + if (pal->pinfuserows[index].pin == pin) + { + return &pal->pinfuserows[index]; + } + } + + return nullptr; +} + + + +/*------------------------------------------------- + find_fuse_columns - finds the fuse column + data for an input pin. +-------------------------------------------------*/ + +static const pin_fuse_columns* find_fuse_columns(const pal_data* pal, uint16_t pin) +{ + uint16_t index; + + for (index = 0; index < pal->pinfusecolumnscount; ++index) + { + if (pal->pinfusecolumns[index].pin == pin) + { + return &pal->pinfusecolumns[index]; + } + } + + return nullptr; +} + + + +/*------------------------------------------------- + find_pin_from_fuse_row - finds the pin + associated with a fuse row +-------------------------------------------------*/ + +static uint16_t find_pin_from_fuse_row(const pal_data* pal, uint16_t fuserow) +{ + int index; + + for (index = 0; index < pal->pinfuserowscount; ++index) + { + if (pal->pinfuserows[index].fuserowoutputenable != NO_OUTPUT_ENABLE_FUSE_ROW) + { + if (pal->pinfuserows[index].fuserowoutputenable == fuserow) + { + return pal->pinfuserows[index].pin; + } + } + + if (fuserow >= pal->pinfuserows[index].fuserowtermstart && + fuserow <= pal->pinfuserows[index].fuserowtermend) + { + return pal->pinfuserows[index].pin; + } + } + + return 0; +} + + + +/*------------------------------------------------- + calc_fuse_column_count - calculates the total + columns of a pal +-------------------------------------------------*/ + +static uint16_t calc_fuse_column_count(const pal_data* pal) +{ + return pal->pinfusecolumnscount * 2; +} + + + +/*------------------------------------------------- + all_fuses_in_row_blown - checks if a fuse row + is all blown +-------------------------------------------------*/ + +static int all_fuses_in_row_blown(const pal_data* pal, const jed_data* jed, uint16_t fuserow) +{ + uint16_t columncount = calc_fuse_column_count(pal); + uint16_t column; + + for (column = 0; column < columncount; ++column) + { + if (!jed_get_fuse(jed, fuserow + column)) + { + return 0; + } + } + + return 1; +} + + + +/*------------------------------------------------- + does_output_enable_fuse_row_allow_output - checks + if an output enable fuse row contains a product + term that enables the output. +-------------------------------------------------*/ + +static int does_output_enable_fuse_row_allow_output(const pal_data* pal, const jed_data* jed, uint16_t fuserow) +{ + int lowfusestate, highfusestate; + uint16_t index; + + for (index = 0; index < pal->pinfusecolumnscount; ++index) + { + lowfusestate = jed_get_fuse(jed, fuserow + pal->pinfusecolumns[index].lowfusecolumn); + highfusestate = jed_get_fuse(jed, fuserow + pal->pinfusecolumns[index].highfusecolumn); + + if (!lowfusestate && !highfusestate) + { + return 0; + } + } + + return 1; +} + + + +/*------------------------------------------------- + set_input_pins - saves the pins that can be + used by a product term +-------------------------------------------------*/ + +static void set_input_pins(const uint16_t* pins, uint16_t pin_count) +{ + uint16_t index; + + for (index = 0; index < pin_count; ++index) + { + inputpins[index] = pins[index]; + + ++inputpinscount; + } +} + + + +/*------------------------------------------------- + set_output_pins - saves the output pins +-------------------------------------------------*/ + +static void set_output_pins(const pin_output_config* pin_output_configs, uint16_t pin_count) +{ + uint16_t index; + + for (index = 0; index < pin_count; ++index) + { + outputpins[index].pin = pin_output_configs[index].pin; + outputpins[index].flags = pin_output_configs[index].flags; + + ++outputpinscount; + } +} + + + +/*------------------------------------------------- + is_output_pin - determines if the pin is an + output pin +-------------------------------------------------*/ + +static int is_output_pin(uint16_t pin) +{ + uint16_t index; + + for (index = 0; index < outputpinscount; ++index) + { + if (outputpins[index].pin == pin) + { + return 1; + } + } + + return 0; +} + + + +/*------------------------------------------------- + get_pin_output_flags - gets the flags + of an output pin +-------------------------------------------------*/ + +static uint16_t get_pin_output_flags(uint16_t pin) +{ + uint16_t index; + + for (index = 0; index < outputpinscount; ++index) + { + if (outputpins[index].pin == pin) + { + return outputpins[index].flags; + } + } + + return 0; +} + + + +/*------------------------------------------------- + get_pin_fuse_state - gets the fuse state of + an input pin +-------------------------------------------------*/ + +static uint16_t get_pin_fuse_state(const pal_data* pal, const jed_data* jed, uint16_t pin, uint16_t fuserow) +{ + const pin_fuse_columns* fuse_columns = find_fuse_columns(pal, pin); + int lowfusestate, highfusestate; + + if (!fuse_columns) + { + fprintf(stderr, "Fuse column data missing for pin %d!\n", pin); + + return NO_FUSE_BLOWN; + } + + lowfusestate = jed_get_fuse(jed, fuserow + fuse_columns->lowfusecolumn); + highfusestate = jed_get_fuse(jed, fuserow + fuse_columns->highfusecolumn); + + if (!lowfusestate && highfusestate) + { + return LOW_FUSE_BLOWN; + } + else if (lowfusestate && !highfusestate) + { + return HIGH_FUSE_BLOWN; + } + else if (!lowfusestate && !highfusestate) + { + return NO_FUSE_BLOWN; + } + + return LOWHIGH_FUSE_BLOWN; +} + + + +/*------------------------------------------------- + generate_product_terms - prints the product + terms for a fuse row +-------------------------------------------------*/ + +static void generate_product_terms(const pal_data* pal, const jed_data* jed, uint16_t fuserow, char* buffer) +{ + uint16_t index, pin, fuse_state, haveterm, flags; + char tmpbuffer[20]; + + *buffer = 0; + haveterm = 0; + + if (pal->is_product_term_enabled && !pal->is_product_term_enabled(pal, jed, fuserow)) + { + return; + } + + for (index = 0; index < inputpinscount; ++index) + { + pin = inputpins[index]; + + if (pal->get_pin_fuse_state) + { + fuse_state = pal->get_pin_fuse_state(pal, jed, pin, fuserow); + } + else + { + fuse_state = get_pin_fuse_state(pal, jed, pin, fuserow); + } + + if (fuse_state == LOW_FUSE_BLOWN) + { + if (haveterm) + { + strcat(buffer, " " AND_SYMBOL " "); + } + + if (!is_output_pin(pin)) + { + sprintf(tmpbuffer, LOW_SYMBOL INPUT_SYMBOL "%d", pin); + strcat(buffer, tmpbuffer); + } + else + { + flags = get_pin_output_flags(pin); + + if (flags & OUTPUT_FEEDBACK_OUTPUT) + { + if (flags & OUTPUT_COMBINATORIAL) + { + sprintf(tmpbuffer, LOW_SYMBOL OUTPUT_SYMBOL "%d", pin); + } + else if (flags & OUTPUT_REGISTERED) + { + sprintf(tmpbuffer, LOW_SYMBOL REGISTERED_FEEDBACK_OUTPUT_SYMBOL "%d", pin); + } + else + { + tmpbuffer[0] = 0; + + fprintf(stderr, "Unknown output feedback controlled by output enable type for pin %d!\n", pin); + } + } + else if (flags & OUTPUT_FEEDBACK_COMBINATORIAL) + { + sprintf(tmpbuffer, LOW_SYMBOL OUTPUT_FEEDBACK_SYMBOL "%d", pin); + } + else if (flags & OUTPUT_FEEDBACK_REGISTERED) + { + sprintf(tmpbuffer, LOW_SYMBOL REGISTERED_FEEDBACK_SYMBOL "%d", pin); + } + else + { + tmpbuffer[0] = 0; + + fprintf(stderr, "Unknown output feedback type for pin %d!\n", pin); + } + + strcat(buffer, tmpbuffer); + } + + haveterm = 1; + } + + if (fuse_state == HIGH_FUSE_BLOWN) + { + if (haveterm) + { + strcat(buffer, " " AND_SYMBOL " "); + } + + if (!is_output_pin(pin)) + { + sprintf(tmpbuffer, INPUT_SYMBOL "%d", pin); + strcat(buffer, tmpbuffer); + } + else + { + flags = get_pin_output_flags(pin); + + if (flags & OUTPUT_FEEDBACK_OUTPUT) + { + if (flags & OUTPUT_COMBINATORIAL) + { + sprintf(tmpbuffer, OUTPUT_SYMBOL "%d", pin); + } + else if (flags & OUTPUT_REGISTERED) + { + sprintf(tmpbuffer, REGISTERED_FEEDBACK_OUTPUT_SYMBOL "%d", pin); + } + else + { + tmpbuffer[0] = 0; + + fprintf(stderr, "Unknown output feedback controlled by output enable type for pin %d!\n", pin); + } + } + else if (flags & OUTPUT_FEEDBACK_COMBINATORIAL) + { + sprintf(tmpbuffer, OUTPUT_FEEDBACK_SYMBOL "%d", pin); + } + else if (flags & OUTPUT_FEEDBACK_REGISTERED) + { + sprintf(tmpbuffer, REGISTERED_FEEDBACK_SYMBOL "%d", pin); + } + else + { + tmpbuffer[0] = 0; + + fprintf(stderr, "Unknown output feedback type for pin %d!\n", pin); + } + + strcat(buffer, tmpbuffer); + } + + haveterm = 1; + } + } +} + + + +/*------------------------------------------------- + print_input_pins - prints out the input pins +-------------------------------------------------*/ + +static void print_input_pins() +{ + uint16_t index; + + printf("Inputs:\n\n"); + + for (index = 0; index < inputpinscount; ++index) + { + printf("%d", inputpins[index]); + + if (index + 1 < inputpinscount) + { + printf(", "); + } + } + + printf("\n\n"); +} + + + +/*------------------------------------------------- + print_output_pins - prints out the output pins +-------------------------------------------------*/ + +static void print_output_pins() +{ + uint16_t index, flags; + + printf("Outputs:\n\n"); + + for (index = 0; index < outputpinscount; ++index) + { + flags = outputpins[index].flags; + + printf("%d (", outputpins[index].pin); + + if (flags & OUTPUT_COMBINATORIAL) + { + printf("Combinatorial, "); + } + else if (flags & OUTPUT_REGISTERED) + { + printf("Registered, "); + } + else + { + fprintf(stderr, "Unknown output type for pin %d!\n", outputpins[index].pin); + } + + if (flags & OUTPUT_FEEDBACK_OUTPUT) + { + printf("Output feedback output, "); + } + else if (flags & OUTPUT_FEEDBACK_COMBINATORIAL) + { + printf("Output feedback combinatorial, "); + } + else if (flags & OUTPUT_FEEDBACK_REGISTERED) + { + printf("Output feedback registered, "); + } + else if (flags & OUTPUT_FEEDBACK_NONE) + { + printf("No output feedback, "); + } + else + { + fprintf(stderr, "Unknown output feedback type for pin %d!\n", outputpins[index].pin); + } + + if (flags & OUTPUT_ACTIVELOW) + { + printf("Active low"); + } + else if (flags & OUTPUT_ACTIVEHIGH) + { + printf("Active high"); + } + else + { + fprintf(stderr, "Unknown output state type for pin %d!\n", outputpins[index].pin); + } + + printf(")\n"); + } + + printf("\n"); +} + + + +/*------------------------------------------------- + print_product_terms - prints the product terms + for a pal +-------------------------------------------------*/ + +static void print_product_terms(const pal_data* pal, const jed_data* jed) +{ + uint16_t index, columncount, flags, row, haveterms; + char buffer[200]; + int indent, indentindex; + const pin_fuse_rows* fuse_rows; + + columncount = calc_fuse_column_count(pal); + + print_input_pins(); + print_output_pins(); + + printf("Equations:\n\n"); + + for (index = 0; index < outputpinscount; ++index) + { + flags = outputpins[index].flags; + + indent = 0; + + if (flags & OUTPUT_ACTIVELOW) + { + printf(LOW_SYMBOL); + + indent += strlen(LOW_SYMBOL); + } + + if (flags & OUTPUT_COMBINATORIAL) + { + sprintf(buffer, OUTPUT_SYMBOL "%d " COMBINATORIAL_ASSIGNMENT " ", outputpins[index].pin); + } + else if (flags & OUTPUT_REGISTERED) + { + sprintf(buffer, REGISTERED_FEEDBACK_SYMBOL "%d " REGISTERED_ASSIGNMENT " ", outputpins[index].pin); + } + else + { + fprintf(stderr, "Unknown output type for pin %d!\n", outputpins[index].pin); + + continue; + } + + printf("%s", buffer); + + haveterms = 0; + indent += strlen(buffer); + + fuse_rows = find_fuse_rows(pal, outputpins[index].pin); + + if (!fuse_rows) + { + fprintf(stderr, "Fuse row data missing!\n"); + + continue; + } + + for (row = fuse_rows->fuserowtermstart; row <= fuse_rows->fuserowtermend; + row += columncount) + { + generate_product_terms(pal, jed, row, buffer); + + if (strlen(buffer) > 0) + { + if (haveterms) + { + printf(" " OR_SYMBOL "\n"); + + for (indentindex = 0; indentindex < indent; ++indentindex) + { + printf(" "); + } + } + else + { + haveterms = 1; + } + + printf("%s", buffer); + } + } + + printf("\n"); + + /* output enable equations */ + + if (flags & OUTPUT_COMBINATORIAL) + { + printf(OUTPUT_SYMBOL "%d.oe " COMBINATORIAL_ASSIGNMENT " ", outputpins[index].pin); + + if (fuse_rows->fuserowoutputenable == NO_OUTPUT_ENABLE_FUSE_ROW || + all_fuses_in_row_blown(pal, jed, fuse_rows->fuserowoutputenable)) + { + printf("vcc\n"); + } + else + { + generate_product_terms(pal, jed, fuse_rows->fuserowoutputenable, buffer); + + printf("%s\n", buffer); + } + } + else if (flags & OUTPUT_REGISTERED) + { + printf(REGISTERED_FEEDBACK_SYMBOL "%d.oe " COMBINATORIAL_ASSIGNMENT " ", outputpins[index].pin); + + if (fuse_rows->fuserowoutputenable == NO_OUTPUT_ENABLE_FUSE_ROW) + { + printf("OE\n"); + } + else if (all_fuses_in_row_blown(pal, jed, fuse_rows->fuserowoutputenable)) + { + printf("vcc\n"); + } + else + { + generate_product_terms(pal, jed, fuse_rows->fuserowoutputenable, buffer); + + printf("%s\n", buffer); + } + } + + printf("\n"); + } +} + + + +/*------------------------------------------------- + print_pal20xxx_product_terms - prints the product + terms for a PAL20X4, PAL20X8 and PAL20X10 +-------------------------------------------------*/ + +static void print_pal20xxx_product_terms(const pal_data* pal, const jed_data* jed) +{ + uint16_t index, columncount, flags, row, haveterms, tmpindex; + char buffer[200]; + int indent, indentindex, rowhasterms[4]; + const pin_fuse_rows* fuse_rows; + + columncount = calc_fuse_column_count(pal); + + print_input_pins(); + print_output_pins(); + + printf("Equations:\n\n"); + + for (index = 0; index < outputpinscount; ++index) + { + flags = outputpins[index].flags; + + indent = 0; + + if (flags & OUTPUT_COMBINATORIAL) + { + sprintf(buffer, LOW_SYMBOL OUTPUT_SYMBOL "%d " COMBINATORIAL_ASSIGNMENT " ", outputpins[index].pin); + + printf("%s", buffer); + + haveterms = 0; + indent += strlen(buffer); + + fuse_rows = find_fuse_rows(pal, outputpins[index].pin); + + for (row = fuse_rows->fuserowtermstart; row <= fuse_rows->fuserowtermend; + row += columncount) + { + generate_product_terms(pal, jed, row, buffer); + + if (strlen(buffer) > 0) + { + if (haveterms) + { + printf(" "); + printf(OR_SYMBOL); + printf("\n"); + + for (indentindex = 0; indentindex < indent; ++indentindex) + { + printf(" "); + } + } + else + { + haveterms = 1; + } + + printf("%s", buffer); + } + } + + printf("\n"); + + /* output enable equation */ + + printf(OUTPUT_SYMBOL "%d.oe " COMBINATORIAL_ASSIGNMENT " ", outputpins[index].pin); + + if (all_fuses_in_row_blown(pal, jed, fuse_rows->fuserowoutputenable)) + { + printf("vcc\n"); + } + else + { + generate_product_terms(pal, jed, fuse_rows->fuserowoutputenable, buffer); + + printf("%s\n", buffer); + } + } + else if (flags & OUTPUT_REGISTERED) + { + sprintf(buffer, LOW_SYMBOL REGISTERED_FEEDBACK_SYMBOL "%d " REGISTERED_ASSIGNMENT " ", outputpins[index].pin); + + printf("%s", buffer); + + haveterms = 0; + indent += strlen(buffer); + + fuse_rows = find_fuse_rows(pal, outputpins[index].pin); + tmpindex = 0; + + memset(rowhasterms, 0, sizeof(rowhasterms)); + + for (row = fuse_rows->fuserowtermstart; row <= fuse_rows->fuserowtermend; + row += columncount) + { + generate_product_terms(pal, jed, row, buffer); + + if (strlen(buffer) > 0) + { + rowhasterms[tmpindex] = 1; + + if (haveterms) + { + if (tmpindex == 1) + { + printf(" " OR_SYMBOL "\n"); + } + else if (tmpindex == 2) + { + printf(" " XOR_SYMBOL "\n"); + } + else if (tmpindex == 3) + { + if (rowhasterms[2]) + { + printf(" " OR_SYMBOL "\n"); + } + else + { + printf(" " XOR_SYMBOL "\n"); + } + } + + for (indentindex = 0; indentindex < indent; ++indentindex) + { + printf(" "); + } + } + else + { + haveterms = 1; + } + + printf("%s", buffer); + } + + ++tmpindex; + } + + printf("\n"); + + /* output enable equation */ + + printf(REGISTERED_FEEDBACK_SYMBOL "%d.oe " COMBINATORIAL_ASSIGNMENT " OE\n", outputpins[index].pin); + } + else + { + fprintf(stderr, "Unknown output type for pin %d!\n", outputpins[index].pin); + } + + printf("\n"); + } +} + + + +/*------------------------------------------------- + print_pal10l8_product_terms - prints the product + terms for a PAL10L8 +-------------------------------------------------*/ + +static void print_pal10l8_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal10h8_product_terms - prints the product + terms for a PAL10H8 +-------------------------------------------------*/ + +static void print_pal10h8_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal12l6_product_terms - prints the product + terms for a PAL12L6 +-------------------------------------------------*/ + +static void print_pal12l6_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal12h6_product_terms - prints the product + terms for a PAL12H6 +-------------------------------------------------*/ + +static void print_pal12h6_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal14l4_product_terms - prints the product + terms for a PAL14L4 +-------------------------------------------------*/ + +static void print_pal14l4_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal14h4_product_terms - prints the product + terms for a PAL14H4 +-------------------------------------------------*/ + +static void print_pal14h4_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal16l2_product_terms - prints the product + terms for a PAL16L2 +-------------------------------------------------*/ + +static void print_pal16l2_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal16h2_product_terms - prints the product + terms for a PAL16H2 +-------------------------------------------------*/ + +static void print_pal16h2_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal16h2_product_terms - prints the product + terms for a PAL16C1 +-------------------------------------------------*/ + +static void print_pal16c1_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal16l8_product_terms - prints the product + terms for a PAL16L8 +-------------------------------------------------*/ + +static void print_pal16l8_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal16r4_product_terms - prints the product + terms for a PAL16R4 +-------------------------------------------------*/ + +static void print_pal16r4_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal16r6_product_terms - prints the product + terms for a PAL16R6 +-------------------------------------------------*/ + +static void print_pal16r6_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal16r8_product_terms - prints the product + terms for a PAL16R8 +-------------------------------------------------*/ + +static void print_pal16r8_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_gal16v8_product_terms - prints the product + terms for a GAL16V8 +-------------------------------------------------*/ + +static void print_gal16v8_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_peel18cv8_product_terms - prints the product + terms for a PEEL18CV8 +-------------------------------------------------*/ + +static void print_peel18cv8_product_terms(const pal_data* pal, const jed_data* jed) +{ + char buffer[200]; + + print_product_terms(pal, jed); + + /* Synchronous Preset */ + + generate_product_terms(pal, jed, 2592, buffer); + + if (strlen(buffer)) + { + printf("Synchronous Preset:\n\n"); + printf("%s\n", buffer); + printf("\n"); + } + + /* Asynchronous Clear */ + + generate_product_terms(pal, jed, 2628, buffer); + + if (strlen(buffer)) + { + printf("Asynchronous Clear:\n\n"); + printf("%s\n", buffer); + printf("\n"); + } +} + + + +/*------------------------------------------------- + print_ampal18p8_product_terms - prints the product + terms for an AMPAL18P8 +-------------------------------------------------*/ + +static void print_ampal18p8_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + +/*------------------------------------------------- + print_gal18v10_product_terms - prints the product + terms for a GAL18V10 +-------------------------------------------------*/ + +static void print_gal18v10_product_terms(const pal_data* pal, const jed_data* jed) +{ + char buffer[200]; + + print_product_terms(pal, jed); + + /* Synchronous Preset */ + + generate_product_terms(pal, jed, 3420, buffer); + + if (strlen(buffer)) + { + printf("Synchronous Preset:\n\n"); + printf("%s\n", buffer); + printf("\n"); + } + + /* Asynchronous Reset */ + + generate_product_terms(pal, jed, 0, buffer); + + if (strlen(buffer)) + { + printf("Asynchronous Reset:\n\n"); + printf("%s\n", buffer); + printf("\n"); + } +} + + + +/*------------------------------------------------- + print_pal20l8_product_terms - prints the product + terms for a PAL20L8 +-------------------------------------------------*/ + +static void print_pal20l8_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal20l10_product_terms - prints the product + terms for a PAL20L10 +-------------------------------------------------*/ + +static void print_pal20l10_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal20r4_product_terms - prints the product + terms for a PAL20R4 +-------------------------------------------------*/ + +static void print_pal20r4_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal20r6_product_terms - prints the product + terms for a PAL20R6 +-------------------------------------------------*/ + +static void print_pal20r6_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal20r8_product_terms - prints the product + terms for a PAL20R8 +-------------------------------------------------*/ + +static void print_pal20r8_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- +print_pal20ra10_product_terms - prints the product +terms for a PAL20RA10 +-------------------------------------------------*/ + +static void print_pal20ra10_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal20x4_product_terms - prints the product + terms for a PAL20X4 +-------------------------------------------------*/ + +static void print_pal20x4_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_pal20xxx_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal20x8_product_terms - prints the product + terms for a PAL20X8 +-------------------------------------------------*/ + +static void print_pal20x8_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_pal20xxx_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal20x10_product_terms - prints the product + terms for a PAL20X10 +-------------------------------------------------*/ + +static void print_pal20x10_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_pal20xxx_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_gal20v8_product_terms - prints the product + terms for a GAL20V8 +-------------------------------------------------*/ + +static void print_gal20v8_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_palce22v10_pal22v10_product_terms - prints the product + terms for a PALCE22V10 and PAL22V10 +-------------------------------------------------*/ + +static void print_palce22v10_pal22v10_product_terms(const pal_data* pal, const jed_data* jed) +{ + char buffer[200]; + + print_product_terms(pal, jed); + + /* Synchronous Preset */ + + generate_product_terms(pal, jed, 5764, buffer); + + if (strlen(buffer)) + { + printf("Synchronous Preset:\n\n"); + printf("%s\n", buffer); + printf("\n"); + } + + /* Asynchronous Reset */ + + generate_product_terms(pal, jed, 0, buffer); + + if (strlen(buffer)) + { + printf("Asynchronous Reset:\n\n"); + printf("%s\n", buffer); + printf("\n"); + } +} + + + +/*------------------------------------------------- + print_gal22v10_product_terms - prints the product + terms for a GAL22V10 +-------------------------------------------------*/ + +static void print_gal22v10_product_terms(const pal_data* pal, const jed_data* jed) +{ + char buffer[200]; + + print_product_terms(pal, jed); + + /* Synchronous Preset */ + + generate_product_terms(pal, jed, 5764, buffer); + + if (strlen(buffer)) + { + printf("Synchronous Preset:\n\n"); + printf("%s\n", buffer); + printf("\n"); + } + + /* Asynchronous Reset */ + + generate_product_terms(pal, jed, 0, buffer); + + if (strlen(buffer)) + { + printf("Asynchronous Reset:\n\n"); + printf("%s\n", buffer); + printf("\n"); + } +} + +/*------------------------------------------------- + print_atf22v10_power_down_mode_product_terms - prints the product + terms for a ATF22V10 configured in power-down mode +-------------------------------------------------*/ + +static void print_atf22v10_power_down_mode_product_terms(const pal_data* pal, const jed_data* jed) +{ + char buffer[200]; + + print_product_terms(pal, jed); + + /* Synchronous Preset */ + + generate_product_terms(pal, jed, 5764, buffer); + + if (strlen(buffer)) + { + printf("Synchronous Preset:\n\n"); + printf("%s\n", buffer); + printf("\n"); + } + + /* Asynchronous Reset */ + + generate_product_terms(pal, jed, 0, buffer); + + if (strlen(buffer)) + { + printf("Asynchronous Reset:\n\n"); + printf("%s\n", buffer); + printf("\n"); + } + + /* Pin 4 (DIP/SOIC package) and Pin 5 (PLCC package) controls power down mode */ + + printf("Pin 4 (DIP/SOIC package) and Pin 5 (PLCC package) Controls Power Down Mode\n\n"); + printf("\n"); +} + + + +/*------------------------------------------------- + print_82s153_pls153_product_terms - prints the product + terms for a 82S153/PLS153 +-------------------------------------------------*/ + +static void print_82s153_pls153_product_terms(const pal_data* pal, const jed_data* jed) +{ + uint16_t index, columncount, flags, row, haveterms, or_column, fuserow; + char buffer[200]; + int indent, indentindex; + const pin_fuse_rows* fuse_rows; + + columncount = calc_fuse_column_count(pal); + + print_input_pins(); + print_output_pins(); + + printf("Equations:\n\n"); + + for (index = 0; index < outputpinscount; ++index) + { + flags = outputpins[index].flags; + + indent = 0; + + if (flags & OUTPUT_ACTIVELOW) + { + printf(LOW_SYMBOL); + + indent += strlen(LOW_SYMBOL); + } + + sprintf(buffer, OUTPUT_SYMBOL "%d " COMBINATORIAL_ASSIGNMENT " ", outputpins[index].pin); + + printf("%s", buffer); + + haveterms = 0; + indent += strlen(buffer); + + fuse_rows = find_fuse_rows(pal, outputpins[index].pin); + fuserow = 0; + + if (outputpins[index].pin != 9) + { + or_column = 19 - outputpins[index].pin; + } + else + { + or_column = 9; + } + + for (row = 0; row < 32; ++row) + { + if (!jed_get_fuse(jed, fuserow + columncount + or_column)) + { + generate_product_terms(pal, jed, fuserow, buffer); + + if (strlen(buffer) > 0) + { + if (haveterms) + { + printf(" " OR_SYMBOL "\n"); + + for (indentindex = 0; indentindex < indent; ++indentindex) + { + printf(" "); + } + } + else + { + haveterms = 1; + } + + printf("%s", buffer); + } + } + + fuserow += (columncount + 10); + } + + printf("\n"); + + /* output enable equations */ + + printf(OUTPUT_SYMBOL "%d.oe " COMBINATORIAL_ASSIGNMENT " ", outputpins[index].pin); + + if (all_fuses_in_row_blown(pal, jed, fuse_rows->fuserowoutputenable)) + { + printf("vcc\n"); + } + else + { + generate_product_terms(pal, jed, fuse_rows->fuserowoutputenable, buffer); + + printf("%s\n", buffer); + } + + printf("\n"); + } +} + + + +/*------------------------------------------------- + print_ck2605_product_terms - prints the product + terms for a CK2605 +-------------------------------------------------*/ + +static void print_ck2605_product_terms(const pal_data* pal, const jed_data* jed) +{ + uint16_t index, columncount, flags, row, haveterms, or_column, fuserow; + char buffer[200]; + int indent, indentindex; + const pin_fuse_rows* fuse_rows; + + columncount = calc_fuse_column_count(pal); + + print_input_pins(); + print_output_pins(); + + printf("Equations:\n\n"); + + for (index = 0; index < outputpinscount; ++index) + { + flags = outputpins[index].flags; + + indent = 0; + + if (flags & OUTPUT_ACTIVELOW) + { + printf(LOW_SYMBOL); + + indent += strlen(LOW_SYMBOL); + } + + sprintf(buffer, OUTPUT_SYMBOL "%d " COMBINATORIAL_ASSIGNMENT " ", outputpins[index].pin); + + printf("%s", buffer); + + haveterms = 0; + indent += strlen(buffer); + + fuse_rows = find_fuse_rows(pal, outputpins[index].pin); + fuserow = 0; + + if (outputpins[index].pin != 9) + { + or_column = 19 - outputpins[index].pin; + } + else + { + or_column = 9; + } + + for (row = 0; row < 16; ++row) + { + if (!jed_get_fuse(jed, fuserow + columncount + or_column)) + { + generate_product_terms(pal, jed, fuserow, buffer); + + if (strlen(buffer) > 0) + { + if (haveterms) + { + printf(" " OR_SYMBOL "\n"); + + for (indentindex = 0; indentindex < indent; ++indentindex) + { + printf(" "); + } + } + else + { + haveterms = 1; + } + + printf("%s", buffer); + } + } + + fuserow += (columncount + 10); + } + + printf("\n"); + + /* output enable equations */ + + printf(OUTPUT_SYMBOL "%d.oe " COMBINATORIAL_ASSIGNMENT " ", outputpins[index].pin); + + if (all_fuses_in_row_blown(pal, jed, fuse_rows->fuserowoutputenable)) + { + printf("vcc\n"); + } + else + { + generate_product_terms(pal, jed, fuse_rows->fuserowoutputenable, buffer); + + printf("%s\n", buffer); + } + + printf("\n"); + } +} + + + +#if defined(ricoh_pals) +/*------------------------------------------------- + print_epl10p8_product_terms - prints the product + terms for a EPL10P8 +-------------------------------------------------*/ + +static void print_epl10p8_product_terms(const pal_data* pal, const jed_data* jed) +{ + typedef struct _memory_cell memory_cell; + struct _memory_cell + { + uint16_t pin; + uint16_t or_fuse; /* 0 - intact? */ + uint16_t xor_fuse; /* 0 - intact? */ + }; + + static memory_cell memory_cells[] = { + {12, 661, 662}, + {13, 658, 659}, + {14, 655, 656}, + {15, 652, 653}, + {16, 649, 650}, + {17, 646, 647}, + {18, 643, 644}, + {19, 640, 641}}; + uint16_t index, columncount, flags, haveterms, fuserow; + char buffer[200]; + int indent, row, indentindex; + const pin_fuse_rows* fuse_rows; + + printf("Warning: This is experimental support!\n"); + + columncount = calc_fuse_column_count(pal); + + print_input_pins(); + print_output_pins(); + + printf("Equations:\n\n"); + + for (index = 0; index < std::size(memory_cells); ++index) + { + flags = outputpins[index].flags; + + indent = 0; + + if (flags & OUTPUT_ACTIVELOW) + { + printf(LOW_SYMBOL); + + indent += strlen(LOW_SYMBOL); + } + + sprintf(buffer, OUTPUT_SYMBOL "%d " COMBINATORIAL_ASSIGNMENT " ", outputpins[index].pin); + + printf("%s", buffer); + + haveterms = 0; + indent += strlen(buffer); + + fuse_rows = find_fuse_rows(pal, outputpins[index].pin); + + if (!jed_get_fuse(jed, memory_cells[index].or_fuse) || + !jed_get_fuse(jed, memory_cells[index].xor_fuse)) + { + /* MMI PAL pin compatible configuration */ + + fuserow = fuse_rows->fuserowtermstart; + + for (row = 0; row < 2; ++row) + { + generate_product_terms(pal, jed, fuserow, buffer); + + if (strlen(buffer) > 0) + { + if (haveterms) + { + printf(" " OR_SYMBOL "\n"); + + for (indentindex = 0; indentindex < indent; ++indentindex) + { + printf(" "); + } + } + else + { + haveterms = 1; + } + + printf("%s", buffer); + } + + fuserow += columncount; + } + + printf("\n"); + + printf(OUTPUT_SYMBOL "%d.oe " COMBINATORIAL_ASSIGNMENT " vcc\n", outputpins[index].pin); + + printf("\n"); + } + else if (!jed_get_fuse(jed, memory_cells[index].or_fuse) || + jed_get_fuse(jed, memory_cells[index].xor_fuse)) + { + /* or configuration */ + + fuserow = fuse_rows->fuserowtermstart; + + for (row = 0; row < 4; ++row) + { + generate_product_terms(pal, jed, fuserow, buffer); + + if (strlen(buffer) > 0) + { + if (haveterms) + { + printf(" " OR_SYMBOL "\n"); + + for (indentindex = 0; indentindex < indent; ++indentindex) + { + printf(" "); + } + } + else + { + haveterms = 1; + } + + printf("%s", buffer); + } + + fuse_rows += columncount; + } + + printf("\n"); + + printf(OUTPUT_SYMBOL "%d.oe " COMBINATORIAL_ASSIGNMENT " vcc\n", outputpins[index].pin); + + printf("\n"); + } + else if (jed_get_fuse(jed, memory_cells[index].or_fuse) || + !jed_get_fuse(jed, memory_cells[index].xor_fuse)) + { + /* xor configuration */ + } + else + { + fprintf(stderr, "Unknown fuse configuration for pin %d!", memory_cells[index].pin); + } + } + + printf("Warning: This is experimental support!\n"); +} + + + +/*------------------------------------------------- + print_epl12p6_product_terms - prints the product + terms for a EPL12P6 +-------------------------------------------------*/ + +static void print_epl12p6_product_terms(const pal_data* pal, const jed_data* jed) +{ + fprintf(stderr, "Printing product terms not supported for this device!\n"); +} + + + +/*------------------------------------------------- + print_epl14p4_product_terms - prints the product + terms for a EPL14P4 +-------------------------------------------------*/ + +static void print_epl14p4_product_terms(const pal_data* pal, const jed_data* jed) +{ + fprintf(stderr, "Printing product terms not supported for this device!\n"); +} + + + +/*------------------------------------------------- + print_epl16p2_product_terms - prints the product + terms for a EPL16P2 +-------------------------------------------------*/ + +static void print_epl16p2_product_terms(const pal_data* pal, const jed_data* jed) +{ + fprintf(stderr, "Printing product terms not supported for this device!\n"); +} + + + +/*------------------------------------------------- + print_epl16p8_product_terms - prints the product + terms for a EPL16P8 +-------------------------------------------------*/ + +static void print_epl16p8_product_terms(const pal_data* pal, const jed_data* jed) +{ + fprintf(stderr, "Printing product terms not supported for this device!\n"); +} + + + +/*------------------------------------------------- + print_epl16rp8_product_terms - prints the product + terms for a EPL16RP8 +-------------------------------------------------*/ + +static void print_epl16rp8_product_terms(const pal_data* pal, const jed_data* jed) +{ + fprintf(stderr, "Printing product terms not supported for this device!\n"); +} + + + +/*------------------------------------------------- + print_epl16rp6_product_terms - prints the product + terms for a EPL16RP6 +-------------------------------------------------*/ + +static void print_epl16rp6_product_terms(const pal_data* pal, const jed_data* jed) +{ + fprintf(stderr, "Printing product terms not supported for this device!\n"); +} + + + +/*------------------------------------------------- + print_epl16rp4_product_terms - prints the product + terms for a EPL16RP4 +-------------------------------------------------*/ + +static void print_epl16rp4_product_terms(const pal_data* pal, const jed_data* jed) +{ + fprintf(stderr, "Printing product terms not supported for this device!\n"); +} +#endif + + + +/*------------------------------------------------- + print_pal10p8_product_terms - prints the product + terms for a PAL10P8 +-------------------------------------------------*/ + +static void print_pal10p8_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_epl12p6_product_terms - prints the product + terms for a PAL12P6 +-------------------------------------------------*/ + +static void print_pal12p6_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_epl14p4_product_terms - prints the product + terms for a PAL14P4 +-------------------------------------------------*/ + +static void print_pal14p4_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_epl16p2_product_terms - prints the product + terms for a PAL16P2 +-------------------------------------------------*/ + +static void print_pal16p2_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal16p8_product_terms - prints the product + terms for a PAL16P8 +-------------------------------------------------*/ + +static void print_pal16p8_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal16rp4_product_terms - prints the product + terms for a PAL16RP4 +-------------------------------------------------*/ + +static void print_pal16rp4_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal16rp6_product_terms - prints the product + terms for a PAL16RP6 +-------------------------------------------------*/ + +static void print_pal16rp6_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal16rp8_product_terms - prints the product + terms for a PAL16RP8 +-------------------------------------------------*/ + +static void print_pal16rp8_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal6l16_product_terms - prints the product + terms for a PAL6L16 +-------------------------------------------------*/ + +static void print_pal6l16_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal8l14_product_terms - prints the product + terms for a PAL8L14 +-------------------------------------------------*/ + +static void print_pal8l14_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal12h10_product_terms - prints the product + terms for a PAL12H10 +-------------------------------------------------*/ + +static void print_pal12h10_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal12l10_product_terms - prints the product + terms for a PAL12L10 +-------------------------------------------------*/ + +static void print_pal12l10_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal14h8_product_terms - prints the product + terms for a PAL14H8 +-------------------------------------------------*/ + +static void print_pal14h8_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal14l8_product_terms - prints the product + terms for a PAL14L8 +-------------------------------------------------*/ + +static void print_pal14l8_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal16h6_product_terms - prints the product + terms for a PAL16H6 +-------------------------------------------------*/ + +static void print_pal16h6_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal16l6_product_terms - prints the product + terms for a PAL16L6 +-------------------------------------------------*/ + +static void print_pal16l6_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal18h4_product_terms - prints the product + terms for a PAL18H4 +-------------------------------------------------*/ + +static void print_pal18h4_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal18l4_product_terms - prints the product + terms for a PAL18L4 +-------------------------------------------------*/ + +static void print_pal18l4_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal20lc1_product_terms - prints the product + terms for a PAL20LC1 +-------------------------------------------------*/ + +static void print_pal20c1_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_pal20l2_product_terms - prints the product + terms for a PAL20L2 +-------------------------------------------------*/ + +static void print_pal20l2_product_terms(const pal_data* pal, const jed_data* jed) +{ + print_product_terms(pal, jed); +} + + + +/*------------------------------------------------- + print_82s100_pls100_product_terms - prints the product + terms for a 82S100 and PLS100 +-------------------------------------------------*/ + +static void print_82s100_pls100_product_terms(const pal_data* pal, const jed_data* jed) +{ + uint16_t index, columncount, flags, row, haveterms, or_column, fuserow; + char buffer[200]; + int indent, indentindex; + + columncount = calc_fuse_column_count(pal); + + print_input_pins(); + print_output_pins(); + + printf("Equations:\n\n"); + + for (index = 0; index < outputpinscount; ++index) + { + flags = outputpins[index].flags; + + indent = 0; + + if (flags & OUTPUT_ACTIVELOW) + { + printf(LOW_SYMBOL); + + indent += strlen(LOW_SYMBOL); + } + + sprintf(buffer, OUTPUT_SYMBOL "%d " COMBINATORIAL_ASSIGNMENT " ", outputpins[index].pin); + + printf("%s", buffer); + + haveterms = 0; + indent += strlen(buffer); + + fuserow = 0; + + if (outputpins[index].pin >= 15) + { + or_column = 18 - outputpins[index].pin; + } + else + { + or_column = 17 - outputpins[index].pin; + } + + for (row = 0; row < 48; ++row) + { + if (!jed_get_fuse(jed, fuserow + columncount + or_column)) + { + generate_product_terms(pal, jed, fuserow, buffer); + + if (strlen(buffer) > 0) + { + if (haveterms) + { + printf(" " OR_SYMBOL "\n"); + + for (indentindex = 0; indentindex < indent; ++indentindex) + { + printf(" "); + } + } + else + { + haveterms = 1; + } + + printf("%s", buffer); + } + } + + fuserow += (columncount + 8); + } + + printf("\n"); + + /* output enable equations */ + + printf(OUTPUT_SYMBOL "%d.oe " COMBINATORIAL_ASSIGNMENT " ", outputpins[index].pin); + printf("OE\n"); + + printf("\n"); + } +} + + + +/*------------------------------------------------- + config_pal10l8_pins - configures the pins for + a PAL10L8 +-------------------------------------------------*/ + +static void config_pal10l8_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11}; + static pin_output_config output_pins[] = { + {12, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {13, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {14, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {15, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {16, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {17, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {18, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {19, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal10h8_pins - configures the pins for + a PAL10H8 +-------------------------------------------------*/ + +static void config_pal10h8_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11}; + static pin_output_config output_pins[] = { + {12, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {13, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {14, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {15, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {16, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {17, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {18, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {19, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal12l6_pins - configures the pins for + a PAL12L6 +-------------------------------------------------*/ + +static void config_pal12l6_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 19}; + static pin_output_config output_pins[] = { + {13, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {14, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {15, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {16, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {17, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {18, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal12h6_pins - configures the pins for + a PAL12H6 +-------------------------------------------------*/ + +static void config_pal12h6_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 19}; + static pin_output_config output_pins[] = { + {13, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {14, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {15, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {16, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {17, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {18, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal14l4_pins - configures the pins for + a PAL14L4 +-------------------------------------------------*/ + +static void config_pal14l4_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 18, 19}; + static pin_output_config output_pins[] = { + {14, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {15, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {16, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {17, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal14h4_pins - configures the pins for + a PAL14H4 +-------------------------------------------------*/ + +static void config_pal14h4_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 18, 19}; + static pin_output_config output_pins[] = { + {14, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {15, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {16, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {17, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal16l2_pins - configures the pins for + a PAL16L2 +-------------------------------------------------*/ + +static void config_pal16l2_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 17, 18, 19}; + static pin_output_config output_pins[] = { + {15, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {16, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal16h2_pins - configures the pins for + a PAL16H2 +-------------------------------------------------*/ + +static void config_pal16h2_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 17, 18, 19}; + static pin_output_config output_pins[] = { + {15, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {16, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal16c1_pins - configures the pins for + a PAL16C1 +-------------------------------------------------*/ + +static void config_pal16c1_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 17, 18, 19}; + static pin_output_config output_pins[] = { + {15, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {16, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal16l8_pins - configures the pins for + a PAL16L8 +-------------------------------------------------*/ + +static void config_pal16l8_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 14, 15, 16, 17, 18}; + pin_output_config output_pins[8]; + uint16_t output_pin_count, index; + + output_pin_count = 0; + + for (index = 0; index < pal->pinfuserowscount; ++index) + { + if (does_output_enable_fuse_row_allow_output(pal, jed, pal->pinfuserows[index].fuserowoutputenable)) + { + output_pins[output_pin_count].pin = pal->pinfuserows[index].pin; + output_pins[output_pin_count].flags = OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + ++output_pin_count; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, output_pin_count); +} + + + +/*------------------------------------------------- + config_pal16r4_pins - configures the pins for + a PAL16R4 +-------------------------------------------------*/ + +static void config_pal16r4_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19}; + static uint16_t registered_pins[] = {14, 15, 16, 17}; + pin_output_config output_pins[8]; + uint16_t output_pin_count, index; + + output_pin_count = 0; + + if (does_output_enable_fuse_row_allow_output(pal, jed, 1792)) + { + output_pins[output_pin_count].pin = 12; + output_pins[output_pin_count].flags = OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + ++output_pin_count; + } + + if (does_output_enable_fuse_row_allow_output(pal, jed, 1536)) + { + output_pins[output_pin_count].pin = 13; + output_pins[output_pin_count].flags = OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + ++output_pin_count; + } + + for (index = 0; index < std::size(registered_pins); ++index) + { + output_pins[output_pin_count].pin = registered_pins[index]; + output_pins[output_pin_count].flags = OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED; + + ++output_pin_count; + } + + if (does_output_enable_fuse_row_allow_output(pal, jed, 256)) + { + output_pins[output_pin_count].pin = 18; + output_pins[output_pin_count].flags = OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + ++output_pin_count; + } + + if (does_output_enable_fuse_row_allow_output(pal, jed, 0)) + { + output_pins[output_pin_count].pin = 19; + output_pins[output_pin_count].flags = OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + ++output_pin_count; + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, output_pin_count); +} + + + +/*------------------------------------------------- + config_pal16r6_pins - configures the pins + for a PAL16R6 +-------------------------------------------------*/ + +static void config_pal16r6_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19}; + static uint16_t registered_pins[] = {13, 14, 15, 16, 17, 18}; + pin_output_config output_pins[8]; + uint16_t output_pin_count, index; + + output_pin_count = 0; + + if (does_output_enable_fuse_row_allow_output(pal, jed, 1792)) + { + output_pins[output_pin_count].pin = 12; + output_pins[output_pin_count].flags = OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + ++output_pin_count; + } + + for (index = 0; index < std::size(registered_pins); ++index) + { + output_pins[output_pin_count].pin = registered_pins[index]; + output_pins[output_pin_count].flags = OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED; + + ++output_pin_count; + } + + if (does_output_enable_fuse_row_allow_output(pal, jed, 0)) + { + output_pins[output_pin_count].pin = 19; + output_pins[output_pin_count].flags = OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + ++output_pin_count; + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, output_pin_count); +} + + + +/*------------------------------------------------- + config_pal16r8_pins - configures the pins for + a PAL16R8 +-------------------------------------------------*/ + +static void config_pal16r8_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19}; + static pin_output_config output_pins[] = { + {12, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {13, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {14, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {15, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {16, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {17, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {18, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {19, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_gal16v8_pins - configures the pins for + a GAL16V8 +-------------------------------------------------*/ + +static void config_gal16v8_pins(const pal_data* pal, const jed_data* jed) +{ + typedef struct _output_logic_macrocell output_logic_macrocell; + struct _output_logic_macrocell + { + uint16_t pin; + uint16_t xor_fuse; + uint16_t ac1_fuse; + }; + + static output_logic_macrocell macrocells[] = { + {12, 2055, 2127}, + {13, 2054, 2126}, + {14, 2053, 2125}, + {15, 2052, 2124}, + {16, 2051, 2123}, + {17, 2050, 2122}, + {18, 2049, 2121}, + {19, 2048, 2120}}; + static pin_fuse_rows pinfuserows_registered[] = { + {12, NO_OUTPUT_ENABLE_FUSE_ROW, 1792, 2016}, + {13, NO_OUTPUT_ENABLE_FUSE_ROW, 1536, 1760}, + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 1280, 1504}, + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 1024, 1248}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 768, 992}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 512, 736}, + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 256, 480}, + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 224}}; + static pin_fuse_rows pinfuserows_combinatorialcomplex[] = { + {12, 1792, 1824, 2016}, + {13, 1536, 1568, 1760}, + {14, 1280, 1312, 1504}, + {15, 1024, 1056, 1248}, + {16, 768, 800, 992}, + {17, 512, 544, 736}, + {18, 256, 288, 480}, + {19, 0, 32, 224}}; + static pin_fuse_rows pinfuserows_combinatorialsimple[] = { + {12, NO_OUTPUT_ENABLE_FUSE_ROW, 1792, 2016}, + {13, NO_OUTPUT_ENABLE_FUSE_ROW, 1536, 1760}, + {14, NO_OUTPUT_ENABLE_FUSE_ROW, 1280, 1504}, + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 1024, 1248}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 768, 992}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 512, 736}, + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 256, 480}, + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 224}}; + static pin_fuse_columns pinfusecolumns_registered[] = { + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {12, 31, 30}, + {13, 27, 26}, + {14, 23, 22}, + {15, 19, 18}, + {16, 15, 14}, + {17, 11, 10}, + {18, 7, 6}, + {19, 3, 2}}; + static pin_fuse_columns pinfusecolumns_combinatorialcomplex[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {11, 31, 30}, + {13, 27, 26}, + {14, 23, 22}, + {15, 19, 18}, + {16, 15, 14}, + {17, 11, 10}, + {18, 7, 6}}; + static pin_fuse_columns pinfusecolumns_combinatorialsimple[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {11, 31, 30}, + {12, 27, 26}, + {13, 23, 22}, + {14, 19, 18}, + {17, 15, 14}, + {18, 11, 10}, + {19, 7, 6}}; + static uint16_t input_pins_registered[] = {2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19}; + static uint16_t input_pins_combinatorialcomplex[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 14, 15, 16, 17, 18}; + uint16_t input_pins_combinatorialsimple[18]; + pin_output_config output_pins[std::size(macrocells)]; + uint16_t index, input_pin_count, output_pin_count; + + output_pin_count = 0; + + /* SYN Fuse: 0 - registered, 1 - combinatorial */ + + if (jed_get_fuse(jed, 2192)) + { + /* Combinatorial */ + /* AC0 Fuse: 0 - simple mode, 1 - complex mode */ + + if (jed_get_fuse(jed, 2193)) + { + /* Complex Mode */ + + set_input_pins(input_pins_combinatorialcomplex, std::size(input_pins_combinatorialcomplex)); + + memcpy(gal16v8pinfuserows, pinfuserows_combinatorialcomplex, sizeof(pinfuserows_combinatorialcomplex)); + memcpy(gal16v8pinfusecolumns, pinfusecolumns_combinatorialcomplex, sizeof(pinfusecolumns_combinatorialcomplex)); + + for (index = 0; index < std::size(macrocells); ++index) + { + if (is_gal16v8_product_term_enabled(pal, jed, pal->pinfuserows[index].fuserowoutputenable) && + does_output_enable_fuse_row_allow_output(pal, jed, pal->pinfuserows[index].fuserowoutputenable)) + { + output_pins[output_pin_count].pin = macrocells[index].pin; + output_pins[output_pin_count].flags = OUTPUT_COMBINATORIAL; + + if (jed_get_fuse(jed, macrocells[index].xor_fuse)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + + if (output_pins[output_pin_count].pin != 12 && + output_pins[output_pin_count].pin != 19) + { + output_pins[output_pin_count].flags |= OUTPUT_FEEDBACK_OUTPUT; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_FEEDBACK_NONE; + } + + ++output_pin_count; + } + } + } + else + { + /* Simple Mode */ + + input_pin_count = 0; + + for (index = 1; index < 10; ++index) + { + input_pins_combinatorialsimple[input_pin_count] = index; + + ++input_pin_count; + } + + input_pins_combinatorialsimple[input_pin_count] = 11; + + ++input_pin_count; + + memcpy(gal16v8pinfuserows, pinfuserows_combinatorialsimple, sizeof(pinfuserows_combinatorialsimple)); + memcpy(gal16v8pinfusecolumns, pinfusecolumns_combinatorialsimple, sizeof(pinfusecolumns_combinatorialsimple)); + + for (index = 0; index < std::size(macrocells); ++index) + { + if (jed_get_fuse(jed, macrocells[index].ac1_fuse)) + { + /* Pin is for input only */ + + input_pins_combinatorialsimple[input_pin_count] = macrocells[index].pin; + + ++input_pin_count; + + if (macrocells[index].pin == 15 || macrocells[index].pin == 16) + { + fprintf(stderr, "Pin %d cannot be configured as an input pin.\n", + macrocells[index].pin); + } + } + else + { + output_pins[output_pin_count].pin = macrocells[index].pin; + output_pins[output_pin_count].flags = OUTPUT_COMBINATORIAL; + + if (jed_get_fuse(jed, macrocells[index].xor_fuse)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + + if (output_pins[output_pin_count].pin != 15 && + output_pins[output_pin_count].pin != 16) + { + output_pins[output_pin_count].flags |= OUTPUT_FEEDBACK_OUTPUT; + + input_pins_combinatorialsimple[input_pin_count] = macrocells[index].pin; + + ++input_pin_count; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_FEEDBACK_NONE; + } + + ++output_pin_count; + } + } + + set_input_pins(input_pins_combinatorialsimple, input_pin_count); + } + } + else + { + /* Registered */ + + set_input_pins(input_pins_registered, std::size(input_pins_registered)); + + memcpy(gal16v8pinfusecolumns, pinfusecolumns_registered, sizeof(pinfusecolumns_registered)); + + for (index = 0; index < std::size(macrocells); ++index) + { + if (jed_get_fuse(jed, macrocells[index].ac1_fuse)) + { + /* combinatorial pin */ + + gal16v8pinfuserows[index].fuserowoutputenable = pinfuserows_combinatorialcomplex[index].fuserowoutputenable; + gal16v8pinfuserows[index].fuserowtermstart = pinfuserows_combinatorialcomplex[index].fuserowtermstart; + gal16v8pinfuserows[index].fuserowtermend = pinfuserows_combinatorialcomplex[index].fuserowtermend; + + if (is_gal16v8_product_term_enabled(pal, jed, pal->pinfuserows[index].fuserowoutputenable) && + does_output_enable_fuse_row_allow_output(pal, jed, pal->pinfuserows[index].fuserowoutputenable)) + { + output_pins[output_pin_count].pin = macrocells[index].pin; + output_pins[output_pin_count].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + if (jed_get_fuse(jed, macrocells[index].xor_fuse)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + + ++output_pin_count; + } + } + else + { + /* registered pin */ + + gal16v8pinfuserows[index].fuserowoutputenable = pinfuserows_registered[index].fuserowoutputenable; + gal16v8pinfuserows[index].fuserowtermstart = pinfuserows_registered[index].fuserowtermstart; + gal16v8pinfuserows[index].fuserowtermend = pinfuserows_registered[index].fuserowtermend; + + output_pins[output_pin_count].pin = macrocells[index].pin; + output_pins[output_pin_count].flags = OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED; + + if (jed_get_fuse(jed, macrocells[index].xor_fuse)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + + ++output_pin_count; + } + } + } + + set_output_pins(output_pins, output_pin_count); +} + + + +/*------------------------------------------------- + config_peel18cv8_pins - configures the pins + for a PEEL18CV8 +-------------------------------------------------*/ + +static void config_peel18cv8_pins(const pal_data* pal, const jed_data* jed) +{ + typedef struct _output_logic_macrocell output_logic_macrocell; + struct _output_logic_macrocell + { + uint16_t pin; + uint16_t polarity_fuse; /* 0 = active high or 1 = active low */ + uint16_t type_fuse; /* 1 = registered or 0 = combinatorial */ + uint16_t feedback1_fuse; + uint16_t feedback2_fuse; + }; + + static output_logic_macrocell macrocells[] = { + {12, 2692, 2693, 2694, 2695}, + {13, 2688, 2689, 2690, 2691}, + {14, 2684, 2685, 2686, 2687}, + {15, 2680, 2681, 2682, 2683}, + {16, 2676, 2677, 2678, 2679}, + {17, 2672, 2673, 2674, 2675}, + {18, 2668, 2669, 2670, 2671}, + {19, 2664, 2665, 2666, 2667}}; + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19}; + pin_output_config output_pins[std::size(macrocells)]; + uint16_t index, output_pin_count; + + set_input_pins(input_pins, std::size(input_pins)); + + output_pin_count = 0; + + for (index = 0; index < std::size(macrocells); ++index) + { + if (jed_get_fuse(jed, macrocells[index].feedback1_fuse) && + !jed_get_fuse(jed, macrocells[index].feedback2_fuse)) + { + /* Combinatorial Feedback (pin is output only) */ + + output_pins[output_pin_count].pin = macrocells[index].pin; + output_pins[output_pin_count].flags = OUTPUT_FEEDBACK_COMBINATORIAL; + + if (jed_get_fuse(jed, macrocells[index].type_fuse)) + { + output_pins[output_pin_count].flags |= OUTPUT_REGISTERED; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_COMBINATORIAL; + } + + if (jed_get_fuse(jed, macrocells[index].polarity_fuse)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + + ++output_pin_count; + } + else if (!jed_get_fuse(jed, macrocells[index].feedback1_fuse) && + !jed_get_fuse(jed, macrocells[index].feedback2_fuse)) + { + /* Register Feedback (pin is output only) */ + + output_pins[output_pin_count].pin = macrocells[index].pin; + output_pins[output_pin_count].flags = OUTPUT_FEEDBACK_REGISTERED; + + if (jed_get_fuse(jed, macrocells[index].type_fuse)) + { + output_pins[output_pin_count].flags |= OUTPUT_REGISTERED; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_COMBINATORIAL; + } + + if (jed_get_fuse(jed, macrocells[index].polarity_fuse)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + + ++output_pin_count; + } + else if (jed_get_fuse(jed, macrocells[index].feedback1_fuse) && + jed_get_fuse(jed, macrocells[index].feedback2_fuse)) + { + /* Bi-directional I/O (pin can be input or output) */ + + if (does_output_enable_fuse_row_allow_output(pal, jed, pal->pinfuserows[index].fuserowoutputenable)) + { + output_pins[output_pin_count].pin = macrocells[index].pin; + output_pins[output_pin_count].flags = OUTPUT_FEEDBACK_OUTPUT; + + if (jed_get_fuse(jed, macrocells[index].type_fuse)) + { + output_pins[output_pin_count].flags |= OUTPUT_REGISTERED; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_COMBINATORIAL; + } + + if (jed_get_fuse(jed, macrocells[index].polarity_fuse)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + + ++output_pin_count; + } + } + else if (!jed_get_fuse(jed, macrocells[index].feedback1_fuse) && + jed_get_fuse(jed, macrocells[index].feedback2_fuse)) + { + fprintf(stderr, "Unknown input/feedback select configuration. (Pin %d)\n", + macrocells[index].pin); + + continue; + } + } + + set_output_pins(output_pins, output_pin_count); +} + + + +/*------------------------------------------------- + config_ampal18p8_pins - configures the pins + for an AMPAL18P8 +-------------------------------------------------*/ + +static void config_ampal18p8_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19}; + pin_output_config output_pins[8]; + uint16_t index, output_pin_count; + + set_input_pins(input_pins, std::size(input_pins)); + + output_pin_count = 0; + + for (index = 0; index < pal->pinfuserowscount; ++index) + { + if (does_output_enable_fuse_row_allow_output(pal, jed, pal->pinfuserows[index].fuserowoutputenable)) + { + output_pins[output_pin_count].pin = pal->pinfuserows[index].pin; + output_pins[output_pin_count].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + if (jed_get_fuse(jed, 2591 + (8 - index))) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + + ++output_pin_count; + } + } + + set_output_pins(output_pins, output_pin_count); +} + + + +/*------------------------------------------------- + config_gal18v10_pins - configures the pins + for a GAL18V10 +-------------------------------------------------*/ + +static void config_gal18v10_pins(const pal_data* pal, const jed_data* jed) +{ + typedef struct _output_logic_macrocell output_logic_macrocell; + struct _output_logic_macrocell + { + uint16_t pin; + uint16_t s0_fuse; /* 0 - active low, 1 - active high */ + uint16_t s1_fuse; /* 0 - registered, 1 - combinatorial */ + }; + + static output_logic_macrocell macrocells[] = { + {9, 3474, 3475}, + {11, 3472, 3473}, + {12, 3470, 3471}, + {13, 3468, 3469}, + {14, 3466, 3467}, + {15, 3464, 3465}, + {16, 3462, 3463}, + {17, 3460, 3461}, + {18, 3458, 3459}, + {19, 3456, 3457}}; + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19}; + pin_output_config output_pins[std::size(macrocells)]; + uint16_t index, output_pin_count; + + output_pin_count = 0; + + for (index = 0; index < std::size(output_pins); ++index) + { + if (jed_get_fuse(jed, macrocells[index].s1_fuse)) + { + /* Combinatorial output or dedicated input */ + + if (does_output_enable_fuse_row_allow_output(pal, jed, pal->pinfuserows[index].fuserowoutputenable)) + { + output_pins[output_pin_count].pin = macrocells[index].pin; + output_pins[output_pin_count].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + if (!jed_get_fuse(jed, macrocells[index].s0_fuse)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + + ++output_pin_count; + } + } + else + { + /* Registered output */ + + output_pins[output_pin_count].pin = macrocells[index].pin; + output_pins[output_pin_count].flags = OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED; + + if (!jed_get_fuse(jed, macrocells[index].s0_fuse)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + + ++output_pin_count; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, output_pin_count); +} + + + +/*------------------------------------------------- + config_pal20l8_pins - configures the pins for + a PAL20L8 +-------------------------------------------------*/ + +static void config_pal20l8_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 16, 17, 18, 19, 20, 21, 23}; + pin_output_config output_pins[8]; + uint16_t output_pin_count, index; + + output_pin_count = 0; + + for (index = 0; index < pal->pinfuserowscount; ++index) + { + if (does_output_enable_fuse_row_allow_output(pal, jed, pal->pinfuserows[index].fuserowoutputenable)) + { + output_pins[output_pin_count].pin = pal->pinfuserows[index].pin; + output_pins[output_pin_count].flags = OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL; + + if (pal->pinfuserows[index].pin != 15 && + pal->pinfuserows[index].pin != 22) + { + output_pins[output_pin_count].flags |= OUTPUT_FEEDBACK_OUTPUT; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_FEEDBACK_NONE; + } + + ++output_pin_count; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, output_pin_count); +} + + + +/*------------------------------------------------- + config_pal20l10_pins - configures the pins for + a PAL20L10 +-------------------------------------------------*/ + +static void config_pal20l10_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 16, 17, 18, 19, 20, 21, 22}; + pin_output_config output_pins[10]; + uint16_t output_pin_count, index; + + output_pin_count = 0; + + for (index = 0; index < pal->pinfuserowscount; ++index) + { + if (does_output_enable_fuse_row_allow_output(pal, jed, pal->pinfuserows[index].fuserowoutputenable)) + { + output_pins[output_pin_count].pin = pal->pinfuserows[index].pin; + output_pins[output_pin_count].flags = OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL; + + if (pal->pinfuserows[index].pin != 23 && + pal->pinfuserows[index].pin != 14) + { + output_pins[output_pin_count].flags |= OUTPUT_FEEDBACK_OUTPUT; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_FEEDBACK_NONE; + } + + ++output_pin_count; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, output_pin_count); +} + + + +/*------------------------------------------------- + config_pal20r4_pins - configures the pins for + a PAL20R4 +-------------------------------------------------*/ + +static void config_pal20r4_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; + static uint16_t registered_pins[] = {17, 18, 19, 20}; + pin_output_config output_pins[8]; + uint16_t output_pin_count, index; + + output_pin_count = 0; + + if (does_output_enable_fuse_row_allow_output(pal, jed, 2240)) + { + output_pins[output_pin_count].pin = 15; + output_pins[output_pin_count].flags = OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + ++output_pin_count; + } + + if (does_output_enable_fuse_row_allow_output(pal, jed, 1920)) + { + output_pins[output_pin_count].pin = 16; + output_pins[output_pin_count].flags = OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + ++output_pin_count; + } + + for (index = 0; index < std::size(registered_pins); ++index) + { + output_pins[output_pin_count].pin = registered_pins[index]; + output_pins[output_pin_count].flags = OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED; + + ++output_pin_count; + } + + if (does_output_enable_fuse_row_allow_output(pal, jed, 320)) + { + output_pins[output_pin_count].pin = 21; + output_pins[output_pin_count].flags = OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + ++output_pin_count; + } + + if (does_output_enable_fuse_row_allow_output(pal, jed, 0)) + { + output_pins[output_pin_count].pin = 22; + output_pins[output_pin_count].flags = OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + ++output_pin_count; + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, output_pin_count); +} + + + +/*------------------------------------------------- + config_pal20r6_pins - configures the pins for + a PAL20R6 +-------------------------------------------------*/ + +static void config_pal20r6_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; + static uint16_t registered_pins[] = {16, 17, 18, 19, 20, 21}; + pin_output_config output_pins[8]; + uint16_t output_pin_count, index; + + output_pin_count = 0; + + if (does_output_enable_fuse_row_allow_output(pal, jed, 2240)) + { + output_pins[output_pin_count].pin = 15; + output_pins[output_pin_count].flags = OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + ++output_pin_count; + } + + for (index = 0; index < std::size(registered_pins); ++index) + { + output_pins[output_pin_count].pin = registered_pins[index]; + output_pins[output_pin_count].flags = OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED; + + ++output_pin_count; + } + + if (does_output_enable_fuse_row_allow_output(pal, jed, 0)) + { + output_pins[output_pin_count].pin = 22; + output_pins[output_pin_count].flags = OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + ++output_pin_count; + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, output_pin_count); +} + + + +/*------------------------------------------------- + config_pal20r8_pins - configures the pins for + a PAL20R8 +-------------------------------------------------*/ + +static void config_pal20r8_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; + static pin_output_config output_pins[] = { + {15, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {16, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {17, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {18, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {19, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {20, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {21, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {22, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- +config_pal20ra10_pins - configures the pins for +a PAL20RA10 +-------------------------------------------------*/ + +static void config_pal20ra10_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }; + static pin_output_config output_pins[] = { + {14, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {15, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {16, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {17, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {18, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {19, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {20, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {21, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {22, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {23, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal20x4_pins - configures the pins for + a PAL20X4 +-------------------------------------------------*/ + +static void config_pal20x4_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; + static pin_output_config output_pins[] = { + {14, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT}, + {15, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT}, + {16, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT}, + {17, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {18, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {19, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {20, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {21, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT}, + {22, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT}, + {23, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + +/*------------------------------------------------- + config_pal20x8_pins - configures the pins for + a PAL20X8 +-------------------------------------------------*/ + +static void config_pal20x8_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; + static pin_output_config output_pins[] = { + {14, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT}, + {15, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {16, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {17, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {18, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {19, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {20, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {21, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {22, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {23, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal20x10_pins - configures the pins for + a PAL20X10 +-------------------------------------------------*/ + +static void config_pal20x10_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; + static pin_output_config output_pins[] = { + {14, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {15, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {16, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {17, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {18, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {19, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {20, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {21, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {22, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {23, OUTPUT_ACTIVELOW | OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_gal20v8_pins - configures the pins for + a GAL20V8 +-------------------------------------------------*/ + +static void config_gal20v8_pins(const pal_data* pal, const jed_data* jed) +{ + typedef struct _output_logic_macrocell output_logic_macrocell; + struct _output_logic_macrocell + { + uint16_t pin; + uint16_t xor_fuse; + uint16_t ac1_fuse; + }; + + static output_logic_macrocell macrocells[] = { + {15, 2567, 2639}, + {16, 2566, 2638}, + {17, 2565, 2637}, + {18, 2564, 2636}, + {19, 2563, 2635}, + {20, 2562, 2634}, + {21, 2561, 2633}, + {22, 2560, 2632}}; + static pin_fuse_rows pinfuserows_registered[] = { + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 2240, 2520}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 1920, 2200}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 1600, 1880}, + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 1280, 1560}, + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 960, 1240}, + {20, NO_OUTPUT_ENABLE_FUSE_ROW, 640, 920}, + {21, NO_OUTPUT_ENABLE_FUSE_ROW, 320, 600}, + {22, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 280}}; + static pin_fuse_rows pinfuserows_combinatorialcomplex[] = { + {15, 2240, 2280, 2520}, + {16, 1920, 1960, 2200}, + {17, 1600, 1640, 1880}, + {18, 1280, 1320, 1560}, + {19, 960, 1000, 1240}, + {20, 640, 680, 920}, + {21, 320, 360, 600}, + {22, 0, 40, 280}}; + static pin_fuse_rows pinfuserows_combinatorialsimple[] = { + {15, NO_OUTPUT_ENABLE_FUSE_ROW, 2240, 2520}, + {16, NO_OUTPUT_ENABLE_FUSE_ROW, 1920, 2200}, + {17, NO_OUTPUT_ENABLE_FUSE_ROW, 1600, 1880}, + {18, NO_OUTPUT_ENABLE_FUSE_ROW, 1280, 1560}, + {19, NO_OUTPUT_ENABLE_FUSE_ROW, 960, 1240}, + {20, NO_OUTPUT_ENABLE_FUSE_ROW, 640, 920}, + {21, NO_OUTPUT_ENABLE_FUSE_ROW, 320, 600}, + {22, NO_OUTPUT_ENABLE_FUSE_ROW, 0, 280}}; + static pin_fuse_columns pinfusecolumns_registered[] = { + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {10, 33, 32}, + {11, 37, 36}, + {14, 39, 38}, + {15, 35, 34}, + {16, 31, 30}, + {17, 27, 26}, + {18, 23, 22}, + {19, 19, 18}, + {20, 15, 14}, + {21, 11, 10}, + {22, 7, 6}, + {23, 3, 2}}; + static pin_fuse_columns pinfusecolumns_combinatorialcomplex[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {10, 33, 32}, + {11, 37, 36}, + {13, 39, 38}, + {14, 35, 34}, + {16, 31, 30}, + {17, 27, 26}, + {18, 23, 22}, + {19, 19, 18}, + {20, 15, 14}, + {21, 11, 10}, + {23, 7, 6}}; + static pin_fuse_columns pinfusecolumns_combinatorialsimple[] = { + {1, 3, 2}, + {2, 1, 0}, + {3, 5, 4}, + {4, 9, 8}, + {5, 13, 12}, + {6, 17, 16}, + {7, 21, 20}, + {8, 25, 24}, + {9, 29, 28}, + {10, 33, 32}, + {11, 37, 36}, + {13, 39, 38}, + {14, 35, 34}, + {15, 31, 30}, + {16, 27, 26}, + {17, 23, 22}, + {20, 19, 18}, + {21, 15, 14}, + {22, 11, 10}, + {23, 7, 6}}; + static uint16_t input_pins_registered[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; + static uint16_t input_pins_combinatorialcomplex[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 16, 17, 18, 19, 20, 21, 23}; + uint16_t input_pins_combinatorialsimple[22]; + pin_output_config output_pins[std::size(macrocells)]; + uint16_t index, input_pin_count, output_pin_count; + + output_pin_count = 0; + + /* SYN Fuse: 0 - registered, 1 - combinatorial */ + + if (jed_get_fuse(jed, 2704)) + { + /* Combinatorial */ + /* AC0 Fuse: 0 - simple mode, 1 - complex mode */ + + if (jed_get_fuse(jed, 2705)) + { + /* Complex Mode */ + + set_input_pins(input_pins_combinatorialcomplex, std::size(input_pins_combinatorialcomplex)); + + memcpy(gal20v8pinfuserows, pinfuserows_combinatorialcomplex, sizeof(pinfuserows_combinatorialcomplex)); + memcpy(gal20v8pinfusecolumns, pinfusecolumns_combinatorialcomplex, sizeof(pinfusecolumns_combinatorialcomplex)); + + for (index = 0; index < std::size(macrocells); ++index) + { + if (is_gal20v8_product_term_enabled(pal, jed, pal->pinfuserows[index].fuserowoutputenable) && + does_output_enable_fuse_row_allow_output(pal, jed, pal->pinfuserows[index].fuserowoutputenable)) + { + output_pins[output_pin_count].pin = macrocells[index].pin; + output_pins[output_pin_count].flags = OUTPUT_COMBINATORIAL; + + if (jed_get_fuse(jed, macrocells[index].xor_fuse)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + + if (output_pins[output_pin_count].pin != 15 && + output_pins[output_pin_count].pin != 22) + { + output_pins[output_pin_count].flags |= OUTPUT_FEEDBACK_OUTPUT; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_FEEDBACK_NONE; + } + + ++output_pin_count; + } + } + } + else + { + /* Simple Mode */ + + input_pin_count = 0; + + for (index = 1; index < 12; ++index) + { + input_pins_combinatorialsimple[input_pin_count] = index; + + ++input_pin_count; + } + + for (index = 13; index < 15; ++index) + { + input_pins_combinatorialsimple[input_pin_count] = index; + + ++input_pin_count; + } + + memcpy(gal20v8pinfuserows, pinfuserows_combinatorialsimple, sizeof(pinfuserows_combinatorialsimple)); + memcpy(gal20v8pinfusecolumns, pinfusecolumns_combinatorialsimple, sizeof(pinfusecolumns_combinatorialsimple)); + + for (index = 0; index < std::size(macrocells); ++index) + { + if (jed_get_fuse(jed, macrocells[index].ac1_fuse)) + { + /* Pin is for input only */ + + input_pins_combinatorialsimple[input_pin_count] = macrocells[index].pin; + + ++input_pin_count; + + if (macrocells[index].pin == 18 || macrocells[index].pin == 19) + { + fprintf(stderr, "Pin %d cannot be configured as an input pin.\n", + macrocells[index].pin); + } + } + else + { + output_pins[output_pin_count].pin = macrocells[index].pin; + output_pins[output_pin_count].flags = OUTPUT_COMBINATORIAL; + + if (jed_get_fuse(jed, macrocells[index].xor_fuse)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + + if (macrocells[index].pin != 18 && macrocells[index].pin != 19) + { + output_pins[output_pin_count].flags |= OUTPUT_FEEDBACK_OUTPUT; + + input_pins_combinatorialsimple[input_pin_count] = macrocells[index].pin; + + ++input_pin_count; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_FEEDBACK_NONE; + } + + ++output_pin_count; + } + } + + input_pins_combinatorialsimple[input_pin_count] = 23; + + ++input_pin_count; + + set_input_pins(input_pins_combinatorialsimple, input_pin_count); + } + } + else + { + /* Registered */ + + set_input_pins(input_pins_registered, std::size(input_pins_registered)); + + memcpy(gal20v8pinfusecolumns, pinfusecolumns_registered, sizeof(pinfusecolumns_registered)); + + for (index = 0; index < std::size(macrocells); ++index) + { + if (jed_get_fuse(jed, macrocells[index].ac1_fuse)) + { + /* combinatorial pin */ + + gal20v8pinfuserows[index].fuserowoutputenable = pinfuserows_combinatorialcomplex[index].fuserowoutputenable; + gal20v8pinfuserows[index].fuserowtermstart = pinfuserows_combinatorialcomplex[index].fuserowtermstart; + gal20v8pinfuserows[index].fuserowtermend = pinfuserows_combinatorialcomplex[index].fuserowtermend; + + if (is_gal20v8_product_term_enabled(pal, jed, pal->pinfuserows[index].fuserowoutputenable) && + does_output_enable_fuse_row_allow_output(pal, jed, pal->pinfuserows[index].fuserowoutputenable)) + { + output_pins[output_pin_count].pin = macrocells[index].pin; + output_pins[output_pin_count].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + if (jed_get_fuse(jed, macrocells[index].xor_fuse)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + + ++output_pin_count; + } + } + else + { + /* registered pin */ + + gal20v8pinfuserows[index].fuserowoutputenable = pinfuserows_registered[index].fuserowoutputenable; + gal20v8pinfuserows[index].fuserowtermstart = pinfuserows_registered[index].fuserowtermstart; + gal20v8pinfuserows[index].fuserowtermend = pinfuserows_registered[index].fuserowtermend; + + output_pins[output_pin_count].pin = macrocells[index].pin; + output_pins[output_pin_count].flags = OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED; + + if (jed_get_fuse(jed, macrocells[index].xor_fuse)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + + ++output_pin_count; + } + } + } + + set_output_pins(output_pins, output_pin_count); +} + + + +/*------------------------------------------------- + config_palce22v10_pal22v10_pins - configures the pins for + a PALCE22V10 +-------------------------------------------------*/ + +static void config_palce22v10_pal22v10_pins(const pal_data* pal, const jed_data* jed) +{ + typedef struct _output_logic_macrocell output_logic_macrocell; + struct _output_logic_macrocell + { + uint16_t pin; + uint16_t s0_fuse; /* output polarity (0 - low, 1 - high) */ + uint16_t s1_fuse; /* registers allowed (0 - registered, 1 - not registered) */ + }; + + static output_logic_macrocell macrocells[] = { + {14, 5826, 5827}, + {15, 5824, 5825}, + {16, 5822, 5823}, + {17, 5820, 5821}, + {18, 5818, 5819}, + {19, 5816, 5817}, + {20, 5814, 5815}, + {21, 5812, 5813}, + {22, 5810, 5811}, + {23, 5808, 5809}}; + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; + pin_output_config output_pins[std::size(macrocells)]; + uint16_t index, output_pin_count; + + output_pin_count = 0; + + for (index = 0; index < std::size(output_pins); ++index) + { + if (jed_get_fuse(jed, macrocells[index].s1_fuse)) + { + /* Combinatorial output or dedicated input */ + + if (does_output_enable_fuse_row_allow_output(pal, jed, pal->pinfuserows[index].fuserowoutputenable)) + { + output_pins[output_pin_count].pin = macrocells[index].pin; + output_pins[output_pin_count].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + if (!jed_get_fuse(jed, macrocells[index].s0_fuse)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + + ++output_pin_count; + } + } + else + { + /* Registered output */ + + output_pins[output_pin_count].pin = macrocells[index].pin; + output_pins[output_pin_count].flags = OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED; + + if (!jed_get_fuse(jed, macrocells[index].s0_fuse)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + + ++output_pin_count; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, output_pin_count); +} + + + +/*------------------------------------------------- + config_gal22v10_pins - configures the pins for + a GAL22V10 +-------------------------------------------------*/ + +static void config_gal22v10_pins(const pal_data* pal, const jed_data* jed) +{ + typedef struct _output_logic_macrocell output_logic_macrocell; + struct _output_logic_macrocell + { + uint16_t pin; + uint16_t s0_fuse; /* 0 - active low, 1 - active high */ + uint16_t s1_fuse; /* 0 - registered, 1 - combinatorial */ + }; + + static output_logic_macrocell macrocells[] = { + {14, 5826, 5827}, + {15, 5824, 5825}, + {16, 5822, 5823}, + {17, 5820, 5821}, + {18, 5818, 5819}, + {19, 5816, 5817}, + {20, 5814, 5815}, + {21, 5812, 5813}, + {22, 5810, 5811}, + {23, 5808, 5809}}; + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; + pin_output_config output_pins[std::size(macrocells)]; + uint16_t index, output_pin_count; + + output_pin_count = 0; + + for (index = 0; index < std::size(output_pins); ++index) + { + if (jed_get_fuse(jed, macrocells[index].s1_fuse)) + { + /* Combinatorial output or dedicated input */ + + if (does_output_enable_fuse_row_allow_output(pal, jed, pal->pinfuserows[index].fuserowoutputenable)) + { + output_pins[output_pin_count].pin = macrocells[index].pin; + output_pins[output_pin_count].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + if (!jed_get_fuse(jed, macrocells[index].s0_fuse)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + + ++output_pin_count; + } + } + else + { + /* Registered output */ + + output_pins[output_pin_count].pin = macrocells[index].pin; + output_pins[output_pin_count].flags = OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED; + + if (!jed_get_fuse(jed, macrocells[index].s0_fuse)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + + ++output_pin_count; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, output_pin_count); +} + + + +/*------------------------------------------------- + config_atf22v10_power_down_mode_pins - configures the pins for + a ATF22V10 configured in power down mode. +-------------------------------------------------*/ + +static void config_atf22v10_power_down_mode_pins(const pal_data* pal, const jed_data* jed) +{ + typedef struct _output_logic_macrocell output_logic_macrocell; + struct _output_logic_macrocell + { + uint16_t pin; + uint16_t s0_fuse; /* 0 - active low, 1 - active high */ + uint16_t s1_fuse; /* 0 - registered, 1 - combinatorial */ + }; + + static output_logic_macrocell macrocells[] = { + {14, 5826, 5827}, + {15, 5824, 5825}, + {16, 5822, 5823}, + {17, 5820, 5821}, + {18, 5818, 5819}, + {19, 5816, 5817}, + {20, 5814, 5815}, + {21, 5812, 5813}, + {22, 5810, 5811}, + {23, 5808, 5809}}; + static uint16_t input_pins[] = {1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23}; + pin_output_config output_pins[std::size(macrocells)]; + uint16_t index, output_pin_count; + + output_pin_count = 0; + + if (jed_get_fuse(jed, 5893)) + { + fprintf(stderr, "Warning: Power down fuse not blown!\n"); + } + + for (index = 0; index < std::size(output_pins); ++index) + { + if (jed_get_fuse(jed, macrocells[index].s1_fuse)) + { + /* Combinatorial output or dedicated input */ + + if (does_output_enable_fuse_row_allow_output(pal, jed, pal->pinfuserows[index].fuserowoutputenable)) + { + output_pins[output_pin_count].pin = macrocells[index].pin; + output_pins[output_pin_count].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + if (!jed_get_fuse(jed, macrocells[index].s0_fuse)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + + ++output_pin_count; + } + } + else + { + /* Registered output */ + + output_pins[output_pin_count].pin = macrocells[index].pin; + output_pins[output_pin_count].flags = OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED; + + if (!jed_get_fuse(jed, macrocells[index].s0_fuse)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + + ++output_pin_count; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, output_pin_count); +} + + + +/*------------------------------------------------- + config_82s153_pls153_pins - configures the pins for + a 82S153/PLS153 +-------------------------------------------------*/ + +static void config_82s153_pls153_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19}; + pin_output_config output_pins[10]; + uint16_t output_pin_count, index; + + output_pin_count = 0; + + for (index = 0; index < pal->pinfuserowscount; ++index) + { + if (does_output_enable_fuse_row_allow_output(pal, jed, pal->pinfuserows[index].fuserowoutputenable)) + { + output_pins[output_pin_count].pin = pal->pinfuserows[index].pin; + output_pins[output_pin_count].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + if (jed_get_fuse(jed, 1832 + (9 - index))) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + + ++output_pin_count; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, output_pin_count); +} + + + +/*------------------------------------------------- + config_ck2605_pins - configures the pins for + a CK2605 +-------------------------------------------------*/ + +static void config_ck2605_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19}; + pin_output_config output_pins[10]; + uint16_t output_pin_count, index; + + output_pin_count = 0; + + for (index = 0; index < pal->pinfuserowscount; ++index) + { + if (does_output_enable_fuse_row_allow_output(pal, jed, pal->pinfuserows[index].fuserowoutputenable)) + { + output_pins[output_pin_count].pin = pal->pinfuserows[index].pin; + output_pins[output_pin_count].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + if (jed_get_fuse(jed, 1096 + (9 - index))) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + + ++output_pin_count; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, output_pin_count); +} + + + +#if defined(ricoh_pals) +/*------------------------------------------------- + config_epl10p8_pins - configures the pins for + a EPL10P8 +-------------------------------------------------*/ + +static void config_epl10p8_pins(const pal_data* pal, const jed_data* jed) +{ + typedef struct _memory_cell memory_cell; + struct _memory_cell + { + uint16_t pin; + uint16_t polarity_fuse; /* 0 - active low?, 1 - active high? */ + }; + + static memory_cell memory_cells[] = { + {12, 663}, + {13, 660}, + {14, 657}, + {15, 654}, + {16, 651}, + {17, 648}, + {18, 645}, + {19, 642}}; + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11}; + pin_output_config output_pins[8]; + uint16_t index; + + for (index = 0; index < std::size(memory_cells); ++index) + { + output_pins[index].pin = memory_cells[index].pin; + output_pins[index].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE; + + if (!jed_get_fuse(jed, memory_cells[index].polarity_fuse)) + { + output_pins[index].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[index].flags |= OUTPUT_ACTIVEHIGH; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_epl12p6_pins - configures the pins for + a EPL12P6 +-------------------------------------------------*/ + +static void config_epl12p6_pins(const pal_data* pal, const jed_data* jed) +{ + typedef struct _memory_cell memory_cell; + struct _memory_cell + { + uint16_t pin; + uint16_t polarity_fuse; /* 0 - active low?, 1 - active high? */ + uint16_t or_fuse; /* 0 - intact? */ + uint16_t xor_fuse; /* 0 - intact? */ + }; + + static memory_cell memory_cells[] = { + {13, 785, 783, 784}, + {14, 782, 780, 781}, + {15, 779, 777, 778}, + {16, 776, 774, 775}, + {17, 773, 771, 772}, + {18, 770, 768, 769}}; + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 19}; + pin_output_config output_pins[8]; + uint16_t index; + + for (index = 0; index < std::size(memory_cells); ++index) + { + output_pins[index].pin = memory_cells[index].pin; + output_pins[index].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE; + + if (!jed_get_fuse(jed, memory_cells[index].polarity_fuse)) + { + output_pins[index].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[index].flags |= OUTPUT_ACTIVEHIGH; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_epl14p4_pins - configures the pins for + a EPL14P4 +-------------------------------------------------*/ + +static void config_epl14p4_pins(const pal_data* pal, const jed_data* jed) +{ + typedef struct _memory_cell memory_cell; + struct _memory_cell + { + uint16_t pin; + uint16_t polarity_fuse; /* 0 - active low?, 1 - active high? */ + uint16_t or_fuse; /* 0 - intact? */ + uint16_t xor_fuse; /* 0 - intact? */ + }; + + static memory_cell memory_cells[] = { + {14, 907, 905, 906}, + {15, 904, 902, 903}, + {16, 901, 899, 900}, + {17, 898, 896, 897}}; + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 18, 19}; + pin_output_config output_pins[8]; + uint16_t index; + + for (index = 0; index < std::size(memory_cells); ++index) + { + output_pins[index].pin = memory_cells[index].pin; + output_pins[index].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE; + + if (!jed_get_fuse(jed, memory_cells[index].polarity_fuse)) + { + output_pins[index].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[index].flags |= OUTPUT_ACTIVEHIGH; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_epl16p2_pins - configures the pins for + a EPL16P2 +-------------------------------------------------*/ + +static void config_epl16p2_pins(const pal_data* pal, const jed_data* jed) +{ + typedef struct _memory_cell memory_cell; + struct _memory_cell + { + uint16_t pin; + uint16_t polarity_fuse; /* 0 - active low?, 1 - active high? */ + uint16_t or_fuse; /* 0 - intact? */ + uint16_t xor_fuse; /* 0 - intact? */ + }; + + static memory_cell memory_cells[] = { + {15, 1029, 1027, 1028}, + {16, 1026, 1024, 1025}}; + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 17, 18, 19}; + pin_output_config output_pins[8]; + uint16_t index; + + for (index = 0; index < std::size(memory_cells); ++index) + { + output_pins[index].pin = memory_cells[index].pin; + output_pins[index].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE; + + if (!jed_get_fuse(jed, memory_cells[index].polarity_fuse)) + { + output_pins[index].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[index].flags |= OUTPUT_ACTIVEHIGH; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_epl16p8_pins - configures the pins for + a EPL16P8 +-------------------------------------------------*/ + +static void config_epl16p8_pins(const pal_data* pal, const jed_data* jed) +{ + typedef struct _memory_cell memory_cell; + struct _memory_cell + { + uint16_t pin; + uint16_t polarity_fuse; /* 0 - active low?, 1 - active high? */ + uint16_t or_fuse; /* 0 - intact? */ + uint16_t xor_fuse; /* 0 - intact? */ + }; + + static memory_cell memory_cells[] = { + {12, 2071, 2069, 2070}, + {13, 2068, 2066, 2067}, + {14, 2065, 2063, 2064}, + {15, 2062, 2060, 2061}, + {16, 2059, 2057, 2058}, + {17, 2056, 2054, 2055}, + {18, 2053, 2051, 2052}, + {19, 2050, 2048, 2049}}; + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11}; + pin_output_config output_pins[8]; + uint16_t index; + + for (index = 0; index < std::size(memory_cells); ++index) + { + output_pins[index].pin = memory_cells[index].pin; + output_pins[index].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE; + + if (!jed_get_fuse(jed, memory_cells[index].polarity_fuse)) + { + output_pins[index].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[index].flags |= OUTPUT_ACTIVEHIGH; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_epl16rp8_pins - configures the pins for + a EPL16RP8 +-------------------------------------------------*/ + +static void config_epl16rp8_pins(const pal_data* pal, const jed_data* jed) +{ + typedef struct _memory_cell memory_cell; + struct _memory_cell + { + uint16_t pin; + uint16_t polarity_fuse; /* 0 - active low?, 1 - active high? */ + uint16_t or_fuse; /* 0 - intact? */ + uint16_t xor_fuse; /* 0 - intact? */ + }; + + static memory_cell memory_cells[] = { + {12, 2071, 2069, 2070}, + {13, 2068, 2066, 2067}, + {14, 2065, 2063, 2064}, + {15, 2062, 2060, 2061}, + {16, 2059, 2057, 2058}, + {17, 2056, 2054, 2055}, + {18, 2053, 2051, 2052}, + {19, 2050, 2048, 2049}}; + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11}; + pin_output_config output_pins[8]; + uint16_t index; + + for (index = 0; index < std::size(memory_cells); ++index) + { + output_pins[index].pin = memory_cells[index].pin; + output_pins[index].flags = OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED; + + if (!jed_get_fuse(jed, memory_cells[index].polarity_fuse)) + { + output_pins[index].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[index].flags |= OUTPUT_ACTIVEHIGH; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_epl16rp6_pins - configures the pins for + a EPL16RP6 +-------------------------------------------------*/ + +static void config_epl16rp6_pins(const pal_data* pal, const jed_data* jed) +{ + typedef struct _memory_cell memory_cell; + struct _memory_cell + { + uint16_t pin; + uint16_t polarity_fuse; /* 0 - active low?, 1 - active high? */ + uint16_t or_fuse; /* 0 - intact? */ + uint16_t xor_fuse; /* 0 - intact? */ + }; + + static memory_cell memory_cells[] = { + {12, 2071, 2069, 2070}, + {13, 2068, 2066, 2067}, + {14, 2065, 2063, 2064}, + {15, 2062, 2060, 2061}, + {16, 2059, 2057, 2058}, + {17, 2056, 2054, 2055}, + {18, 2053, 2051, 2052}, + {19, 2050, 2048, 2049}}; + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11}; + pin_output_config output_pins[8]; + uint16_t index; + + for (index = 0; index < std::size(memory_cells); ++index) + { + output_pins[index].pin = memory_cells[index].pin; + + if (memory_cells[index].pin == 13 || memory_cells[index].pin == 14 || + memory_cells[index].pin == 15 || memory_cells[index].pin == 16 || + memory_cells[index].pin == 17 || memory_cells[index].pin == 18) + { + output_pins[index].flags = OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED; + } + else + { + output_pins[index].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + } + + if (!jed_get_fuse(jed, memory_cells[index].polarity_fuse)) + { + output_pins[index].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[index].flags |= OUTPUT_ACTIVEHIGH; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_epl16rp4_pins - configures the pins for + a EPL16RP4 +-------------------------------------------------*/ + +static void config_epl16rp4_pins(const pal_data* pal, const jed_data* jed) +{ + typedef struct _memory_cell memory_cell; + struct _memory_cell + { + uint16_t pin; + uint16_t polarity_fuse; /* 0 - active low?, 1 - active high? */ + uint16_t or_fuse; /* 0 - intact? */ + uint16_t xor_fuse; /* 0 - intact? */ + }; + + static memory_cell memory_cells[] = { + {12, 2071, 2069, 2070}, + {13, 2068, 2066, 2067}, + {14, 2065, 2063, 2064}, + {15, 2062, 2060, 2061}, + {16, 2059, 2057, 2058}, + {17, 2056, 2054, 2055}, + {18, 2053, 2051, 2052}, + {19, 2050, 2048, 2049}}; + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11}; + pin_output_config output_pins[8]; + uint16_t index; + + for (index = 0; index < std::size(memory_cells); ++index) + { + output_pins[index].pin = memory_cells[index].pin; + + if (memory_cells[index].pin == 14 || memory_cells[index].pin == 15 || + memory_cells[index].pin == 16 || memory_cells[index].pin == 17) + { + output_pins[index].flags = OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED; + } + else + { + output_pins[index].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + } + + if (!jed_get_fuse(jed, memory_cells[index].polarity_fuse)) + { + output_pins[index].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[index].flags |= OUTPUT_ACTIVEHIGH; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} +#endif + + + +/*------------------------------------------------- + config_pal10p8_pins - configures the pins for + a PAL10P8 +-------------------------------------------------*/ + +static void config_pal10p8_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11}; + pin_output_config output_pins[8]; + uint16_t index; + + for (index = 0; index < std::size(output_pins); ++index) + { + output_pins[index].pin = index + 12; + output_pins[index].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE; + + if (!jed_get_fuse(jed, 327 - index)) + { + output_pins[index].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[index].flags |= OUTPUT_ACTIVEHIGH; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal12p6_pins - configures the pins for + a PAL12P6A +-------------------------------------------------*/ + +static void config_pal12p6_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 19}; + pin_output_config output_pins[6]; + uint16_t index; + + for (index = 0; index < std::size(output_pins); ++index) + { + output_pins[index].pin = index + 13; + output_pins[index].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE; + + if (!jed_get_fuse(jed, 389 - index)) + { + output_pins[index].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[index].flags |= OUTPUT_ACTIVEHIGH; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal14p4_pins - configures the pins for + a PAL14P4 +-------------------------------------------------*/ + +static void config_pal14p4_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 18, 19}; + pin_output_config output_pins[4]; + uint16_t index; + + for (index = 0; index < std::size(output_pins); ++index) + { + output_pins[index].pin = index + 14; + output_pins[index].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE; + + if (!jed_get_fuse(jed, 451 - index)) + { + output_pins[index].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[index].flags |= OUTPUT_ACTIVEHIGH; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal16p2_pins - configures the pins for + a PAL16P2 +-------------------------------------------------*/ + +static void config_pal16p2_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 17, 18, 19}; + pin_output_config output_pins[2]; + uint16_t index; + + for (index = 0; index < std::size(output_pins); ++index) + { + output_pins[index].pin = index + 15; + output_pins[index].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE; + + if (!jed_get_fuse(jed, 513 - index)) + { + output_pins[index].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[index].flags |= OUTPUT_ACTIVEHIGH; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal16p8_pins - configures the pins for + a PAL16P8 +-------------------------------------------------*/ + +static void config_pal16p8_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 13, 14, 15, 16, 17, 18}; + pin_output_config output_pins[8]; + uint16_t output_pin_count, index; + + output_pin_count = 0; + + for (index = 0; index < pal->pinfuserowscount; ++index) + { + if (does_output_enable_fuse_row_allow_output(pal, jed, pal->pinfuserows[index].fuserowoutputenable)) + { + output_pins[output_pin_count].pin = pal->pinfuserows[index].pin; + output_pins[output_pin_count].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + if (!jed_get_fuse(jed, 2055 - index)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + + ++output_pin_count; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, output_pin_count); +} + + + +/*------------------------------------------------- + config_pal16rp4_pins - configures the pins for + a PAL16RP4 +-------------------------------------------------*/ + +static void config_pal16rp4_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19}; + static uint16_t registered_pins[] = {14, 15, 16, 17}; + pin_output_config output_pins[8]; + uint16_t output_pin_count, index; + + output_pin_count = 0; + + if (does_output_enable_fuse_row_allow_output(pal, jed, 1792)) + { + output_pins[output_pin_count].pin = 12; + output_pins[output_pin_count].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + if (!jed_get_fuse(jed, 2055)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + + ++output_pin_count; + } + + if (does_output_enable_fuse_row_allow_output(pal, jed, 1536)) + { + output_pins[output_pin_count].pin = 13; + output_pins[output_pin_count].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + if (!jed_get_fuse(jed, 2054)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + + ++output_pin_count; + } + + for (index = 0; index < std::size(registered_pins); ++index) + { + output_pins[output_pin_count].pin = registered_pins[index]; + output_pins[output_pin_count].flags = OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED; + + if (!jed_get_fuse(jed, 2053 - index)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + + ++output_pin_count; + } + + if (does_output_enable_fuse_row_allow_output(pal, jed, 256)) + { + output_pins[output_pin_count].pin = 18; + output_pins[output_pin_count].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + if (!jed_get_fuse(jed, 2049)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + + ++output_pin_count; + } + + if (does_output_enable_fuse_row_allow_output(pal, jed, 0)) + { + output_pins[output_pin_count].pin = 19; + output_pins[output_pin_count].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + if (!jed_get_fuse(jed, 2048)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + + ++output_pin_count; + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, output_pin_count); +} + + + +/*------------------------------------------------- + config_pal16rp6_pins - configures the pins for + a PAL16RP6 +-------------------------------------------------*/ + +static void config_pal16rp6_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19}; + static uint16_t registered_pins[] = {13, 14, 15, 16, 17, 18}; + pin_output_config output_pins[8]; + uint16_t output_pin_count, index; + + output_pin_count = 0; + + if (does_output_enable_fuse_row_allow_output(pal, jed, 1792)) + { + output_pins[output_pin_count].pin = 12; + output_pins[output_pin_count].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + if (!jed_get_fuse(jed, 2055)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + + ++output_pin_count; + } + + for (index = 0; index < std::size(registered_pins); ++index) + { + output_pins[output_pin_count].pin = registered_pins[index]; + output_pins[output_pin_count].flags = OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED; + + if (!jed_get_fuse(jed, 2054 - index)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + + ++output_pin_count; + } + + if (does_output_enable_fuse_row_allow_output(pal, jed, 0)) + { + output_pins[output_pin_count].pin = 19; + output_pins[output_pin_count].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_OUTPUT; + + if (!jed_get_fuse(jed, 2048)) + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[output_pin_count].flags |= OUTPUT_ACTIVEHIGH; + } + + ++output_pin_count; + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, output_pin_count); +} + + + +/*------------------------------------------------- + config_pal16rp8_pins - configures the pins for + a PAL16RP8 +-------------------------------------------------*/ + +static void config_pal16rp8_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19}; + pin_output_config output_pins[] = { + {12, OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {13, OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {14, OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {15, OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {16, OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {17, OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {18, OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}, + {19, OUTPUT_REGISTERED | OUTPUT_FEEDBACK_REGISTERED}}; + uint16_t index; + + for (index = 0; index < std::size(output_pins); ++index) + { + if (!jed_get_fuse(jed, 2055 - index)) + { + output_pins[index].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[index].flags |= OUTPUT_ACTIVEHIGH; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal6l16_pins - configures the pins for + a PAL6L16 +-------------------------------------------------*/ + +static void config_pal6l16_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {4, 5, 6, 7, 8, 9}; + static pin_output_config output_pins[] = { + {1, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {2, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {3, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {10, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {11, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {13, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {14, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {15, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {16, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {17, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {18, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {19, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {20, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {21, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {22, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {23, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal8l14_pins - configures the pins for + a PAL8L14 +-------------------------------------------------*/ + +static void config_pal8l14_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {3, 4, 5, 6, 7, 8, 9, 10}; + static pin_output_config output_pins[] = { + {1, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {2, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {11, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {13, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {14, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {15, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {16, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {17, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {18, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {19, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {20, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {21, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {22, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {23, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal12h10_pins - configures the pins for + a PAL12H10 +-------------------------------------------------*/ + +static void config_pal12h10_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13}; + static pin_output_config output_pins[] = { + {14, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {15, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {16, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {17, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {18, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {19, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {20, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {21, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {22, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {23, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal12l10_pins - configures the pins for + a PAL12L10 +-------------------------------------------------*/ + +static void config_pal12l10_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13}; + static pin_output_config output_pins[] = { + {14, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {15, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {16, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {17, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {18, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {19, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {20, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {21, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {22, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {23, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal14h8_pins - configures the pins for + a PAL14H8 +-------------------------------------------------*/ + +static void config_pal14h8_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 23}; + static pin_output_config output_pins[] = { + {15, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {16, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {17, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {18, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {19, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {20, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {21, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {22, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal14l8_pins - configures the pins for + a PAL14L8 +-------------------------------------------------*/ + +static void config_pal14l8_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 23}; + static pin_output_config output_pins[] = { + {15, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {16, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {17, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {18, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {19, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {20, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {21, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {22, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal16h6_pins - configures the pins for + a PAL16H6 +-------------------------------------------------*/ + +static void config_pal16h6_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 22, 23}; + static pin_output_config output_pins[] = { + {16, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {17, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {18, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {19, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {20, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {21, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal16l6_pins - configures the pins for + a PAL16L6 +-------------------------------------------------*/ + +static void config_pal16l6_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 22, 23}; + static pin_output_config output_pins[] = { + {16, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {17, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {18, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {19, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {20, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {21, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal18h4_pins - configures the pins for + a PAL18H4 +-------------------------------------------------*/ + +static void config_pal18h4_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 21, 22, 23}; + static pin_output_config output_pins[] = { + {17, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {18, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {19, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {20, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal18l4_pins - configures the pins for + a PAL18L4 +-------------------------------------------------*/ + +static void config_pal18l4_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 21, 22, 23}; + static pin_output_config output_pins[] = { + {17, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {18, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {19, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {20, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal20c1_pins - configures the pins for + a PAL20C1 +-------------------------------------------------*/ + +static void config_pal20c1_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 20, 21, 22, 23}; + static pin_output_config output_pins[] = { + {18, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {19, OUTPUT_ACTIVEHIGH | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_pal20l2_pins - configures the pins for + a PAL20L2 +-------------------------------------------------*/ + +static void config_pal20l2_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 20, 21, 22, 23}; + static pin_output_config output_pins[] = { + {18, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}, + {19, OUTPUT_ACTIVELOW | OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE}}; + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + config_82s100_pls100_pins - configures the pins for + a 82S100 and PLS100 +-------------------------------------------------*/ + +static void config_82s100_pls100_pins(const pal_data* pal, const jed_data* jed) +{ + static uint16_t input_pins[] = {9, 8, 7, 6, 5, 4, 3, 2, 27, 26, 25, 24, 23, 22, 21, 20}; + pin_output_config output_pins[8]; + + for (uint16_t index = 0; index < std::size(output_pins); ++index) + { + output_pins[index].pin = pal->pinfuserows[index].pin; + output_pins[index].flags = OUTPUT_COMBINATORIAL | OUTPUT_FEEDBACK_NONE; + + if (jed_get_fuse(jed, 1920 + index)) + { + output_pins[index].flags |= OUTPUT_ACTIVELOW; + } + else + { + output_pins[index].flags |= OUTPUT_ACTIVEHIGH; + } + } + + set_input_pins(input_pins, std::size(input_pins)); + set_output_pins(output_pins, std::size(output_pins)); +} + + + +/*------------------------------------------------- + is_gal16v8_product_term_enabled - determines if + a fuse row in a GAL16V8 is enabled +-------------------------------------------------*/ + +static int is_gal16v8_product_term_enabled(const pal_data* pal, const jed_data* jed, uint16_t fuserow) +{ + uint16_t fuse_ptd; + + fuse_ptd = (fuserow / calc_fuse_column_count(pal)) + 2128; + + if (fuse_ptd > 2191) + { + fprintf(stderr, "Fuse row %d is illegal!\n", fuserow); + + return 0; + } + + return jed_get_fuse(jed, fuse_ptd); +} + + + +/*------------------------------------------------- + is_gal20v8_product_term_enabled - determines if + a fuse row in a GAL20V8 is enabled +-------------------------------------------------*/ + +static int is_gal20v8_product_term_enabled(const pal_data* pal, const jed_data* jed, uint16_t fuserow) +{ + uint16_t fuse_ptd; + + fuse_ptd = (fuserow / calc_fuse_column_count(pal)) + 2640; + + if (fuse_ptd > 2703) + { + fprintf(stderr, "Fuse row %d is illegal!\n", fuserow); + + return 0; + } + + return jed_get_fuse(jed, fuse_ptd); +} + + + +/*------------------------------------------------- + get_peel18cv8_pin_fuse_state - determines the + fuse state of an input pin in a fuse row +-------------------------------------------------*/ + +static uint16_t get_peel18cv8_pin_fuse_state(const pal_data* pal, const jed_data* jed, uint16_t pin, uint16_t fuserow) +{ + const pin_fuse_columns* fuse_columns; + int lowfusestate, highfusestate, tmpfusestate, swapfusestates; + uint16_t cfgpin; + + /* Synchronous Preset or Asynchronous Clear fuse row? */ + + if (fuserow == 2592 || fuserow == 2628) + { + return get_pin_fuse_state(pal, jed, pin, fuserow); + } + + fuse_columns = find_fuse_columns(pal, pin); + + if (!fuse_columns) + { + fprintf(stderr, "Fuse column data missing for pin %d!\n", pin); + + return NO_FUSE_BLOWN; + } + + cfgpin = find_pin_from_fuse_row(pal, fuserow); + + if (!cfgpin) + { + fprintf(stderr, "Pin from fuse row failed! (Fuse row: %d)\n", fuserow); + + return get_pin_fuse_state(pal, jed, pin, fuserow); + } + + lowfusestate = jed_get_fuse(jed, fuserow + fuse_columns->lowfusecolumn); + highfusestate = jed_get_fuse(jed, fuserow + fuse_columns->highfusecolumn); + swapfusestates = 0; + + if (is_output_pin(pin) && is_output_pin(cfgpin)) + { + if (get_pin_output_flags(cfgpin) & OUTPUT_FEEDBACK_COMBINATORIAL) + { + if ((get_pin_output_flags(pin) & OUTPUT_ACTIVELOW) && + (get_pin_output_flags(pin) & OUTPUT_FEEDBACK_COMBINATORIAL)) + { + swapfusestates = 1; + } + } + else if (get_pin_output_flags(cfgpin) & OUTPUT_FEEDBACK_REGISTERED) + { + if ((get_pin_output_flags(pin) & OUTPUT_ACTIVELOW) && + (get_pin_output_flags(pin) & OUTPUT_FEEDBACK_REGISTERED)) + { + swapfusestates = 1; + } + } + else if (get_pin_output_flags(cfgpin) & OUTPUT_FEEDBACK_OUTPUT) + { + if ((get_pin_output_flags(pin) & OUTPUT_ACTIVELOW) && + (get_pin_output_flags(pin) & OUTPUT_FEEDBACK_REGISTERED)) + { + swapfusestates = 1; + } + } + else + { + fprintf(stderr, "Unknown output pin type! (Fuse row: %d)\n", fuserow); + } + } + + if (swapfusestates) + { + tmpfusestate = lowfusestate; + lowfusestate = highfusestate; + highfusestate = tmpfusestate; + } + + if (!lowfusestate && highfusestate) + { + return LOW_FUSE_BLOWN; + } + else if (lowfusestate && !highfusestate) + { + return HIGH_FUSE_BLOWN; + } + else if (!lowfusestate && !highfusestate) + { + return NO_FUSE_BLOWN; + } + + return LOWHIGH_FUSE_BLOWN; +} + + + +/*------------------------------------------------- + write_dest_file - write a memory buffer raw + into a desintation file +-------------------------------------------------*/ + +static int write_dest_file(const char *dstfile) +{ + size_t bytes; + FILE *file; + + /* open the source file */ + file = fopen(dstfile, "wb"); + if (!file) + { + fprintf(stderr, "Unable to open target file '%s'!\n", dstfile); + return 1; + } + + /* write the data */ + bytes = fwrite(dstbuf, 1, dstbuflen, file); + if (bytes != dstbuflen) + { + fprintf(stderr, "Error writing %d bytes to the target!\n", (int)dstbuflen); + fclose(file); + return 1; + } + + /* close up shop */ + fclose(file); + return 0; +} + + + +/*------------------------------------------------- + print_usage - prints out the supported command + line arguments +-------------------------------------------------*/ + +static int print_usage() +{ + fprintf(stderr, + "Usage:\n" + " jedutil -convert [fuses] -- convert JEDEC to binary form\n" + " jedutil -convert [fuses] -- convert Berkeley standard PLA to binary form\n" + " jedutil -convert -- convert binary to JEDEC form\n" + " jedutil -view -- dump JED logic equations\n" + " jedutil -view -- dump binary logic equations\n" + " jedutil -viewlist -- view list of supported devices\n" + " jedutil -listcompatible -- list compatible devices\n" + " jedutil -listcompatible -- list compatible devices\n" + ); + + return 0; +} + + + +/*------------------------------------------------- + command_convert - convert files +-------------------------------------------------*/ + +static int command_convert(int argc, char *argv[]) +{ + const char *srcfile, *dstfile; + int src_is_jed, src_is_pla, dst_is_jed; + int numfuses = 0; + jed_data jed; + + if (argc < 2) + { + return print_usage(); + } + + /* extract arguments */ + srcfile = argv[0]; + dstfile = argv[1]; + if (argc >= 3) + numfuses = atoi(argv[2]); + + /* does the source end in '.jed'? */ + src_is_jed = is_jed_file(srcfile); + + /* does the source end in '.pla'? */ + src_is_pla = is_pla_file(srcfile); + + /* does the destination end in '.jed'? */ + dst_is_jed = is_jed_file(dstfile); + + /* error if neither or both are .jed */ + if (!src_is_jed && !src_is_pla && !dst_is_jed) + { + fprintf(stderr, "At least one of the filenames must end in .jed or .pla!\n"); + return 1; + } + if (src_is_jed && dst_is_jed) + { + fprintf(stderr, "Both filenames cannot end in .jed!\n"); + return 1; + } + + /* read the source file */ + auto src = util::stdio_read(fopen(srcfile, "rb")); + if (!src) + { + fprintf(stderr, "Unable to open source file '%s'!\n", srcfile); + return 1; + } + + memset(&jed, 0, sizeof(jed)); + + /* if the source is JED or PLA, convert to binary */ + if (src_is_jed || src_is_pla) + { + printf("Converting '%s' to binary form '%s'\n", srcfile, dstfile); + + /* read the fuse data */ + int err; + if (src_is_jed) + err = jed_parse(*src, &jed); + else /* if (src_is_pla) */ + err = pla_parse(*src, &jed); + src.reset(); + + switch (err) + { + case JEDERR_INVALID_DATA: fprintf(stderr, "Fatal error: Invalid source file\n"); return 1; + case JEDERR_BAD_XMIT_SUM: fprintf(stderr, "Fatal error: Bad transmission checksum\n"); return 1; + case JEDERR_BAD_FUSE_SUM: fprintf(stderr, "Fatal error: Bad fusemap checksum\n"); return 1; + } + + /* override the number of fuses */ + if (numfuses != 0) + jed.numfuses = numfuses; + + /* print out data */ + printf("Source file read successfully\n"); + printf(" Total fuses = %d\n", jed.numfuses); + + /* generate the output */ + dstbuflen = jedbin_output(&jed, nullptr, 0); + dstbuf = (uint8_t *)malloc(dstbuflen); + if (!dstbuf) + { + fprintf(stderr, "Unable to allocate %d bytes for the target buffer!\n", (int)dstbuflen); + return 1; + } + dstbuflen = jedbin_output(&jed, dstbuf, dstbuflen); + } + + /* if the source is binary, convert to JED */ + else + { + printf("Converting '%s' to JED form '%s'\n", srcfile, dstfile); + + /* read the binary data */ + int err = jedbin_parse(*src, &jed); + src.reset(); + + switch (err) + { + case JEDERR_INVALID_DATA: fprintf(stderr, "Fatal error: Invalid binary JEDEC file\n"); return 1; + } + + /* print out data */ + printf("Source file read successfully\n"); + printf(" Total fuses = %d\n", jed.numfuses); + + /* generate the output */ + dstbuflen = jed_output(&jed, nullptr, 0); + dstbuf = (uint8_t *)malloc(dstbuflen); + if (!dstbuf) + { + fprintf(stderr, "Unable to allocate %d bytes for the target buffer!\n", (int)dstbuflen); + return 1; + } + dstbuflen = jed_output(&jed, dstbuf, dstbuflen); + } + + /* write the destination file */ + int err = write_dest_file(dstfile); + if (err != 0) + return 1; + + printf("Target file written successfully\n"); + + return 0; +} + + + +/*------------------------------------------------- + command_view - views the contents of a file +-------------------------------------------------*/ + +static int command_view(int argc, char *argv[]) +{ + int result = 0; + const char *srcfile, *palname; + int is_jed; + pal_data_vector pal_data_vector; + const pal_data* pal; + jed_data jed; + int err; + + if (argc < 2) + { + return print_usage(); + } + + /* extract arguments */ + srcfile = argv[0]; + palname = argv[1]; + + /* does the source end in '.jed'? */ + is_jed = is_jed_file(srcfile); + + /* find the pal entry */ + find_pal_data(palname, pal_data_vector); + + if (pal_data_vector.size() == 0) + { + fprintf(stderr, "Unknown pal name.\n"); + return 1; + } + + /* read the source file */ + auto src = util::stdio_read(fopen(srcfile, "rb")); + if (!src) + { + fprintf(stderr, "Unable to open source file '%s'!\n", srcfile); + goto end; + } + + /* if the source is JED, convert to binary */ + if (is_jed) + { + /* read the JEDEC data */ + err = jed_parse(*src, &jed); + src.reset(); + switch (err) + { + case JEDERR_INVALID_DATA: fprintf(stderr, "Fatal error: Invalid .JED file\n"); result = 1; goto end; + case JEDERR_BAD_XMIT_SUM: fprintf(stderr, "Fatal error: Bad transmission checksum\n"); result = 1; goto end; + case JEDERR_BAD_FUSE_SUM: fprintf(stderr, "Fatal error: Bad fusemap checksum\n"); result = 1; goto end; + } + } + else + { + /* read the binary data */ + err = jedbin_parse(*src, &jed); + src.reset(); + switch (err) + { + case JEDERR_INVALID_DATA: fprintf(stderr, "Fatal error: Invalid binary JEDEC file\n"); result = 1; goto end; + } + } + + pal = nullptr; + + for (pal_data_vector::iterator it = pal_data_vector.begin(); pal == nullptr && it != pal_data_vector.end(); ++it) + { + if (jed.numfuses == (*it)->numfuses) + { + pal = *it; + } + } + + if (pal == nullptr) + { + fprintf(stderr, "Fuse count does not match this pal type."); + result = 1; + goto end; + } + + /* generate equations from fuse map */ + + pal->config_pins(pal, &jed); + + if (pal->print_product_terms) + { + pal->print_product_terms(pal, &jed); + } + else + { + fprintf(stderr, "Viewing product terms not supported for this pal type."); + result = 1; + } + +end: + return result; +} + + + +/*------------------------------------------------- + command_viewlist - views the list of supported + jeds +-------------------------------------------------*/ + +static int command_viewlist(int argc, char *argv[]) +{ + typedef std::set string_set; + + string_set nameset; + int index; + + if (argc > 0) + { + return print_usage(); + } + + for (index = 0; index < std::size(paldata); ++index) + { + nameset.insert(paldata[index].name); + } + + for (string_set::iterator it = nameset.begin(); it != nameset.end(); ++it) + { + printf("%s\n", (*it).c_str()); + } + + return 0; +} + + + +/*------------------------------------------------- + command_listcompatible - views the list of + compatible devices +-------------------------------------------------*/ + +static int command_listcompatible(int argc, char *argv[]) +{ + int result = 0; + const char *srcfile; + int is_jed; + jed_data jed; + int err; + int index; + + if (argc != 1) + { + return print_usage(); + } + + /* extract arguments */ + srcfile = argv[0]; + + /* does the source end in '.jed'? */ + is_jed = is_jed_file(srcfile); + + /* read the source file */ + auto src = util::stdio_read(fopen(srcfile, "rb")); + if (!src) + { + fprintf(stderr, "Unable to open source file '%s'!\n", srcfile); + goto end; + } + + /* if the source is JED, convert to binary */ + if (is_jed) + { + /* read the JEDEC data */ + err = jed_parse(*src, &jed); + src.reset(); + switch (err) + { + case JEDERR_INVALID_DATA: fprintf(stderr, "Fatal error: Invalid .JED file\n"); result = 1; goto end; + case JEDERR_BAD_XMIT_SUM: fprintf(stderr, "Fatal error: Bad transmission checksum\n"); result = 1; goto end; + case JEDERR_BAD_FUSE_SUM: fprintf(stderr, "Fatal error: Bad fusemap checksum\n"); result = 1; goto end; + } + } + else + { + /* read the binary data */ + err = jedbin_parse(*src, &jed); + src.reset(); + switch (err) + { + case JEDERR_INVALID_DATA: fprintf(stderr, "Fatal error: Invalid binary JEDEC file\n"); result = 1; goto end; + } + } + + for (index = 0; index < std::size(paldata); ++index) + { + if (paldata[index].numfuses == jed.numfuses) + { + printf("%s\n", paldata[index].name); + } + } + +end: + return result; +} + + + +/*------------------------------------------------- + main - primary entry point +-------------------------------------------------*/ + +int main(int argc, char *argv[]) +{ + command_entry command_entries[] = { + {"-convert", &command_convert}, + {"-view", &command_view}, + {"-viewlist", &command_viewlist}, + {"-listcompatible", &command_listcompatible}}; + int index; + + if (argc < 2) + { + return print_usage(); + } + + for (index = 0; index < std::size(command_entries); ++index) + { + if (!strcmp(argv[1], command_entries[index].command)) + return command_entries[index].command_func(argc - 2, &argv[2]); + } + + return print_usage(); +} diff --git a/src/tools/ldresample.cpp b/src/tools/ldresample.cpp new file mode 100644 index 0000000..f7c5fc8 --- /dev/null +++ b/src/tools/ldresample.cpp @@ -0,0 +1,575 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + ldresample.c + + Laserdisc audio synchronizer and resampler. + +****************************************************************************/ + +#include "avhuff.h" +#include "bitmap.h" +#include "chd.h" +#include "vbiparse.h" + +#include +#include +#include +#include +#include +#include + + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +// size of window where we scan ahead to find maximum; this should be large enough to +// catch peaks of even slow waves +const uint32_t MAXIMUM_WINDOW_SIZE = 40; + +// number of standard deviations away from silence that we consider a real signal +const uint32_t SIGNAL_DEVIATIONS = 100; + +// number of standard deviations away from silence that we consider the start of a signal +const uint32_t SIGNAL_START_DEVIATIONS = 5; + +// number of consecutive entries of signal before we consider that we found it +const uint32_t MINIMUM_SIGNAL_COUNT = 20; + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +struct movie_info +{ + double framerate; + int iframerate; + int numfields; + int width; + int height; + int samplerate; + int channels; + int interlaced; + bitmap_yuy16 bitmap; + std::vector lsound; + std::vector rsound; + uint32_t samples; +}; + + +// ======================> chd_resample_compressor + +class chd_resample_compressor : public chd_file_compressor +{ +public: + // construction/destruction + chd_resample_compressor(chd_file &source, movie_info &info, int64_t ioffset, int64_t islope) + : m_source(source), + m_info(info), + m_ioffset(ioffset), + m_islope(islope) { } + + // read interface + virtual uint32_t read_data(void *_dest, uint64_t offset, uint32_t length) + { + assert(offset % m_source.hunk_bytes() == 0); + assert(length % m_source.hunk_bytes() == 0); + + uint32_t startfield = offset / m_source.hunk_bytes(); + uint32_t endfield = startfield + length / m_source.hunk_bytes(); + uint8_t *dest = reinterpret_cast(_dest); + + for (uint32_t fieldnum = startfield; fieldnum < endfield; fieldnum++) + { + generate_one_frame(dest, m_source.hunk_bytes(), fieldnum); + dest += m_source.hunk_bytes(); + } + return length; + } + +private: + // internal helpers + void generate_one_frame(uint8_t *dest, uint32_t datasize, uint32_t fieldnum); + + // internal state + chd_file & m_source; + movie_info & m_info; + int64_t m_ioffset; + int64_t m_islope; +}; + + + +//************************************************************************** +// INLINE FUNCTIONS +//************************************************************************** + +//------------------------------------------------- +// field_to_sample_number - given a field number +// compute the absolute sample number for the +// first sample of that field +//------------------------------------------------- + +inline uint32_t field_to_sample_number(const movie_info &info, uint32_t field) +{ + return (uint64_t(info.samplerate) * uint64_t(field) * uint64_t(1000000) + info.iframerate - 1) / uint64_t(info.iframerate); +} + + +//------------------------------------------------- +// sample_number_to_field - given a sample number +// compute the field where it is located and +// the offset within the field +//------------------------------------------------- + +inline uint32_t sample_number_to_field(const movie_info &info, uint32_t samplenum, uint32_t &offset) +{ + uint32_t guess = (uint64_t(samplenum) * uint64_t(info.iframerate) + (uint64_t(info.samplerate) * uint64_t(1000000) - 1)) / (uint64_t(info.samplerate) * uint64_t(1000000)); + while (1) + { + uint32_t fieldstart = field_to_sample_number(info, guess); + uint32_t fieldend = field_to_sample_number(info, guess + 1); + if (samplenum >= fieldstart && samplenum < fieldend) + { + offset = samplenum - fieldstart; + return guess; + } + else if (samplenum < fieldstart) + guess--; + else + guess++; + } +} + + + +//************************************************************************** +// CHD HANDLING +//************************************************************************** + +//------------------------------------------------- +// open_chd - open a CHD file and return +// information about it +//------------------------------------------------- + +static std::error_condition open_chd(chd_file &file, const char *filename, movie_info &info) +{ + // open the file + std::error_condition chderr = file.open(filename); + if (chderr) + { + fprintf(stderr, "Error opening CHD file: %s\n", chderr.message().c_str()); + return chderr; + } + + // get the metadata + std::string metadata; + chderr = file.read_metadata(AV_METADATA_TAG, 0, metadata); + if (chderr) + { + fprintf(stderr, "Error getting A/V metadata: %s\n", chderr.message().c_str()); + return chderr; + } + + // extract the info + int fps, fpsfrac, width, height, interlaced, channels, rate; + if (sscanf(metadata.c_str(), AV_METADATA_FORMAT, &fps, &fpsfrac, &width, &height, &interlaced, &channels, &rate) != 7) + { + fprintf(stderr, "Improperly formatted metadata\n"); + return chd_file::error::INVALID_METADATA; + } + + // extract movie info + info.iframerate = fps * 1000000 + fpsfrac; + info.framerate = info.iframerate / 1000000.0; + info.numfields = file.hunk_count(); + info.width = width; + info.height = height; + info.interlaced = interlaced; + info.samplerate = rate; + info.channels = channels; + + // allocate buffers + info.bitmap.resize(info.width, info.height); + info.lsound.resize(info.samplerate); + info.rsound.resize(info.samplerate); + return std::error_condition(); +} + + +//------------------------------------------------- +// create_chd - create a new CHD file +//------------------------------------------------- + +static std::error_condition create_chd(chd_file_compressor &file, const char *filename, chd_file &source, const movie_info &info) +{ + // create the file + chd_codec_type compression[4] = { CHD_CODEC_AVHUFF }; + std::error_condition chderr = file.create(filename, source.logical_bytes(), source.hunk_bytes(), source.unit_bytes(), compression); + if (chderr) + { + fprintf(stderr, "Error creating new CHD file: %s\n", chderr.message().c_str()); + return chderr; + } + + // clone the metadata + chderr = file.clone_all_metadata(source); + if (chderr) + { + fprintf(stderr, "Error cloning metadata: %s\n", chderr.message().c_str()); + return chderr; + } + + // begin compressing + file.compress_begin(); + return std::error_condition(); +} + + +//------------------------------------------------- +// read_chd - read a field from a CHD file +//------------------------------------------------- + +static bool read_chd(chd_file &file, uint32_t field, movie_info &info, uint32_t soundoffs) +{ + // configure the codec + avhuff_decoder::config avconfig; + avconfig.video = &info.bitmap; + avconfig.maxsamples = info.lsound.size(); + avconfig.actsamples = &info.samples; + avconfig.audio[0] = &info.lsound[soundoffs]; + avconfig.audio[1] = &info.rsound[soundoffs]; + + // configure the decompressor for this field + file.codec_configure(CHD_CODEC_AVHUFF, AVHUFF_CODEC_DECOMPRESS_CONFIG, &avconfig); + + // read the field + std::error_condition chderr = file.read_hunk(field, nullptr); + return !chderr; +} + + + +//************************************************************************** +// CORE IMPLEMENTATION +//************************************************************************** + +//------------------------------------------------- +// find_edge_near_field - given a field number, +// load +/- 1/2 second on either side and find +// an audio edge +//------------------------------------------------- + +static bool find_edge_near_field(chd_file &srcfile, uint32_t fieldnum, movie_info &info, bool report_best_field, int32_t &delta) +{ + // clear the sound buffers + memset(&info.lsound[0], 0, info.lsound.size() * 2); + memset(&info.rsound[0], 0, info.rsound.size() * 2); + + // read 1 second around the target area + int fields_to_read = info.iframerate / 1000000; + int32_t firstfield = fieldnum - (fields_to_read / 2); + uint32_t targetsoundstart = 0; + uint32_t firstfieldend = 0; + uint32_t fieldstart[100]; + uint32_t soundend = 0; + for (int32_t curfield = 0; curfield < fields_to_read; curfield++) + { + // remember the start of each field + fieldstart[curfield] = soundend; + + // remember the sound offset where the initial fieldnum is + if (firstfield + curfield == fieldnum) + targetsoundstart = soundend; + + // read the frame and samples + if (firstfield + curfield >= 0) + { + read_chd(srcfile, firstfield + curfield, info, soundend); + soundend += info.samples; + + // also remember the offset at the end of the first field + if (firstfieldend == 0) + firstfieldend = soundend; + } + } + + // compute absolute deltas across the samples + for (uint32_t sampnum = 0; sampnum < soundend; sampnum++) + { + info.lsound[sampnum] = labs(info.lsound[sampnum + 1] - info.lsound[sampnum]); + info.rsound[sampnum] = labs(info.rsound[sampnum + 1] - info.rsound[sampnum]); + } + + // for each sample in the collection, find the highest deltas over the + // next few samples, and take the nth highest value (to remove outliers) + for (uint32_t sampnum = 0; sampnum < soundend - MAXIMUM_WINDOW_SIZE; sampnum++) + { + // scan forward over the maximum window + uint32_t lmax = 0, rmax = 0; + for (uint32_t scannum = 0; scannum < MAXIMUM_WINDOW_SIZE; scannum++) + { + if (info.lsound[sampnum + scannum] > lmax) + lmax = info.lsound[sampnum + scannum]; + if (info.rsound[sampnum + scannum] > rmax) + rmax = info.rsound[sampnum + scannum]; + } + + // replace this sample with the maximum value found + info.lsound[sampnum] = lmax; + info.rsound[sampnum] = rmax; + } + + // now compute the average over the first field, which is assumed to be silence + uint32_t firstlavg = 0; + uint32_t firstravg = 0; + for (uint32_t sampnum = 0; sampnum < firstfieldend; sampnum++) + { + firstlavg += info.lsound[sampnum]; + firstravg += info.rsound[sampnum]; + } + firstlavg /= firstfieldend; + firstravg /= firstfieldend; + + // then compute the standard deviation over the first field + uint32_t firstldev = 0; + uint32_t firstrdev = 0; + for (uint32_t sampnum = 0; sampnum < firstfieldend; sampnum++) + { + firstldev += (info.lsound[sampnum] - firstlavg) * (info.lsound[sampnum] - firstlavg); + firstrdev += (info.rsound[sampnum] - firstravg) * (info.rsound[sampnum] - firstravg); + } + firstldev = sqrt(double(firstldev) / firstfieldend); + firstrdev = sqrt(double(firstrdev) / firstfieldend); + + // scan forward through the samples, counting consecutive samples more than + // SIGNAL_DEVIATIONS standard deviations away from silence + uint32_t lcount = 0; + uint32_t rcount = 0; + uint32_t sampnum = 0; + for (sampnum = 0; sampnum < soundend; sampnum++) + { + // left speaker + if (info.lsound[sampnum] > firstlavg + SIGNAL_DEVIATIONS * firstldev) + lcount++; + else + lcount = 0; + + // right speaker + if (info.rsound[sampnum] > firstravg + SIGNAL_DEVIATIONS * firstrdev) + rcount++; + else + rcount = 0; + + // stop if we find enough + if (lcount > MINIMUM_SIGNAL_COUNT || rcount > MINIMUM_SIGNAL_COUNT) + break; + } + + // if we didn't find any, return failure + if (sampnum >= soundend) + { + if (!report_best_field) + printf("Field %5d: Unable to find edge\n", fieldnum); + return false; + } + + // scan backwards to find the start of the signal + for ( ; sampnum > 0; sampnum--) + if (info.lsound[sampnum - 1] < firstlavg + SIGNAL_START_DEVIATIONS * firstldev || + info.rsound[sampnum - 1] < firstravg + SIGNAL_START_DEVIATIONS * firstrdev) + break; + + // if we're to report the best field, figure out which field we are in + if (report_best_field) + { + int32_t curfield; + for (curfield = 0; curfield < fields_to_read - 1; curfield++) + if (sampnum < fieldstart[curfield + 1]) + break; + printf("Field %5d: Edge found at offset %d (frame %.1f)\n", firstfield + curfield, sampnum - fieldstart[curfield], (double)(firstfield + curfield) * 0.5); + } + + // otherwise, compute the delta from the provided field number + else + { + printf("Field %5d: Edge at offset %d from expected (found at %d, expected %d)\n", fieldnum, sampnum - targetsoundstart, sampnum, targetsoundstart); + delta = sampnum - targetsoundstart; + } + return true; +} + + +//------------------------------------------------- +// generate_one_frame - generate a single +// resampled frame +//------------------------------------------------- + +void chd_resample_compressor::generate_one_frame(uint8_t *dest, uint32_t datasize, uint32_t fieldnum) +{ + // determine the first field needed to cover this range of samples + uint32_t srcbegin = field_to_sample_number(m_info, fieldnum); + int64_t dstbegin = (int64_t(srcbegin) << 24) + m_ioffset + m_islope * fieldnum; + uint32_t dstbeginoffset; + int32_t dstbeginfield; + if (dstbegin >= 0) + dstbeginfield = sample_number_to_field(m_info, dstbegin >> 24, dstbeginoffset); + else + { + dstbeginfield = -1 - sample_number_to_field(m_info, -dstbegin >> 24, dstbeginoffset); + dstbeginoffset = (field_to_sample_number(m_info, -dstbeginfield) - field_to_sample_number(m_info, -dstbeginfield - 1)) - dstbeginoffset; + } + + // determine the last field needed to cover this range of samples + uint32_t srcend = field_to_sample_number(m_info, fieldnum + 1); + int64_t dstend = (int64_t(srcend) << 24) + m_ioffset + m_islope * (fieldnum + 1); + uint32_t dstendoffset; + int32_t dstendfield; + if (dstend >= 0) + dstendfield = sample_number_to_field(m_info, dstend >> 24, dstendoffset); + else + { + dstendfield = -1 - -sample_number_to_field(m_info, -dstend >> 24, dstendoffset); + dstendoffset = (field_to_sample_number(m_info, -dstendfield) - field_to_sample_number(m_info, -dstendfield - 1)) - dstendoffset; + } +/* +printf("%5d: start=%10d (%5d.%03d) end=%10d (%5d.%03d)\n", + fieldnum, + (int32_t)(dstbegin >> 24), dstbeginfield, dstbeginoffset, + (int32_t)(dstend >> 24), dstendfield, dstendoffset); +*/ + // read all samples required into the end of the sound buffers + uint32_t dstoffset = srcend - srcbegin; + for (int32_t dstfield = dstbeginfield; dstfield <= dstendfield; dstfield++) + { + if (dstfield >= 0) + read_chd(m_source, dstfield, m_info, dstoffset); + else + { + m_info.samples = field_to_sample_number(m_info, -dstfield) - field_to_sample_number(m_info, -dstfield - 1); + memset(&m_info.lsound[dstoffset], 0, m_info.samples * sizeof(m_info.lsound[0])); + memset(&m_info.rsound[dstoffset], 0, m_info.samples * sizeof(m_info.rsound[0])); + } + dstoffset += m_info.samples; + } + + // resample the destination samples to the source + dstoffset = srcend - srcbegin; + int64_t dstpos = dstbegin; + int64_t dststep = (dstend - dstbegin) / int64_t(srcend - srcbegin); + for (uint32_t srcoffset = 0; srcoffset < srcend - srcbegin; srcoffset++) + { + m_info.lsound[srcoffset] = m_info.lsound[(int)(dstoffset + dstbeginoffset + (dstpos >> 24) - (dstbegin >> 24))]; + m_info.rsound[srcoffset] = m_info.rsound[(int)(dstoffset + dstbeginoffset + (dstpos >> 24) - (dstbegin >> 24))]; + dstpos += dststep; + } + + // read the original frame, pointing the sound buffer past where we've calculated + read_chd(m_source, fieldnum, m_info, srcend - srcbegin); + + // assemble the final frame + std::vector buffer; + int16_t *sampledata[2] = { &m_info.lsound[0], &m_info.rsound[0] }; + avhuff_encoder::assemble_data(buffer, m_info.bitmap, m_info.channels, m_info.samples, sampledata); + memcpy(dest, &buffer[0], std::min(buffer.size(), size_t(datasize))); + if (buffer.size() < datasize) + memset(&dest[buffer.size()], 0, datasize - buffer.size()); +} + + +//------------------------------------------------- +// usage - display program usage +//------------------------------------------------- + +static int usage(void) +{ + fprintf(stderr, "Usage: \n"); + fprintf(stderr, " ldresample source.chd\n"); + fprintf(stderr, " ldresample source.chd output.chd offset [slope]\n"); + fprintf(stderr, "\n"); + fprintf(stderr, "Where offset and slope make a linear equation f(x) which\n"); + fprintf(stderr, "describes the sample offset from the source as a function\n"); + fprintf(stderr, "of field number.\n"); + return 1; +} + + +//------------------------------------------------- +// main - main entry point +//------------------------------------------------- + +int main(int argc, char *argv[]) +{ + // verify arguments + if (argc < 2) + return usage(); + const char *srcfilename = argv[1]; + const char *dstfilename = (argc < 3) ? nullptr : argv[2]; + double offset = (argc < 4) ? 0.0 : atof(argv[3]); + double slope = (argc < 5) ? 1.0 : atof(argv[4]); + + // print basic information + printf("Input file: %s\n", srcfilename); + if (dstfilename != nullptr) + { + printf("Output file: %s\n", dstfilename); + printf("Offset: %f\n", offset); + printf("Slope: %f\n", slope); + } + + // open the source file + chd_file srcfile; + movie_info info; + std::error_condition err = open_chd(srcfile, srcfilename, info); + if (err) + { + fprintf(stderr, "Unable to open file '%s'\n", srcfilename); + return 1; + } + + // output some basics + printf("Video dimensions: %dx%d\n", info.width, info.height); + printf("Video frame rate: %.2fHz\n", info.framerate); + printf("Sample rate: %dHz\n", info.samplerate); + printf("Total fields: %d\n", info.numfields); + + // if we don't have a destination file, scan for edges + if (dstfilename == nullptr) + { + for (uint32_t fieldnum = 60; fieldnum < info.numfields - 60; fieldnum += 30) + { + fprintf(stderr, "Field %5d\r", fieldnum); + int32_t delta; + find_edge_near_field(srcfile, fieldnum, info, true, delta); + } + } + + // otherwise, resample the source to the destination + else + { + // open the destination file + chd_resample_compressor dstfile(srcfile, info, int64_t(offset * 65536.0 * 256.0), int64_t(slope * 65536.0 * 256.0)); + err = create_chd(dstfile, dstfilename, srcfile, info); + if (!dstfile.opened()) + { + fprintf(stderr, "Unable to create file '%s'\n", dstfilename); + return 1; + } + + // loop over all the fields in the source file + double progress, ratio; + osd_ticks_t last_update = 0; + while (dstfile.compress_continue(progress, ratio) == chd_file::error::COMPRESSING) + if (osd_ticks() - last_update > osd_ticks_per_second() / 4) + { + last_update = osd_ticks(); + printf("Processing, %.1f%% complete....\r", progress * 100.0); + } + } + return 0; +} diff --git a/src/tools/ldverify.cpp b/src/tools/ldverify.cpp new file mode 100644 index 0000000..bb95c3f --- /dev/null +++ b/src/tools/ldverify.cpp @@ -0,0 +1,795 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + ldverify.c + + Laserdisc AVI/CHD verifier. + +****************************************************************************/ + +#include "avhuff.h" +#include "aviio.h" +#include "bitmap.h" +#include "chd.h" +#include "coretmpl.h" +#include "vbiparse.h" + +#include +#include +#include +#include +#include + + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + +const int REPORT_BLANKS_THRESHOLD = 50; + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +struct movie_info +{ + double framerate; + int numframes; + int width; + int height; + int samplerate; + int channels; +}; + +struct video_info +{ + int first_whitefield; + bool saw_leadin; + bool saw_leadout; + int last_frame; + int last_chapter; + int cadence; + uint32_t cadence_history; + int prev_whitefield; + int min_overall; + int max_overall; + int first_blank_frame; + int first_blank_field; + int num_blank_fields; + int first_low_frame; + int first_low_field; + int num_low_fields; + int first_high_frame; + int first_high_field; + int num_high_fields; +}; + +struct audio_info +{ + int min_lsample; + int min_rsample; + int max_lsample; + int max_rsample; + int min_lsample_count; + int min_rsample_count; + int max_lsample_count; + int max_rsample_count; + int sample_count; +}; + + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +static bool chdinterlaced; + + + +//************************************************************************** +// AVI HANDLING +//************************************************************************** + +//------------------------------------------------- +// open_avi - open an AVI file and return +// information about it +//------------------------------------------------- + +static void *open_avi(const char *filename, movie_info &info) +{ + // open the file + avi_file::ptr avi; + avi_file::error avierr = avi_file::open(filename, avi); + if (avierr != avi_file::error::NONE) + { + fprintf(stderr, "Error opening AVI file: %s\n", avi_file::error_string(avierr)); + return nullptr; + } + + // extract movie info + const avi_file::movie_info &aviinfo = avi->get_movie_info(); + info.framerate = (double)aviinfo.video_timescale / (double)aviinfo.video_sampletime; + info.numframes = aviinfo.video_numsamples; + info.width = aviinfo.video_width; + info.height = aviinfo.video_height; + info.samplerate = aviinfo.audio_samplerate; + info.channels = aviinfo.audio_channels; + return avi.release(); +} + + +//------------------------------------------------- +// read_avi - read a frame from an AVI file +//------------------------------------------------- + +static bool read_avi(void *file, int frame, bitmap_yuy16 &bitmap, int16_t *lsound, int16_t *rsound, int &samples) +{ + avi_file *avifile = reinterpret_cast(file); + + // read the frame + avi_file::error avierr = avifile->read_video_frame(frame, bitmap); + if (avierr != avi_file::error::NONE) + return false; + + // read the samples + const avi_file::movie_info &aviinfo = avifile->get_movie_info(); + uint32_t firstsample = (uint64_t(aviinfo.audio_samplerate) * uint64_t(frame) * uint64_t(aviinfo.video_sampletime) + aviinfo.video_timescale - 1) / uint64_t(aviinfo.video_timescale); + uint32_t lastsample = (uint64_t(aviinfo.audio_samplerate) * uint64_t(frame + 1) * uint64_t(aviinfo.video_sampletime) + aviinfo.video_timescale - 1) / uint64_t(aviinfo.video_timescale); + avierr = avifile->read_sound_samples(0, firstsample, lastsample - firstsample, lsound); + avierr = avifile->read_sound_samples(1, firstsample, lastsample - firstsample, rsound); + if (avierr != avi_file::error::NONE) + return false; + samples = lastsample - firstsample; + return true; +} + + +//------------------------------------------------- +// close_avi - close an AVI file +//------------------------------------------------- + +static void close_avi(void *file) +{ + avi_file *avifile = reinterpret_cast(file); + delete avifile; +} + + + +//************************************************************************** +// CHD HANDLING +//************************************************************************** + +//------------------------------------------------- +// open_chd - open a CHD file and return +// information about it +//------------------------------------------------- + +static void *open_chd(const char *filename, movie_info &info) +{ + auto chd = new chd_file; + + // open the file + std::error_condition chderr = chd->open(filename); + if (chderr) + { + fprintf(stderr, "Error opening CHD file: %s\n", chderr.message().c_str()); + delete chd; + return nullptr; + } + + // get the metadata + std::string metadata; + chderr = chd->read_metadata(AV_METADATA_TAG, 0, metadata); + if (chderr) + { + fprintf(stderr, "Error getting A/V metadata: %s\n", chderr.message().c_str()); + delete chd; + return nullptr; + } + + // extract the info + int fps, fpsfrac, width, height, interlaced, channels, rate; + if (sscanf(metadata.c_str(), AV_METADATA_FORMAT, &fps, &fpsfrac, &width, &height, &interlaced, &channels, &rate) != 7) + { + fprintf(stderr, "Improperly formatted metadata\n"); + delete chd; + return nullptr; + } + + // extract movie info + info.framerate = (fps * 1000000 + fpsfrac) / 1000000.0; + info.numframes = chd->hunk_count(); + info.width = width; + info.height = height; + info.samplerate = rate; + info.channels = channels; + + // convert to an interlaced frame + chdinterlaced = interlaced; + if (interlaced) + { + info.framerate /= 2; + info.numframes = (info.numframes + 1) / 2; + info.height *= 2; + } + return chd; +} + + +//------------------------------------------------- +// read_chd - read a frame from a CHD file +//------------------------------------------------- + +static int read_chd(void *file, int frame, bitmap_yuy16 &bitmap, int16_t *lsound, int16_t *rsound, int &samples) +{ + chd_file *chdfile = reinterpret_cast(file); + + // loop over fields + int interlace_factor = chdinterlaced ? 2 : 1; + samples = 0; + for (int fieldnum = 0; fieldnum < interlace_factor; fieldnum++) + { + // make a fake bitmap for this field + bitmap_yuy16 video; + video.wrap(&bitmap.pix(fieldnum), bitmap.width(), bitmap.height() / interlace_factor, bitmap.rowpixels() * interlace_factor); + avhuff_decoder::config avconfig; + avconfig.video = &video; + + // configure the codec + uint32_t numsamples; + avconfig.maxsamples = 48000; + avconfig.actsamples = &numsamples; + avconfig.audio[0] = &lsound[samples]; + avconfig.audio[1] = &rsound[samples]; + + // configure the decompressor for this frame + chdfile->codec_configure(CHD_CODEC_AVHUFF, AVHUFF_CODEC_DECOMPRESS_CONFIG, &avconfig); + + // read the frame + std::error_condition chderr = chdfile->read_hunk(frame * interlace_factor + fieldnum, nullptr); + if (chderr) + return false; + + // account for samples read + samples += numsamples; + } + return true; +} + + +//------------------------------------------------- +// close_chd - close a CHD file +//------------------------------------------------- + +static void close_chd(void *file) +{ + chd_file *chdfile = reinterpret_cast(file); + delete chdfile; +} + + + +//************************************************************************** +// CORE IMPLEMENTATION +//************************************************************************** + +//------------------------------------------------- +// init_video - init video info structure +//------------------------------------------------- + +static void init_video(video_info &video) +{ + video.first_whitefield = -1; + video.saw_leadin = false; + video.saw_leadout = false; + video.last_frame = -1; + video.last_chapter = -1; + video.cadence = -1; + video.cadence_history = 0; + video.prev_whitefield = -1; + video.min_overall = 255; + video.max_overall = 0; + video.first_blank_frame = -1; + video.first_blank_field = -1; + video.num_blank_fields = -1; + video.first_low_frame = -1; + video.first_low_field = -1; + video.num_low_fields = -1; + video.first_high_frame = -1; + video.first_high_field = -1; + video.num_high_fields = -1; +} + + +//------------------------------------------------- +// verify_video - verify video frame +//------------------------------------------------- + +static void verify_video(video_info &video, int frame, bitmap_yuy16 &bitmap) +{ + // loop over fields + const int fields_per_frame = 2; + for (int fieldnum = 0; fieldnum < fields_per_frame; fieldnum++) + { + // output status + if (frame % 10 == 0 && fieldnum == 0) + fprintf(stderr, "%6d.%d...\r", frame, fieldnum); + + // parse the VBI data + vbi_metadata metadata; + vbi_parse_all(&bitmap.pix(fieldnum), bitmap.rowpixels() * 2, bitmap.width(), 8, &metadata); + + // if we have data in both 17 and 18, it should match + if (metadata.line17 != 0 && metadata.line18 != 0 && metadata.line17 != metadata.line18) + { + printf("%6d.%d: line 17 and 18 data does not match (17=%06X 18=%06X) (WARNING)\n", frame, fieldnum, metadata.line17, metadata.line18); + printf("%6d.%d: selected %06X based on bit confidence\n", frame, fieldnum, metadata.line1718); + } + + // is this a lead-in code? + if (metadata.line1718 == VBI_CODE_LEADIN) + { + // if we haven't seen lead-in yet, detect it + if (!video.saw_leadin) + { + video.saw_leadin = true; + printf("%6d.%d: lead-in code detected\n", frame, fieldnum); + } + + // if we've previously seen chapters/frames, that's weird + if (video.last_frame != -1 || video.last_chapter != -1) + printf("%6d.%d: lead-in code detected after frame/chapter data (WARNING)\n", frame, fieldnum); + } + + // is this a lead-out code? + if (metadata.line1718 == VBI_CODE_LEADOUT) + { + // if we haven't seen lead-in yet, detect it + if (!video.saw_leadout) + { + video.saw_leadout = true; + printf("%6d.%d: lead-out code detected\n", frame, fieldnum); + if (video.last_frame != -1) + printf("%6d.%d: final frame number was %d\n", frame, fieldnum, video.last_frame); + else + printf("%6d.%d: never detected any frame numbers (ERROR)\n", frame, fieldnum); + } + + // if we've previously seen chapters/frames, that's weird + if (video.last_frame == -1) + printf("%6d.%d: lead-out code detected with no frames detected beforehand (WARNING)\n", frame, fieldnum); + } + + // is this a frame code? + if ((metadata.line1718 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE) + { + int framenum = VBI_CAV_PICTURE(metadata.line1718); + + // did we see any leadin? + if (!video.saw_leadin) + { + printf("%6d.%d: detected frame number but never saw any lead-in (WARNING)\n", frame, fieldnum); + video.saw_leadin = true; + } + + // if this is the first frame, make sure it's 1 + if (video.last_frame == -1) + { + if (framenum == 0) + printf("%6d.%d: detected frame 0\n", frame, fieldnum); + else if (framenum == 1) + printf("%6d.%d: detected frame 1\n", frame, fieldnum); + else + printf("%6d.%d: first frame number is not 0 or 1 (%d) (ERROR)\n", frame, fieldnum, framenum); + } + + // print an update every 10000 frames + if (framenum != 0 && framenum % 10000 == 0) + printf("%6d.%d: detected frame %d\n", frame, fieldnum, framenum); + + // if this frame is not consecutive, it's an error + if (video.last_frame != -1 && framenum != video.last_frame + 1) + printf("%6d.%d: gap in frame number sequence (%d->%d) (ERROR)\n", frame, fieldnum, video.last_frame, framenum); + + // remember the frame number + video.last_frame = framenum; + + // if we've seen a white flag before, but it's not here, warn + if (video.first_whitefield != -1 && !metadata.white) + printf("%6d.%d: detected frame number but no white flag (WARNING)\n", frame, fieldnum); + } + + // is the whiteflag set? + int field = frame * fields_per_frame + fieldnum; + if (metadata.white) + { + // if this is the first white flag we see, count it + if (video.first_whitefield == -1) + { + video.first_whitefield = field; + printf("%6d.%d: first white flag seen\n", frame, fieldnum); + } + + // if we've seen frame numbers before, but not here, warn + if (video.last_frame != -1 && (metadata.line1718 & VBI_MASK_CAV_PICTURE) != VBI_CODE_CAV_PICTURE) + printf("%6d.%d: detected white flag but no frame number (WARNING)\n", frame, fieldnum); + } + + // if this is the start of a frame, handle cadence + if (metadata.white || (metadata.line1718 & VBI_MASK_CAV_PICTURE) == VBI_CODE_CAV_PICTURE) + { + // if we've seen frames, but we're not yet to the lead-out, check the cadence + if (video.last_frame != -1 && !video.saw_leadout) + { + // make sure we have a proper history + if (video.prev_whitefield != -1) + video.cadence_history = (video.cadence_history << 4) | ((field - video.prev_whitefield) & 0x0f); + video.prev_whitefield = field; + + // if we don't know our cadence yet, determine it + if (video.cadence == -1 && (video.cadence_history & 0xf00) != 0) + { + if ((video.cadence_history & 0xfff) == 0x222) + { + printf("%6d.%d: detected 2:2 cadence\n", frame, fieldnum); + video.cadence = 4; + } + else if ((video.cadence_history & 0xfff) == 0x323) + { + printf("%6d.%d: detected 3:2 cadence\n", frame, fieldnum); + video.cadence = 5; + } + else if ((video.cadence_history & 0xfff) == 0x232) + { + printf("%6d.%d: detected 2:3 cadence\n", frame, fieldnum); + video.cadence = 5; + } + else + { + printf("%6d.%d: unknown cadence (history %d:%d:%d) (WARNING)\n", frame, fieldnum, + (video.cadence_history >> 8) & 15, (video.cadence_history >> 4) & 15, video.cadence_history & 15); + } + } + + // if we know our cadence, make sure we stick to it + if (video.cadence != -1) + { + if (video.cadence == 4 && (video.cadence_history & 0xfff) != 0x222) + { + printf("%6d.%d: missed cadence (history %d:%d:%d) (WARNING)\n", frame, fieldnum, + (video.cadence_history >> 8) & 15, (video.cadence_history >> 4) & 15, video.cadence_history & 15); + video.cadence = -1; + video.cadence_history = 0; + } + else if (video.cadence == 5 && (video.cadence_history & 0xfff) != 0x323 && (video.cadence_history & 0xfff) != 0x232) + { + printf("%6d.%d: missed cadence (history %d:%d:%d) (WARNING)\n", frame, fieldnum, + (video.cadence_history >> 8) & 15, (video.cadence_history >> 4) & 15, video.cadence_history & 15); + video.cadence = -1; + video.cadence_history = 0; + } + } + } + } + + // now examine the active video signal + uint32_t yhisto[256] = { 0 }; + uint32_t crhisto[256] = { 0 }; + uint32_t cbhisto[256] = { 0 }; + int pixels = 0; + for (int y = 22*2 + fieldnum; y < bitmap.height(); y += 2) + { + for (int x = 16; x < 720 - 16; x++) + { + yhisto[bitmap.pix(y, x) >> 8]++; + if (x % 2 == 0) + cbhisto[bitmap.pix(y, x) & 0xff]++; + else + crhisto[bitmap.pix(y, x) & 0xff]++; + } + pixels += 720 - 16 - 16; + } + + // remove the top/bottom 0.1% of Y + int remaining = pixels / 1000; + int yminval; + for (yminval = 0; (remaining -= yhisto[yminval]) >= 0; yminval++) ; + remaining = pixels / 1000; + int ymaxval; + for (ymaxval = 255; (remaining -= yhisto[ymaxval]) >= 0; ymaxval--) ; + + // remove the top/bottom 0.1% of Cb + remaining = pixels / 500; + int cbminval; + for (cbminval = 0; (remaining -= cbhisto[cbminval]) >= 0; cbminval++) ; + remaining = pixels / 500; + int cbmaxval; + for (cbmaxval = 255; (remaining -= cbhisto[cbmaxval]) >= 0; cbmaxval--) ; + + // remove the top/bottom 0.1% of Cr + remaining = pixels / 500; + int crminval; + for (crminval = 0; (remaining -= crhisto[crminval]) >= 0; crminval++) ; + remaining = pixels / 500; + int crmaxval; + for (crmaxval = 255; (remaining -= crhisto[crmaxval]) >= 0; crmaxval--) ; + + // track blank frames + if (ymaxval - yminval < 10 && cbmaxval - cbminval < 10 && crmaxval - cbmaxval < 10) + { + if (video.first_blank_frame == -1) + { + video.first_blank_frame = frame; + video.first_blank_field = fieldnum; + video.num_blank_fields = 0; + } + video.num_blank_fields++; + } + else if (video.num_blank_fields > 0) + { + if (video.num_blank_fields >= REPORT_BLANKS_THRESHOLD) + printf("%6d.%d-%6d.%d: blank frames for %d fields (INFO)\n", video.first_blank_frame, video.first_blank_field, frame, fieldnum, video.num_blank_fields); + video.first_blank_frame = video.first_blank_field = video.num_blank_fields = -1; + } + + // update the overall min/max + video.min_overall = std::min(yminval, video.min_overall); + video.max_overall = std::max(ymaxval, video.max_overall); + + // track low fields + if (yminval <= 0) + { + if (video.first_low_frame == -1) + { + video.first_low_frame = frame; + video.first_low_field = fieldnum; + video.num_low_fields = 0; + } + video.num_low_fields++; + } + else if (video.num_low_fields > 0) + { + printf("%6d.%d-%6d.%d: active video signal level low for %d fields (WARNING)\n", video.first_low_frame, video.first_low_field, frame, fieldnum, video.num_low_fields); + video.first_low_frame = video.first_low_field = video.num_low_fields = -1; + } + + // track high fields + if (ymaxval >= 255) + { + if (video.first_high_frame == -1) + { + video.first_high_frame = frame; + video.first_high_field = fieldnum; + video.num_high_fields = 0; + } + video.num_high_fields++; + } + else if (video.num_high_fields > 0) + { + printf("%6d.%d-%6d.%d: active video signal level high for %d fields (WARNING)\n", video.first_high_frame, video.first_high_field, frame, fieldnum, video.num_high_fields); + video.first_high_frame = video.first_high_field = video.num_high_fields = -1; + } + } +} + + +//------------------------------------------------- +// verify_video_final - final verification +//------------------------------------------------- + +static void verify_video_final(video_info &video, int frame, bitmap_yuy16 &bitmap) +{ + int fields_per_frame = (bitmap.height() >= 288) ? 2 : 1; + int field = frame * fields_per_frame; + + // did we ever see any white flags? + if (video.first_whitefield == -1) + printf("Track %6d.%d: never saw any white flags (WARNING)\n", field / fields_per_frame, 0); + + // did we ever see any lead-out? + if (!video.saw_leadout) + printf("Track %6d.%d: never saw any lead-out (WARNING)\n", field / fields_per_frame, 0); + + // any remaining high/low reports? + if (video.num_blank_fields >= REPORT_BLANKS_THRESHOLD) + printf("%6d.%d-%6d.%d: blank frames for %d fields (INFO)\n", video.first_blank_frame, video.first_blank_field, frame, 0, video.num_blank_fields); + if (video.num_low_fields > 0) + printf("%6d.%d-%6d.%d: active video signal level low for %d fields (WARNING)\n", video.first_low_frame, video.first_low_field, frame, 0, video.num_low_fields); + if (video.num_high_fields > 0) + printf("%6d.%d-%6d.%d: active video signal level high for %d fields (WARNING)\n", video.first_high_frame, video.first_high_field, frame, 0, video.num_high_fields); + + // summary info + printf("\nVideo summary:\n"); + printf(" Overall video range: %d-%d (%02X-%02X)\n", video.min_overall, video.max_overall, video.min_overall, video.max_overall); +} + + +//------------------------------------------------- +// init_audio - init audio info structure +//------------------------------------------------- + +static void init_audio(audio_info &audio) +{ + audio.min_lsample = 32767; + audio.min_rsample = 32767; + audio.max_lsample = -32768; + audio.max_rsample = -32768; + audio.min_lsample_count = 0; + audio.min_rsample_count = 0; + audio.max_lsample_count = 0; + audio.max_rsample_count = 0; + audio.sample_count = 0; +} + + +//------------------------------------------------- +// verify_audio - verify audio data +//------------------------------------------------- + +static void verify_audio(audio_info &audio, const int16_t *lsound, const int16_t *rsound, int samples) +{ + // count the overall samples + audio.sample_count += samples; + + // iterate over samples, tracking min/max + for (int sampnum = 0; sampnum < samples; sampnum++) + { + // did we hit a minimum on the left? + if (lsound[sampnum] < audio.min_lsample) + { + audio.min_lsample = lsound[sampnum]; + audio.min_lsample_count = 1; + } + else if (lsound[sampnum] == audio.min_lsample) + audio.min_lsample_count++; + + // did we hit a maximum on the left? + if (lsound[sampnum] > audio.max_lsample) + { + audio.max_lsample = lsound[sampnum]; + audio.max_lsample_count = 1; + } + else if (lsound[sampnum] == audio.max_lsample) + audio.max_lsample_count++; + + // did we hit a minimum on the right? + if (rsound[sampnum] < audio.min_rsample) + { + audio.min_rsample = rsound[sampnum]; + audio.min_rsample_count = 1; + } + else if (rsound[sampnum] == audio.min_rsample) + audio.min_rsample_count++; + + // did we hit a maximum on the right? + if (rsound[sampnum] > audio.max_rsample) + { + audio.max_rsample = rsound[sampnum]; + audio.max_rsample_count = 1; + } + else if (rsound[sampnum] == audio.max_rsample) + audio.max_rsample_count++; + } +} + + +//------------------------------------------------- +// verify_audio_final - final verification +//------------------------------------------------- + +static void verify_audio_final(audio_info &audio) +{ + printf("\nAudio summary:\n"); + printf(" Overall channel 0 range: %d-%d (%04X-%04X)\n", audio.min_lsample, audio.max_lsample, uint16_t(audio.min_lsample), uint16_t(audio.max_lsample)); + printf(" Overall channel 1 range: %d-%d (%04X-%04X)\n", audio.min_rsample, audio.max_rsample, uint16_t(audio.min_rsample), uint16_t(audio.max_rsample)); +} + + +//------------------------------------------------- +// usage - display program usage +//------------------------------------------------- + +static int usage(void) +{ + fprintf(stderr, "Usage: \n"); + fprintf(stderr, " ldverify \n"); + return 1; +} + + +//------------------------------------------------- +// main - main entry point +//------------------------------------------------- + +int main(int argc, char *argv[]) +{ + try + { + // init globals + audio_info audio; + init_audio(audio); + video_info video; + init_video(video); + + // verify arguments + if (argc < 2) + return usage(); + const char *srcfile = argv[1]; + + // check extension of file + int srcfilelen = strlen(srcfile); + if (srcfilelen < 4) + return usage(); + bool isavi; + if (tolower((uint8_t)srcfile[srcfilelen-3]) == 'a' && tolower((uint8_t)srcfile[srcfilelen-2]) == 'v' && tolower((uint8_t)srcfile[srcfilelen-1]) == 'i') + isavi = true; + else if (tolower((uint8_t)srcfile[srcfilelen-3]) == 'c' && tolower((uint8_t)srcfile[srcfilelen-2]) == 'h' && tolower((uint8_t)srcfile[srcfilelen-1]) == 'd') + isavi = false; + else + return usage(); + + // open the file + printf("Processing file: %s\n", srcfile); + movie_info info = { 0 }; + void *file = isavi ? open_avi(srcfile, info) : open_chd(srcfile, info); + if (file == nullptr) + { + fprintf(stderr, "Unable to open file '%s'\n", srcfile); + return 1; + } + + // comment on the video dimensions + printf("Video dimensions: %dx%d\n", info.width, info.height); + if (info.width != 720) + printf("WARNING: Unexpected video width (should be 720)\n"); + if (info.height != 524) + printf("WARNING: Unexpected video height (should be 262 or 524)\n"); + + // comment on the video frame rate + printf("Video frame rate: %.2fHz\n", info.framerate); + if (int(info.framerate * 100.0 + 0.5) != 2997) + printf("WARNING: Unexpected frame rate (should be 29.97Hz)\n"); + + // comment on the sample rate + printf("Sample rate: %dHz\n", info.samplerate); + if (info.samplerate != 48000) + printf("WARNING: Unexpected sampele rate (should be 48000Hz)\n"); + + // allocate a bitmap + bitmap_yuy16 bitmap(info.width, info.height); + + // allocate sound buffers + std::vector lsound(info.samplerate); + std::vector rsound(info.samplerate); + + // loop over frames + int frame = 0; + int samples = 0; + while (isavi ? read_avi(file, frame, bitmap, &lsound[0], &rsound[0], samples) : read_chd(file, frame, bitmap, &lsound[0], &rsound[0], samples)) + { + verify_video(video, frame, bitmap); + verify_audio(audio, &lsound[0], &rsound[0], samples); + frame++; + } + + // close the files + isavi ? close_avi(file) : close_chd(file); + + // final output + verify_video_final(video, frame, bitmap); + verify_audio_final(audio); + } + catch (std::bad_alloc &) + { + fprintf(stderr, "Out of memory allocating memory\n"); + return 1; + } + + return 0; +} diff --git a/src/tools/pngcmp.cpp b/src/tools/pngcmp.cpp new file mode 100644 index 0000000..480f9fa --- /dev/null +++ b/src/tools/pngcmp.cpp @@ -0,0 +1,200 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + pngcmp.c + + PNG comparison (based on regrep.c) + +****************************************************************************/ + +#include "corefile.h" +#include "png.h" + +#include "osdfile.h" + +#include +#include +#include +#include +#include +#include + + +/*************************************************************************** + CONSTANTS & DEFINES +***************************************************************************/ + +#define BITMAP_SPACE 4 + +/*************************************************************************** + PROTOTYPES +***************************************************************************/ + +static int generate_png_diff(const std::string& imgfile1, const std::string& imgfile2, const std::string& outfilename); + +/*************************************************************************** + MAIN +***************************************************************************/ + +/*------------------------------------------------- + main - main entry point +-------------------------------------------------*/ + +int main(int argc, char *argv[]) +{ + /* first argument is the directory */ + if (argc < 4) + { + fprintf(stderr, "Usage:\npngcmp \n"); + return 10; + } + std::string imgfilename1(argv[1]); + std::string imgfilename2(argv[2]); + std::string outfilename(argv[3]); + + try { + return generate_png_diff(imgfilename1, imgfilename2, outfilename); + } + catch(...) + { + printf("Exception occurred"); + return 1000; + } +} + +/*------------------------------------------------- + generate_png_diff - create a new PNG file + that shows multiple differing PNGs side by + side with a third set of differences +-------------------------------------------------*/ + +static int generate_png_diff(const std::string& imgfile1, const std::string& imgfile2, const std::string& outfilename) +{ + bitmap_argb32 bitmap1; + bitmap_argb32 bitmap2; + bitmap_argb32 finalbitmap; + int width, height, maxwidth; + util::core_file::ptr file; + std::error_condition filerr; + int error = 100; + + /* open the source image */ + filerr = util::core_file::open(imgfile1, OPEN_FLAG_READ, file); + if (filerr) + { + printf("Could not open %s (%s)\n", imgfile1.c_str(), filerr.message().c_str()); + goto error; + } + + /* load the source image */ + filerr = util::png_read_bitmap(*file, bitmap1); + file.reset(); + if (filerr) + { + printf("Could not read %s (%s)\n", imgfile1.c_str(), filerr.message().c_str()); + goto error; + } + + /* open the source image */ + filerr = util::core_file::open(imgfile2, OPEN_FLAG_READ, file); + if (filerr) + { + printf("Could not open %s (%s)\n", imgfile2.c_str(), filerr.message().c_str()); + goto error; + } + + /* load the source image */ + filerr = util::png_read_bitmap(*file, bitmap2); + file.reset(); + if (filerr) + { + printf("Could not read %s (%s)\n", imgfile2.c_str(), filerr.message().c_str()); + goto error; + } + + /* if the sizes are different, we differ; otherwise start off assuming we are the same */ + bool bitmaps_differ; + bitmaps_differ = (bitmap2.width() != bitmap1.width() || bitmap2.height() != bitmap1.height()); + + /* compare scanline by scanline */ + for (int y = 0; y < bitmap2.height() && !bitmaps_differ; y++) + { + uint32_t const *base = &bitmap1.pix(y); + uint32_t const *curr = &bitmap2.pix(y); + + /* scan the scanline */ + int x; + for (x = 0; x < bitmap2.width(); x++) + if (*base++ != *curr++) + break; + bitmaps_differ = (x != bitmap2.width()); + } + + if (bitmaps_differ) + { + /* determine the size of the final bitmap */ + height = width = 0; + { + /* determine the maximal width */ + maxwidth = std::max(bitmap1.width(), bitmap2.width()); + width = bitmap1.width() + BITMAP_SPACE + maxwidth + BITMAP_SPACE + maxwidth; + + /* add to the height */ + height += std::max(bitmap1.height(), bitmap2.height()); + } + + /* allocate the final bitmap */ + finalbitmap.allocate(width, height); + + /* now copy and compare each set of bitmaps */ + int curheight = std::max(bitmap1.height(), bitmap2.height()); + /* iterate over rows in these bitmaps */ + for (int y = 0; y < curheight; y++) + { + uint32_t const *src1 = (y < bitmap1.height()) ? &bitmap1.pix(y) : nullptr; + uint32_t const *src2 = (y < bitmap2.height()) ? &bitmap2.pix(y) : nullptr; + uint32_t *dst1 = &finalbitmap.pix(y); + uint32_t *dst2 = &finalbitmap.pix(y, bitmap1.width() + BITMAP_SPACE); + uint32_t *dstdiff = &finalbitmap.pix(y, bitmap1.width() + BITMAP_SPACE + maxwidth + BITMAP_SPACE); + + /* now iterate over columns */ + for (int x = 0; x < maxwidth; x++) + { + int pix1 = -1, pix2 = -2; + + if (src1 != nullptr && x < bitmap1.width()) + pix1 = dst1[x] = src1[x]; + if (src2 != nullptr && x < bitmap2.width()) + pix2 = dst2[x] = src2[x]; + dstdiff[x] = (pix1 != pix2) ? 0xffffffff : 0xff000000; + } + } + + /* write the final PNG */ + filerr = util::core_file::open(outfilename, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file); + if (filerr) + { + printf("Could not open %s (%s)\n", outfilename.c_str(), filerr.message().c_str()); + goto error; + } + filerr = util::png_write_bitmap(*file, nullptr, finalbitmap, 0, nullptr); + file.reset(); + if (filerr) + { + printf("Could not write %s (%s)\n", outfilename.c_str(), filerr.message().c_str()); + goto error; + } + } + + /* if we get here, we are error free */ + if (bitmaps_differ) + error = 1; + else + error = 0; + +error: + if (error == -1) + osd_file::remove(outfilename); + return error; +} diff --git a/src/tools/regrep.cpp b/src/tools/regrep.cpp new file mode 100644 index 0000000..b18b6b1 --- /dev/null +++ b/src/tools/regrep.cpp @@ -0,0 +1,1113 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + Regression test report generator + +****************************************************************************/ + +#include "corefile.h" +#include "corestr.h" +#include "png.h" + +#include "osdcomm.h" + +#include +#include +#include +#include +#include +#include + + +using util::string_format; + + +/*************************************************************************** + CONSTANTS & DEFINES +***************************************************************************/ + +#define MAX_COMPARES 16 +#define BITMAP_SPACE 4 + +enum +{ + STATUS_NOT_PRESENT = 0, + STATUS_SUCCESS, + STATUS_SUCCESS_DIFFERENT, + STATUS_MISSING_FILES, + STATUS_EXCEPTION, + STATUS_FATAL_ERROR, + STATUS_FAILED_VALIDITY, + STATUS_OTHER, + STATUS_COUNT +}; + +enum +{ + BUCKET_UNKNOWN = 0, + BUCKET_IMPROVED, + BUCKET_REGRESSED, + BUCKET_CHANGED, + BUCKET_MULTI_ERROR, + BUCKET_CONSISTENT_ERROR, + BUCKET_GOOD, + BUCKET_GOOD_BUT_CHANGED, + BUCKET_GOOD_BUT_CHANGED_SCREENSHOTS, + BUCKET_COUNT +}; + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +struct summary_file +{ + summary_file * next; + char name[20]; + char source[100]; + uint8_t status[MAX_COMPARES]; + uint8_t matchbitmap[MAX_COMPARES]; + std::string text[MAX_COMPARES]; +}; + + +struct summary_list +{ + summary_list * next; + summary_file * files; + char * dir; + char version[40]; +}; + + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +static summary_file *filehash[128][128]; +static summary_list lists[MAX_COMPARES]; +static int list_count; + +static const char *const bucket_name[] = +{ + "Unknown", + "Games That Have Improved", + "Games That Have Regressed", + "Games With Changed Screenshots", + "Games With Multiple Errors", + "Games With Consistent Errors", + "Games That Are Consistently Good", + "Games That Regressed But Improved", + "Games With Changed Screenshots", +}; + +static const int bucket_output_order[] = +{ + BUCKET_REGRESSED, + BUCKET_IMPROVED, + BUCKET_CHANGED, + BUCKET_GOOD_BUT_CHANGED_SCREENSHOTS, + BUCKET_GOOD_BUT_CHANGED, + BUCKET_MULTI_ERROR, + BUCKET_CONSISTENT_ERROR +}; + +static const char *const status_text[] = +{ + "", + "Success", + "Changed", + "Missing Files", + "Exception", + "Fatal Error", + "Failed Validity Check", + "Other Unknown Error" +}; + +static const char *const status_color[] = +{ + "", + "background:#00A000", + "background:#E0E000", + "background:#8000C0", + "background:#C00000", + "background:#C00000", + "background:#C06000", + "background:#C00000", + "background:#C00000", +}; + + + +/*************************************************************************** + PROTOTYPES +***************************************************************************/ + +/* summary parsing */ +static int read_summary_log(const char *filename, int index); +static summary_file *parse_driver_tag(char *linestart, int index); +static summary_file *get_file(const char *filename); +static int CLIB_DECL compare_file(const void *file0ptr, const void *file1ptr); +static summary_file *sort_file_list(void); + +/* HTML helpers */ +static util::core_file::ptr create_file_and_output_header(std::string &filename, std::string &templatefile, std::string &title); +static void output_footer_and_close_file(util::write_stream::ptr &&file, std::string &templatefile, std::string &title); + +/* report generators */ +static void output_report(std::string &dirname, std::string &tempheader, std::string &tempfooter, summary_file *filelist); +static int compare_screenshots(summary_file *curfile); +static int generate_png_diff(const summary_file *curfile, std::string &destdir, const char *destname); +static void create_linked_file(std::string &dirname, const summary_file *curfile, const summary_file *prevfile, const summary_file *nextfile, const char *pngfile, std::string &tempheader, std::string &tempfooter); +static void append_driver_list_table(const char *header, std::string &dirname, util::core_file &indexfile, const summary_file *listhead, std::string &tempheader, std::string &tempfooter); + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*------------------------------------------------- + trim_string - trim leading/trailing spaces + from a string +-------------------------------------------------*/ + +static inline char *trim_string(char *string) +{ + int length; + + /* trim leading spaces */ + while (*string != 0 && isspace((uint8_t)*string)) + string++; + + /* trim trailing spaces */ + length = strlen(string); + while (length > 0 && isspace((uint8_t)string[length - 1])) + string[--length] = 0; + + return string; +} + + +/*------------------------------------------------- + get_unique_index - get the unique bitmap + index for a given entry +-------------------------------------------------*/ + +static inline int get_unique_index(const summary_file *curfile, int index) +{ + int listnum, curindex = 0; + + /* if we're invalid, just return that */ + if (curfile->matchbitmap[index] == 0xff) + return -1; + + /* count unique elements up to us */ + for (listnum = 0; listnum < curfile->matchbitmap[index]; listnum++) + if (curfile->matchbitmap[listnum] == listnum) + curindex++; + return curindex; +} + + + +/*************************************************************************** + MAIN +***************************************************************************/ + +/*------------------------------------------------- + main - main entry point +-------------------------------------------------*/ + +int main(int argc, char *argv[]) +{ + uint32_t bufsize; + void *buffer; + int listnum; + int result; + + /* first argument is the directory */ + if (argc < 4) + { + fprintf(stderr, "Usage:\nregrep